18

Hi I would like to know if there is any way in Java to reduce the size of an image (use any kind of compression) that was loaded as a BufferedImage and is going to be saved as an PNG.

Maybe some sort of png imagewriteparam? I didnt find anything helpful so im stuck.

heres a sample how the image is loaded and saved

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));
}
ubernoob
  • 181
  • 1
  • 1
  • 5
  • 1
    Does "any kind of compression" include lossy compression? Because then storing the image as a in JPEG format might save a lot of space. – Joachim Sauer Apr 27 '10 at 13:07
  • PNG is a lossless compression format (as long as your source image doesn't use more than 8-bits per channel: if you convert, say, a 48-bit RGB picture to PNG, PNG becomes lossy) hence you can't gain much. There are however lots of tools that produces PNG much smaller than usual, and they're particularly useful if you need your PNGs for memory-constrained devices: PNGOUT is such a tool and was programmed by one of the very best programmer ever: http://advsys.net/ken/utils.htm#pngout – SyntaxT3rr0r Apr 27 '10 at 13:30
  • 1
    right, like Joachim asked, no lossy compression, only reduce the size of the image file like used by zip and so and there should be no external tools involved like PNGOUT, so it could be some sort of library that supports and ImageWriteParam for PNG like JPEGImageWriteParam, looking at xmlgraphics-commons 1.3.1 atm. Sorry if im getting confusing in some parts im still a novice, just thoguht the answers could help me out or inspire other people with a similar problem. Thanks for the suggestions so far. – ubernoob Apr 27 '10 at 13:41

4 Answers4

5

By calling ImageIO.write you use the default compression quality for pngs. You can choose an explicit compression mode which reduces file size a lot.

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();
}
Kristof Neirynck
  • 3,934
  • 1
  • 33
  • 47
  • 1
    Have you actually tried this? The default `com.sun.imageio.plugins.png.PNGImageWriteParam` in Java 7 and 8 throws `java.lang.UnsupportedOperationException: Compression not supported.` – mwoodman Feb 22 '19 at 20:11
  • Yes it works on Java 9. Java 8 and older always use maximum compression. You can put the setCompression… statements in an if (param.isCompressionSupported) or something like that for Java 8 compatibility. – Kristof Neirynck Feb 23 '19 at 22:26
  • It works, but in my experiments I could reduce the file size only by a very small amount, to the 99.86% of the defaults used in the question. Better PNG compressions are available, but it seems that they are not implemented in imageio. – lbalazscs Dec 10 '20 at 22:31
4

You may want to try pngtastic. It's a pure java png image optimizer that can shrink may png images down to some degree.

depsypher
  • 1,084
  • 11
  • 20
2

If it's going to be saved as PNG, compression will be done at that stage. PNG has a lossless compression algorithm (basically prediction followed by lempel-ziv compression) with few adjustable parameters (types of "filters") and not much impact in compression amount - in general the default will be optimal.

leonbloy
  • 73,180
  • 20
  • 142
  • 190
  • 2
    From what I've read about gimps png-save options, the compression level (a setting from 1 to 9) determines the compression (effort of the encoder), although it is loss-less no matter what you set. – aioobe Apr 27 '10 at 20:31
  • Yes, its just a matter of choosing one of the four 'filters' (actually predictors, see above link) to use, perhaps using distinct filters for different lines. It always is lossless. – leonbloy Apr 27 '10 at 20:37
0

Have a look at the ImageWriterParam class in the same package as the ImageIO class. It mentions compression.

https://docs.oracle.com/javase/7/docs/api/javax/imageio/ImageWriteParam.html

Also look at the example at http://exampledepot.com/egs/javax.imageio/JpegWrite.html and see if it translates well for PNG files.

Christoph Grimmer
  • 4,210
  • 4
  • 40
  • 64
aioobe
  • 413,195
  • 112
  • 811
  • 826
  • 5
    It will throw an exception if you want to use compression mode explicit for PNGs informing you that this is not supported for this format. So the parameters applied to JpegWriter are not applied to PNG. – Francisco Spaeth May 20 '12 at 09:21