H.264中SAD SATD及常见知识点

经常有人问我这方面的问题,今天总结归纳一下。

众所周知,评价编码效率的有两大指标:码率和PSNR。码流越小,则压缩率越大;PSNR越大,重建图像越好。在模式选择的时候,判别公式实质上也就是对二者的综合评价。

首先以RDO为例,模式对应的代价:J(mode)=SSD+λ*R(ref,mode,mv,residual)

这里,SSD是指重建块与源图像的差值均方和;λ是拉格朗日乘子,就当是权值吧^_^;R就是该模式下宏块编码的实际码流,包括对参考帧、模式、运动矢量、残差等的比特总和。当然如果是帧内模式,就只有R(mode,residual)。

很多人迷惑的是,改宏块还没编码啊,怎么知道它的码流和重建图像?实际上,RDO就是对每个模式都实际编码一次,得到J(mode),然后选择J(mode)最小的模式为实际编码模式。就像编码器引入了一个大反馈,这也正是JM选用RDO编码起来龟速的原因,当然,编码效率最佳。

后来,“随意”注意到,不论熵编码选用cavlc还是cabac,各个模式下的residual编码都使用cavlc,这就是说选用cabac,模式选择时得到的R不是实际的R,为什么此时不用cabac呢?难道cabac复杂么?我的看法是因为cabac会对模型表更新数据。解码端是没有模式选择模块的,如果编码端此时使用cabac,会造成编解码端模型表不匹配,不能正常解码。

 λ的取值是根据实验得到的。使用B帧与使用B帧的λ值是不一样的。具体值忘了,^_^,看相关文章。

前已所述,RDO包含各模式的实际编码过程,也就是变换量化、熵编码、反变换反量化、重建等,计算量是相当大的,实时编码领域不可能直接使用。因此,就有了下面的替代公式:

J(mode)=SAD+λ*R(ref,mode,mv)

J(mode)=SATD+λ*R(ref,mode,mv)

这里SAD就是该模式下预测块与源图像的绝对误差和。比特R中少了对residual的编码,也就是运动估计后就可以直接得到该模式的J(mode)值,极大的减少了运算复杂度。SATD就是对残差进行哈德曼变换后的系数绝对和,在大多数情形下,SATD比SAD评价效果更好些,我对foreman CIF图像的测试,psnr增加了约0.2db,码流差不多。当然,SATD比SAD多了个变换,计算量大些。

注意,此时的λ与RDO的λ取值是不一样的。

容易困惑的还有,运动估计的匹配准则,很多运动估计的论文中都直接是SAD或SSE。编码器中对残差、MV、ref都要编码,所以匹配准则也就是SAD和码流R的综合评价!!!在同一个模式下,参考块与编码块的不同信息有ref、MV,故匹配准则为:

Jmotion=SAD+λ*R(ref,mv)

                                     【上文出自:http://zmshy2128.blog.163.com/blog/static/2544637200658104210/】

——————————————————————————————————————————————————————————

《群“H264乐园”中的帖子》

264标准只定义了码流的格式编码器实现是各公司自己的事,只要形成的码流符合标准就行解码器必须按照这个格式来,这样任何符合标准的码流都可以解出来
Q:什么是SAD,SAE,SATD,SSD,SSE,MAD,MAE,MSD,MSE?
A:SAD(Sum of Absolute Difference)=SAE(Sum of Absolute Error)即绝对误差和
SATD(Sum of Absolute Transformed Difference)即hadamard变换后再绝对值求和
SSD(Sum of Squared Difference)=SSE(Sum of Squared Error)即差值的平方和
MAD(Mean Absolute Difference)=MAE(Mean Absolute Error)即平均绝对差值
MSD(Mean Squared Difference)=MSE(Mean Squared Error)即平均平方误差
 
Q:如果不用率失真最优化,为什么选择SATD+delta×r(mv,mode)作为模式选择的依据?为什么运动估计中,整象素搜索用SAD,而亚象素用SATD?为什么帧内模式选择要用SATD?
SAD即绝对误差和,仅反映残差时域差异,影响PSNR值,不能有效反映码流的大小。SATD即将残差经哈德曼变换的4×4块的预测残差绝对值总和,可以将其看作简单的时频变换,其值在一定程度上可以反映生成码流的大小。因此,不用率失真最优化时,可将其作为模式选择的依据。
一般帧内要对所有的模式进行检测,帧内预测选用SATD的原因同上。
在做运动估计时,一般而言,离最优匹配点越远,匹配误差值SAD越大,这就是有名的单一平面假设,现有的运动估计快速算法大都利用该特性。但是,转换后SATD值并不满足该条件,如果在整象素中运用SATD搜索,容易陷入局部最优点。而在亚象素中,待搜索点不多,各点处的SAD差异相对不大,可以用SATD选择码流较少的匹配位置
 
