2012-06-12 24 views
12

Pracuję nad częścią aplikacji Java, która pobiera obraz jako tablicę bajtów, odczytuje go w instancji java.awt.image.BufferedImage i przekazuje do innej biblioteki w celu przetworzenia.Czy istnieje prosty sposób porównywania wystąpień BufferedImage?

Dla testu jednostkowego chcę zrobić zdjęcie (z pliku na dysku) i stwierdzić, że jest ono równe temu samemu obrazowi, który został przetworzony przez kod.

  • My spodziewaćBufferedImage jest czytany z pliku PNG na dysku przy użyciu ImageIO.read(URL).
  • Mój kod test odczytuje ten sam plik w postaci BufferedImage i zapisuje go do tablicy bajtów jako PNG w celu dostarczenia testowanemu systemowi.

Gdy system badanego pisze tablicę bajtów do nowego BufferedImage chcę twierdzić, że oba obrazy są równe w znaczący sposób. Używanie equals() (odziedziczonej po Object) nie działa (oczywiście). Porównywanie wartości BufferedImage.toString() również nie działa, ponieważ ciąg wyjściowy zawiera informacje o obiekcie.

Czy ktoś zna jakieś skróty? Wolałbym nie udostępniać biblioteki innej firmy do testu pojedynczej jednostki w małej części dużej aplikacji.

+0

Czy możesz wyjaśnić, dlaczego dokładnie '.equals()' nie będzie działać? –

+1

@JakeKing: jeśli jest dziedziczone z Object, nie będzie działać, ponieważ tylko tożsamość obiektu. – Thilo

+2

Nie możesz po prostu porównać tablic bajtów (które zawierają PNG)? – Thilo

Odpowiedz

13

To najlepsze podejście. Nie trzeba utrzymywać zmiennej, aby stwierdzić, czy obraz jest nadal równy. Po prostu zwróć false natychmiast, gdy warunek jest fałszywy.Ocena zwarcia pomaga zaoszczędzić czas pętli nad pikselami po tym, jak porównanie nie powiedzie się, tak jak w przypadku parametru trumpetlick: answer.

/** 
* Compares two images pixel by pixel. 
* 
* @param imgA the first image. 
* @param imgB the second image. 
* @return whether the images are both the same or not. 
*/ 
public static boolean compareImages(BufferedImage imgA, BufferedImage imgB) { 
    // The images must be the same size. 
    if (imgA.getWidth() == imgB.getWidth() && imgA.getHeight() == imgB.getHeight()) { 
    int width = imgA.getWidth(); 
    int height = imgA.getHeight(); 

    // Loop over every pixel. 
    for (int y = 0; y < height; y++) { 
     for (int x = 0; x < width; x++) { 
     // Compare the pixels for equality. 
     if (imgA.getRGB(x, y) != imgB.getRGB(x, y)) { 
      return false; 
     } 
     } 
    } 
    } else { 
    return false; 
    } 

    return true; 
} 
+0

ładne i proste rozwiązanie! Dokładnie to, czego szukałem! – Waylander

3

Możesz napisać własną procedurę porównywania!

int width; 
int height; 
boolean imagesEqual = true; 

if(image1.getWidth() == (width = image2.getWidth()) && 
    image1.getHeight() == (height = image2.getHeight())){ 

    for(int x = 0;imagesEqual == true && x < width; x++){ 
     for(int y = 0;imagesEqual == true && y < height; y++){ 
      if(image1.getRGB(x, y) != image2.getRGB(x, y)){ 
       imagesEqual = false; 
      } 
     } 
    } 
}else{ 
    imagesEqual = false; 
} 

To byłby jeden sposób !!!

+2

musi również zawierać wartość false, jeśli rozmiary się nie zgadzają. Ustaw wartość logiczną na true tylko wewnątrz bloku 'if'. – Thilo

+0

Great Point, Ive edytowane! – trumpetlicks

+1

@trumpetlicks Również, że 'break' nie zrobi wiele, masz zagnieżdżone pętle' for'. – Jeffrey

0

nie mogę myśleć o niczym oprócz brute force „zrobić pętlę”:

