Is there a simple way to compare BufferedImage instances?

前端 未结 8 899
刺人心
刺人心 2020-12-05 19:01

I am working on part of a Java application that takes an image as a byte array, reads it into a java.awt.image.BufferedImage instance and passes it to a third-p

相关标签:
8条回答
  • 2020-12-05 19:06

    You could write your own routine for comparison!

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

    This would be one way!!!

    0 讨论(0)
  • 2020-12-05 19:08

    If you want to use Mockito, then you could write a 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;
      }
    }
    

    and use it like this

    assertThat(actual, new BufferedImageMatcher(expected));
    
    0 讨论(0)
  • 2020-12-05 19:09

    working well but not efficient

    public static boolean compareImage(File fileA, File fileB) {        
        try {
            // take buffer data from botm image files //
            BufferedImage biA = ImageIO.read(fileA);
            DataBuffer dbA = biA.getData().getDataBuffer();
            int sizeA = dbA.getSize();                      
            BufferedImage biB = ImageIO.read(fileB);
            DataBuffer dbB = biB.getData().getDataBuffer();
            int sizeB = dbB.getSize();
            // compare data-buffer objects //
            if(sizeA == sizeB) {
                for(int i=0; i<sizeA; i++) { 
                    if(dbA.getElem(i) != dbB.getElem(i)) {
                        return false;
                    }
                }
                return true;
            }
            else {
                return false;
            }
        } 
        catch (Exception e) { 
            e.printStackTrace();
            return  false;
        }
    }
    
    0 讨论(0)
  • 2020-12-05 19:12

    If speed is an issue, and both BufferedImages are of the same bit-depth, arrangement, etc. (which seems like it must be true here) you can do this:

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

    figure out which type it is, e.g. a DataBufferInt

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

    do a few "sanity checks" for equals on the sizes and banks of the DataBuffers, then loop

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

    This is close to as fast as you can get cause you are grabbing a chunk of the data at a time, not one at a time.

    0 讨论(0)
  • 2020-12-05 19:15

    This is the best approach. No need to keep a variable to tell whether the image is still equal. Simply return false immediately when the condition if false. Short-circuit evaluation helps save time looping over pixels after the comparison fails as is the case in trumpetlick's 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()) {
        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;
    }
    
    0 讨论(0)
  • 2020-12-05 19:24

    You can write that image via imageio through an OutputStream to a byte[]. In my code, it looks more or less like this:

    byte[] encodeJpegLossless(BufferedImage img){...using imageio...}
    ...
    Assert.assertTrue(Arrays.equals(encodeJpegLossless(img1)
                                   ,encodeJpegLossless(img2)
                                   )
                     );
    
    0 讨论(0)
提交回复
热议问题