Burning a PNG file with a smaller disk size in Java

I have a BufferedImage :

 BufferedImage bi = new BufferedImage(14400, 14400, BufferedImage.TYPE_INT_ARGB); 

I saved this image in a PNG file using the following code:

 public static void saveGridImage(BufferedImage sourceImage, int DPI, File output) throws IOException { output.delete(); final String formatName = "png"; for (Iterator<ImageWriter> iw = ImageIO .getImageWritersByFormatName(formatName); iw.hasNext();) { ImageWriter writer = iw.next(); ImageWriteParam writeParam = writer.getDefaultWriteParam(); ImageTypeSpecifier typeSpecifier = ImageTypeSpecifier .createFromBufferedImageType(BufferedImage.TYPE_INT_RGB); IIOMetadata metadata = writer.getDefaultImageMetadata( typeSpecifier, writeParam); if (metadata.isReadOnly() || !metadata.isStandardMetadataFormatSupported()) { continue; } setDPI(metadata, DPI); final ImageOutputStream stream = ImageIO .createImageOutputStream(output); try { writer.setOutput(stream); writer.write(metadata, new IIOImage(sourceImage, null, metadata), writeParam); } finally { stream.close(); } break; } } public static void setDPI(IIOMetadata metadata, int DPI) throws IIOInvalidTreeException { double INCH_2_CM = 2.54; // for PNG, it dots per millimeter double dotsPerMilli = 1.0 * DPI / 10 / INCH_2_CM; IIOMetadataNode horiz = new IIOMetadataNode("HorizontalPixelSize"); horiz.setAttribute("value", Double.toString(dotsPerMilli)); IIOMetadataNode vert = new IIOMetadataNode("VerticalPixelSize"); vert.setAttribute("value", Double.toString(dotsPerMilli)); IIOMetadataNode dim = new IIOMetadataNode("Dimension"); dim.appendChild(horiz); dim.appendChild(vert); IIOMetadataNode root = new IIOMetadataNode("javax_imageio_1.0"); root.appendChild(dim); metadata.mergeTree("javax_imageio_1.0", root); } 

When the code is executed, a PNG file is created with 400 DPI and a disk size of 168 MB ; it's too much.

Is there a way or options that I can use to save a smaller PNG?

I used to have a 1.20 GB TIFF file, and when I converted it to PNG using imagemagick at 400 DPI, the resulting file size was only 700 KB.

So, I think I could save the above file.

Can pngj help me? Since I now have a png file that I can read in the pngj library.

+3
source share
2 answers

Image size 14400x14400 ARGB8 has a raw (uncompressed) size of 791 MB. It will compress more or less in accordance with its nature (has uniform or smooth zones) and according to (less important) PNG compression parameters.

when I convert it using imagemagic to PNG using 400 DPI, the resulting file size is only 700 KB.

(I don’t understand why you are talking about DPI, which has nothing to do with the size of pixels in pixels). Are you saying you get 14400x14400 ARGB 700KB? This will be 1/1000 compression, which is hard to believe if the image is practically not flat. You must first understand what is happening here.

Anyway, here is a sample code with PNGJ

 /** writes a BufferedImage of type TYPE_INT_ARGB to PNG using PNGJ */ public static void writeARGB(BufferedImage bi, OutputStream os) { if(bi.getType() != BufferedImage.TYPE_INT_ARGB) throw new PngjException("This method expects BufferedImage.TYPE_INT_ARGB" ); ImageInfo imi = new ImageInfo(bi.getWidth(), bi.getHeight(), 8, true); PngWriter pngw = new PngWriter(os, imi); pngw.setCompLevel(9);// maximum compression, not critical usually pngw.setFilterType(FilterType.FILTER_AGGRESSIVE); // see what you prefer here DataBufferInt db =((DataBufferInt) bi.getRaster().getDataBuffer()); SinglePixelPackedSampleModel samplemodel = (SinglePixelPackedSampleModel) bi.getSampleModel(); if(db.getNumBanks()!=1) throw new PngjException("This method expects one bank"); ImageLine line = new ImageLine(imi); for (int row = 0; row < imi.rows; row++) { int elem=samplemodel.getOffset(0,row); for (int col = 0,j=0; col < imi.cols; col++) { int sample = db.getElem(elem++); line.scanline[j++] = (sample & 0xFF0000)>>16; // R line.scanline[j++] = (sample & 0xFF00)>>8; // G line.scanline[j++] = (sample & 0xFF); // B line.scanline[j++] = (((sample & 0xFF000000)>>24)&0xFF); // A } pngw.writeRow(line, row); } pngw.end(); } 
+2
source

I would try messing with the settings of the writeParam object you are creating. You are currently calling getDefaultWriteParam(); which gives you the base writeParam object. I assume that by default there will be NO compression.

After that, you can perhaps set some of the compression modes to reduce the file size.

 writeParam.setCompressionMode(int mode); writeParam.setCompressionQuality(float quality); writeParam.setCompressionType(String compressionType); 

See http://docs.oracle.com/javase/6/docs/api/javax/imageio/ImageWriteParam.html and especially http://docs.oracle.com/javase/6/docs/api/javax/imageio /ImageWriteParam.html#setCompressionMode(int)

+1
source

All Articles