Q:下面代码的功能是什么?
if(x & (~255))
{
pix[i] = (-x) >> 31;
}
else
{
pix[i] = (unsigned char)x;
}
A:
x的定义是short型,pix定义的是unsigned char型
这段代码可以这么理解(这段代码的功能):
如果x<0,-x为正,-x左移31位肯定为0,那么pix[i]=0,
如果x>255,那么pix[i]=255,否则pix[i]=x;
 
#include <stdio.h>
main()
 {
 short x;
 unsigned char pix;
 x=1024*16;
if(x & (~255))
pix = (-x) >> 16;
else
pix = (unsigned char)x;
printf("pix=%d\n",pix);
}
 
Q:CABAC中开始时各字符出现的概率是怎么得到的?
A:基于查表实现的
 
Q:What is RVLC?
A:It is a VLC method which can be decoded from left to right and from right to left exclusively
 
Q:RDO模型用来干什么?RQ模型又用来干什么?
A:RDO用来确定编码模式的,保证码率比特数和图像失真的最佳权衡点,而RQ是在上一层码率数一定的情况下用来确定下一层分配的比特数。RQ先于RDO进行。
Q:帧,场,图像的联系与区别是什么?
frame;逐行扫描图像
field:隔行扫描图像,偶数行成为顶场行,奇数行称为为底场行,所有顶场行称为顶场,同样所有底场行称为底场。
pictue:场和帧都可认为是图像
[注:SUPERPUMA语]
顶底场分别编码,对应位置的宏块叫做宏块对。顶场对已编码的顶/底场预测编码。底场一般对顶场预测编码
frame;逐行扫描图像
field:隔行扫描图像,偶数行成为顶场行,奇数行称为为底场行,所有顶场行称为顶场,同样所有底场行称为底场。
pictue:场和帧都可认为是图像
[注:SUPERPUMA语]
顶底场分别编码,对应位置的宏块叫做宏块对。顶场对已编码的顶/底场预测编码。底场一般对顶场预测编码
 
Q: I帧和P帧的概念比较好懂,B帧的概念有些模糊,只知道加了B帧图像质量会更好,请问对B帧该怎么理解?
A: B 帧在 MPEG-4 中有四种参考模式如果是同时参考前后的画面压缩则记录的是 和 (前画面 pixel 值 + 后画面 pixel 值)/2 的差值,也就是 和 「前后画面的平均」的差值。所以记录的差值个数和 P 帧一样,只有一个,没有增加。而因为 B 帧位于前后画面的中间,以「前后画面的平均」,也就是「前后画面的中间值」来作为预测数值(预测 B 帧的 pixel 数值为多少?如果有误差,再记录差值),这样这个预测数值会比单独使用前一个画面来预测,更接近目前真正的 B 帧的数值,可想而知,如此所需要记录的差值就会很小甚至可以根本不用记录,所以便可以省下很多的 bits,提高压缩率。
除了压缩率以外,B 帧对画质的影响也是有的,因为 B 帧这种参考前后画面的特性,等于有内插(interpolation)的效果,所以可以减少噪讯。
Golomb 用于运动矢量,模式类型,头信息等编码
CAVLC用于残差编码
CABAC都可以
 
常用的测试序列选择:
Coastguard :为物体的相对运动和镜头移动
flower :为物体的剧烈运动和镜头的快速移动
garphone :为物体的快速转换
foreman :为物体转换和镜头移动
mobile&calendar :为物体的多种运动和镜头移动
 
为什么帧内预测要用未滤波的图像而不是滤波后的图像呢?
帧内预测只能用滤波前的值,因为帧内预测的时候边界滤波还不能进行,帧内预测所需要的象素点的边界的滤波不能进行,因为需要等到当前宏块解完后才开始,但是你现在就在解当前宏块,所以为了避免预测和滤波的耦合应该分开来单独做,prediction的时候保存一个line buffer就解决问题了
 
Q:CQM_4IY,CQM_4IC,CQM_4PY,CQM_4PC,CQM_8IY,CQM_8PY的含义?
A:cqm_4iy->INTRA4X4_LUMA,cqm_4ic->INTRA4X4_CHROMA
cqm_4py->INTER4X4_LUMA,cqm_4ic->INTER4X4_CHROMA
cqm_4py->INTER8X8_LUMA,cqm_4ic->INTER8X8_CHROMA
 
 
 
