编程语言
首页 > 编程语言> > java-ImageIO.write奇怪的行为:不同的运行,不同的压缩

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%).我总是得到这两组精确的结果,只有这些结果,并且从未混淆.

当我得到两个结果之一似乎是随机的时,我找不到模式.如果仅使用一个文件而不是一长串的文件运行此文件,则只会得到一个结果(不好的结果).我只能用一张图像来重现此图像,尽管“好”情况偶尔会发生,请参见下面的更新.

我已经检查了几对好/坏图像,它们还可以,没什么奇怪的,看起来好像它们是由不同的编码器编码的.

谁能解释一下?

更新:这是一对“ good”和“ bad”图像.

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