java-ImageIO.write奇怪的行为:不同的运行,不同的压缩
作者:互联网
我试图获取有关使用PNG文件进行Java ImageIO压缩的压缩性能的统计信息,但我被这种疯狂的行为所困扰.
我有一堆PNG(例如)150张测试图像,并且用以下简单代码喂饱了它们:
for (File ori : files) {
BufferedImage img = ImageIO.read(ori);
File dest = new File("C:/temp/x.png"); // whatever
OutputStream nos = new FileOutputStream(dest)
ImageIO.write(img, "PNG", nos);
nos.close();
long size = dest.length();
// report size
}
(我删除了异常处理和一些不重要的代码-同样,在我的实际实现中,我不写入文件,而是写入仅计数字节的虚拟输出流-所有这些都没有影响).这是从控制台Java程序调用的,无非就是main()调用它.从Eclipse运行,JRE 1.7.0_55-b14(32位).
我的问题是,这在不同的运行中给出了完全不同的结果.实际上,它给出了两组不同的结果.在其中一种情况下,我们称其为“好”情况,压缩后的大小比“坏”情况小得多(平均约87%).我总是得到这两组精确的结果,只有这些结果,并且从未混淆.
当我得到两个结果之一似乎是随机的时,我找不到模式.如果仅使用一个文件而不是一长串的文件运行此文件,则只会得到一个结果(不好的结果).我只能用一张图像来重现此图像,尽管“好”情况偶尔会发生,请参见下面的更新.
我已经检查了几对好/坏图像,它们还可以,没什么奇怪的,看起来好像它们是由不同的编码器编码的.
谁能解释一下?
Update2:好的,我只可以复制一张图像.这是代码:
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Locale;
import javax.imageio.ImageIO;
public class ImageIoTest {
public static void main(String[] args) throws Exception {
Locale.setDefault(Locale.US);
for (int i = 0; i < 3; i++)
testWith(new File("C:/temp/m0550.png"));
}
private static long testWith(File f) throws Exception {
File dest = new File(f.getParent(), "new" + f.getName());
BufferedImage img = ImageIO.read(f);
OutputStream nos = new FileOutputStream(dest);
ImageIO.write(img, "PNG", nos);
nos.close();
long size = dest.length();
System.out.printf("%s\t%d\n", dest, size);
return size;
}
适当设置图像路径.我使用this image(1103429字节),尽管这并不重要(但是目前我不信任任何“应该”).如果我反复运行上述命令,则循环内的4个调用始终返回相同的值.但是,相同的值因运行而异.有时它给出1099207,有时它给出1498218(甚至更奇怪的是,有时在注释Locale行时得到更改)…令人费解.
最终更新和解释:@anonymous,在接受的答案中,知道了.问题是有两个ImageWriters(我认为这是因为一旦我安装了JAI).看来,在这种情况下,Java是根据……选择首选的.上帝知道,也许是随机的.
在这里,我们再次得到大家都赞赏的确定性行为:
public class ImageIoTest {
public static void main(String[] args) throws Exception {
Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName("PNG");
for(;iter.hasNext();) {
ImageWriter iw = iter.next();
testWith(new File("C:/temp/m0550.png"),iw);
}
}
private static long testWith(File f, ImageWriter iw) throws Exception {
File dest = new File(f.getParent(), "new" + f.getName());
BufferedImage img = ImageIO.read(f);
OutputStream nos = new FileOutputStream(dest);
ImageOutputStream ios = ImageIO.createImageOutputStream(nos);
iw.setOutput(ios);
iw.write(img);
ios.close();
nos.close();
long size = dest.length();
System.out.printf("%s\t%d\t%s\n", dest, size,iw.getOriginatingProvider().getPluginClassName());
return size;
}
}
在我的情况下打印:
C:\temp\newm0550.png 1099207 com.sun.media.imageioimpl.plugins.png.CLibPNGImageWriter
C:\temp\newm0550.png 1498218 com.sun.imageio.plugins.png.PNGImageWriter
解决方法:
我正在运行Java 1.8.0.
但是后来我记得我读过那篇有关使用任意ImageWriter的javadoc.所以我写了下面的代码,它只打印了1个ImageWriter.也许您的系统中有多个ImageWriter,这可以解释每次运行时获得的不同结果. `
对于Java 8
ImageIO.getImageWritersByFormatName("PNG").forEachRemaining(System.out::println);
对于Java 1.8之前的版本
for(Iterator<ImageWriter> iter=ImageIO.getImageWritersByFormatName("PNG");iter.hasNext();) {
System.out.println(iter.next());
}
尝试看看您获得了多少ImageWriter.如果它只为您列出一个ImageWriter,恐怕我就没主意了.
只是一个想法.假设您有多个ImageWriter,由于选择了任意ImageWriter,因此对ImageIO.write的每次调用应给您带来不同的结果.除非它仅在第一次调用和随后的调用中执行此操作,否则仅重用它而不是尝试查找要使用的另一个“随机” ImageWriter.然后可以解释您的结果.
标签:javax-imageio,png,java 来源: https://codeday.me/bug/20191121/2054421.html