笔者按,最近在做视频TM的相关工作,具体是给定一个HDR视频(10bit的YUV420格式),要对其进行TM,写了一个算法但总是有非常离谱的色差,尤其是红色和蓝色通道。仔细检查之后发现是拿到的HDR视频的颜色空间是BT2020的,但转换后的SDR视频是BT709的,需要先对颜色空间进行降级才可以进行处理。这里记录一下处理过程。
首先贴两个地址
这两个都是ITU搞出来的标准纲领性文件。第一个文件讲了怎么把BT709转到2020(包括YUV、RGB的处理办法),第二个讲了怎么把BT2020转换到BT709(但只有RGB信号)。
在文献[2]中,简单粗暴地给出了一个示意图
E'RGB指的是“归一化的非线性RGB值”,而E_RGB指的是“归一化的线性RGB值”。
但在算法中,我拿到的数据是10bit的YUV420数据,所以需要先把10bitYUV420数据转换到E'RGB。怎么转?文献没提。但文献[1]中将如何将709转换到2020时,给了一个图
这里除了 E'RGB和E_RGB之外,还出现了E'YCBCR和D'YCBCR。其中E'YCBCR指的是“归一化的YCBCR值”,而D'YCBCR指的是“量化的YCBCR值”
所以我这里明白了,我在算法输入端所拿到的所谓10bitYUV420数据,实际上就是这个D'YCBCR值。那么接下来,我们结合两篇文献,就可以推导出相应过程了。
Step 1. 将D’YCbCr(2020)转为E’YCbCr(2020)
其实就是如下过程的反过程
简单推导,得
E_Y = ((D_Y / 4) - 16) / 219
E_Cb = ((D_Cb / 4) - 128) / 224
E_Cr = ((D_Cr / 4) - 128) / 224
Step 2. 将E’YCbCr(2020)转为E’RGB(2020)
文献[1]中给出了反过程
所以这里只要做一个简单的矩阵求逆(这里直接给出了逆)
需要注意的是,下面给出的代码中的E_YCbCr和E_RGB这样的变量,其形状都是height x width x 3的,所以这里讨巧做了一个线性代数的转换,将左乘变成了右乘。下面很多地方都利用了这个方法,后续就不再做解释说明了。
m_YCbCr2020_to_RGB2020 = np.array([[1.00000000e+00, -7.82308321e-18, 1.47460000e+00],
[1.00000000e+00, -1.64553127e-01, -5.71353127e-01],
[1.00000000e+00, 1.88140000e+00, 2.38961873e-17]])
E_RGB = np.matmul(E_YCbCr, m_YCbCr2020_to_RGB2020.T)
Step 3. 将E’RGB(2020)转为E RGB(2020)
E_RGB = np.power(E_RGB, 2)
# 另一个版本是
# E_RGB = np.power(E_RGB, 2.4)
Step 4. 将E RGB(2020)转为E RGB(709)
m_RGB2020_to_RGB709 = np.array([[1.66051121, -0.58771059, -0.07280062],
[-0.12456141, 1.13296051, -0.00839911],
[-0.01816769, -0.1005606, 1.11872828]])
E_RGB = np.matmul(E_RGB, m_RGB2020_to_RGB709.T)
Step 5. 将E RGB(709)转为E’RGB(709)
E_RGB = np.power(E_RGB, 1/2)
# 另一个版本是
# E_RGB = np.power(E_RGB, 1/2.4)
Step 6. E’RGB(709)转为D’RGB(709)
【这里是以10bit为例的,如果是8bit,去掉后面的*4即可】
D_RGB = ((E_RGB * 219 + 16) * 4).astype("uint16")
这样转换过后,就没有了色偏问题。