使用JAI(Java高级成像)压缩PNG

时间:2014-07-02 18:28:40

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

我正在使用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);

2 个答案:

答案 0 :(得分:7)

与JRE捆绑在一起的默认com.sun.imageio.plugins.png.PNGImageWriter似乎不支持设置压缩。这有点令人惊讶,因为格式显然支持压缩。但是,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.jar的{​​{1}}支持设置压缩。您只需要查看jai-imageio-tools.jar迭代器,看看是否安装了它:

ImageWriter

使用正确的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 ;-) } } ,您的代码应按预期工作。

答案 1 :(得分:0)

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

ImageWriteParam writeParam = writer.getDefaultWriteParam();

writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);

writeParam.setCompressionType("PackBits");

writeParam.setCompressionQuality(0.5f);

感谢。