22 votes

Existe-t-il un moyen simple de comparer des instances de BufferedImage ?

Je travaille sur une partie d'une application Java qui prend une image sous la forme d'un tableau d'octets, la lit dans un fichier de type java.awt.image.BufferedImage et la transmet à une bibliothèque tierce pour traitement.

Pour un test unitaire, je veux prendre une image (d'un fichier sur disque) et affirmer qu'elle est égale à la même image qui a été traitée par le code.

  • Mon attendu BufferedImage est lu à partir d'un fichier PNG sur le disque en utilisant ImageIO.read(URL) .
  • Mon test lit le même fichier dans un fichier BufferedImage et l'écrit dans un tableau d'octets en PNG pour le fournir au système testé.

Lorsque le système testé écrit le tableau d'octets dans un nouveau fichier BufferedImage Je veux affirmer que les deux images sont égales d'une manière significative. En utilisant equals() (hérité de Object ) ne fonctionne pas (bien sûr). Comparaison de BufferedImage.toString() ne fonctionne pas non plus car la chaîne de sortie comprend des informations sur les références d'objets.

Quelqu'un connaît-il des raccourcis ? Je préférerais ne pas faire appel à une bibliothèque tierce pour un seul test unitaire dans une petite partie d'une grande application.

20voto

Mr. Polywhirl Points 3677

C'est la meilleure approche. Il n'est pas nécessaire de conserver une variable pour savoir si l'image est toujours égale. Il suffit de renvoyer immédiatement false lorsque la condition est fausse. L'évaluation en circuit court permet d'économiser du temps en bouclant sur les pixels après l'échec de la comparaison, comme c'est le cas dans l'exemple de trumpetlick. réponse .

/**
 * 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()) {
    return false;
  }

  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;
      }
    }
  }

  return true;
}

8voto

user949300 Points 7954

Si la vitesse est un problème, et que les deux BufferedImages sont de la même profondeur de bit, de la même disposition, etc. (ce qui semble devoir être vrai ici), vous pouvez faire ceci :

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

déterminer de quel type il s'agit, par exemple une DataBufferInt

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

effectuer quelques "contrôles d'intégrité" pour les égaux sur les tailles et les banques des DataBuffers, puis boucler

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));
}

C'est presque aussi rapide que possible, car vous saisissez une partie des données à la fois, et non une seule à la fois.

3voto

trumpetlicks Points 5830

Vous pourriez écrire votre propre routine pour la comparaison !

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;
}

Ce serait un moyen ! !!

1voto

stokito Points 89

J'ai changé fonction qui égalise par pixels en Groovy, peut être utile :

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
}

1voto

G. Fiedler Points 449

Si vous voulez utiliser Mockito alors vous pourriez écrire un Hamcrest Matcher

import org.mockito.ArgumentMatcher;

public class BufferedImageMatcher extends ArgumentMatcher<BufferedImage> {

  private final BufferedImage expected;

  public BufferedImageMatcher(BufferedImage expected) {
    this.expected = expected;
  }

  @Override
  public boolean matches(Object argument) {
    BufferedImage actual = (BufferedImage) argument;

    assertEquals(expected.getWidth(), actual.getWidth());
    assertEquals(expected.getHeight(), actual.getHeight());

    for (int x = 0; x < actual.getWidth(); x++) {
      for (int y = 0; y < actual.getHeight(); y++) {
        assertEquals(expected.getRGB(x, y), actual.getRGB(x, y));
      }
    }

    return true;
  }
}

et l'utiliser comme ceci

assertThat(actual, new BufferedImageMatcher(expected));

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