Idée générale
Option 1: chargez les deux images sous forme de tableaux (scipy.misc.imread
) et calculez une différence élément par élément (pixel par pixel). Calculez la norme de la différence.
Option 2: chargez les deux images. Calculez un vecteur de caractéristiques pour chacun d'eux (comme un histogramme). Calculez la distance entre les vecteurs d'entités plutôt que les images.
Cependant, certaines décisions doivent être prises en premier.
Des questions
Vous devez d'abord répondre à ces questions:
Les images ont-elles la même forme et la même dimension?
Sinon, vous devrez peut-être les redimensionner ou les recadrer. La bibliothèque PIL vous aidera à le faire en Python.
S'ils sont pris avec les mêmes paramètres et le même appareil, ils sont probablement les mêmes.
Les images sont-elles bien alignées?
Sinon, vous souhaiterez peut-être exécuter d'abord la corrélation croisée pour trouver le meilleur alignement en premier. SciPy a des fonctions pour le faire.
Si l'appareil photo et la scène sont fixes, les images sont susceptibles d'être bien alignées.
L'exposition des images est-elle toujours la même? (La luminosité / contraste est-il le même?)
Sinon, vous voudrez peut- être normaliser les images.
Mais attention, dans certaines situations, cela peut faire plus de mal que de bien. Par exemple, un seul pixel clair sur un fond sombre rendra l'image normalisée très différente.
Les informations de couleur sont-elles importantes?
Si vous souhaitez remarquer des changements de couleur, vous aurez un vecteur de valeurs de couleur par point, plutôt qu'une valeur scalaire comme dans l'image en niveaux de gris. Vous avez besoin de plus d'attention lors de l'écriture de ce code.
Y a-t-il des bords distincts dans l'image? Sont-ils susceptibles de déménager?
Si oui, vous pouvez d'abord appliquer l'algorithme de détection des contours (par exemple, calculer le gradient avec la transformée Sobel ou Prewitt, appliquer un certain seuil), puis comparer les contours de la première image aux contours de la seconde.
Y a-t-il du bruit dans l'image?
Tous les capteurs polluent l'image avec une certaine quantité de bruit. Les capteurs bon marché ont plus de bruit. Vous souhaiterez peut-être appliquer une réduction du bruit avant de comparer les images. Le flou est l'approche la plus simple (mais pas la meilleure) ici.
Quel genre de changements voulez-vous remarquer?
Cela peut affecter le choix de la norme à utiliser pour la différence entre les images.
Pensez à utiliser la norme de Manhattan (la somme des valeurs absolues) ou la norme zéro (le nombre d'éléments non égal à zéro) pour mesurer à quel point l'image a changé. Le premier vous dira à quel point l'image est décalée, le second ne dira que combien de pixels diffèrent.
Exemple
Je suppose que vos images sont bien alignées, de même taille et de même forme, éventuellement avec une exposition différente. Pour plus de simplicité, je les convertis en niveaux de gris même s'il s'agit d'images couleur (RVB).
Vous aurez besoin de ces importations:
import sys
from scipy.misc import imread
from scipy.linalg import norm
from scipy import sum, average
Fonction principale, lire deux images, convertir en niveaux de gris, comparer et imprimer les résultats:
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
Comment comparer. img1
et img2
sont des tableaux 2D SciPy ici:
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)
Si le fichier est une image couleur, imread
renvoie un tableau 3D, des canaux RVB moyens (le dernier axe du tableau) pour obtenir l'intensité. Pas besoin de le faire pour les images en niveaux de gris (par exemple .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 normalisation est triviale, vous pouvez choisir de normaliser à [0,1] au lieu de [0,255]. arr
est un tableau SciPy ici, donc toutes les opérations sont élémentaires:
def normalize(arr):
rng = arr.max()-arr.min()
amin = arr.min()
return (arr-amin)*255/rng
Exécutez la main
fonction:
if __name__ == "__main__":
main()
Vous pouvez maintenant mettre tout cela dans un script et exécuter deux images. Si nous comparons l'image à elle-même, il n'y a pas de différence:
$ python compare.py one.jpg one.jpg
Manhattan norm: 0.0 / per pixel: 0.0
Zero norm: 0 / per pixel: 0.0
Si nous floutons l'image et la comparons à l'original, il y a une différence:
$ python compare.py one.jpg one-blurred.jpg
Manhattan norm: 92605183.67 / per pixel: 13.4210411116
Zero norm: 6900000 / per pixel: 1.0
PS Script compare.py complet .
Mise à jour: techniques pertinentes
Comme la question concerne une séquence vidéo, où les images sont susceptibles d'être presque les mêmes et que vous recherchez quelque chose d'inhabituel, j'aimerais mentionner quelques approches alternatives qui peuvent être pertinentes:
- soustraction et segmentation d'arrière-plan (pour détecter les objets de premier plan)
- flux optique clairsemé (pour détecter le mouvement)
- comparer des histogrammes ou d'autres statistiques au lieu d'images
Je recommande fortement de jeter un œil au livre «Learning OpenCV», aux chapitres 9 (parties d'images et segmentation) et 10 (suivi et mouvement). Le premier enseigne à utiliser la méthode de soustraction d'arrière-plan, le second donne quelques informations sur les méthodes de flux optique. Toutes les méthodes sont implémentées dans la bibliothèque OpenCV. Si vous utilisez Python, je suggère d'utiliser OpenCV ≥ 2.3, et soncv2
module Python.
La version la plus simple de la soustraction d'arrière-plan:
- apprendre la valeur moyenne μ et l'écart type σ pour chaque pixel de l'arrière-plan
- comparer les valeurs de pixel actuelles à la plage de (μ-2σ, μ + 2σ) ou (μ-σ, μ + σ)
Des versions plus avancées prennent en compte des séries temporelles pour chaque pixel et gèrent des scènes non statiques (comme le déplacement d'arbres ou d'herbe).
L'idée du flux optique est de prendre deux images ou plus et d'attribuer un vecteur de vitesse à chaque pixel (flux optique dense) ou à certains d'entre eux (flux optique clairsemé). Pour estimer un flux optique clairsemé, vous pouvez utiliser la méthode Lucas-Kanade (elle est également implémentée dans OpenCV). Évidemment, s'il y a beaucoup de flux (moyenne élevée sur les valeurs maximales du champ de vitesse), alors quelque chose bouge dans le cadre et les images suivantes sont plus différentes.
La comparaison d'histogrammes peut aider à détecter des changements soudains entre des images consécutives. Cette approche a été utilisée dans Courbon et al, 2010 :
Similitude des images consécutives. La distance entre deux images consécutives est mesurée. S'il est trop élevé, cela signifie que la deuxième trame est corrompue et donc l'image est éliminée. La distance Kullback – Leibler , ou entropie mutuelle, sur les histogrammes des deux cadres:
où p et q sont les histogrammes des cadres est utilisé. Le seuil est fixé à 0,2.