BufferedImage bi1, bi2, ... 
    ... 
    Raster r1 = bi1.getData(); 
    DataBuffer db1 = r1.getDataBuffer(); 
    if (db1.getSize() != db2.getSize()) 
    ... 
    for (int i = 0; i < db1.getSize(); i++) { 
    int px = db1.getElem(i); 
    } 
+0

Dobra odpowiedź, ale używa się dwukrotnie więcej pamięci, ponieważ kopiuje cały obraz do innego bufora! Może mieć szansę na szybszą realizację mniej rzeczywistych rutynowych połączeń :-) Nie ma możliwości poznania bez testowania !!! – trumpetlicks

+0

@trumpetlicks +1 dla * "Nie ma możliwości poznania bez testowania !!!" * Podejrzewam, że będzie to * szybsze * niż 'getRGB()' na piksel, ale testy posortują go. –

+0

@AndrewThompson - Nie jestem pewien, on wywołuje db1.getSize każdej iteracji, a także potencjalnie 4 kopie danych. 1 dla tworzenia r1, innego dla db1 i tego samego 2 dla r2 i db2. Następnie ma również db1.getElem (i). W rzeczywistości będzie to wolniejsze, ponieważ wywołuje rutynę, aby uzyskać oba elementy w tablicach. db1.getSize() powróci (szerokość * wysokość) obrazu. Więc nie tylko kopiuje dane, ale wywołuje taką samą ilość procedur w pętli. On nawet nie wykonuje operacji porównania !!! – trumpetlicks

5

Jeśli prędkość jest problem, a zarówno BufferedImages są tej samej głębi, układ, itp (co wydaje się, że to musi być prawda) możesz to zrobić:

DataBuffer dbActual = myBufferedImage.getRaster().getDataBuffer(); 
DataBuffer dbExpected = bufferImageReadFromAFile.getRaster().getDataBuffer(); 

dowiedzieć się, jaki to jest, np. DataBufferInt

DataBufferInt actualDBAsDBInt = (DataBufferInt) dbActual ; 
DataBufferInt expectedDBAsDBInt = (DataBufferInt) dbExpected ; 

zrobić kilka „testów poprawności” na równych na rozmiarach i brzegach DataBuffers, wówczas pętla

for (int bank = 0; bank < actualDBAsDBInt.getNumBanks(); bank++) { 
    int[] actual = actualDBAsDBInt.getData(bank); 
    int[] expected = expectedDBAsDBInt.getData(bank); 

    // this line may vary depending on your test framework 
    assertTrue(Arrays.equals(actual, expected)); 
} 

Jest blisko tak szybko, jak można dostać bo ty bierzesz fragment danych na raz, a nie pojedynczo.

+0

Ta sama głębia bitowa, aranżacja itp. Jest dość duża. Właśnie odpowiedział wczoraj na pytanie chłopaków, w którym zasadniczo porównywał obrazy, i odkrył, że jeden był z ARGB_8888, a drugi z RGB_565. To są duże założenia. Rzeczywiście masz rację, jeśli te parametry są prawdziwe, to byłaby to najszybsza metoda :-) – trumpetlicks

+0

Ponieważ jest to test jednostkowy, wydaje się, że jego testowy obraz powinien mieć tę samą głębokość bitową itp. Ale zgadzam się z tobą, że w ogólnie, to trochę założenie. – user949300

0

Możesz napisać to zdjęcie poprzez imageio poprzez OutputStream do byte[]. W moim kodu, wygląda mniej więcej tak:

byte[] encodeJpegLossless(BufferedImage img){...using imageio...} 
... 
Assert.assertTrue(Arrays.equals(encodeJpegLossless(img1) 
           ,encodeJpegLossless(img2) 
           ) 
       ); 
1

Zmieniłem function that equals by pixels w Groovy, mogą być pomocne:

boolean imagesAreEqual(BufferedImage image1, BufferedImage image2) { 
    if (image1.width != image2.width || image1.height != image2.height) { 
     return false 
    } 
    for (int x = 1; x < image2.width; x++) { 
     for (int y = 1; y < image2.height; y++) { 
      if (image1.getRGB(x, y) != image2.getRGB(x, y)) { 
       return false 
      } 
     } 
    } 
    return true 
} 
Powiązane problemy