Idea generale
Opzione 1: carica entrambe le immagini come array ( scipy.misc.imread
) e calcola una differenza di elemento (pixel per pixel). Calcola la norma della differenza.
Opzione 2: carica entrambe le immagini. Calcola un vettore caratteristica per ciascuno di essi (come un istogramma). Calcola la distanza tra i vettori delle caratteristiche anziché le immagini.
Tuttavia, ci sono alcune decisioni da prendere per prime.
Domande
Dovresti rispondere prima a queste domande:
Le immagini hanno la stessa forma e dimensione?
In caso contrario, potrebbe essere necessario ridimensionarli o ritagliarli. La libreria PIL aiuterà a farlo in Python.
Se sono presi con le stesse impostazioni e lo stesso dispositivo, probabilmente sono gli stessi.
Le immagini sono ben allineate?
In caso contrario, potresti voler eseguire prima la correlazione incrociata per trovare prima il miglior allineamento. SciPy ha funzioni per farlo.
Se la fotocamera e la scena sono ferme, è probabile che le immagini siano ben allineate.
L'esposizione delle immagini è sempre la stessa? (La luminosità / il contrasto sono uguali?)
In caso contrario, potresti voler normalizzare le immagini.
Ma attenzione, in alcune situazioni questo può fare più male che bene. Ad esempio, un singolo pixel luminoso su uno sfondo scuro renderà l'immagine normalizzata molto diversa.
Le informazioni sul colore sono importanti?
Se vuoi notare cambiamenti di colore, avrai un vettore di valori di colore per punto, piuttosto che un valore scalare come nell'immagine in scala di grigi. Hai bisogno di più attenzione quando scrivi questo codice.
Ci sono bordi distinti nell'immagine? È probabile che si muovano?
In caso affermativo, è possibile applicare prima l'algoritmo di rilevamento dei bordi (ad es. Calcolare il gradiente con trasformazione Sobel o Prewitt, applicare una soglia), quindi confrontare i bordi della prima immagine con i bordi della seconda.
C'è del rumore nell'immagine?
Tutti i sensori inquinano l'immagine con una certa quantità di rumore. I sensori a basso costo hanno più rumore. Potresti voler applicare una riduzione del rumore prima di confrontare le immagini. Blur è l'approccio più semplice (ma non il migliore) qui.
Che tipo di modifiche vuoi notare?
Ciò può influire sulla scelta della norma da utilizzare per la differenza tra le immagini.
Prendi in considerazione l'utilizzo della norma Manhattan (la somma dei valori assoluti) o della norma zero (il numero di elementi non uguale a zero) per misurare quanto l'immagine è cambiata. Il primo ti dirà quanto l'immagine è spenta, il secondo dirà solo quanti pixel differiscono.
Esempio
Presumo che le tue immagini siano ben allineate, stesse dimensioni e forma, possibilmente con diversa esposizione. Per semplicità, li converto in scala di grigi anche se sono immagini a colori (RGB).
Avrai bisogno di queste importazioni:
import sys
from scipy.misc import imread
from scipy.linalg import norm
from scipy import sum, average
Funzione principale, leggere due immagini, convertire in scala di grigi, confrontare e stampare i risultati:
def main():
file1, file2 = sys.argv[1:1+2]
# read images as 2D arrays (convert to grayscale for simplicity)
img1 = to_grayscale(imread(file1).astype(float))
img2 = to_grayscale(imread(file2).astype(float))
# compare
n_m, n_0 = compare_images(img1, img2)
print "Manhattan norm:", n_m, "/ per pixel:", n_m/img1.size
print "Zero norm:", n_0, "/ per pixel:", n_0*1.0/img1.size
Come confrontare. img1
e img2
sono array 2D SciPy qui:
def compare_images(img1, img2):
# normalize to compensate for exposure difference, this may be unnecessary
# consider disabling it
img1 = normalize(img1)
img2 = normalize(img2)
# calculate the difference and its norms
diff = img1 - img2 # elementwise for scipy arrays
m_norm = sum(abs(diff)) # Manhattan norm
z_norm = norm(diff.ravel(), 0) # Zero norm
return (m_norm, z_norm)
Se il file è un'immagine a colori, imread
restituisce un array 3D, canali RGB medi (l'ultimo asse dell'array) per ottenere intensità. Non è necessario farlo per le immagini in scala di grigi (ad es. .pgm
):
def to_grayscale(arr):
"If arr is a color image (3D array), convert it to grayscale (2D array)."
if len(arr.shape) == 3:
return average(arr, -1) # average over the last axis (color channels)
else:
return arr
La normalizzazione è banale, puoi scegliere di normalizzare a [0,1] invece di [0,255]. arr
è un array SciPy qui, quindi tutte le operazioni sono elementari:
def normalize(arr):
rng = arr.max()-arr.min()
amin = arr.min()
return (arr-amin)*255/rng
Esegui la main
funzione:
if __name__ == "__main__":
main()
Ora puoi mettere tutto in uno script ed eseguire due immagini. Se confrontiamo l'immagine con se stessa, non c'è differenza:
$ python compare.py one.jpg one.jpg
Manhattan norm: 0.0 / per pixel: 0.0
Zero norm: 0 / per pixel: 0.0
Se sfociamo l'immagine e confrontiamo l'originale, ci sono alcune differenze:
$ python compare.py one.jpg one-blurred.jpg
Manhattan norm: 92605183.67 / per pixel: 13.4210411116
Zero norm: 6900000 / per pixel: 1.0
PS Intero script compare.py .
Aggiornamento: tecniche pertinenti
Poiché la domanda riguarda una sequenza video, in cui i frame sono probabilmente gli stessi, e cerchi qualcosa di insolito, vorrei menzionare alcuni approcci alternativi che potrebbero essere rilevanti:
- sottrazione e segmentazione dello sfondo (per rilevare oggetti in primo piano)
- flusso ottico scarso (per rilevare il movimento)
- confrontando istogrammi o alcune altre statistiche invece di immagini
Consiglio vivamente di dare un'occhiata al libro "Learning OpenCV", Capitoli 9 (Parti di immagini e segmentazione) e 10 (Tracciamento e movimento). Il primo insegna a utilizzare il metodo di sottrazione di sfondo, il secondo fornisce alcune informazioni sui metodi di flusso ottico. Tutti i metodi sono implementati nella libreria OpenCV. Se usi Python, ti suggerisco di usare OpenCV ≥ 2.3 e il suo cv2
modulo Python.
La versione più semplice della sottrazione di sfondo:
- impara il valore medio μ e la deviazione standard σ per ogni pixel dello sfondo
- confrontare i valori correnti di pixel con l'intervallo di (μ-2σ, μ + 2σ) o (μ-σ, μ + σ)
Le versioni più avanzate tengono conto delle serie temporali per ogni pixel e gestiscono scene non statiche (come spostare alberi o erba).
L'idea del flusso ottico è di prendere due o più fotogrammi e assegnare il vettore di velocità a ogni pixel (flusso ottico denso) o ad alcuni di essi (flusso ottico rado). Per stimare il flusso ottico sparso, è possibile utilizzare il metodo Lucas-Kanade (implementato anche in OpenCV). Ovviamente, se c'è molto flusso (alta media su valori massimi del campo di velocità), allora qualcosa si sta muovendo nel frame e le immagini successive sono più diverse.
Il confronto degli istogrammi può aiutare a rilevare cambiamenti improvvisi tra fotogrammi consecutivi. Questo approccio è stato utilizzato in Courbon et al, 2010 :
Somiglianza di frame consecutivi. Viene misurata la distanza tra due fotogrammi consecutivi. Se è troppo alto, significa che il secondo fotogramma è danneggiato e quindi l'immagine viene eliminata. La distanza di Kullback-Leibler , o entropia reciproca, sugli istogrammi dei due fotogrammi:
dove p e q sono utilizzati gli istogrammi dei frame. La soglia è fissata su 0,2.