java 有没有一种简单的方法来比较 BufferedImage 实例?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11006394/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 03:23:13  来源:igfitidea点击:

Is there a simple way to compare BufferedImage instances?

javaawt

提问by pharsicle

I am working on part of a Java application that takes an image as a byte array, reads it into a java.awt.image.BufferedImageinstance and passes it to a third-party library for processing.

我正在处理 Java 应用程序的一部分,该应用程序将图像作为字节数组,将其读入java.awt.image.BufferedImage实例并将其传递给第三方库进行处理。

For a unit test, I want to take an image (from a file on disk) and assert that it is equal to the same image that has been processed by the code.

对于单元测试,我想获取一个图像(来自磁盘上的文件)并断言它与代码处理过的图像相同。

  • My expectedBufferedImageis read from a PNG file on disk using ImageIO.read(URL).
  • My testcode reads the same file into a BufferedImageand writes that to a byte array as PNG to provide to the system under test.
  • 我的预期BufferedImage是使用ImageIO.read(URL).
  • 我的测试代码将相同的文件读入 aBufferedImage并将其作为 PNG 写入字节数组以提供给被测系统。

When the system under test writes the byte array to a new BufferedImageI want to assert that the two images are equal in a meaningful way. Using equals()(inherited from Object) doesn't work (of course). Comparing BufferedImage.toString()values also doesn't work because the output string includes object reference information.

当被测系统将字节数组写入新的时,BufferedImage我想以一种有意义的方式断言这两个图像是相等的。使用equals()(继承自Object)不起作用(当然)。比较BufferedImage.toString()值也不起作用,因为输出字符串包含对象引用信息。

Does anybody know any shortcuts? I would prefer not to bring in a third-party library for a single unit test in a small part of a large application.

有人知道捷径吗?我不想为大型应用程序的一小部分中的单个单元测试引入第三方库。

采纳答案by Mr. Polywhirl

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.

这是最好的方法。无需保留变量来判断图像是否仍然相等。当条件为假时,只需立即返回假。短路评估有助于节省比较失败后像素循环的时间,就像 trumpetlick 的答案一样

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

回答by user949300

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

如果速度是一个问题,并且两者BufferedImages都具有相同的位深度、排列等(这里似乎必须如此),您可以这样做:

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

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

弄清楚它是哪种类型,例如 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

对 DataBuffers 的大小和组做一些“健全性检查”,然后循环

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.

这几乎是您可以获得的最快速度,因为您一次抓取了一大块数据,而不是一次一个。

回答by trumpetlicks

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!!!

这将是一种方式!!!

回答by Sergey Ponomarev

I changed function that equals by pixelsin Groovy, may be helpful:

我在 Groovy 中更改了按像素相等的函数,可能会有所帮助:

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
}

回答by cyril

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

回答by G. Fiedler

If you want to use Mockito, then you could write a HamcrestMatcher

如果你想使用Mockito,那么你可以编写一个HamcrestMatcher

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

回答by G. Fiedler

I can't think of anything besides a brute force "do loop":

除了蛮力“做循环”之外,我想不出任何事情:

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

回答by comonad

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

您可以imageio通过OutputStreama写入该图像byte[]。在我的代码中,它或多或少是这样的:

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