Optimierung beim Vergleichen von 2 Bildern

raimannma

Mitglied
Hallo,

Ich habe diesen Code hier:
Java:
private static double getDifferencePercent(BufferedImage img1, BufferedImage img2) {
        int width = img1.getWidth();
        int height = img1.getHeight();
        int width2 = img2.getWidth();
        int height2 = img2.getHeight();
        if (width != width2 || height != height2) {
            throw new IllegalArgumentException(String.format("Images must have the same dimensions: (%d,%d) vs. (%d,%d)", width, height, width2, height2));
        }
        long diff = 0;
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                diff += pixelDiff(img1.getRGB(x, y), img2.getRGB(x, y));
            }
        }
        long maxDiff = 3L * 255 * width * height;
        return 100.0 * diff / maxDiff;
    }
    private static int pixelDiff(int rgb1, int rgb2) {
        int r1 = (rgb1 >> 16) & 0xff;
        int g1 = (rgb1 >>  8) & 0xff;
        int b1 =  rgb1        & 0xff;
        int r2 = (rgb2 >> 16) & 0xff;
        int g2 = (rgb2 >>  8) & 0xff;
        int b2 =  rgb2        & 0xff;
        return Math.abs(r1 - r2) + Math.abs(g1 - g2) + Math.abs(b1 - b2);
    }

Und der funktioniert auch so ohne Problem, wenn ich also
Code:
getDifferencePercent(img1,img2)
aufrufe bekomme ich eine Prozentzahl, die mir angibt wie unterschiedlich die Bilder sind.

Allerdings sollte das mehrere Male pro Sekunde aufgerufen werden.

Mein gesamtes Programm habe ich mit VisualVM (Profiler) analysiert.
Und dabei festgestellt, dass genau diese Methode
Code:
getDifferencePercent()
für lange Ladezeiten sorgt.

Deshalb ist meine Frage, ob und wie es möglich ist diesen Code zu optimieren.

Info:
Die Bilder die verglichen werden sind nicht in Farbe sondern nur Grau, vielleicht kann man damit etwas machen.
 
Du kannst dir den raw buffer holen mit

Imagebuffer.getRaster().getDataBuffer().getData()

Dann kannst du direkt die Pixel 1:1 vergleichen und sparst dir den ganzen RGB Blödsinn.

Gruß

Claus
 
R G und B müssten bei graustufen den selben wert haben. du könntest also 2 vergleiche sparen, bzw bei den raw pixeln werte überspringeb
 
Vielleicht ist es noch interessant zu wissen woher er das hat:
https://www.java-forum.org/thema/hs...plikator-ermitteln.181852/page-2#post-1156599

Gefragt wurde ich natürlich nicht danach.
Besser als Klauen, ein Plagiat verstehst Du?

Du glaubst doch nicht wirklich, dass du er einzige bist, der so einen einfachen Vergleiche zwischen zwei Pixeln hinbekommt? 😉

Wäre nett, nicht gleich mit so haltlosen Unterstellungen zu kommen...
 
Wenn es wirklich nur für GRaustufen gehen muss ja. Frage ist warum du überhaupt einen RGB Buffer bekommst/nimmst wenn es nur ein Graustufenbild ist. Da würde ich eigentlich anfangen zu optimieren. Wenn Du nur ein Graustufenbild einlist hast du 1/3 weniger Daten die du laden und vergleichen musst. Das würde deutlich die Performance steigern.
 
Glauben ist so eine Sache, ich würd ihn für die zukunft raten, NICHT weiter Sachen abzuschreiben. Aber das muss er wissen, welchen Preis das dann hat.
Und dir würde ich raten, solche haltlosen Unterstellungen sein zu lassen 😉
Abgesehen davon ist der Code auch noch so unglaublich einfach, dass das vermutlich selbst wenn niemand ernsthaft als abschreiben bezeichnen würde... (und wenn doch: Die Worte "Glauben ist so eine Sache" hab ich auch schon mal gesagt, du solltest sowas nicht klauen 😉 )
 
und wenn doch: Die Worte "Glauben ist so eine Sache" hab ich auch schon mal gesagt,
ja es kommt dann darauf an, wer als erstes wann was gesagt hat.

solche haltlosen Unterstellungen
Ich würd mir ja auch für ihn wünschen das ich unrecht behalte. Allerdings ist ein Pessimist Optimist mit Erfahrung und realistisch betrachtet wird das wohl nicht konsequenzfrei bleiben.

Aber was solls wiss. Arbeiten liegt nicht jedem.

