46 votes

Comparer deux images à la manière de python/linux

Essayer de résoudre un problème d'empêcher le téléchargement d'images en double.

J'ai deux fichiers JPG. En les regardant, je peux voir qu'ils sont en fait identiques. Mais pour une raison quelconque, ils ont une taille de fichier différente (l'un est tiré d'une sauvegarde, l'autre est un autre téléchargement) et donc ils ont une somme de contrôle md5 différente.

Comment puis-je comparer efficacement et en toute confiance deux images de la même manière qu'un humain serait capable de voir qu'elles sont clairement identiques ?

Exemple: http://static.peterbe.com/a.jpg et http://static.peterbe.com/b.jpg

Mise à jour

J'ai écrit ce script:

import math, operator
from PIL import Image
def compare(file1, file2):
    image1 = Image.open(file1)
    image2 = Image.open(file2)
    h1 = image1.histogram()
    h2 = image2.histogram()
    rms = math.sqrt(reduce(operator.add,
                           map(lambda a,b: (a-b)**2, h1, h2))/len(h1))
    return rms

if __name__=='__main__':
    import sys
    file1, file2 = sys.argv[1:]
    print compare(file1, file2)

Ensuite, j'ai téléchargé les deux images visuellement identiques et ai exécuté le script. Sortie:

58.9830484122

Est-ce que quelqu'un peut me dire quel devrait être le seuil approprié ?

Mise à jour II

La différence entre a.jpg et b.jpg est que le deuxième a été enregistré avec PIL:

b=Image.open('a.jpg')
b.save(open('b.jpg','wb'))

Cela applique apparemment quelques modifications de qualité très légères. J'ai maintenant résolu mon problème en appliquant le même enregistrement PIL au fichier en cours de téléchargement sans rien faire avec lui et maintenant cela fonctionne !

0 votes

Autant que je sache, aucun d'entre eux ne contient de données exif.

3 votes

réduire(opérateur.ajouter(...)) -> sum(...).

0 votes

Pour ce que cela vaut (principalement en référence à des informations de base), c'est comme une version simplifiée de cette question : stackoverflow.com/questions/1819124/algorithme-de-comparaison-d'images

29voto

AutomatedTester Points 14713

Il existe un projet OSS qui utilise WebDriver pour prendre des captures d'écran, puis compare les images pour voir s'il y a des problèmes (http://code.google.com/p/fighting-layout-bugs/)). Il le fait en ouvrant le fichier dans un flux, puis en comparant chaque bit.

Vous pouvez peut-être faire quelque chose de similaire avec PIL.

MODIFIER:

Après plus de recherches, j'ai trouvé

h1 = Image.open("image1").histogram()
h2 = Image.open("image2").histogram()

rms = math.sqrt(reduce(operator.add,
    map(lambda a,b: (a-b)**2, h1, h2))/len(h1))

sur http://snipplr.com/view/757/compare-two-pil-images-in-python/ and http://effbot.org/zone/pil-comparing-images.htm

0 votes

J'ai appliqué cette technique, mais pour les deux images que j'ai liées ci-dessus, le RMS est de 58.9. Si je compare a.jpg avec a.jpg, j'obtiens 0,0 comme prévu. J'en ai modifié une dans gimp en y dessinant quelque chose et le RMS est passé à 675.6.

16 votes

Ce code trouve l'erreur RMS entre les histogrammes des images, PAS entre les images elles-mêmes. Considérez que deux images pourraient avoir des histogrammes identiques mais être complètement différentes, par exemple si vous avez "brouillé" les pixels.

6 votes

Il convient de noter que si vous remplacez l'appel map + reduce par l'équivalent en compréhension de liste, vous obtiendrez un gain de vitesse très appréciable (environ 80% plus rapide sur mon Mac). Utilisez plutôt le code suivant (désolé pour la mise en forme médiocre) : diff_squares = [(h1[i] - h2[i]) ** 2 for i in xrange(len(h1))]; rms = math.sqrt(sum(diff_squares) / len(h1));. Cela concerne Python 2.7.3 et j'ai reproduit les résultats sur OS X et Linux. Votre expérience peut varier, mais la différence est suffisamment grande pour justifier une enquête.

25voto

Xolve Points 4422

De ici

La façon la plus rapide de déterminer si deux images ont exactement le même contenu est d'obtenir la différence entre les deux images, puis de calculer le rectangle englobant des régions non nulles dans cette image.

Si les images sont identiques, tous les pixels de l'image de différence sont nuls, et la fonction de rectangle englobant renvoie None.

from PIL import ImageChops

def equal(im1, im2):
    return ImageChops.difference(im1, im2).getbbox() is None

13voto

fortran Points 26495

Je suppose que vous devriez décoder les images et faire une comparaison pixel par pixel pour voir si elles sont raisonnablement similaires.

Avec PIL et Numpy, vous pouvez le faire assez facilement :

import Image
import numpy
import sys

def main():
    img1 = Image.open(sys.argv[1])
    img2 = Image.open(sys.argv[2])

    if img1.size != img2.size or img1.getbands() != img2.getbands():
        return -1

    s = 0
    for band_index, band in enumerate(img1.getbands()):
        m1 = numpy.array([p[band_index] for p in img1.getdata()]).reshape(*img1.size)
        m2 = numpy.array([p[band_index] for p in img2.getdata()]).reshape(*img2.size)
        s += numpy.sum(numpy.abs(m1-m2))
    print s

if __name__ == "__main__":
    sys.exit(main())

Cela vous donnera une valeur numérique qui devrait être très proche de 0 si les images sont assez similaires.

Notez que les images qui sont déplacées / tournées seront signalées comme très différentes, car les pixels ne correspondent pas un par un.

0 votes

Les histogrammes de couleur normalisés (dans la question de l'OP) ne sont pas affectés par les décalages/rotations. Vous pouvez réduire la sensibilité de votre algorithme aux petites rotations/décalages en sous-échantillonnant avant de différencier, par exemple en additionnant les pixels des boîtes 10x10 et en différenciant ces "pixels" 100 fois moins nombreux.

0 votes

Comment pouvez-vous comparer une image avec une image tournée qui semble identique? Pouvez-vous fournir un code d'exemple pour cela?

11voto

subiet Points 781

En utilisant ImageMagick, vous pouvez simplement utiliser dans votre shell [ou appeler via la bibliothèque OS à partir d'un programme]

compare image1 image2 output

Cela créera une image de sortie avec les différences marquées

compare -metric AE -fuzz 5% image1 image2 output

Vous donnera un facteur de tolérance de 5% pour ignorer les différences de pixels mineures. Plus d'informations peuvent être obtenues ici

4 votes

Y a-t-il un moyen de déterminer de manière programmable si la sortie résultante est vide ?

0 votes

Vous pouvez utiliser NULL: comme sortie. Il affichera alors la valeur sur stdout.

4voto

meduz Points 1119

Le problème de savoir ce qui rend certaines caractéristiques de l'image plus importantes que d'autres est tout un programme scientifique. Je proposerais quelques alternatives en fonction de la solution que vous souhaitez:

  • si votre problème est de voir s'il y a un basculement de bits dans vos JPEG, essayez d'imager l'image de différence (il y a peut-être eu une petite modification localement?),

  • pour voir si les images sont globalement les mêmes, utilisez la distance de Kullback Leibler pour comparer vos histogrammes,

  • pour voir si vous avez un changement qualitatif, avant d'appliquer d'autres réponses, filtrez votre image en utilisant les fonctions ci-dessous pour augmenter l'importance des fréquences de haut niveau:

code:

def FTfilter(image,FTfilter):
    from scipy.fftpack import fft2, fftshift, ifft2, ifftshift
    from scipy import real
    FTimage = fftshift(fft2(image)) * FTfilter
    return real(ifft2(ifftshift(FTimage)))
    #return real(ifft2(fft2(image)* FTfilter))

#### blanchiment
def olshausen_whitening_filt(size, f_0 = .78, alpha = 4., N = 0.01):
    """
    Renvoie le filtre de blanchiment utilisé par (Olshausen, 98)

    f_0 = 200 / 512

    /!\ vous aurez des problèmes en déblanchissant sans un passe-bas

    """
    from scipy import mgrid, absolute
    fx, fy = mgrid[-1:1:1j*size[0],-1:1:1j*size[1]]
    rho = numpy.sqrt(fx**2+fy**2)
    K_ols = (N**2 + rho**2)**.5 * low_pass(size, f_0 = f_0, alpha = alpha)
    K_ols /= numpy.max(K_ols)

    return  K_ols

def low_pass(size, f_0, alpha):
    """
    Renvoie le filtre passe-bas utilisé par (Olshausen, 98)

    paramètres provenant de Atick (p.240)
    f_0 = 22 c/d dans les primates : l'image complète est d'environ 45 deg
    alpha change l'aspect (1=diamant sur le vert et l'hor, 2 = anisotropique)

    """

    from scipy import mgrid, absolute
    fx, fy = mgrid[-1:1:1j*size[0],-1:1:1j*size[1]]
    rho = numpy.sqrt(fx**2+fy**2)
    low_pass = numpy.exp(-(rho/f_0)**alpha)

    return  low_pass

(copie éhontée depuis http://www.incm.cnrs-mrs.fr/LaurentPerrinet/Publications/Perrinet08spie )

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X