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-party library for processing.
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.
BufferedImage
is read from a PNG file on disk using ImageIO.read(URL)
.BufferedImage
and writes that to a byte array as PNG to provide to the system under test.When the system under test writes the byte array to a new BufferedImage
I 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.
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.
A BufferedImage is essentially an Image with an accessible data buffer. It is therefore more efficient to work directly with BufferedImage . A BufferedImage has a ColorModel and a Raster of image data. The ColorModel provides a color interpretation of the image's pixel data.
List, the difference between Image and BufferedImage is the same as the difference between List and LinkedList. Image is a generic concept and BufferedImage is the concrete implementation of the generic concept; kind of like BMW is a make of a Car. Show activity on this post. Image is an abstract class.
getRGB. Returns an array of integer pixels in the default RGB color model (TYPE_INT_ARGB) and default sRGB color space, from a portion of the image data. Color conversion takes place if the default model does not match the image ColorModel .
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;
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With