读取灰度PNG图像文件而不失真 [英] Reading Grayscale PNG image files without distortion

查看:51
本文介绍了读取灰度PNG图像文件而不失真的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I need to read and process a large number of PNG files that are grayscale. By that I mean that if they are opened in either Photoshop or GIMP, the image mode is Grayscale - not an RGB image with grayscale values.

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

ImageIO does not seem to achieve this. It appears to treat all image files as sRGB. This mangles grayscale values. I need to read and process these PNG files where (in my code) each pixel has exactly the same value as if I had opened the grayscale file in Photoshop or GIMP. Does anybody know of some open source software that can achieve this, please? Or better how to achieve this using ImageIO.

附加信息:

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

I am using getRGB() on a BufferedImage. The underlying pixel in the image file is 0x86. I understand that this does not necessarily correspond to an ARGB pixel containing 0xFF868686, as this depends upon luminance/gamma. However, in the absence of a getter with a gamma type argument, I would have expected the default mapping to be to ARGB=0xFF868686. If I use GIMP or Photoshop to convert a grayscale image containing a pixel with the value of 0x86 to RGB then the pixel becomes 0xFF868686. This is the obvious default.

然而,ImageIO 似乎对灰度图像文件使用了一个奇怪的伽玛(不管你喜欢与否),这使得灰度像素在映射到 ARGB 后非常非常轻.在这种情况下,0x86 映射到 0xFFC0C0C0.这不仅非常轻,还会导致大量数据丢失,因为许多灰度值可以映射到较少的 ARGB 值.这种失真不会导致数据丢失的唯一时间是非常暗的灰度图像.适当的 Gamma 取决于上下文,不同的物理媒体会以不同的方式扭曲亮度.然而,在没有上下文的情况下,映射:0x86 --> 0xFF868686 最有意义 - 见证为 GIMP 和 Photoshop 所做的选择.

However, ImageIO seems to use a weird gamma (whether you like it or not) with grayscale image files that makes the grayscale pixels very, very light after mapping to ARGB. In this case, 0x86 maps to 0xFFC0C0C0. This is not only very light, it can also result in considerable data loss as many grayscale values can be mapped to fewer ARGB values. The only time that this distortion will not result in data loss is for very dark grayscale images. An appropriate Gamma is context dependent, different physical media will distort luminance differently. However, in the absence of a context, the mapping: 0x86 --> 0xFF868686 makes most sense - witness the choices made for GIMP and Photoshop.

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

Leaving the getRGB() issue to one side, having loaded the grayscale image (using ImageIO.read( imageFile )), the getType() method of BufferedImage returns Type=0 (Custom) and not Type=10 (TYPE_BYTE_GRAY) as I would have expected.

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

In short, ImageIO does not seem to provide a nice and simple high level way of reading and manipulating existing grayscale images. I had hoped not to have to mess around under the covers with Rasters, ICC, sampling etc. Nor do I want to have to physically convert all the grayscale image files to RGB. All I wanted was an API load() method for BufferedImage that works just like open file does in GIMP or Photoshop. I have not been able to achieve this. I am hoping that this is my ignorance and not a limitation of Java ImageIO.

可能的解决方案:

在深入研究之后,我提供了以下作为访问底层灰度值的可能技术:

After digging around I have the following to offer as a possible technique for accessing the underlying grayscale values:

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 不是很好,所以我不能保证这是正确的,但它似乎对我有用.

The javadoc is not great, so I cannot guarantee that this is correct, but it seems to work for me.

推荐答案

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

In case you want to try a third party (mine) lib: https://github.com/leonbloy/pngj/

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

If you are certain that the image is plain grayscale (8 bits, no alpha, no palette, no profile), it's quite simple:

    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(); 

这篇关于读取灰度PNG图像文件而不失真的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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