分页: 10 / 21

Re: x264 10bit编码推广讨论

发表于 : 2011-08-07 23:14
histamine
好吧,我再做一点补充

对于Dither_convert_8_to_16().Dither_convey_yuv4xxp16_on_yvxx()输出的16bit,LSB全是0,这是一个特殊的情况,也就是说不管是直接右移6位还是使用error diffusion dither方法转换到10bit均不会产生精度损失,也不会产生banding,两者结果应该是一致的(或者说两者应该是等价的?)

现在看一下x264里面的dither算法
65 #define DITHER_PLANE( pitch ) \
66 static void dither_plane_##pitch( pixel *dst, int dst_stride, uint16_t *src, int src_stride, \
67 int width, int height, int16_t *errors ) \
68 { \
69 const int lshift = 16-BIT_DEPTH; \
70 const int rshift = 2*BIT_DEPTH-16; \
71 const int pixel_max = (1 << BIT_DEPTH)-1; \
72 const int half = 1 << (16-BIT_DEPTH); \
73 memset( errors, 0, (width+1) * sizeof(int16_t) ); \
74 for( int y = 0; y < height; y++, src += src_stride, dst += dst_stride ) \
75 { \
76 int err = 0; \
77 for( int x = 0; x < width; x++ ) \
78 { \
79 err = err*2 + errors[x] + errors[x+1]; \
80 dst[x*pitch] = x264_clip3( (((src[x*pitch]+half)<<2)+err)*pixel_max >> 18, 0, pixel_max ); \
81 errors[x] = err = src[x*pitch] - (dst[x*pitch] << lshift) - (dst[x*pitch] >> rshift); \
82 } \
83 } \
84 }
注意我标红的那行,对于我们输入的LSB全是0的16bit数据,此时计算出来的量化误差却不等于0?!
我们分析下err = src[x*pitch] - (dst[x*pitch] << lshift) - (dst[x*pitch] >> rshift)这个运算
对于输入的某个像素点,假定其值为235*2^8,右移6位转换到10bit应该是235*2^2,由于没有精度损失,也不会产生banding,所以应该没有量化误差,也就是说基于error diffusion dither方法,计算该点的量化误差应该是0
我们把235*2^8和235*2^2代入上面那个式子
err = 235*2^8 - ( 235*2^2*2^6 ) - ( 235*2^2/2^4) = -58
看出问题了吗?原本应该是0的量化误差,按照这个error diffusion dither算法算出来却是-58

这个式子的问题在于它将16bit数值量化后的10bit数值,通过(dst[x*pitch] << lshift) + (dst[x*pitch] >> rshift)再次转换到16bit,再计算和最初16bit数值差值。但是我们输入的16bit数据是通过8bit数据左移8位得到的,如果我们肯定了“8bit数据左移2位得到10bit数据”的做法是正确的,那么10bit数据到16bit数据的转换应该是左移6位。

所以我认为err = src[x*pitch] - (dst[x*pitch] << lshift) - (dst[x*pitch] >> rshift);应该改成err = src[x*pitch] - (dst[x*pitch] << lshift);
这样err = 235*2^8 - ( 235*2^2*2^6 ) = 0,符合量化误差等于0
“这样修改过后的error diffusion dither算法将LSB全是0的16bit数据转换到10bit”和”将LSB全是0的16bit数据直接右移6位转换到10bit”是等价的

当然我上面也说过了,即使不修改这个dither算法,最终结果的误差也是很小的,肉眼可能无法察觉,对于某些数据可能最终结果根本没有误差

至于16bit输入x264时到底有没有调用这个dither函数,各位可以用printf大法验证

Re: x264 10bit编码推广讨论

发表于 : 2011-08-07 23:59
bakabakashi
从编码的结果来看,16bit输入时应该是有用这个dither函数的
8bit->10bit和8bit->16bit->10bit对比的话,两者有差异
但是跟histamine所说的一样"误差也是很小的,肉眼可能无法察觉"
当然为了准确的结果还是应该用printf大法验证

Re: x264 10bit编码推广讨论

发表于 : 2011-08-08 8:36
histamine
现在我开始认为
dst[x*pitch] = x264_clip3( (((src[x*pitch]+half)<<2)+err)*pixel_max >> 18, 0, pixel_max );
这行也有问题,为什么要“ *pixel_max >> 18 ”,改成“ >> 8 ”行吗?似乎这样改是错的啊
{:cat_14}

或者说我们其实根本不该用目前x264里面的dither算法,应该自己再写一个?
(如果做了上面两处修改之后,和修改之前的dither算法就不一样了)

Re: x264 10bit编码推广讨论

发表于 : 2011-08-08 9:36
06_taro
应该直接给avs写一个O10的function……

Re: x264 10bit编码推广讨论

发表于 : 2011-08-08 9:54
histamine
06_taro 写了:应该直接给avs写一个O10的function……
求思路

好吧,我想到一个思路
先计算出10bit数据,然后左移6位到16bit,输出给x264,在x264内部右移6位,得到我们之前计算出来的10bit数据

这样就避免了由于我们对“dither”理解不同引发的一系列问题 {:cat_16}

Re: x264 10bit编码推广讨论

发表于 : 2011-08-08 10:33
SAPikachu
06_taro 写了:应该直接给avs写一个O10的function……
直接O10看起来不可能,x264内部总是会先把源先转到16,然后路径又一样了。所以还是得直接改源代码。

Re: x264 10bit编码推广讨论

发表于 : 2011-08-08 10:44
histamine
histamine 写了:现在我开始认为
dst[x*pitch] = x264_clip3( (((src[x*pitch]+half)<<2)+err)*pixel_max >> 18, 0, pixel_max );
这行也有问题,为什么要“ *pixel_max >> 18 ”,改成“ >> 8 ”行吗?
验证了一下,这个改法是有问题的(仅指“ *pixel_max >> 18 ”改成“ >> 8 ”),所以可以无视我说的这段话(关于“ *pixel_max >> 18 ”改成“ >> 8 ”的那段话)

Re: x264 10bit编码推广讨论

发表于 : 2011-08-08 11:25
akiduki
06_taro 写了: 是的,其实看709的时候我也纠结了半天到底是不是和601一样是指直接<<2,不过我觉得应该不会是在两位LSB上补什么其他数据吧= =
如果理解成另一种,就是把原8-bit数据的低2位加回来,也恰好会发生235->943的问题,只不过这个原因是因为第二位也是11-_-

搞不清楚709在这么重要的地方用这样一句恶心的带着歧义的文字,而且这句话造成的歧义还能让自己扇自己嘴巴,果然德法佬弄出来的所谓标准就是千疮百孔。

Re: x264 10bit编码推广讨论

发表于 : 2011-08-08 12:30
dgwxx
像AU似的,做成那种无关range的计算不行么……
http://www.nmm-hd.org/bbs/thread-1035-1-1.html

Re: x264 10bit编码推广讨论

发表于 : 2011-08-08 17:57
06_taro
SAPikachu 写了: 直接O10看起来不可能,x264内部总是会先把源先转到16,然后路径又一样了。所以还是得直接改源代码。
输入10bit输出也是10bit还要做转换的话就不可理喻了……