当前位置: 首页 > 知识库问答 >
问题:

Java ImageIO灰度PNG问题

令狐声
2023-03-14

我有一个我想尝试的灰度图像(实际上是“lena”)。我得到了一个512x512的PNG文件,有216种灰色阴影。

事情是这样的,当我用JavaImageIO阅读它时,就像这样:

    String name = args[0];
    File fi = new File(name);
    BufferedImage img = ImageIO.read(fi);

我得到一个只有154种颜色的BuffereImage!我只是意识到了这一点,因为我处理过的图像看起来发黄,缺乏深黑色。

更令人恼火的是,当我使用XnView将PNG转换为GIF时,这是一个无损的过程,在这种情况下,用上面的代码读取GIF,我的BuffereImage中得到了所有216种颜色。

是否有一些文档或描述,当ImageIO读取PNG时,我的PNG会发生什么?有解决这个问题的设置吗?我在最近的JDK1上做了这些实验。8.只是我现在对Java PNG支持失去了信任,以后我会使用彩色PNG。

共有2个答案

元鸿波
2023-03-14

不知何故,您已将图像从线性灰度(gamma=1.0)转换为sRGB灰度(gamma=1/2.2)。这可以用GraphicsMagick演示。从莱娜开始。从维基百科下载png,然后删除sRGB块以创建lena。那么巴布亚新几内亚

gm convert lena.png -colorspace gray -depth 8 -strip lena-gray.png

lena-gray.png有216种颜色

gm convert lena-gray.png -gamma 2.2 -depth 8 -strip lena-gray-gm22.png

lena-gray-gm22.png有154种颜色,看起来褪色或褪色。

我在libpng-1.6.17中使用graphicsmagick(1.4版)的最新测试版。

要计算我使用ImageMagick的颜色:

identify -verbose file.png | grep Colors

我曾经用过

pngcheck -v file.png

来验证Lenna。png包含IHDR、sRGB、IDAT和IEND块,而lena gray。png和lena-gray-gm22。png只包含IHDR、IDAT和IEND块。

陈阳舒
2023-03-14

欢迎来到Java隐式颜色管理的“伟大”世界!

对于Java(至少是ImageIO),内部的一切都是sRGB,它隐含地进行颜色管理,这对于一个人真正想要做的事情通常是适得其反的。对于灰度图像,至少在大多数读者中使用ImageIO,至少对于没有嵌入式ICC配置文件的灰度图像(我还没有测试其他),Java自动“分配”一个WhitePoint=D50, Gamma=1.0的ICC配置文件。我也偶然发现了这一点。

然后,当你访问像素时(我假设你使用img.getRGB()或类似的东西?),实际上,您可以访问sRGB值(Windows上Java的默认颜色空间)。

结果是,当转换为sRGB时,它的伽马值约为2.2(sRGB的伽马值实际上有点复杂,但总体接近2.2),这会有效地对图像应用(1/gamma)=2.2的伽马校正,(a)使图像看起来“亮”,以及(b)由于伽马校正从256到256离散值,也会有效地减少一些灰度。

如果以不同的方式访问BuffereImage的数据,也可以看到效果:a)访问配置文件:

ColorSpace colorSpace = img.getColorModel().getColorSpace();
if ( colorSpace instanceof ICC_ColorSpace ) {
    ICC_Profile profile = ((ICC_ColorSpace)colorSpace).getProfile();
    if ( profile instanceof ICC_ProfileGray ) {
        float gamma = ((ICC_ProfileGray)profile).getGamma();
        system.out.println("Gray Profile Gamma: "+gamma); // 1.0 !
    }
}

b) 以不同的方式访问某些像素值。。。

//access sRGB values (Colors translated from img's ICC profile to sRGB)
System.out.println( "pixel 0,0 value (sRGB): " + Integer.toHexString(img.getRGB(0,0)) ); // getRGB() actually means "getSRGB()"
//access raw raster data, this will give you the uncorrected gray value
//as it is in the image file
Raster raster = image.getRaster();
System.out.println( "pixel 0,0 value (RAW gray value): " + Integer.toHexString(raster.getSample(0,0,0)) );

如果像素(0,0)不是100%黑色或100%白色,您将看到sRGB值比灰度值“更高”,例如gray=d1-

