如何使用Java压缩PNG图像

18

大家好,我想知道在Java中是否有任何方法可以减小作为BufferedImage加载并将其保存为PNG格式的图像的大小(使用任何类型的压缩)。

也许使用某种png imagewriteparam?但是我没有找到任何有用的东西,所以卡住了。

这里是一个加载和保存图像的示例:

public static BufferedImage load(String imageUrl) {         
    Image image = new ImageIcon(imageUrl).getImage();
    bufferedImage = new BufferedImage(image.getWidth(null),
                                                    image.getHeight(null),
                                                    BufferedImage.TYPE_INT_ARGB);
    Graphics2D g2D = bufferedImage.createGraphics();
    g2D.drawImage(image, 0, 0, null);
    return bufferedImage;
}

public static void storeImageAsPng(BufferedImage image, String imageUrl) throws IOException {
    ImageIO.write(image, "png", new File(imageUrl));
}

1
“任何类型的压缩”是否包括有损压缩?如果是的话,将图像以JPEG格式存储可能会节省很多空间。 - Joachim Sauer
PNG是一种无损压缩格式(只要您的源图像每个通道不超过8位:如果您将48位RGB图片转换为PNG,PNG就会变得有损),因此您无法获得太多优势。然而,有很多工具可以生成比通常更小的PNG文件,并且它们在需要为内存受限设备创建PNG文件时非常有用:PNGOUT就是这样一种工具,由最优秀的程序员之一编写:http://advsys.net/ken/utils.htm#pngout - SyntaxT3rr0r
1
就像Joachim所问的那样,不要使用有损压缩,只需像zip一样减小图像文件的大小,而且不应涉及任何外部工具,如PNGOUT,因此可能是支持PNG的ImageWriteParam的某种库,就像JPEGImageWriteParam一样,目前正在查看xmlgraphics-commons 1.3.1。如果我在某些部分变得混乱,请原谅,我仍然是个新手,只是认为这些答案可以帮助我或启发其他遇到类似问题的人。感谢迄今为止提出的建议。 - ubernoob
4个回答

5
通过调用ImageIO.write,您使用png的默认压缩质量。您可以选择显式压缩模式来大幅减小文件大小。
String inputPath = "a.png";
String outputPath = "b.png";

BufferedImage image;
IIOMetadata metadata;

try (ImageInputStream in = ImageIO.createImageInputStream(Files.newInputStream(Paths.get(inputPath)))) {
    ImageReader reader = ImageIO.getImageReadersByFormatName("png").next();
    reader.setInput(in, true, false);
    image = reader.read(0);
    metadata = reader.getImageMetadata(0);
    reader.dispose();
}

try (ImageOutputStream out = ImageIO.createImageOutputStream(Files.newOutputStream(Paths.get(outputPath)))) {
    ImageTypeSpecifier type = ImageTypeSpecifier.createFromRenderedImage(image);
    ImageWriter writer = ImageIO.getImageWriters(type, "png").next();

    ImageWriteParam param = writer.getDefaultWriteParam();
    if (param.canWriteCompressed()) {
        param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
        param.setCompressionQuality(0.0f);
    }

    writer.setOutput(out);
    writer.write(null, new IIOImage(image, null, metadata), param);
    writer.dispose();
}

1
你是否实际尝试过这个?在Java 7和8中,默认的com.sun.imageio.plugins.png.PNGImageWriteParam会抛出java.lang.UnsupportedOperationException: Compression not supported. - mwoodman
是的,它可以在Java 9上运行。Java 8及更早版本始终使用最大压缩。您可以将setCompression...语句放在if(param.isCompressionSupported)中,以实现Java 8兼容性。 - Kristof Neirynck
它可以工作,但在我的实验中,我只能将文件大小减少了很小的一部分,降至问题中使用的默认值的99.86%。有更好的PNG压缩方法可用,但似乎它们没有在imageio中实现。 - lbalazscs

4
你可以尝试使用 pngtastic。它是一个纯Java的PNG图像优化器,可以将许多PNG图像缩小到某种程度。

2
如果将其保存为PNG,则会在那个阶段进行压缩。PNG有一种无损压缩算法(基本上是预测后跟随Lempel-Ziv压缩),具有少量可调参数(即"filters"类型),对压缩量影响不大 - 通常默认值最优。

2
根据我所了解的GIMP PNG保存选项,压缩级别(从1到9的设置)决定了压缩程度(编码器的努力),尽管无论您设置什么,它都是无损的。 - aioobe
是的,这只是选择其中一个四个“过滤器”(实际上是预测器,请参见上面的链接)来使用的问题,也许对于不同的行使用不同的过滤器。它始终是无损的。 - leonbloy

0

5
如果你想要对PNG使用压缩模式,它会抛出一个异常,告诉你该格式不支持此操作。因此,应用于JpegWriter的参数不适用于PNG。 - Francisco Spaeth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接