java - 读取不失真的灰度 PNG 图像文件

标签 java bufferedimage javax.imageio grayscale

我需要读取和处理大量灰度的 PNG 文件。我的意思是,如果它们在 Photoshop 或 GIMP 中打开,则图像模式为灰度 - 而不是具有灰度值的 RGB 图像。

ImageIO 似乎没有实现这一点。它似乎将所有图像文件视为 sRGB。这会破坏灰度值。我需要读取和处理这些 PNG 文件,其中(在我的代码中)每个像素的值与我在 Photoshop 或 GIMP 中打开灰度文件时完全相同。请问有人知道一些可以实现此目的的开源软件吗?或者更好地使用 ImageIO 来实现这一点。

其他信息:

我在 BufferedImage 上使用 getRGB()。图像文件中的底层像素是 0x86。我知道这不一定对应于包含 0xFF868686 的 ARGB 像素,因为这取决于亮度/ Gamma 。然而,在没有带有 gamma 类型参数的 getter 的情况下,我预计默认映射为 ARGB=0xFF868686。如果我使用 GIMP 或 Photoshop 将包含值为 0x86 的像素的灰度图像转换为 RGB,则该像素将变为 0xFF868686。这是明显的默认设置。

但是,ImageIO 似乎对灰度图像文件使用了奇怪的 Gamma (无论您是否喜欢),这使得映射到 ARGB 后灰度像素非常非常亮。在本例中,0x86 映射到 0xFFC0C0C0。这不仅非常轻,还可能导致大量数据丢失,因为许多灰度值可以映射到更少的 ARGB 值。这种失真不会导致数据丢失的唯一情况是对于非常暗的灰度图像。适当的 Gamma 值取决于上下文,不同的物理介质会对亮度产生不同的扭曲。然而,在没有上下文的情况下,映射:0x86 --> 0xFF868686 最有意义 - 见证为 GIMP 和 Photoshop 所做的选择。

将 getRGB() 问题放在一边,加载灰度图像(使用 ImageIO.read( imageFile ))后,BufferedImage 的 getType() 方法返回 Type=0(自定义)而不是 Type=10 (TYPE_BYTE_GRAY)正如我所预料的。

简而言之,ImageIO 似乎没有提供一种读取和操作现有灰度图像的良好且简单的高级方法。我原本希望不必在光栅、ICC、采样等方面搞乱。我也不想将所有灰度图像文件物理转换为 RGB。我想要的只是 BufferedImage 的 API load() 方法,它的工作方式就像在 GIMP 或 Photoshop 中打开文件一样。我没能做到这一点。我希望这是我的无知,而不是 Java ImageIO 的限制。

可能的解决方案:

经过深入研究,我提供以下内容作为访问底层灰度值的可能技术:

final File imageFile = new File( "test.png" );
final BufferedImage image = ImageIO.read( imageFile );
// --- Confirm that image has ColorSpace Type is GRAY and PixelSize==16
final Raster raster = image.getData();
// --- Confirm that: raster.getTransferType() == DataBuffer.TYPE_BYTE

for( int x=0, xLimit=image.getWidth(); x < xLimit; x++ ) {
    for( int y=0, yLimit=image.getHeight(); y < yLimit; y++ ) {

        final Object dataObject = raster.getDataElements( x, y, null );
        // --- Confirm that dataObject is instance of byte[]
        final byte[] pixelData = (byte[]) dataObject;
        // --- Confirm that: pixelData.length == 2

        final int grayscalePixelValue = pixelData[0] & 0xFF;
        final int grayscalePixelAlpha = pixelData[1] & 0xFF;

        // --- Do something with the grayscale pixel data
    }
}

javadoc 不是很好,所以我不能保证这是正确的,但它似乎对我有用。

最佳答案

如果您想尝试第三方(我的)库:https://github.com/leonbloy/pngj/

如果您确定图像是纯灰度图像(8 位,无 Alpha,无调色板,无配置文件),则非常简单:

    PngReaderByte pngr = new PngReaderByte(new File(filename)); // 
    if (pngr.imgInfo.channels!=1 || pngr.imgInfo.bitDepth != 8 || pngr.imgInfo.indexed)
        throw new RuntimeException("This method is for gray images");
    for (int row = 0; row < pngr.imgInfo.rows; row++) { 
        ImageLineByte line = pngr.readRowByte();
        byte [] buf = line.getScanlineByte();
        // do what you want
    }
    pngr.end(); 

关于java - 读取不失真的灰度 PNG 图像文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32583772/

相关文章:

java - 异常无法转换为 Throwable

java - 切换一组输入的大小写?

java - Amazon Lex AWS Lambda 钩子(Hook)的 Jackson JSON 反序列化

java - 我想做一个随机图像生成器,但它不起作用

java - 灰度图像滤镜

具有持续通信功能的 Java 套接字

Java 无法使用 javax.imageio 或 Sanselan 读取图像

java - 使用 ImageIO 处理奇怪的 png-8

java - 如何使用 JComponent 返回多个 JTextField?

android - 在我的 Android Activity 中使用 BufferedImage 和 ImageIO 类