Idée générale
Chargez les deux images sous forme de tableaux ( scipy.misc.imread
) et calculer une différence par élément. Calculez la norme de la différence.
Cependant, il y a des décisions à prendre.
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.
Si elles sont prises avec les mêmes paramètres et le même appareil, elles sont probablement identiques.
-
Les images sont-elles bien alignées ?
Si ce n'est pas le cas, vous pouvez d'abord effectuer une corrélation croisée pour trouver le meilleur alignement. SciPy dispose de fonctions pour le faire.
Si la caméra et la scène sont immobiles, les images sont susceptibles d'être bien alignées.
-
L'exposition des images est-elle toujours la même ? (La luminosité/contraste est-elle la même ?)
Si non, vous pouvez vouloir pour normaliser des 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 sur la couleur sont-elles importantes ?
Si vous voulez remarquer les changements de couleur, vous aurez un vecteur de valeurs de couleur par point, plutôt qu'une valeur scalaire comme dans une image en niveaux de gris. Vous devez faire plus attention en écrivant un tel code.
-
Y a-t-il des bords distincts dans l'image ? Sont-ils susceptibles de se déplacer ?
Si oui, vous pouvez d'abord appliquer un algorithme de détection des bords (par exemple, calculer le gradient avec la transformée de Sobel ou de Prewitt, appliquer un certain seuil), puis comparer les bords de la première image à ceux 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 pouvez 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 constater ?
Cela peut affecter le choix de la norme à utiliser pour la différence entre les images.
Envisagez d'utiliser la norme Manhattan (la somme des valeurs absolues) ou la norme zéro (le nombre d'éléments non égaux à zéro) pour mesurer l'ampleur du changement de l'image. La première vous dira de combien l'image est décalée, la seconde vous dira seulement combien de pixels diffèrent.
Exemple
Je suppose que vos images sont bien alignées, de même taille et de même forme, avec éventuellement une exposition différente. Pour simplifier, je les convertis en niveaux de gris même s'il s'agit d'images en 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, lecture de deux images, conversion en niveaux de gris, comparaison et impression des 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 SciPy 2D 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
retourne un tableau 3D, moyenne les canaux RVB (le dernier axe du tableau) pour obtenir l'intensité. Il n'est pas nécessaire de le faire pour les images en niveaux de gris (par ex. .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 se font par élément :
def normalize(arr):
rng = arr.max()-arr.min()
amin = arr.min()
return (arr-amin)*255/rng
Exécuter le main
fonction :
if __name__ == "__main__":
main()
Maintenant, vous pouvez mettre tout cela dans un script et l'exécuter contre deux images. Si on compare 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 certaine 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
P.S. Entire compare.py script.
Mise à jour : techniques pertinentes
Comme la question porte sur une séquence vidéo, où les images sont susceptibles d'être presque identiques, et que vous recherchez quelque chose d'inhabituel, je voudrais mentionner quelques approches alternatives qui peuvent être pertinentes :
- soustraction de l'arrière-plan
- flux optique clairsemé
Je vous recommande vivement de jeter un coup d'œil au livre "Learning OpenCV", chapitres 9 (Parties d'image et segmentation) et 10 (Suivi et mouvement). Le premier apprend à utiliser la méthode de soustraction d'arrière-plan, le second donne des 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 vous suggère d'utiliser OpenCV ≥ 2.3, et son cv2
Module Python.
La version la plus simple de la soustraction de l'arrière-plan :
- apprendre la valeur moyenne μ et l'écart-type σ pour chaque pixel de l'arrière-plan
- comparer les valeurs actuelles des pixels à la plage de (μ-2σ,μ+2σ) ou (μ-σ,μ+σ).
Les versions plus avancées prennent en compte les séries temporelles pour chaque pixel et traitent les scènes non statiques (comme les arbres ou l'herbe en mouvement).
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 le flux optique clairsemé, vous pouvez utiliser Méthode Lucas-Kanade (il est également implémenté dans OpenCV). Évidemment, s'il y a beaucoup de flux (valeurs moyennes et maximales élevées du champ de vitesse), alors quelque chose bouge dans l'image, et les images suivantes sont plus différentes.
0 votes
En rapport : stackoverflow.com/questions/25977/
0 votes
Deux approches quantitatives : 1) l'utilisation de SSIM pour la comparaison d'images de mêmes dimensions et 2) les représentations vectorielles denses pour les images à échelle invariante et indifférentes à la transformation. Les deux méthodes renvoient un score de similarité entre 0 et 100 et peuvent être utilisées pour déterminer les images dupliquées/identiques ou quasi-similaires. Voir aussi Détecter et visualiser les différences entre deux images