Image processing 从记录709到sRGB的正确转换

Image processing 从记录709到sRGB的正确转换,image-processing,colors,yuv,gamma,Image Processing,Colors,Yuv,Gamma,如何正确地将存储为Y'CrCb(使用记录709)的颜色转换为sRGB 我正在处理HDTV视频,我正在使用libavcodec提取原始数据。 虽然我已经成功地进行了转换,但我还不能确信我做得正确。VLC提供了一个结果,在Gimp中使用“compose”结果在另一个结果中进行转换,并且使用来自web的代码也不一致。所以我还没有找到一个可靠的参考来比较 我的研究和目前的最佳选择如下。(值为浮点值,范围为0.0-1.0) 我最不确定的是伽马校正。它使它比我预期的轻一点,但我也不能说它看起来不对 工作室秋

如何正确地将存储为Y'CrCb(使用记录709)的颜色转换为sRGB

我正在处理HDTV视频,我正在使用libavcodec提取原始数据。 虽然我已经成功地进行了转换,但我还不能确信我做得正确。VLC提供了一个结果,在Gimp中使用“compose”结果在另一个结果中进行转换,并且使用来自web的代码也不一致。所以我还没有找到一个可靠的参考来比较

我的研究和目前的最佳选择如下。(值为浮点值,范围为0.0-1.0) 我最不确定的是伽马校正。它使它比我预期的轻一点,但我也不能说它看起来不对

工作室秋千拆除 对于8位,Y'的范围从16到235。Cr和Cb的范围从16到240,以128为中心

y = (y - (16 / 255.0)) * ( 1 + 16.0 / 255.0 + (256-235) / 255.0 );
u = (u - (16 / 255.0)) * ( 1 + 16.0 / 255.0 + (256-240) / 255.0 );
v = (v - (16 / 255.0)) * ( 1 + 16.0 / 255.0 + (256-240) / 255.0 );

//Move chroma
u -= 0.5;
v -= 0.5;
我不确定假设永远不会得到超出范围的值是否安全,或者是否需要钳制它

对于更高的位深度,规范规定忽略LSB。这是什么意思?我也在处理10位编码的材料,所以我很感兴趣

从Y'CrCb到RGB 记录709规范说明了如何将RGB转换为Y'CrCb:

E'y = 0.2126 * E'r + 0.7152 * E'g + 0.0722 * E'b
E'cb = 0.5389 * ( E'b - E'y )
E'cr = 0.6350 * ( E'r - E'y )
为Cb和Cr提供了更精确的定义:

Pb = 0.5 * (B' - Y') / (1 - Kb)
Pr = 0.5 * (R' - Y') / (1 - Kr)
其中Kb和Kr是E'b和E'r的因子。规范中的值似乎是根据这些方程式四舍五入的

RGB可以通过颠倒方程式(使用维基百科版本)找到:

G可直接使用Cr和Cb完成:

double g = y - 2*kr*(1-kr)/kg * v - 2*kb*(1-kb)/kg * u;
(y的系数为(1-kr-kb)/kg,当kr+kb+kg=1时为kg/kg)

RGB到sRGB 我根本没有看到任何包含此步骤的代码示例。 我们需要将建议709指定的颜色空间转换为sRGB中指定的颜色空间。最后,两者之间的唯一区别是传递函数(即伽马)。记录709指定的XY坐标与sRGB匹配,但我不知道为什么sRGB包含“Z”坐标,而记录709不包含。这有区别吗?(我对CIE XYZ一无所知。)

记录709规定了如何对线性RGB进行gamma编码:

V = 1.099 * L^0.45 - 0.099    for    1 >= L >= 0.018
V = 4.500 * L                 for 0.018 > L >= 0
我们需要反转它,但是线性截止0.018在两个方程中给出的V值并不相同。那么,反向版本的范围是什么

L = ( ( V + 0.099 ) / 1.099 ) ^ (1/0.45)    for  1 >= V >= ?
L = V / 4.5000                              for  ? >  V >= 0
sRGB也有同样的问题,但修订为0.0031308,更准确。我记得有人设计了一个分数,它精确地代表了sRGB,但我再也找不到了

