PIL Bildvergleich klappt nicht

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Gisi
User
Beiträge: 15
Registriert: Sonntag 13. Mai 2007, 17:28

Hi,

ich moechte zwei Bilder vergleichen und bin zuerst auf das PIL Modul und anschliessend auf diese Funktion gestossen:

Code: Alles auswählen

import ImageChops
import math, operator

def rmsdiff(im1, im2):
    "Calculate the root-mean-square difference between two images"

    h = ImageChops.difference(im1, im2).histogram()

    # calculate rms
    return math.sqrt(reduce(operator.add,
        map(lambda h, i: h*(i**2), h, range(256))
    ) / (float(im1.size[0]) * im1.size[1]))
Siehe hier: http://effbot.org/zone/pil-comparing-images.htm

Wenn ich das entsprechend umsetze und das hier probiere:

Code: Alles auswählen

import ImageChops
import math, operator
import PIL.Image

def rmsdiff(im1, im2):
    "Calculate the root-mean-square difference between two images"

    h = ImageChops.difference(im1, im2).histogram()

    # calculate rms
    return math.sqrt(reduce(operator.add,
        map(lambda h, i: h*(i**2), h, range(256))
    ) / (float(im1.size[0]) * im1.size[1]))

im1 = PIL.Image.open(r"C:\Python25\bild1.jpg")
im2 = PIL.Image.open(r"C:\Python25\bild2.jpg")

rmsdiff(im1, im2)
erhalte ich aber folgende Fehlermeldung:

Traceback (most recent call last):
File "C:\Python25\test.py", line 18, in <module>
rmsdiff(im1, im2)
File "C:\Python25\test.py", line 12, in rmsdiff
map(lambda h, i: h*(i**2), h, range(256))
File "C:\Python25\test.py", line 12, in <lambda>
map(lambda h, i: h*(i**2), h, range(256))
TypeError: unsupported operand type(s) for ** or pow(): 'NoneType' and 'int'

Kann mir jemand erklaeren, was ich falsch mache?

Gisi
pudeldestodes
User
Beiträge: 65
Registriert: Samstag 9. Juni 2007, 23:45

Bin mir nicht ganz sicher, aber aus der Doku:
map(): [...] If one iterable is shorter than another it is assumed to be extended with None items. [...]

Offensichtlich haben h und range(256) unterschiedlich viele Elemente, so dass irgendwann aus dem einen iterable (wie heißt das auf Deutsch ;-) ) eine Zahl und aus dem anderen ein None-Element gezogen wird.
BlackJack

Der Fehler kommt zustande, wenn `h` mehr als 256 Elemente hat. `map()` füllt das kürzeren "iterable" nämlich mit `None`\s auf.
Gisi
User
Beiträge: 15
Registriert: Sonntag 13. Mai 2007, 17:28

Danke euch beiden. Hab das 256 durch len(h) ersetzt, damit funktioniert es.

/edit: Doch nicht. Ich erhalte jetzt zwar eine Zahl, aber selbst bei exakt identischen Bildern ist diese nicht 0, sondern 572.43340224.
pudeldestodes
User
Beiträge: 65
Registriert: Samstag 9. Juni 2007, 23:45

Naja, du berechnest da ja eigentlich einfach die Wurzel eines Quotienten aus irgendeiner Summe und dem Produkt der Bildgrößen? Damit das Null ergibt, muss also der Zähler des Quotienten (die Summe) gleich Null sein. Die einzelnen Summanden sind immer positiv (die histogram-Methode gibt ja eine Liste positiver Werte, wenn ich das richtig sehe), also muss, damit die Summe Null ergibt, jeder Summand gleich Null sein.

Kurz: Offensichtlich liefert dir ImageChops.difference(im1, im2).histogram() eine Liste, in der nicht nur Nullen stehen. Weiter kann ich dir aber leider nicht helfen, da ich mich zu wenig damit auskenne. Aber das von dir verlinkte Snippet ist mehr als 10 Jahre alt, vielleicht hat sich ja in der PIL etwas verändert.
Gisi
User
Beiträge: 15
Registriert: Sonntag 13. Mai 2007, 17:28

Richtig, wenn ich mir die Liste h ausgeben lasse, stehen da nicht nur Nullen drin. Ich hab testweise ein 2 mal 2 Pixel grosses Bild gebastelt, in dem nur der obere linke Pixel schwarz ist. Das hab ich unter bild1.jpg und bild2.jpg abgespeichert. Aber wie gesagt, trotzdem ist das Ergebnis nicht Null, und, wie du vermutet hast, steht in der 768 Elemente grossen Liste 3 mal die "4" drin.

Muss ich mich wohl weiter umschauen... Danke euch allen
Gisi
User
Beiträge: 15
Registriert: Sonntag 13. Mai 2007, 17:28

Ok, hab was, das funktioniert...

Code: Alles auswählen

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))
pudeldestodes
User
Beiträge: 65
Registriert: Samstag 9. Juni 2007, 23:45

Nur noch einmal zum ersten Snippet: histogram() kann ja auch gar keine Liste voller Nullen zurückliefern. Denn das Differenzbild ist einfach nur schwarz. Schwarz ist in RGB (0,0,0), demnach kommt eben der entsprechende Farbwert genau so oft vor, wie viele Pixel es gibt. Aber du hast ja schon eine Lösung, ich wollte nur nochmal laut nachdenken ;)
Antworten