使用Java编写具有较少磁盘大小的PNG文件 [英] Write PNG file with less disk size in Java

查看:123
本文介绍了使用Java编写具有较少磁盘大小的PNG文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有 BufferedImage

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

我已使用以下代码将此图像保存为PNG文件:

I have saved this image to 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's 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);
    }

当代码执行时,它会创建一个包含400 DPI和磁盘大小的PNG文件 168 MB ;这太多了。

When the code executes it creates an PNG file with 400 DPI and Disk Size of 168 MB; this is too much.

我可以用任何方式或参数来保存较小的PNG吗?

Is there any way or parameters I can use to save a smaller PNG?

以前,我有一个1.20 GB的TIFF文件,当我使用400maI的imagemagick将其转换为PNG时,生成的文件大小只有700 KB。

Before, I had 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 might be able to save the above file smaller.

pngj能帮助我吗?因为我现在有一个png文件,我可以在pngj库中读取。

推荐答案

14400x14400 ARGB8图像有一个原始(未压缩)大小为791MB。它将根据其性质(具有均匀或平滑区域)或根据(不太重要)PNG压缩参数或多或少地压缩。

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


当我使用imagemagic将其转换为使用400 DPI的PNG时,
生成的文件大小仅为700 KB。

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

(我不明白为什么你说DPI,这无关紧要,重要的是像素的大小)你是说那个你得到的14400x14400 ARGB为700KB?这将代表压缩1/1000,除非图像几乎是平坦的,否则很难相信。
你应该先了解这里发生了什么。

(I don't understand why you speak of DPI, that has nothing to do, what matters is the size in pixels) Are you saying that you are getting a 14400x14400 ARGB of 700KB? That would represent a compression of 1/1000, hard to believe unless the image is practically flat. You should first understand what is going on here.

无论如何,这是一个带PNGJ的示例代码

Anyway, here's 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();
}

这篇关于使用Java编写具有较少磁盘大小的PNG文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