如何在java中保存索引颜色PNG
如何在java中将图像保存为如何在java中保存索引颜色PNG,java,graphics,png,bufferedimage,javax.imageio,Java,Graphics,Png,Bufferedimage,Javax.imageio,如何在java中将图像保存为java.awt.image.IndexColorModelPNG?我正在用ImageIO加载索引颜色png,用Catalino库操作它,不幸的是,它将颜色空间转换为java.awt.image.DirectColorModel 现在我想以与原始图像完全相同的格式保存结果。我尝试了以下代码片段 private static void testIndexedColor() throws IOException { FastBitmap input = n
java.awt.image.IndexColorModel
PNG?我正在用ImageIO加载索引颜色png,用Catalino库操作它,不幸的是,它将颜色空间转换为java.awt.image.DirectColorModel
现在我想以与原始图像完全相同的格式保存结果。我尝试了以下代码片段
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降低。如何在没有质量损失和失真的情况下正确地转换回索引颜色模式?假设我对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块):
这应该(作为奖励)也保持你的PPI不变
PS:对于生产代码,您还需要对上面的
读取器
和编写器
进行dispose()
,但为了保持重点,避免进一步讨论try/finally
;-) 这里发生的是,您的bi
buffereImage
是使用默认的IndexColorModel
创建的(与256色“web安全”调色板的颜色相同)。您需要从input
获取IndexColorModel
或某种形式的调色板条目。如果不可能,最坏的情况是,您可以使用ImageIO
再次读取输入文件以获得它(速度较慢),甚至可以使用某种形式的颜色减少(速度最慢)为修改后的图像重新创建“最佳”调色板。不确定PPI发生了什么(或者这是否重要)。PS:您所指的库是Catalano框架吗?同样值得一提的是,特定的图像是黑白的。当我在gimp模式下打开它时,它显示为索引颜色。但是,如果我用ImageIO将此图像加载到BuffereImage,并立即使用ImageIO将此相同的BuffereImage写入文件,则该模式现在是神秘的灰度模式。我甚至不确定灰度和索引颜色是否相互排斥,或者灰度图像是否可以使用调色板?为什么会发生这种转换?你介意分享PNG吗?在JavaIndexColorModel
中,始终使用sRGB颜色空间。但这并不能阻止颜色变成灰色。所以这实际上取决于你定义的“灰度”。当文件中没有与颜色数据匹配的ColorModel
类时,或者如果这样的ColorModel
不实用(速度慢),通常会发生转换(在Java ImageIO中,我不知道这个细节中的Gimp)。我的更新版本对您有帮助吗?我很高兴您添加了dispose()
,但最终的尝试是没有必要的。g.drawImage
几乎不可能抛出异常。@VGR在内存不足的情况下,这是必要的。如果您打算从OutOfMemoryErrors中“恢复”出来(实际上,您不能),那么您最好最后尝试一下每一行代码。当内存不足时,程序执行将有效地完成,唯一有用的操作是退出。@VGR首先有一个原因是dispose()
方法存在。通常,在Java中,您会让GC处理这个问题。但由于可能涉及本机资源,因此需要显式的dispose()
。这也是为什么您应该使用try/finally
。这与恢复无关。它是关于释放您可能持有的任何资源(与stream的close()
等相同)。我也不同意你其余的陈述,但我不会去那里…;-)我测试了这个,结果以灰度保存。我断言buffereImage
的ColorModel
是IndexColorModel
在read
和drawImage
操作之后的一个实例。似乎是write
方法转换了ColorModel
。有可能绕过这个吗?
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());
}
}