Q:在x264的x264_cqm_init( x264_t *h )函数中:
for( i = 0; i < 16; i++ )
{
h->dequant4_mf[i_list][q][0] = def_dequant4[q] * h->pps->scaling_list[i_list];
h-> quant4_mf[i_list][q][0] = def_quant4[q] * 16 / h->pps->scaling_list[i_list];
}
第二个式子为什么*16?
A:你不要管这个16,这个16是约定成俗的,要和量化,凡量化一直考虑
Q:unquant4_mf[4][52][16]这个矩阵也是量化里面的,你看量化矩阵和反量化矩阵都是4维的,而这个是3维的
A:unquant4_mf[i_list][q],是0~15,是线性排列,quant4_mf[i_list][q%6][0],是[][]矩阵排列
Q:在上面的程序中量化和反量化矩阵为什么第三维只为0呢?
A:为0才对,因为本来定义[4][4],现在要线性访问16个成员,就必须[0]了
 
Q:关于skip模式的问题
A:When a Skipped macroblock is signalled in the bitstream, no further data is sent for that macroblock. The decoder calculates a vector for the skipped macroblock and reconstructs the macroblock using motion-compensated prediction from the first reference picture in list 0.
p_skip 就是说只计算参考帧中的mv,传输的是0数据,直接把参考中的匹配宏块拿过来就行了,skip模式只传送mb_type,其他信息都是从参考桢中获取,在解码端计算MV及恢复残差数据,MV不是从参考帧获得的,是在解码端计算,mv是根据周围的相邻块的mv进行计算,好像是取中间值,有点像MV在运动估计时候的预测,是只传送mb_type,其他信息都可以计算出来,这就是skip。
To have a SKIP mode in H.264, a macroblock should meet following conditions all together [5]:
(i) the best motion compensation block size is 16x16,
(ii) reference frame is just one previous one,
(iii) motion vector is (0,0) or the same as its PMV, and(iv) its transform coefficients are all quantized to zero.
(iv) its transform coefficients are all quantized to zero
 
FrameSkip,该参数是对原始YUV帧丢弃数,就是说每隔一帧(I或者P,不包括B)丢弃FrameSkip帧。
NumberBFrames,就是两个编码帧中间B帧的数目,该数必须小于FrameSkip
FramesToBeEncoded,总共要编码的帧数,不包括B帧.因为在编码过程中 b帧对其他帧并不产生影响,而且在实时编码中,如果负担过重;或带宽有限,可以有选择的丢弃b帧
IntraPeriod,每IntraPeriod帧(I/P帧)有一个I帧编码
如果你选择的frameskip>=1,numberbframes=0,intraperiod>1,序列类型:ipp...ipp...
如果你选择的frameskip>=1,numberbframes=1,intraperiod>1,序列类型:ipbpb...ipbpb...
如果你选择的frameskip>=2,numberbframes=2,intraperiod>1,序列类型:ipbbpbb...ipbbpbb...
 
P_Skip的特别之处在于码流中不传输MVD数据,预测块的大小肯定史16*16。当下面四个条件满足任意一个时,当前宏块的MV预测值直接置为(0,0),不满足时当作普通P宏块处理
– mbAddrA is not available
– mbAddrB is not available
– refIdxL0A is equal to 0 and both components of mvL0A are equal to 0
– refIdxL0B is equal to 0 and both components of mvL0B are equal to 0
 
Q:为何在cavlc编码的时候,第一个负数要加1?
A:如果拖尾小于3,说明第一个level的绝对值值肯定大于1。因此,level为正时,减1;为负时加1。可降低码流
Q:H.264中,术语IDR的意思是什么,有什么用?
A:IDR-instantaneous decoding refresh (IDR)picture;
A coded picture in which all slices are I or SI slices that causes the decoding process to mark all reference pictures as "unused for reference" immediately after decoding the IDR picture. After the decoding of an IDR picture all following coded pictures in decoding order can be decoded without inter prediction from any picture decoded prior to the IDR picture. The first picture of each coded video sequence is an IDR picture.
也就是说,IDR的出现其实是相当于向解码器发出了一个清理reference buffer的信号吧,上面说前于这一帧的所有已编码帧不能为inter做参考帧了。
 
Q:jm各个版本之间的代码做了些什么改动,有没有说明这些的文档啊?
A:每个版本源代码的根目录下都有一个 change.txt 文件,里面详细记录了所有版本的更新。
  针对对象不同,jm90以上全部是针对高保真的视频的。86基本上可以满足一般处理的所有要求。
 