我目前正在使用以下工具:

double cutoff = 1.099 * pow( 0.018, 0.45 ) - 0.099;
v = ( v < cutoff ) ? 1.0/4.5 * v : pow( (v+0.099)/1.099, 1.0/0.45 );
v = ( v <= 0.0031308 ) ? 12.92 * v : 1.055*pow( v, 1.0/2.4 ) - 0.055;
双截止=1.099*pow(0.018,0.45)-0.099;
v=(v<截止)?1.0/4.5*v:pow((v+0.099)/1.099,1.0/0.45);

v=(v对于从线性sRGB到非线性sRGB(压扩过程)和反向过程(逆压扩)的正确转换,我使用以下函数:

public double Companding(double channel)
{
    double v = channel;
    double V = v <= 0.0031308 ? 12.92 * v : 1.055 * Math.Pow(v, 1 / 2.4d) - 0.055;
    return V;
}

public double InverseCompanding(double channel)
{
    double V = channel;
    double v = V <= 0.04045 ? V / 12.92 : Math.Pow((V + 0.055) / 1.055, 2.4);
    return v;
}
公共双压扩(双通道)
{
双v=通道;
双V=V
记录709指定的XY坐标与sRGB匹配

它们是xy,不是xy,它与XYZ中的xy不同

唉,首先,XYZ是一步接一步的线性化,你不需要去那里,因为正如你所说,sRGB已经使用BT.709原色了。RGB线性,R'G'B'是非线性的。Y'Cb'Cr'也是非线性的

我也在处理10位编码的材料,所以我很感兴趣

这意味着您可以对其进行四舍五入以获得8位的正确值。如果10位值的最后两位为10或11,则向上取整到下一个8位值,否则向下取整(00,01为向下取整)。LSB表示最低有效位。但不要忘了1023应四舍五入到255,而不是溢出

我们需要反转它,但是线性截止0.018在两个方程中给出的V值并不相同

不,您不需要反转任何东西。REC.601/REC.709/REC.2020的EOTF不是OETF的反转,EOTF在BT.1886中有规定,对于理想的OLED显示器来说是2.4完美伽马,对于200勒克斯环境光下的非完美LCD来说几乎是sRGB EOTF。这就是为什么Chrome只对BT.709使用sRGB EOTF,这意味着“不”因为windows默认为EOTF

我记得有人设计了一个分数,精确地代表了sRGB


它只是0.04045/12.92==0.003130804954809/258400。

看一看中的第6.5节,我目前手机上只有边缘连接,所以现在很难提供正确的答案……我对随机转换函数不感兴趣,我有很多。我想要有人能证明为什么某个特定函数是正确的。(顺便说一句,这个看起来是错误的。看看Y'的方程式,它使用的系数与建议709中规定的系数不匹配。此外,它们的总和甚至不等于1.0,这改变了Y'的范围。)从定义上看,很明显Y'CbCr已经进行了伽马校正,无需再进行第二次校正。如果你想达到超精度,你可能需要先转换为线性,然后再转换回校正后的sRGB,但这对于大多数应用程序来说都是小巫见大巫。我确实希望非常精确,因为我的应用程序主要是做图像处理。我然而,我看到了(在我看来)与线性RGB的转换和线性RGB的转换有很大区别,尤其是在深色中。也许我做得不正确,这就是我问这个问题的部分原因。另一个原因是,我认为你应该知道正确的方法,然后才能进行近似处理,例如实时应用。这是你可以准确地测量误差。(不是说速度对我来说是个大问题。)限制不为0和255的原因是,他们希望值超出这些限制,并希望有一些净空。尽管sRGB公式经过精心设计,以模拟2.2的简单伽马,但在官方公式如此简单的情况下,没有必要使用简化公式。
public double Companding(double channel)
{
    double v = channel;
    double V = v <= 0.0031308 ? 12.92 * v : 1.055 * Math.Pow(v, 1 / 2.4d) - 0.055;
    return V;
}

public double InverseCompanding(double channel)
{
    double V = channel;
    double v = V <= 0.04045 ? V / 12.92 : Math.Pow((V + 0.055) / 1.055, 2.4);
    return v;
}