RGB与YUV公式转换推导

目录

 

简介

Full Range公式推导

Limit Range推导

验证测试

参考资料

简介

RGB与YUV之间的转换有很多种标准,不同标准系数不一样,而且经常容易搞混淆,另外还有full range和limitrange的不同。其实这些转换系数都是推导出来的,有理论支撑的,并不是标准里直接给出来的系数。本篇文章主要是来介绍公式的推导。

Full Range公式推导

从YUV的定义中可知Y代表红绿蓝的比例混合,U代表蓝色与亮度Y的差量,V代表红色与亮度Y的差量,那么求RGB转YUV就是求混合比例。求YUV转RGB就是它的逆变化。如下图所示用公式展现了如何用混合比例实现RGB转YUV,其中KrKgKb就是RGB的混合比例。

f9de473af5574d8592c143fd74efc300.png

其中,KrKgKb三个系数是RGB转XYZ矩阵的第二行,RGB转XYZ可以参考【精选】颜色空间转换-从RGB到LCH-亮度饱和度色度_rgb转lch-CSDN博客。有了这三个系数,RGB转YUV的系数就确定了,所以RGB转YUV完全是由色域的色坐标决定的。计算RGB2YUV的系数只要对YUV2RGB求逆即可。

109bd823ffab4c44a5d9669033fd8aad.png

参考代码如下:

def GetFullRangeCoef(xy):
    matRGB2XYZ, matXYZ2RGB = GetRGBXYZMatrices(xy)

    rgb2yuv_coef = np.zeros((3,3), np.float32)
    rgb2yuv_coef[0, :] = matRGB2XYZ[1, :]
    kr = matRGB2XYZ[1, 0]
    kg = matRGB2XYZ[1, 1]
    kb = matRGB2XYZ[1, 2]
    rgb2yuv_coef[1, 0] = kr / (2*(kb - 1))
    rgb2yuv_coef[1, 1] = kg / (2*(kb - 1))
    rgb2yuv_coef[1, 2] = 0.5
    rgb2yuv_coef[2, 0] = 0.5
    rgb2yuv_coef[2, 1] = kg / (2*(kr - 1))
    rgb2yuv_coef[2, 2] = kb / (2 * (kr - 1))

    yuv2rgb_coef = np.linalg.inv(rgb2yuv_coef)

    return rgb2yuv_coef, yuv2rgb_coef

Limit Range推导

YCbCr的范围又叫limit range,tv range,YUV的范围又叫full range、pc range。

电视机中的YUV又叫YCbCr(Cb是ColorBlue缩写,Cr是ColorRed缩写),YCbCr为了解决吉布斯现象对范围进行了调整,下图说明了正弦函数模拟原始信号时波形的峰值超过原始信号8.9%,需要缩小YCbCr的范围防止溢出,譬如8位YUV的范围是[0,255],那么8位YCbCr的范围就是Y[16,235]UV[16,240],(255−235)/(235−16)=9.1略大于大于8.9%,16/(235−16)=7.3略小于8.9%。

62c49712e58c4eff9d166f6a11c4795a.png

limit range的YCbCr转RGB的流程如下:

f47c09e4c47c4cfab471782a8b024812.png

以8bit为例,Yscale=255 / (235 - 16),Uscale=255 / (240 - 16),Vscale=255 / (240 - 16),Yoffset=-16,Uoffset=-128,Voffset=-128。RGB2YCbCr只要对其进行求逆即可。

limit range公式推导参考代码如下:

def GetLimitRangeCoef(yuv2rgb_full):
    scale_mat = np.zeros((3, 3), np.float)

    scale_mat[0, 0] = 255 / (235 - 16)
    scale_mat[1, 1] = 255 / (240 - 16)
    scale_mat[2, 2] = 255 / (240 - 16)

    yuv2rgb_limit = yuv2rgb_full @ scale_mat
    rgb2yuv_limit = np.linalg.inv(yuv2rgb_limit)
    return rgb2yuv_limit, yuv2rgb_limit

验证测试

我们常用的YUV与RGB转换的标准主要有BT709,BT601,BT2020,他们的色坐标分别为:

