关于修复YUV视频色彩问题

前言:

最近自己开发的音游遇到WIN7用户背景动画(h264视频)花屏、绿屏,程序崩溃的问题。所以就在寻找一个独立解码方案,最后在朋友的推荐下(感谢沙袋同学)使用了一款叫Vive Media Decoder的插件

但是问题来了,这个插件解码出来的视频颜色明显有别于其他标准播放器(windows原生播放器、potplayer)

话不多说先上图:

播放相同的视频文件,左为Unity中显示截图,右为potplayer中截图

我们初步可以看到左图相对于右图色彩不一致,白色部分不够白,黑色不够黑,整体画面灰蒙蒙。为了进一步证实,我打开ps,用吸管工具验证:

日光灯处吸色,颜色为一个灰色
同样位置吸色,颜色为纯白6个f

根据本人多年看片的经验(并不),大致判断可能是解码时色阶还原不正确。在此之前我有听说过tv电视的一个标准,一个叫有限范围rgb[16, 235],而pc用的是全范围rgb[0, 255]

照这个思路要还原全范围rgb颜色只需要将color=(255/(235-16))*(color-16),也就是重新将[16, 235]映射到[0, 255]范围上

这时候我在插件的shader里面加上代码:color=1.164383*(color-0.0627451)

魔法数1.164383表示255/(235-16), 0.0627451是16映射到float[0, 1]的大小

经过反复测试、对比、吸色判断,颜色还是和标准播放器颜色有较大差距。到此已经翻车了。就在思考到底是哪里出了问题。。。

经过反复查阅相关资料,发现yuv转换rgb问题并不简单: https://en.wikipedia.org/wiki/YUVhttps://en.wikipedia.org/wiki/YCbCr

上面提到转换后y值取值范围是[0, 1],u取值范围是[-0.436, 0.436],v 取值范围[-0.615, 0.615],对应shader里转换公式确实有将yuv贴图采样转化为yuv的标准取值范围:

插件shader代码片段

那么问题来了,既然视频的3通道需要通过转换才能得到正真意义上的yuv值,那么视频本身存储的到底是一种什么格式?

然后我又留意到wiki上Relation with Y′CbCr的解释,大致意思是:

  1. yuv和ycbcr相关,但是又有所不同,yuv和ycbcr是具有不同的偏移量和比例因子的两种格式
  2. yuv u和v分量是带符号的值,可以是正数或负数
  3. ycbcr通常将所有通道缩放到16-235范围或0-255范围,cb和cr重新映射成无符号的值,0对应128
  4. 对于视频格式( 例如MPEG-2 )每个分量有缩放和偏移,y分量取值范围为[16, 235],cb/cr为[16, 240]

所以根据上述信息,我判断ycbcr才是保存成视频的最终格式,原因很简单,从yuv本身取值范围来讲,带符号的u和v分量本身就不可能保存成贴图,而ycbcr cb和cr分量是无符号,并且取值范围是16-235或0-255,这点就符合作为视频格式的存储

既然已经抓到问题的重点接下来就是修复思路:

BT.709转换矩阵: https://www.vocal.com/video/rgb-and-yuv-color-space-conversion/
  1. y分量需要将16-235范围重映射到0-255
  2. cb和cr分量需要将16-240重映射到0-255
  3. 将ycbcr范围重映射到yuv范围
  4. 视频文件信息由potplayer查看,色彩空间是BT.709而不是BT.601,最终色彩转换需要用到上图的转换矩阵

具体公式:

将ycbcr重新映射到全范围0-255:
y=(255/(235-16))*(y-16)
cb=(255/(240-16))*(cb-16)
cr=(255/(240-16))*(cr-16)

根据u和v的取值范围,将ycbcr映射到yuv(整数):
y=y
u=0.872*(cb-128)
v=1.230*(cr-128)

应用矩阵叉乘展开:
r=y+1.28033*v
g=y-0.21482*u-0.38059*v
b=y+2.12798*u

整合公式:
r=(255/(235-16))*(y-16)+(255/(240-16))*1.28033*(1.230*(cr-128))
g=(255/(235-16))*(y-16)-(255/(240-16))*0.21482*(0.872*(cb-128))-(255/(240-16))*0.38059*(1.230*(cr-128))
b=(255/(235-16))*(y-16)+(255/(240-16))*2.12798*(0.872*(cb-128))

计算最终常量,并将其转换为片元shader:

r=1.164384*(y-0.0627451)+1.792748*(cr-0.5019608)
g=1.164384*(y-0.0627451)-0.2132472*(cb-0.5019608)-0.5329109*(cr-0.5019608)
b=1.164384*(y-0.0627451)+2.1124*(cb-0.5019608)
修改后的shader代码片段

最后我们来看看最终效果:

结论:最终对比红色方框内吸管拾取的颜色rgb值完全一致!大成功!

类别:

标签:

没有回应

发表评论