从rec正确转换709到sRGB

时间:2013-07-08 21:37:51

标签: image-processing colors yuv gamma

如何将存储为Y'CrCb的颜色(使用rec.709)正确转换为sRGB?

我正在使用HDTV视频,我正在使用libavcodec提取原始数据。 虽然我已成功完成转换,但我还没有确信我正确地做到了。 VLC提供了一个结果,在Gimp中使用'compose'结果转换为另一个结果,并且使用来自Web的代码也不一致。所以我没有找到可靠的参考资料来比较。

我的研究和目前最好的选择如下。 (值为浮点,范围为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 )

Wikipedia为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;

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

我还没有看到任何代码示例,包括此步骤。 我们需要转换rec指定的颜色空间。 709到sRGB中指定的那个。 AFAIK,两者之间的唯一区别是传递函数(即伽玛)。由rec指定的XY坐标。 709匹配sRGB,但我不知道为什么sRGB在rec时包含'Z'坐标。 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;

2 个答案:

答案 0 :(得分:0)

为了从线性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 是非线性的。

这些功能基于此处的等式: http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html

还有一个选项可以使用简化的sRGB 和压缩扩展函数 v = V ^ gamma ,其中 gamma 是2.2,如上所述网站。

答案 1 :(得分:0)

<块引用>

rec 指定的 XY 坐标。 709 匹配 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 中指定,是 2.4 完美伽玛值,适用于理想的 OLED 显示,几乎 sRGB EOTF,适用于在 200 勒克斯环境光下不完美的 LCD。这就是 Chrome 仅对 BT.709 使用 sRGB EOTF 的原因,这意味着“没有”EOTF,因为 Windows 默认使用。

<块引用>

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

它只是 0.04045/12.92 == 0.003130804954, 809/258400。