头像
mawen1250
核心会员
核心会员
帖子: 670
注册时间: 2011-07-24 20:33

RGB与YCbCr相互转换的测试

测试方法参考了这里:http://goldenhige.cocolog-nifty.com/blo ... isynt.html
基本原理就是,用AVS函数将输入的RGB源(来自图片文件)通过不同方式转换为YCbCr,再转换回RGB并保存为PNG,用IrfanView的“图像——图像信息——单一色彩数”来查看RGB图片的色彩数量。
使用了这里的RGB全色彩图片第三幅作为测试源:http://davidnaylor.org/blog/2005/02/all ... b-colours/
图片

测试用AVS示例:
► 显示剧情透露 Avisynth Internal
► 显示剧情透露 dither package 8bit Rounding
► 显示剧情透露 dither package 8bit Truncate
► 显示剧情透露 dither package 8bit Ordered Dither
► 显示剧情透露 dither package 8bit Floyd-Steinberg Dither
► 显示剧情透露 dither package 10bit Rounding
► 显示剧情透露 dither package 10bit Truncate
结果和下面相同
► 显示剧情透露 dither package 10bit Truncate
中间转换成10bit并使用dither我只想到了这样的方法,看起来有些别扭,不知道有没有更好的办法。
► 显示剧情透露 dither package 10bit Ordered Dither
► 显示剧情透露 dither package 10bit Floyd-Steinberg Dither
► 显示剧情透露 dither package 16bit Rounding
测试结果的截图:http://115.com/file/dput4vr1
色彩数量测试结果:
RGB YUV.png
注:
1.转换时经过Dither处理后的色彩数量仅供参考,并不能直接与Rounding的结果进行比较。Dither更主要的作用是视觉效果上对转换的error进行补偿,所以比较截图更有意义。
2.Chroma Subsample为420和422时的色彩数量也无法与444做比较,这两者的差异在截图里更能显示出来。
3.凡是能还原出16777216色彩的方式,结果都是无损的(对比源png与处理后的png,两者Hash值完全相同)。
4.f3kdb里dither_algo=1时用的bit depth转换方式是Truncate(只舍不入)而不是Rounding(四舍五入),损失会比较大。

结论:
1.将8bit RGB转换为8bit YCbCr后,损失最小的方式是444采样、PC Range,这点无论从数据上还是截图中都能明显看出来。
2.将8bit RGB用Rounding转换为任意bit的YCbCr的方法中,10bit是可以实现无损转换的最小bit depth。
3.将8bit RGB用Truncate转换为任意bit的YCbCr的方法中,11bit是可以实现无损转换的最小bit depth。
4.不知道为什么,Truncate转换成的10bit比9bit色彩数量还要少,测试多次均为这个结果,不知道是哪里出了问题还是本来就是这样。
5.从截图里看到,使用无Dither的方式转换为8bit 422或8bit 420后,色彩过渡区域不但会出现banding,还会出现条纹状pattern,使用任意一种Dither方式后均能明显改善这种情况。在边缘处420与422会产生较明显的模糊,422只在Horizontal上产生,420则是在Horizontal和Vertical上均产生。

实际使用时的注意事项:
以上测试仅仅是理论上的——全过程都是Rounding的话用任何matrix和range的10bit YCbCr都可以与8bit RGB进行无损转换。但在实际使用中这点又是不太实际的,播放中使用Rounding进行RGB转换不会是大部分解码器/渲染器的默认设置。
而且,例如在播放中视频的分辨率和显示器的分辨率不一致时,需要做resize。以madVR为例,会以32bit float在YUV下做resize然后转换成8bit RGB输出——这时,如果通过dithering进行bit depth转换,高精度resize的结果是保留下来了,但用rounding转出来的10bit YCbCr画面本身就是有误差的;而如果是resize之后rounding成8bit RGB输出,之前用rounding转出来的10bit YCbCr再进行rounding转到8bit RGB的结果是正确的,但高精度resize的结果通过rounding转出来的就是有误差的。

针对以上的问题,推荐的方法是使用YCgCo与RGB进行转换,10bit YCgCo可以和8bit RGB进行无损的互相转换,因为转换结果小数点后均为0,所以无需error diffusion——通过rounding或者是error diffusion的dithering转换出来的结果都是一样的。AVS中实际使用的参考方法:
[syntax=avisynth]
# 8bit RGB input
Dither_convert_rgb_to_yuv(lsb=true, tv_range=false, matrix="YCgCo", output="YV24")
# stacked 16bit YCgCo
Down10(stack=false, dither=-1, tvRange=true)
# interleaved 10bit YCgCo output
[/syntax]
x264选用支持444的版本例如x264_64_tMod-10bit-all.exe,加入以下参数。
由于x264默认给444的chroma-qp-offset +6,所以对于这种原生444的视频源--chroma-qp-offset可以适当减少一些(注意psy-rd的值也有影响)。

代码: 全选

--input-depth 10 --input-csp i444 --output-csp i444 --input-range pc --range pc --colormatrix YCgCo --chroma-qp-offset -3
回放环境的话最新版本的MPC-HC、LAV Filters、madVR都能支持YCgCo。

如果仍然坚持转换为YCbCr的话,实际使用时建议还是开启dither进行转换来减小error,毕竟压制时用的也是有损的视频编码,所以通过全程rounding来实现无损本身就是一件不靠谱的事情。
[/color]
上次由 mawen1250 在 2013-05-13 11:02,总共编辑 3 次。

回到 “理论讨论 / Theoratical discussion”