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

Gna*_*air 4 java compression jai image-compression javax.imageio

我使用javax.imageio API和JAI来压缩不同类型的图像.它适用于JPEG使用JPEGImageWriter和GIF使用GIFImageWriter.但它不支持PNG压缩,使用PNGImageWriter它会抛出一个异常,比如没有设置压缩类型或"无有效压缩"等等.所以我在下面使用了这个ImageWriterPNG.它工作但图像质量太差.

任何人都可以建议如何使用PNGImageWriterPNG压缩和哪些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);
Run Code Online (Sandbox Code Playgroud)

har*_*ldK 8

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

您可以从它使用的源代码中看到:

Deflater def = new Deflater(Deflater.BEST_COMPRESSION);
Run Code Online (Sandbox Code Playgroud)

哪个会给你带来良好但缓慢的压缩效果.它对您来说可能已经足够了,但在某些情况下,使用更快的压缩和更大的文件可能会更好.

要修复代码,以便它可以使用任何格式名称,更改行:

ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(0.5f);
Run Code Online (Sandbox Code Playgroud)

至:

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);
}
Run Code Online (Sandbox Code Playgroud)

它仍然会写入压缩的PNG.

如果您需要更多地控制PNG压缩,例如设置压缩或使用的过滤器,您需要找到ImageWriter支持它的.当你提到JAI时,我认为CLibPNGImageWriter这是设置压缩的一部分jai-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 ;-)
    }
}
Run Code Online (Sandbox Code Playgroud)

如果正确ImageWriter,您的代码应该按预期工作.