Graphics 如何处理PNG文件格式中的颜色空间和gamma?

Graphics 如何处理PNG文件格式中的颜色空间和gamma?,graphics,colors,png,libpng,color-space,Graphics,Colors,Png,Libpng,Color Space,我的问题是: 我正在使用光谱样本进行渲染,并希望保存一张显示结果的图像。我通过CIE XYZ颜色匹配函数对光谱功率函数进行加权,以获得XYZ颜色空间结果。我将这个XYZ颜色元组乘以为转换为sRGB而给出的矩阵,并将结果钳制为(0,1) 为了保存图像,我将转换后的元组缩放255并转换为字节,然后将数组传递给libpng的png\u write\u image()。当我看到一个均匀的强度,纯彩色光谱这样渲染,它看起来是错误的;在颜色之间的过渡中有暗条带。这也许并不奇怪,因为要从XYZ转换为sRGB,

我的问题是:

我正在使用光谱样本进行渲染,并希望保存一张显示结果的图像。我通过CIE XYZ颜色匹配函数对光谱功率函数进行加权,以获得XYZ颜色空间结果。我将这个XYZ颜色元组乘以为转换为sRGB而给出的矩阵,并将结果钳制为
(0,1)

为了保存图像,我将转换后的元组缩放255并转换为字节,然后将数组传递给libpng的
png\u write\u image()
。当我看到一个均匀的强度,纯彩色光谱这样渲染,它看起来是错误的;在颜色之间的过渡中有暗条带。这也许并不奇怪,因为要从XYZ转换为sRGB,矩阵后的颜色分量会相乘(如果它们足够小,则会线性缩放)。但如果我这样做,情况看起来更糟!只有在提升到1/2.2之后,它才开始看起来正确。看起来,在我不做任何事情的情况下,解码图像的伽马值是~2.2,应用了两次

这里是我期望它工作的方式:我将矩阵应用到XYZ,我有一个大致能量线性的RGB元组。我把它提高到2.2,现在有了一个感知线性的颜色元组。我按原样对这些数字进行编码(从而有效利用文件精度),并在文件中存储一个字段,该字段表示“这些字节已使用gamma 2.2进行编码”。然后在图像加载时,解码系统取消应用编码伽马,然后在显示前应用系统伽马。(因此,从创作的角度来看,我不必关心观众的系统是什么)。但我得到的结果表明,这种方式行不通

更糟糕的是,我尝试用2.2和1/2.2调用了
png\u set\u gAMA()
,但在图像中看不到任何差异。我在
png\u set\u sRGB()
中得到了类似的结果(我相信这会迫使gamma值达到1/2.2)。 关于我应该如何转换颜色值,或者PNG如何处理gamma和颜色空间,我肯定有一些向后的或者不理解的地方。要将此分解为几个澄清问题:

  • 我希望传递给
    write\u png()
    的字节值的颜色空间是多少
  • 我必须对libpng进行哪些调用(如果有),以指定传递字节的颜色空间和伽马值,以确保正确显示?为什么他们会失败
  • png文件中的伽马场如何与我应用于传递的颜色通道值(如果有)的指数相关
  • 如果我希望在发送图像数据之前反转伽马曲线(我对此表示怀疑,但现在似乎有必要),那么该反转是否应包括sRGB曲线的线性部分

此外,我还看到一些提示,“白点”在XYZ和sRGB之间的转换中起着重要作用。我不清楚上面给出的站点中的矩阵是否包含到D65的重整化(它与Wikipedia的矩阵不匹配)——或者甚至在需要这种转换的时候。我发现的大多数文献都掩盖了细节。在转换过程中是否还有一个步骤没有在wiki文章中提到,或者这将被自动处理?

这几乎是您所期望的方式。png_set_gAMA()导致libpng编写gAMA 输出PNG文件中的块。它不会改变像素本身。符合png的 查看器应该使用区块中的伽马值,结合显示器的伽马值,在显示器上正确写入像素强度值。大多数解码器实际上不会执行您描述的两步(不应用图像gamma,然后应用系统gamma)方法,尽管结果在概念上是相同的:它会将图像gamma与系统gamma组合以创建查找表,然后使用该表在一步中转换像素

从您观察到的情况来看(gamma=2.2和gamma=1/2.2的行为相同),您使用的查看器似乎没有对PNG gAMA块数据进行任何处理。

您写道:“在我不做任何操作的情况下,解码图像的gamma值为~2.2应用了两次。”

我认为您的显示器(Hardware或您的系统icc配置文件)本身已经有伽马设置。

您说过:

因为要从XYZ转换为sRGB,在矩阵相乘后颜色分量必须提高到2.4

不,这是错误的。从线性(XYZ)sRGB,您不会提高到2.4或2.2,也就是说,从sRGB到线性

从线性到sRGB,您提高到
^(1/2.2)
,或者如果分段使用sRGB,您将看到1/2.4-您应用的有效伽马值为^0.45455

在您链接的维基百科页面上,这是正向转换

从XYZ到sRGB:

当然是在应用正确的矩阵之后。假设所有内容都在D65中,则:

直说线性 现实世界中的光是线性的。如果将100个光子增加三倍,则有300个光子。但是人眼看不到三联,相比之下,我们只看到适度的增加

这就是为什么使用转移曲线或“gamma”来最大限度地利用8位图像中的可用代码空间的部分原因(我知道对我来说过于简单)

为此,将线性灯光值提高到0.455的次幂,并将该sRGB值恢复到线性空间,然后将其提高到相反的值,即^1/0.455,也称为^2.2

矩阵的使用必须在线性空间中进行。但是在传递矩阵之后,您需要应用trc或“gamma”编码。根据您的陈述,不,没有两次添加2.2,您只是走错了路。

可能有用:而且