私は、画像をバイト配列として受け取り、それを_Java.awt.image.BufferedImage
_インスタンスに読み込んで、処理のためにサードパーティのライブラリに渡すJavaアプリケーションの一部に取り組んでいます。
単体テストでは、(ディスク上のファイルから)画像を取得し、コードによって処理されたのと同じ画像と等しいことを表明します。
BufferedImage
は、ImageIO.read(URL)
を使用してディスク上のPNGファイルから読み取られます。BufferedImage
に読み込み、それをPNGとしてバイト配列に書き込んで、テスト対象のシステムに提供します。テスト対象のシステムがバイト配列を新しいBufferedImage
に書き込むとき、2つのイメージが意味のある方法で等しいことを表明したいと思います。 equals()
(Object
から継承)の使用は(もちろん)機能しません。出力文字列にはオブジェクト参照情報が含まれているため、BufferedImage.toString()
値の比較も機能しません。
誰かがショートカットを知っていますか?大規模なアプリケーションのごく一部での単一ユニットテスト用にサードパーティのライブラリを持ち込みたくありません。
これが最善のアプローチです。画像がまだ等しいかどうかを判断するために変数を保持する必要はありません。条件がfalseの場合、ただちにfalseを返します。短絡評価は、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()) {
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;
}
速度が問題であり、両方のBufferedImages
が同じビット深度、配置などである場合(ここでは真実である必要があるようです)、次のようにすることができます。
DataBuffer dbActual = myBufferedImage.getRaster().getDataBuffer();
DataBuffer dbExpected = bufferImageReadFromAFile.getRaster().getDataBuffer();
それがどのタイプであるかを把握します。 DataBufferInt
DataBufferInt actualDBAsDBInt = (DataBufferInt) dbActual ;
DataBufferInt expectedDBAsDBInt = (DataBufferInt) dbExpected ;
dataBufferのサイズとバンクで等しいかどうかを確認するためにいくつかの「サニティチェック」を実行してから、ループします。
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));
}
これは、一度に1つずつではなく、一度にデータのチャンクを取得しているため、取得できる速度に近い速度です。
あなたは比較のためにあなた自身のルーチンを書くことができます!
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;
}
これは一方向です!!!
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
}
総当たり攻撃の「doloop」以外は考えられません。
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);
}
Mockito を使用する場合は、 Hamcrest マッチャーを記述できます。
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;
}
}
このように使用します
assertThat(actual, new BufferedImageMatcher(expected));
うまく機能しているが効率的ではない
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;
}
}
そのイメージは、imageio
を介してOutputStream
を介してbyte[]
に書き込むことができます。私のコードでは、多かれ少なかれ次のようになります。
byte[] encodeJpegLossless(BufferedImage img){...using imageio...}
...
Assert.assertTrue(Arrays.equals(encodeJpegLossless(img1)
,encodeJpegLossless(img2)
)
);