Java 9 ImageIO read\write 给出的结果与 Java 8 不同
Java 9 ImageIO read\write gives a different result than Java 8
以下测试在 Java 9 上失败,但在 Java 8 上通过:
@Test
public void getImage_SetValueUsingConstructor_ShouldReturnCorrectValue() throws Exception {
String base64ImageString = "iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAIAAAAmkwkpAAAAEUlEQVR42mNgQAP/wQAbBw4ANwsL9Zo6V30AAAAASUVORK5CYII=";
byte[] rawImageBytes = Base64.getDecoder().decode(base64ImageString);
ByteArrayInputStream bis = new ByteArrayInputStream(rawImageBytes);
RenderedImage image = ImageIO.read(bis);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ImageIO.write(image, "PNG", bos);
byte[] imageBytesFromImage = bos.toByteArray();
assertArrayEquals(imageBytesFromImage, rawImageBytes);
}
Java 9 输出:
数组首先在元素 [42] 处不同;
预期:94
实际:-38
任何人都可以帮助我了解 Java 9 中发生了什么变化,有没有办法编写此代码以使其适用于 Java 8 和 9?
正如@Holger 在评论中指出的那样,确实是测试存在缺陷。虽然相同的 Base64 表示会给出相同的图像,但不同的 Base64 表示并不意味着图像数据不同。这可能只意味着相同的图像数据被不同地编码,并且将解码为完全相同的图像(这里就是这种情况)。
你的测试过去没有错误地通过的原因可能是你使用了 Java 8 PNGImageWriter
(或更早的版本,自 Java 1.4 以来它并没有太大变化),这是在您执行 ImageIO.write(image, "PNG", output)
时使用的 writer 插件,用于对图像进行编码并从中创建 Base64 表示。如果您从另一个 program/library 创建的文件创建了字节的 Base64 表示形式,它几乎肯定会有所不同。
您应该重写您的测试,但是我不太清楚您要在这里测试什么。
如果您只关心像素数据,您可以遍历像素并测试是否相等:
BufferedImage original = ImageIO.read(..);
BufferedImage current = ImageIO.read(..);
assertEquals(original.getWidth(), current.getWidth());
assertEquals(original.getHeight(), current.getHeight());
for (int y = 0; y < original.getHeight(); y++) {
for (int x = 0; x < original.getWidth(); x++) {
assertEquals(original.getRGB(x, y), current.getRGB(x, y));
}
}
如果您还需要保留元数据,您还需要在那里测试是否相等。但是 PNG 并没有真正包含很多有趣的元数据,所以我怀疑你是否需要它。
感谢 Holger 的评论,我所做的是从字节数组中解码图像,然后比较两个图像的 dataBuffer。
下面的测试在 Java 8 和
上都通过了
@Test
public void imageTest() throws Exception {
String base64ImageString = "iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAIAAAAmkwkpAAAAEUlEQVR42mNgQAP/wQAbBw4ANwsL9Zo6V30AAAAASUVORK5CYII=";
byte[] rawImageBytes = Base64.getDecoder().decode(base64ImageString);
ByteArrayInputStream bis = new ByteArrayInputStream(rawImageBytes);
RenderedImage image = ImageIO.read(bis);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ImageIO.write(image, "PNG", bos);
byte[] imageBytesFromImage = bos.toByteArray();
//assertArrayEquals(imageBytesFromImage, rawImageBytes); //fails on Java 9!
bis = new ByteArrayInputStream(imageBytesFromImage);
RenderedImage image2 = ImageIO.read(bis);
DataBuffer dbA = image.getData().getDataBuffer();
int sizeA = dbA.getSize();
DataBuffer dbB = image2.getData().getDataBuffer();
int sizeB = dbB.getSize();
// compare data-buffer objects //
assertEquals(sizeA, sizeB);
for (int i = 0; i < sizeA; i++) {
assertEquals(dbA.getElem(i), dbB.getElem(i));
}
}
比较图像代码取自:How to compare images for similarity using java
以下测试在 Java 9 上失败,但在 Java 8 上通过:
@Test
public void getImage_SetValueUsingConstructor_ShouldReturnCorrectValue() throws Exception {
String base64ImageString = "iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAIAAAAmkwkpAAAAEUlEQVR42mNgQAP/wQAbBw4ANwsL9Zo6V30AAAAASUVORK5CYII=";
byte[] rawImageBytes = Base64.getDecoder().decode(base64ImageString);
ByteArrayInputStream bis = new ByteArrayInputStream(rawImageBytes);
RenderedImage image = ImageIO.read(bis);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ImageIO.write(image, "PNG", bos);
byte[] imageBytesFromImage = bos.toByteArray();
assertArrayEquals(imageBytesFromImage, rawImageBytes);
}
Java 9 输出: 数组首先在元素 [42] 处不同; 预期:94 实际:-38
任何人都可以帮助我了解 Java 9 中发生了什么变化,有没有办法编写此代码以使其适用于 Java 8 和 9?
正如@Holger 在评论中指出的那样,确实是测试存在缺陷。虽然相同的 Base64 表示会给出相同的图像,但不同的 Base64 表示并不意味着图像数据不同。这可能只意味着相同的图像数据被不同地编码,并且将解码为完全相同的图像(这里就是这种情况)。
你的测试过去没有错误地通过的原因可能是你使用了 Java 8 PNGImageWriter
(或更早的版本,自 Java 1.4 以来它并没有太大变化),这是在您执行 ImageIO.write(image, "PNG", output)
时使用的 writer 插件,用于对图像进行编码并从中创建 Base64 表示。如果您从另一个 program/library 创建的文件创建了字节的 Base64 表示形式,它几乎肯定会有所不同。
您应该重写您的测试,但是我不太清楚您要在这里测试什么。
如果您只关心像素数据,您可以遍历像素并测试是否相等:
BufferedImage original = ImageIO.read(..);
BufferedImage current = ImageIO.read(..);
assertEquals(original.getWidth(), current.getWidth());
assertEquals(original.getHeight(), current.getHeight());
for (int y = 0; y < original.getHeight(); y++) {
for (int x = 0; x < original.getWidth(); x++) {
assertEquals(original.getRGB(x, y), current.getRGB(x, y));
}
}
如果您还需要保留元数据,您还需要在那里测试是否相等。但是 PNG 并没有真正包含很多有趣的元数据,所以我怀疑你是否需要它。
感谢 Holger 的评论,我所做的是从字节数组中解码图像,然后比较两个图像的 dataBuffer。
下面的测试在 Java 8 和
上都通过了@Test
public void imageTest() throws Exception {
String base64ImageString = "iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAIAAAAmkwkpAAAAEUlEQVR42mNgQAP/wQAbBw4ANwsL9Zo6V30AAAAASUVORK5CYII=";
byte[] rawImageBytes = Base64.getDecoder().decode(base64ImageString);
ByteArrayInputStream bis = new ByteArrayInputStream(rawImageBytes);
RenderedImage image = ImageIO.read(bis);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ImageIO.write(image, "PNG", bos);
byte[] imageBytesFromImage = bos.toByteArray();
//assertArrayEquals(imageBytesFromImage, rawImageBytes); //fails on Java 9!
bis = new ByteArrayInputStream(imageBytesFromImage);
RenderedImage image2 = ImageIO.read(bis);
DataBuffer dbA = image.getData().getDataBuffer();
int sizeA = dbA.getSize();
DataBuffer dbB = image2.getData().getDataBuffer();
int sizeB = dbB.getSize();
// compare data-buffer objects //
assertEquals(sizeA, sizeB);
for (int i = 0; i < sizeA; i++) {
assertEquals(dbA.getElem(i), dbB.getElem(i));
}
}
比较图像代码取自:How to compare images for similarity using java