如何正确将存储为Y'CrCb(使用rec. 709)的颜色转换为sRGB?
我正在处理HDTV视频,并使用libavcodec提取原始数据。虽然我成功地进行了转换,但我仍然不确定我是否做得正确。VLC提供一个结果,在Gimp中使用'compose'进行转换会得到另一个结果,而使用网上的代码也不一致。因此,我没有找到一个可靠的参考来进行比较。
我的研究和当前的最佳选择如下。(值以浮点数表示,范围为0.0-1.0)我最不确定的是伽马校正。它使它比我预期的要亮一些,但我不能说它看起来有错...
Studio-swing去除
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
REC. 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:
double r = y + 2*(1.0-kr) * v;
double b = y + 2*(1.0-kb) * u;
double g = ( y - kr * rr - kb*rb ) / kg;
使用Cr和Cb直接进行G处理:
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
我没有看到包括此步骤的任何代码示例。我们需要将由rec. 709指定的颜色空间转换为由sRGB指定的颜色空间。据我所知,两者之间唯一的区别是传输函数(即伽马值)。由rec. 709指定的XY坐标与sRGB相匹配,但我不知道为什么sRGB包括“Z”坐标而rec. 709则不包括。这会有影响吗?(我对CIE XYZ一无所知。)
rec. 709规定了如何对线性RGB进行伽马编码:
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;