从我的角度来看,它不仅可以降低您的灰度级别,还可以使您的图像更轻(大约与应用1/gamma值为2.2的gamma校正相同)。如果Java没有嵌入式ICC Profile的灰色图像,要么将灰色转换为R=G=B=grayValue的sRGB,要么分配一个ICC Gray Profile WhitePoint=D50, Gamma=2.2(至少在Windows上),这将更合乎逻辑。由于sRGB不完全是Gamma 2.2,后者仍然会让您失去一些灰色调。

关于使用GIF的原因:GIF格式没有“灰度”或ICC配置文件的概念,因此您的图像是256色调色板图像(256色恰好是256个灰度)。打开GIF时,Java假定RGB值为sRGB。

解决方案:根据您的实际用例,您的解决方案可能是访问每个图像像素的光栅数据(灰色=raster.get样本(x, y,0))并将其放入sRGB图像设置R=G=B=灰色。不过,可能有更优雅的方法。

关于你对java或PNG的信任:由于java ImageIO的隐式颜色转换,我在很多方面都在与之斗争。我们的想法是在开发人员不需要太多颜色管理知识的情况下内置颜色管理。只要您只使用sRGB(并且您的输入也是sRGB,或者没有颜色配置文件,因此可以合法地认为是sRGB),这在一定程度上是有效的。如果输入图像中有其他颜色空间(例如AdobeRGB),问题就会出现。灰色也是另一回事,尤其是ImageIO假设伽马=1.0的(不寻常的)灰色轮廓。现在要理解ImageIO在做什么,您不仅需要了解颜色管理的基本知识,还需要了解java在做什么。我在任何文档中都没有找到这个信息!一句话:ImageIO做的事情肯定被认为是正确的。这往往不是你所期望的,你可能需要更深入地挖掘,找出原因,或者改变行为,如果这不是你想做的。

 类似资料:
  • 我有一个大问题,试图将一个大的科学图像(7MB)16位PNG图像转换为JPG,以便压缩它并检查Python中的任何最终工件。原始图像可在以下位置找到:https://postimg.cc/p5PQG8ry在这里阅读其他答案时,我尝试了枕头和OpenCV,但没有成功。我得到的唯一东西是一张白色的床单。我做错了什么?注释行是使用Python读取16位PNG图像文件的尝试,但似乎不适用于生成数据类型错误

  • 我是opencv4android的新手,试图加载16位深度的灰度png图像。 我试过: 但是png被读取为8bit,值在[0,255]中。我尝试了CV_LOAD_IMAGE_ANYDEPTH标志,但还是一样。我试图创建一个alpha通道或将图像作为原始图像加载,但没有成功。 我还尝试在设备上复制图像,并使用imread读取,如下所示,希望imread能够加载16位通道: 当我检查它确实存在。返回的

  • 我需要用PHP创建一个灰度图像。我说的不是调色板中有灰度值的索引图像,而是真正的灰度图像。不同之处在于PNG(颜色类型)的第26字节: (有关详细信息,请参见如何检查PNG的灰度/alpha颜色类型) 我尝试了以及但我得到的都是RGB灰度图像(颜色类型2)或带有灰度调色板的RGB调色板图像(颜色类型3)。我还尝试使用而不是初始化图像,但这只会导致调色板图像。 有没有办法使用PHP的GD函数(或PH

  • 1. 功能介绍 你可以点击 这里 查看灰度发布功能的介绍信息。 2. 常见问题 2.1 设置方案生效方式后,为什么无法编辑发布规则? 在设置了方案生效方式后,【发布规则】中的添加按钮将无法使用,如下图所示。此时,开发者将无法: 删除已经导入的用户列表 删除已经选择的其他规则 添加新的其他规则 此时,如需更改规则,则需要删除已经配置好的【发布规则】,此时就可以继续编辑发布规则。 2.2 范围内/范围

  • 我想在Javascript中加载一个16位灰度PNG(其中每个像素代表一个数字/深度值。TUM使用这种文件格式来存储深度信息) 然而,opencv读取的图像。js是RGBA 8位(——我想得到CV。CV_16UC1),这似乎是因为图像是由浏览器本身解码的,如文件所示。因此,我们得到的不是一个像素,而是一个介于0和65535(2^8-1)之间的值,而是一个介于0和255(2^8-1)之间的数字,R=

  • 我有一个关于将我的PNG图像更改为灰度的问题。我想做一个方法,接受byte[]图像作为参数,并作为byte[]返回新修改的灰度图像。 我发现了一些链接,使图像灰度与JPEG图像使用下面的片段。 我的参考资料在下面:我如何使用ImageJ作为单独的Java应用程序的库?https://www.mkyong.com/java/how-to-convert-byte-to-bufferedimage-i