ist der Code auch noch so unglaublich einfach
So trivial ist es nicht, aber naja das ist deine meinung, die zählt nicht viel.
 
1. Wenn du vermutest, daß das Bild in weiten Teilen ohnehin gleich ist, könntest du die Pixel zunächst auf Gleichheit prüfen, bevor du das Rechnen anfängst.

2. Du mußt irgendwo Abstriche machen bei der Qualität der Aussage. Also zB nur jeden zehnten Pixel überprüfen.

3. Klar, zwei von drei Farben weglassen bringt auch was. Der größte Zeitfresser ist aber eben, daß du wirklich jeden Pixel ankucken willst.

4. Du verwendest nicht ImageBuffer, sondern DataBufferByte

Vielleicht so:

Je nachdem, wie du SKIP einstellst, prüft er wirklich jede Farbe, nur die Graustufen oder eben noch seltener.

final int SKIP = 1 //echt jeden einzelnen Pixel
final int SKIP = 3 //nur Graustufen
final int SKIP = 12 //Graustufen und nur jeden vierten Pixel

Java:
    private static double getDifferencePercent(BufferedImage img1, BufferedImage img2) {
        final int SKIP = 1;

        byte[] b1 = ((DataBufferByte) img1.getRaster().getDataBuffer()).getData();
        byte[] b2 = ((DataBufferByte) img2.getRaster().getDataBuffer()).getData();
  
        int size = b1.length;
  
        if (img1.getWidth() != img2.getWidth() || img1.getHeight() != img2.getHeight()) {
            throw new IllegalArgumentException(String.format("Images must have the same dimensions: (%d,%d) vs. (%d,%d)", img1.getWidth(), img1.getHeight(), img2.getWidth(), img2.getHeight()));
        }
        long diff = 0;
        int p1=0,p2=0;
  
        for (int p = 0;p < size; p+=SKIP) {
            p1 = ((int) b1[p] & 0xff);
            p2 = ((int) b2[p] & 0xff);
            diff += Math.abs(p1 - p2);
        }

        long maxDiff = 255 * size;
        return 100.0 * diff / maxDiff;
    }

Bei einem Bild mit 901x596 braucht diese Lösung mit SKIP=1 etwa 3,5% der Zeit, mit SKIP=3 sinds noch 1,7% und mit SKIP=12 sinds 0,8%
 
Zuletzt bearbeitet:
Hallo,

ich verstehe nicht wozu man die Abweichung in % zu einem Bild benötigt welches sich nur auf Pixelebene bewegt.
Weil ich gedacht habe - ich denke nicht richtig - habe ich zwei unterschiedliche Bilder eingeladen. Beide Bilder haben wirklich nichts miteinander zu tun und haben dennoch eine Ähnlichkeit von über 41%. Was sagt mir jetzt diese Zahl?
Dann habe ich eines der Bilder verkleinert und künstlich mit einem schwarzen Rand wieder auf die Größe gebracht.
Diese Bilder haben optisch eine sehr hohe Änlichkeit - aber lt. Programm gerade einmal 35%.

Wenn es Dir nur darum geht - die Anzahl der RGB's zu vergleichen - dann laufe durch jedes Bild genau einmal und speichere diese Werte ab und vergleiche beim zweiten Aufruf nur noch diese!!

Das wäre mein Vorschlag um das zu optimieren.

Gruß
Uwe
 
Du addierst die einzelnen Werte und hast danach eine Summe.
Dann vergleichst Du R mit R / G mit G und B mit B.
Hat den Vorteil dass Du halt das Bild nicht immer neu scannen musst - denn die Informationen liegen ja schon vor.
 
Da bräuchten wir dann wohl mal die beiden Beispielbilder, um dir sagen zu könne, warum die so gleich (noch nichtmal ähnlich, sondern gleich) sind. Immerhin vergleichen wir da recht exakte Werte und noch nichtmal +-x zum RGB-Wert.

Aus der Summe läßt sich aber doch keine Ähnlichkeit ableiten? Nimm ein Bild 1 Pixel hoch und 200 Pixel breit. Mach die ersten 100 Pixel schwarz und die zweiten 100 machst du weiß. Die Summe ist die gleiche wie wenn du das umgekehrt hast, nämlich die ersten 100 weiß und die letzten 100 schwarz. Die Bilder sind sich aber nicht grade ähnlich.

Oder möchtest du die beiden Arrays in eines summieren, also pixelErstesBild[1] + pixelZweitesBild[1] =pixelSumme[1], und dieses Array dann durchkucken??

Ansonsten kann ich mir das grad schwer vorstellen.
 

Zurück
Oben