加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

Java ImageIO灰度PNG问题

发布时间:2020-12-15 04:28:42 所属栏目:Java 来源:网络整理
导读:我有一个灰度图像(实际上是“lena”)我想试验一下.我把它作为512×512 PNG文件,有216种灰度. 当我用Java ImageIO阅读它时,会发生什么: String name = args[0]; File fi = new File(name); BufferedImage img = ImageIO.read(fi); 我得到一个只有154种颜色的
我有一个灰度图像(实际上是“lena”)我想试验一下.我把它作为512×512 PNG文件,有216种灰度.

当我用Java ImageIO阅读它时,会发生什么:

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

我得到一个只有154种颜色的BufferedImage!我只是意识到这一点,导致我处理过的图像显得粗糙,缺乏深黑色.

更刺激的是,当我使用XnView将PNG转换为GIF时,在这种情况下这是一个无损程序,用上面的代码读取GIF,我在BufferedImage中得到所有216种颜色.

是否有某种文档或描述,当我的PNG读取它时会发生什么?有设置来解决这个问题吗?我在最近的JDK1.8上做过这些实验.只是我对Java PNG支持的信任现在已经丢失,我稍后会使用彩色PNG.

解决方法

欢迎来到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个离散值,你也有效地松散了你的一些灰色阴影.

如果以不同方式访问BufferedImage的数据,也可以看到效果:
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)不是偶然的100%黑色或100%白色,您将看到sRGB值比灰度值“更高”,例如gray = d1 – > sRGB = ffeaeaea(阿尔法,红色,绿色,蓝色).

从我的角度来看,它不仅会降低您的灰度级,还会使您的图像更亮(与使用1 /伽玛值为2.2的伽马校正相同).如果没有嵌入式ICC配置文件的灰色图像的Java将灰色转换为具有R = G = B = grayValue的sRGB,或者将分配ICC灰度配置文件WhitePoint = D50,Gamma = 2.2(至少在Windows上),则更合乎逻辑.由于sRGB不完全是Gamma 2.2,后者仍然会让你失去一些灰色调.

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

解:
根据您的实际使用情况,您的解决方案可能是您访问每个图像的像素(灰色= raster.getSample(x,y,0))的栅格数据并将其放入sRGB图像设置R = G = B =灰色.但是,可能会有更优雅的方式.

关于您对java或PNG的信任:由于它具有隐含的颜色转换,我在很多方面都在努力研究java ImageIO.我们的想法是内置色彩管理,而开发人员不需要太多关于色彩管理的知识.只要您只使用sRGB(并且您的输入也是sRGB,或者没有颜色配置文件,因此可以合法地认为是sRGB),这在一定程度上有效.如果输入图像中有其他颜色空间(例如AdobeRGB),则会启动麻烦.灰色也是另一回事,尤其是ImageIO假设Gamma = 1.0的(异常)灰度轮廓.现在要了解ImageIO正在做什么,您不仅需要知道您在颜色管理中的ABC,还需要弄清楚java正在做什么.我没有在任何文档中找到此信息!结论:ImageIO确实可以认为是正确的.这通常不是你所期望的,你可能会深入挖掘,找出原因或改变行为,如果它不是你想要做的.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读