#BT709
    xysRGB = np.array([
        [0.64, 0.33],
        [0.30, 0.60],
        [0.15, 0.06],
        [0.3127, 0.3290]
    ])
    #BT2020
    xyBT2020 = np.array([
        [0.708, 0.292],
        [0.17, 0.797],
        [0.131, 0.046],
        [0.3127, 0.3290]
    ])
    #BT601
    xyNTSC = np.array([
        [0.67, 0.33],
        [0.21, 0.71],
        [0.14, 0.08],
        [0.3101, 0.3162]
    ])

然后参考代码如下:

def TestsRGB709():
    #BT709
    xysRGB = np.array([
        [0.64, 0.33],
        [0.30, 0.60],
        [0.15, 0.06],
        [0.3127, 0.3290]
    ])
    #BT2020
    xyBT2020 = np.array([
        [0.708, 0.292],
        [0.17, 0.797],
        [0.131, 0.046],
        [0.3127, 0.3290]
    ])
    #BT601
    xyNTSC = np.array([
        [0.67, 0.33],
        [0.21, 0.71],
        [0.14, 0.08],
        [0.3101, 0.3162]
    ])

    rgb2yuv_coef, yuv2rgb_coef = GetFullRangeCoef(xyNTSC)
    print('rgb2yuv_full:', np.round(rgb2yuv_coef, 4))
    print('yuv2rgb_full:', np.round(yuv2rgb_coef, 4))

    rgb2yuv_limit, yuv2rgb_limit = GetLimitRangeCoef(yuv2rgb_coef)
    print('rgb2yuv_limit:', np.round(rgb2yuv_limit, 4))
    print('yuv2rgb_limit:', np.round(yuv2rgb_limit, 4))

BT601得到的结果如下:

rgb2yuv_full: 
[[ 0.2989  0.5866  0.1144]
 [-0.1688 -0.3312  0.5   ]
 [ 0.5    -0.4184 -0.0816]]
yuv2rgb_full: 
[[ 1.     -0.      1.4021]
 [ 1.     -0.3455 -0.7145]
 [ 1.      1.7711  0.    ]]
rgb2yuv_limit: 
[[ 0.2567  0.5038  0.0983]
 [-0.1483 -0.291   0.4392]
 [ 0.4392 -0.3675 -0.0717]]
yuv2rgb_limit: 
[[ 1.1644 -0.      1.5962]
 [ 1.1644 -0.3933 -0.8134]
 [ 1.1644  2.0162  0.    ]]

 BT709得到的结果:

rgb2yuv_full: 
[[ 0.2126  0.7152  0.0722]
 [-0.1146 -0.3854  0.5   ]
 [ 0.5    -0.4542 -0.0458]]
yuv2rgb_full: 
[[ 1.     -0.      1.5747]
 [ 1.     -0.1873 -0.4682]
 [ 1.      1.8556 -0.    ]]
rgb2yuv_limit: 
[[ 0.1826  0.6142  0.062 ]
 [-0.1007 -0.3386  0.4392]
 [ 0.4392 -0.3989 -0.0403]]
yuv2rgb_limit: 
[[ 1.1644 -0.      1.7927]
 [ 1.1644 -0.2132 -0.533 ]
 [ 1.1644  2.1124 -0.    ]]

BT2020得到的结果:

rgb2yuv_full: 
[[ 0.2627  0.678   0.0593]
 [-0.1396 -0.3604  0.5   ]
 [ 0.5    -0.4598 -0.0402]]
yuv2rgb_full: 
[[ 1.     -0.      1.4746]
 [ 1.     -0.1646 -0.5714]
 [ 1.      1.8814  0.    ]]
rgb2yuv_limit: 
[[ 0.2256  0.5823  0.0509]
 [-0.1227 -0.3166  0.4392]
 [ 0.4392 -0.4039 -0.0353]]
yuv2rgb_limit: 
[[ 1.1644 -0.      1.6787]
 [ 1.1644 -0.1873 -0.6504]
 [ 1.1644  2.1418  0.    ]]

可以看到得到的系数和我们常用的是一样的,这样只要给定色域,其实就有一组对应的转换系数,完全是由色域大小决定的。

参考资料:

HDR转SDR实践之旅(四)YUV转RGB矩阵推导 - 掘金

网上流传的矩阵错了?浅谈如何正确推导视频YUV转RGB矩阵 - 知乎

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值