【问题标题】:Compression of PNG using JAI (Java Advanced Imaging)使用 JAI(Java 高级成像)压缩 PNG
【发布时间】:2014-08-23 16:15:28
【问题描述】:

我正在使用 javax.imageio API 和 JAI 来压缩不同类型的图像。它适用于使用JPEGImageWriter 的JPEG 和使用GIFImageWriter 的GIF。但它不支持使用PNGImageWriter 进行PNG 压缩,这会引发异常,例如未设置压缩类型或“没有有效压缩”等。所以我在ImageWriter 下方使用它来处理PNG。它可以工作,但图像质量太差了。

谁能建议如何使用PNGImageWriter 进行PNG 压缩以及哪个JAR 包含它?

File input = new File("test.png");

InputStream is = new FileInputStream(input);
BufferedImage image = ImageIO.read(is);

File compressedImageFile = new File(input.getName());

OutputStream os =new FileOutputStream(compressedImageFile);

Iterator<ImageWriter>writers = 
        ImageIO.getImageWritersByFormatName("jpg"); // here "png" does not work
ImageWriter writer = (ImageWriter) writers.next();

ImageOutputStream ios = ImageIO.createImageOutputStream(os);
writer.setOutput(ios);

ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(0.5f);

writer.write(null, new IIOImage(image, null, null), param);

【问题讨论】:

    标签: java compression javax.imageio jai image-compression


    【解决方案1】:

    似乎与 JRE 捆绑在一起的默认 com.sun.imageio.plugins.png.PNGImageWriter 不支持 setting 压缩。这有点令人惊讶,因为该格式显然支持压缩。但是,PNGImageWriter 总是 写入压缩。

    从它使用的源代码可以看出:

    Deflater def = new Deflater(Deflater.BEST_COMPRESSION);
    

    这将为您提供良好但缓慢的压缩。这对您来说可能已经足够了,但在某些情况下,使用更快的压缩和更大的文件可能会更好。

    要修复您的代码,使其适用于任何格式名称,请更改以下行:

    ImageWriteParam param = writer.getDefaultWriteParam();
    param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
    param.setCompressionQuality(0.5f);
    

    到:

    ImageWriteParam param = writer.getDefaultWriteParam();
    
    if (param.canWriteCompressed()) { 
        // NOTE: Any method named [set|get]Compression.* throws UnsupportedOperationException if false
        param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
        param.setCompressionQuality(0.5f);
    }
    

    它仍然会写入压缩的 PNG。

    如果您需要对 PNG 压缩进行更多控制,例如设置压缩或使用的过滤器,您需要找到支持它的 ImageWriter。正如您提到的 JAI,我认为 CLibPNGImageWriterjai-imageio.jarjai-imageio-tools.jar 的一部分支持设置压缩。你只需要查看ImageWriters 迭代器,看看你是否安装了它:

    Iterator<ImageWriter>writers = ImageIO.getImageWritersByFormatName("png");
    ImageWriter writer = null;
    
    while (writers.hasNext()) {
        ImageWriter candidate = writers.next();
    
        if (candidate.getClass().getSimpleName().equals("CLibPNGImageWriter")) {
            writer = candidate; // This is the one we want
            break;
        }
        else if (writer == null) {
            writer = candidate; // Any writer is better than no writer ;-)
        }
    }
    

    使用正确的ImageWriter,您的代码应该可以按预期工作。

    【讨论】:

    • 感谢您的详细解释。但是当我尝试这段代码时,它会抛出一个异常,比如“不支持压缩”......有什么想法吗?
    • 是的。您没有应用 if (param.canWriteCompressed()) 行。更重要的是,您没有正确安装 JAI,因此您仍然没有使用 CLibPNGImageWriter。尝试打印 writer 的类,或使用调试器进行验证。
    • 它工作正常。但是压缩后(32位)输出的PNG图像比源图像增加了尺寸
    • 它将大小增加两倍或三倍而不是减小。
    • 2x-3x 大小是什么?另一个PNG?还是 JPEG?
    【解决方案2】:

    请检查此代码是否适合您。

    ImageWriteParam writeParam = writer.getDefaultWriteParam();
    
    writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
    
    writeParam.setCompressionType("PackBits");
    
    writeParam.setCompressionQuality(0.5f);
    

    谢谢。

    【讨论】:

    • 我尝试了解决方案,但它显示 IllegalArgumentException: Unknown compression type!使用 Java 11
    猜你喜欢
    • 1970-01-01
    • 2011-05-12
    • 2011-02-12
    • 1970-01-01
    • 2012-06-22
    • 2015-12-01
    • 2023-04-07
    • 2012-09-02
    • 2013-12-28
    相关资源
    最近更新 更多