PNG-Bilder auf Gleichheit prüfen



  • Hallo zusammen,
    ich habe eine Frage zum Vergleich zweier Bilder im PNG Format. Es soll lediglich überprüft werden ob sich die Bilder unterscheiden um somit zu erkennen, ob nachträglich jemand mit einem Grafikeditor herumgepinselt hat.
    image1 ist die Originaldatei und image2 die die abgeglichen werden soll ob sie sich vom Original unterscheidet. Unten gezeigte Funktion liefert immer true zurück auch wenn ich zwei mal den gleichen Pfad auf ein und dieselbe Datei angebe. Muss ich hier evtl. noch eine Stufe weiter gehen und die Zeile die mir Scanline zurückliefert in ein spezielles Pixel/Bitmap Format casten/konvertieren um vergleichen zu können?

    bool status = false;
    	 TPngImage *image1 = new TPngImage;
    	 TPngImage *image2 = new TPngImage;
    
    	 image1->LoadFromFile(path1);
    	 image2->LoadFromFile(path2);
    
    	 if(image1->Height == image2->Height)
    	 {
    		  for(int i = 0; i < image1->Height; i++)
    		  {
    				 if(image1->Scanline[i] != image2->Scanline[i])
    				{
    				  status = true;
    				  break;
    				}
    		  }
    	 }
    
    	 delete image1;
    	 delete image2;
    	 return status;
    


  • Dein Status liefert deshalb immer true, weil du nicht den Inhalt der jeweiligen Scanline[i] vergleichst, sondern nur die Adressen, auf die Scanline[i] zeigt. Und die sind bei zwei unterschiedlichen Image Objekten auch immer unterschiedlich.

    http://docwiki.embarcadero.com/Libraries/Seattle/de/Vcl.Graphics.TBitmap.ScanLine



  • Vielen Dank für den Input! Die Hilfe hat mir sehr geholfen. Das Vorhaben funktioniert nun

    Edit: Evtl. wäre hier ein "Oder" Vergleich bei den RGB Werten zielführender...

    bool status = false;
    	 TRGBTriple *ptr1;
    	 TRGBTriple *ptr2;
    
    	 TPngImage *image1 = new TPngImage;
    	 TPngImage *image2 = new TPngImage;
    
    	 image1->LoadFromFile(path1);
    	 image2->LoadFromFile(path2);
    
    	 if(image1->Height == image2->Height && image1->Width == image2->Width)
    	 {
    		  for(int i = 0; i < image1->Height; i++)
    		  {
    				 ptr1 = reinterpret_cast <TRGBTriple *> (image1->Scanline[i]);
    				 ptr2 = reinterpret_cast <TRGBTriple *> (image2->Scanline[i]);
    
    				 for(int z = 0; z < image1->Width; z++)
    			   {
    					 if(ptr1[z].rgbtBlue != ptr2[z].rgbtBlue && ptr1[z].rgbtGreen != ptr2[z].rgbtGreen &&
    						ptr1[z].rgbtRed != ptr2[z].rgbtRed)
    					 {
    					   status = true;
    					   break;
    					 }
    			   }
    		  }
    	 }
    
    	 delete image1;
    	 delete image2;
    	 return status;
    


  • Benutz am besten noch smartpointer, dann kannst du sogar direkt return true in die Schleife schreiben.



  • Stimmt, Danke. Ausserdem ist das break auch weniger zielführend da es nur die innere Schleife unterbricht. Muss das alles noch ein wenig feinschleifen



  • Zero01 schrieb:

    Muss das alles noch ein wenig feinschleifen

    Bei großen Bildern dürfte das eh quälend langsam sein... Ich würde den Inhalt der Scanlines direkt vergleichen, ohne den Umweg über RGBTriple zu gehen. Möglicherweise kann man auch direkt den Inhalt der Images selbst vergleichen. Wenn zwei Mal das gleiche Bild geladen wird, dürften der komplette Inhalt der Images gleich sein. Ich würde es zumindest mal ausprobieren.



  • Kannst du nicht einfach die Dateien vergleichen? Blockweise in einen Puffer lesen und per memcmp vergleichen dürfte doch ausreichen.



  • @MillionVoices: Wie würdest du Scanline direkt verwenden? RGBTriple hat mir von meiner Vorstellung des ganzen her sehr in die Karten gespielt. Hier müsste ich doch Anhand des Pointers der mir Scanline liefert doch wieder zu irgendetwas casten oder in eine struct stopfen, oder?

    @Doc: Klar, das geht auch aber irgend etwas sagt mir das diese Anforderung nicht die einzige bleiben wird und somit schonmal mit den RGB Werten erste Erfahrungen sammeln kann. Von der Performance her wären beide Bilder per File- oder Memorystream mit direktem Vergleich sicher besser.



  • Nö, ich würde da ebenfalls ganz simpel mit memcmp dran gehen. Die Größe der Scanline sollte sich errechnen lassen. Bei 24-Bit wären das dann 3 Byte pro Pixel.


Anmelden zum Antworten