【问题标题】:How to save indexed color PNG in java如何在java中保存索引颜色PNG
【发布时间】:2017-06-16 23:02:10
【问题描述】:

如何在java中将图像保存为java.awt.image.IndexColorModel PNG?我正在使用 ImageIO 加载索引颜色 png,使用 Catalino 库对其进行操作,不幸的是,该库将颜色空间转换为 java.awt.image.DirectColorModel

现在我想以与原始图像完全相同的格式保存结果。我尝试了以下sn-p的代码。

private static void testIndexedColor() throws IOException {
        FastBitmap input = new FastBitmap("test.png");
        BufferedImage bi = new BufferedImage(input.getWidth(), input.getHeight(), BufferedImage.TYPE_BYTE_INDEXED);
        bi.getGraphics().drawImage(input.toBufferedImage(), 0, 0, null);
        ImageIO.write(bi, "PNG", new File("test_result.png"));
}

但结果白色背景中出现了奇怪的浅灰色像素伪影,PPI下降。如何正确转换回索引颜色模式而不会造成质量损失和失真?

【问题讨论】:

  • 这里发生的情况是,您的bi BufferedImage 是使用默认IndexColorModel 创建的(恰好与256 色“网络安全”调色板的颜色相同)。您需要从input 获取IndexColorModel 或某种形式的调色板条目。如果这是不可能的,作为最坏的情况,您可以使用ImageIO 再次读取输入文件以获取它(更慢),甚至使用某种形式的颜色减少(最慢)为修改后的图像重新创建“最佳”调色板.不确定 PPI 发生了什么(或者这是否重要)。
  • PS:你指的库是Catalano框架吗?
  • 另外值得一提的是,特定的图像是黑白的。当我在 gimp 模式下打开它时说它是索引颜色。但是,如果我使用 ImageIO 将此图像加载到 BufferedImage 并立即使用 ImageIO 将相同的 BufferedImage 写入文件,则该模式现在是神秘的灰度。我什至不确定灰度和索引颜色是否相互排斥,或者灰度图像是否可以使用调色板?为什么会发生这种转换?
  • 您介意分享PNG吗?在 Java 中 IndexColorModel 始终使用 sRGB 颜色空间。但这并不能阻止颜色全是灰色。所以这真的取决于你定义的“灰度”。当没有与文件中的颜色数据匹配的ColorModel 类,或者如果这样的ColorModel 不切实际(慢)时,通常会发生转换(在Java ImageIO 中,我不知道Gimp 的细节)。
  • 我的更新版本对您有帮助吗?

标签: java graphics png bufferedimage javax.imageio


【解决方案1】:

假设我对 Catalano 框架的看法是正确的,您应该能够像这样重写您的方法:

private static void testIndexedColor() throws IOException {
    BufferedImage bi = ImageIO.read(new File("test.png"));
    FastBitmap input = new FastBitmap(bi);

    Graphics2D g = bi.createGraphics();
    try {
        g.drawImage(input.toBufferedImage(), 0, 0, null);
    }
    finally {
         g.dispose(); // Good practice ;-)
    }

    ImageIO.write(bi, "PNG", new File("test_result.png"));
}

至少你应该摆脱固定的调色板和工件。

但是,这可能会仍然修改 PPI(但这不会影响像素)。甚至在某些情况下,图像可能会被写回为非调色板 PNG。


更新:似乎PNGImageWriter(通过PNGMetadata)实际上将包含完美灰度的IndexColorModel重写为默认的灰度PNG。这通常是一个好主意,因为您通过不写入 PLTE 块来减小文件大小。您应该能够通过传递原始元数据以及图像像素数据来解决此问题,以指示作者保留 IndexColorModel(即写入 PLTE 块):

private static void testIndexedColor() throws IOException {
    File in = new File("test.png");
    File out new File("test_result.png");

    try (ImageInputStream input = ImageIO.createImageInputStream(in);
         ImageOutputStream output = ImageIO.createImageOutputStream(out)) {
        ImageReader reader = ImageIO.getImageReaders(input).next(); // Will fail if no reader
        reader.setInput(input);

        ImageWriter writer = ImageIO.getImageWriter(reader); // Will obtain a writer that understands the metadata from the reader
        writer.setOutput(output);  // Will fail if no writer

        // Now, the important part, we'll read the pixel AND metadata all in one go
        IIOImage image = reader.readAll(0, null); // PNGs only have a single image, so index 0 is safe

        // You can now access and modify the image data using:
        BufferedImage bi = (BufferedImage) image.getRenderedImage();
        FastBitmap fb = new FastBitmap(bi);

        // ...do stuff...

        Graphics2D g = bi.createGraphics();
        try {
            g.drawImage(fb.toBufferedImage(), 0, 0, null);
        }
        finally {
            g.dispose();
        }

        // Write pixel and metadata back
        writer.write(null, image, writer.getDefaultWriteParam());
    }
}

应该(作为奖励)也保持您的 PPI 不变。

PS:对于生产代码,您还想在上面的readerwriter 中添加dispose(),但我将其省略以保持重点并避免进一步讨论try/finally。 ;-)

【讨论】:

  • 我很高兴您添加了 dispose(),但不需要 try-finally。 g.drawImage 几乎不可能抛出异常。
  • @VGR 在内存不足的情况下很有必要。
  • 如果你打算从 OutOfMemoryErrors 中“恢复”(实际上你做不到),你不妨把每一行代码都放在一个 try-finally 中。当你用完内存时,程序执行就有效地完成了,唯一有用的动作就是退出。
  • @VGR dispose() 方法首先存在是有原因的。通常,在 Java 中,你会让 GC 处理这个问题。但由于可能涉及本机资源,您需要明确的dispose()。这也是您应该拥有try/finally 的原因。这不是关于恢复的。这是关于释放您可能持有的任何资源(与流的close() 等相同)。我也不同意你的其余陈述,但我不会去那里...... ;-)
  • 我对此进行了测试,结果保存在灰度中。我断言BufferedImageColorModelIndexColorModelreaddrawImage 操作之后的一个实例。似乎是转换ColorModelwrite 方法。有没有可能绕过这个?
猜你喜欢
  • 2020-02-17
  • 2020-01-05
  • 2012-09-03
  • 2022-11-26
  • 1970-01-01
  • 2011-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多