Q:在VC环境下是如何读入*.yuv序列的?
A:把*.yuv文件当作一般的文件读就可以了。
   如下:

#include <stdio.h>
#include <malloc.h>

void main()
{
   char *Y;
   char *Cb;
   char *Cr;
   int  width = 352,  height = 288;
   FILE *fp;
   FILE *fy;
   int i;

   Y = (char*)malloc(width*height);
   Cb = (char*)malloc(width*height/4);
   Cr = (char*)malloc(width*height/4);
   fp= fopen("input.yuv","rb");
   if(fp == NULL)
      printf("open input.yuv failed\n");

   fy = fopen("output.yuv", "ab+");
   if(fy == NULL)
      printf("open output.yuv failed\n");
  
   for(i = 0; i<1; i++)
   {
      //fseek(fp, i*width*height, 0);
      if(0 == fread(Y, width*height, 1, fp))
         printf("read error\n");
      if(0 == fwrite(Y, width*height, 1, fy))
         printf("write error\n");
     
      fread(Cb, width*height/4, 1, fp);
      fread(Cr, width*height/4, 1, fp);
      fwrite(Cb, width*height/4, 1, fy);
      fwrite(Cr, width*height/4, 1, fy);
   }
   fclose(fp);
   fclose(fy);
   free(Y);
   free(Cb);
   free(Cr);
}
Q:H.264中,术语IDR的意思是什么,有什么用?
A:IDR-instantaneous decoding refresh (解码即时刷新IDR)picture;
A coded picture in which all slices are I or SI slices that causes the decoding process to mark all reference pictures as "unused for reference" immediately after decoding the IDR picture. After the decoding of an IDR picture all following coded pictures in decoding order can be decoded without inter prediction from any picture decoded prior to the IDR picture. The first picture of each coded video sequence is an IDR picture.
也就是说,IDR的出现其实是相当于向解码器发出了一个清理reference buffer的信号吧,上面说前于这一帧的所有已编码帧不能为inter做参考帧了。

Q: YCrCb 4:2:0是什么?像4:4:4和4:2:2一样表示 Y:Cr:Cb是4:2:0吗?
A: 4 : 2 :0 means that Cr and Cb each have half the horizontal and vertical resolution of Y, as shown. The term ‘4 : 2 : 0’ is rather confusing: the numbers do not actually have a sensible interpretation and appear to have been chosen historically as a ‘code’ to identify this particular sampling pattern. 有点4:1:1的味道。
 
Q:CAVLC的过程
A:
编码
4×4的残差块通过Zig-Zag扫描,得到一系列字符,如:0,3,0,1,-1,-1,0,1,0......
由此序列推导出以下变量:TotalCoeffs(全部的非零系数,包括拖尾系数),TotalZeros(最后一个非零系数前面的所有0的个数,方向为从左到右,比如上面的序列中,最后一个非零系数为1),TrailingOnes(托尾系数的个数,并规定不能超过3个),然后通过NC值查表,把
TotalCoeffs ,和TrailingOnes的组合进行编码,称为编码元素coeff_token。接下来,对每个拖尾系数的符合编码,0表示+,1表示负。再接下来,对剩下的非零系数编码(此时拖尾系数已经被编码了,不再包括),编码方向为从右到左,比如上面的序列中,先编码1,再编码3。这些系数被编码后,是由level_prefix和level_suffix两部分组成的。level_prefix的值通过查表得出,level_suffix是由若干个0组成,0的个数由suffixLength决定。再接着对TotalZeros的值编码。然后对RunBefore(每个非零系数前零的个数)进行编码,这个方向也是从右到左,并且最后一个(从左边数的第一个)非零系数前零的个数不需要编码,因为后面的编过后,剩下多少个0只有一个存放位置,就是最前面。
解码
由计算出的bit串长度读出相应的bits,通过查表得到TotalCoeffs和TrailingOnes的值,此时无输出,接着读取拖尾系数的符合,由编码的顺序知,先读到的是最后一个拖尾系数。解码完拖尾系数并依次输出,接下来是剩下的非零系数的值,通过查表解码并输出。然后解码TotalZeros,此时输出不变,仍为以前的解码值。接下来解码RunBefore,因为编码时是从右往左编的,故第一个解码出来的RunBefore应该插到第一个解码出的拖尾系数的前面,即插入的方向也是从右到左,最后一步时,剩下的RunBefore都插入到最前面。
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值