视频ToneMapping(HDR转SDR)中的颜色空间转换问题(BT2020转BT709,YCbCrYUV和RGB)
Posted q274488181
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了视频ToneMapping(HDR转SDR)中的颜色空间转换问题(BT2020转BT709,YCbCrYUV和RGB)相关的知识,希望对你有一定的参考价值。
笔者按,最近在做视频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")
这样转换过后,就没有了色偏问题。
HDR&ToneMapping
HDR
high dynamic range.
很多程序朋友(包括我)都是从dxsdk上看到和学习这个概念,开始学习的更多的是一整套hdr sample的流程:
- 在float render target上去render scene
- 后面很多console上的游戏使用rgbm等编码方式来节省内存和bandwidth
- 通过down sample去计算亮度
- treyarch是cook到场景数据里面,省了这个down sample的过程
- 根据亮度对场景做一个矫正(tone mapping)最后输出到一个rgb8的render target上
- dynamic range:reinhard的<tone mapping>论文中定义:一个场景中最高亮度与最低亮度的比是dynamic range。
- low dynamic range : 之所以出现这种情况是图像存储介质(打印纸,照片,电脑屏幕等)精度有限造成的,导致在range上没法完全记录一个场景的亮度信息,只能记录有限的一部分,比如游戏里常见的在rgba8上渲染,每个channel大于1的部分就被截取到1了。
- high dynamic range : 准确讲是high dynamic range imaging,是指一种图像技术,它能让图像表示一个比原有技术(之前的LowDynamicRange)更大(greater)的dynamic range
- 这样就可以更加准确和真实的描绘一个场景
- 自动根据亮度矫正明暗:让我们晚上看东西也能比较清楚,一开灯眼睛要矫正一会回来
- 局部适应性:比较经典的图是:
- 曝光率问题---解决:exposure adjustment。和照相时候曝光原理差不多,白天亮曝光就短一些,晚上曝光长一些,编程时候就是计算render target的平均亮度,然后矫正,这样沙漠的白天和丛林的夜晚都可以在游戏中的rgba8上有一个良好的体现。在hable的论文里,这个属于不同的场景之间的处理问题范畴。
- 压缩的过程中不可避免的涉及到重新分配亮度值,怎么做来得到更好的尽可能不失真的画面这个解决方案就是tone mapping
ReinhardToneMapping
tone mapping方面比较著名的reinhard哥:
reinhard主页
http://www.cs.ucf.edu/~reinhard/cdrom/
paper link:
http://www.cs.ucf.edu/~reinhard/cdrom/tonemap.pdf
tone mapping干什么的?
dxsdk里面也有说,本来是摄影中提出的概念,解决怎么把场景中范围巨大的亮度值放到范围有限的存储空间中来(照片,打印机。。。),达到一个让人喜欢的结果。
这里面一点是“让人喜欢的结果”,它是一个含有主观意味的东西,没有一个绝对的标准,也没有说什么是绝对的对和错,根据游戏类型和开发者,玩家口味,大可选择自己喜欢的结果,tonemapping是达到这一结果的方法而已。
tonemapping相关的研究是从摄影技术中发展过来的,只不过digital imaging有比摄影洗相片更好的一个优势,可以进一步发展:
首先明确和定义一些概念:
- zone:存储空间的亮度阶这么一个概念,比如print只有11个zone
- middle grey:中间的亮度
- dynamic range:指场景中最高亮度与最低亮度的比值
- 这是一个最学术派的定义,具体上摄像师一般会追求细节还可以明辨的range
- key:描述整个场景亮度的数值
- dodging and burning:把高亮度的东西亮度降低为dodging,把低亮度的部分加亮为burning
tone mapping就是一个原始颜色向目标颜色映射的过程,不同的函数呈现一些不同的特点,这里列一些,看下对比:
ppt中有更多的一些对比,这里直接总结filmic tone mapping的好处就是:
- 向暗色过渡的更“脆”
- 高亮部分更柔和
- 在input color的match上也更接近linear
A = Shoulder Strength
B = Linear Strength
C = Linear Angle
D = Toe Strength
E = Toe Numerator
F = Toe Denominator
Note: E/F = Toe Angle
LinearWhite = Linear White Point Value
F(x) = ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F)) - E/F;
FinalColor = F(LinearColor)/F(LinearWhite)
在具体的代码看hable的blog更好了:
float A = 0.15;
float B = 0.50;
float C = 0.10;
float D = 0.20;
float E = 0.02;
float F = 0.30;
float W = 11.2;
float3 Uncharted2Tonemap(float3 x)
return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F;
float4 ps_main( float2 texCoord : TEXCOORD0 ) : COLOR
float3 texColor = tex2D(Texture0, texCoord );
texColor *= 16; // Hardcoded Exposure Adjustment
float ExposureBias = 2.0f;
float3 curr = Uncharted2Tonemap(ExposureBias*texColor);
float3 whiteScale = 1.0f/Uncharted2Tonemap(W);
float3 color = curr*whiteScale;
float3 retColor = pow(color,1/2.2);
return float4(retColor,1);
以上是关于视频ToneMapping(HDR转SDR)中的颜色空间转换问题(BT2020转BT709,YCbCrYUV和RGB)的主要内容,如果未能解决你的问题,请参考以下文章