MPEG-4编/解码设计与剖析(4)

来源:互联网 发布:黑客牛还是程序员厉害 编辑:程序博客网 时间:2024/05/01 08:08

① FrameCodeI

I帧编码又称为关键帧编码,是消除图像的空间冗余。I帧编码与静态图像的编码算法与JPEG有些类似。

I帧编码流程如图14-4所示。

 图14-4  I帧编码流程框图

 

根据该流程,I帧编码代码实现如下。

 

int FrameCodeI(Encoder * pEnc, Bitstream * bs)
{
int bits = BitstreamPos(bs);
uint32_t mb_width  = pEnc->mbParam.mb_width;   /*宏块宽*/
uint32_t mb_height  = pEnc->mbParam.mb_height;   /*宏块高*/
uint32_t edged_width  = pEnc->mbParam.edged_width;  /*扩展后的图像宽度*/
DECLARE_ALIGNED_MATRIX(dct_coeff , 6, 64, int16_t, CACHE_LINE);/*DCT系数*/
DECLARE_ALIGNED_MATRIX(qnt_coeff , 6, 64, int16_t, CACHE_LINE);/*DCT系数量化结
果*/
uint16_t x, y; 
pEnc->current->coding_type = I_VOP;                 /*当前边编码类型I_VOP*/
pEnc->current->mbs[0].quant = pEnc->current->quant; /*获取量化步长*/
SetMacroblockQuants(&pEnc->mbParam, pEnc->current); /*设置每个宏块的量化大小*/
BitstreamWriteVolHeader(bs, &pEnc->mbParam, pEnc->current);/*写VOL头信息*/
set_timecodes(pEnc->current,pEnc->reference,pEnc->mbParam.fbase);
BitstreamPad(bs);                                        /*写VOP头信息*/
BitstreamWriteVopHeader(bs, &pEnc->mbParam,
pEnc->current, 1, pEnc->current-> mbs[0].quant);
/*初始化统计编码信息*/
pEnc->current->sStat.iTextBits = 0;
pEnc->current->sStat.kblks = mb_width * mb_height;
pEnc->current->sStat.mblks = pEnc->current->sStat.ublks = 0;
for (y = 0; y < mb_height; y++)
for (x = 0; x < mb_width; x++) {
MACROBLOCK *pMB = &pEnc->current->mbs[x + y * pEnc->mbParam.mb_width];
CodeIntraMB(pEnc, pMB);
/*图像数据DCT变换、量化、反量化、IDCT变换,更新当前图像数据*/
MBTransQuantIntra(&pEnc->mbParam, pEnc->
current, pMB, x, y,dct_coeff, qnt_coeff);   /*AC/DC预测*/
MBPrediction(pEnc->current, x, y, pEnc->mbParam.mb_width, qnt_coeff);
/*系数VLC编码*/
MBCoding(pEnc->current, pMB, qnt_coeff, bs, &pEnc->current->sStat);
}
BitstreamPadAlways(bs);                /*填充对齐*/
pEnc->current->length = (BitstreamPos(bs) - bits) /
8; /*获取当前帧编码后的码流长速*/
pEnc->mbParam.m_fcode = 1;          /*强制运动估计的搜索窗口*/
pEnc->current->is_edged = 0;       /*没有扩展*/
return 1;             /* intra */
}

 

I帧编码比较简单,编码过程一目了然。AC/DC是对宏块变换系数的第一行和第一列作预测,以进一步增加零系数的数目,提高压缩比。

② FrameCodeP

P帧编码是帧间编码,利用前面已经编码、解码重建的帧作为参考,插值出多个参考帧,在这些参考帧中搜索最佳匹配块。将当前编码块和匹配块相减,对残差做DCT、量化和编码。不同的搜索算法、不同的搜索窗口,搜索的匹配块的运动向量(MV)、匹配相似度等有不同。

在图像帧P帧编码中,有一定数量的Intra模式的块编码,其他绝大部分为Inter模式或not_coded不编码模式。算法支持当整整的Intra块编码模式超过一定数量时,强制为I帧编码。

Inter为帧间编码,即对残差进行编码。not_coded表示当前宏块没有编码,为零块,则在解码重建时,直接把参考帧的块复制过来作为解码图像,这种块模式的宏块一般都是背景图像。

P帧编码FrameCodeP()的流程如图14-5所示。

 图14-5  P帧编码流程框图
根据该流程,P帧编码代码实现如下。

 

int FrameCodeP(Encoder * pEnc,Bitstream * bs)
{
int bits = BitstreamPos(bs);
int iLimit,x,y;
int bIntra=0, skip_possible;
FRAMEINFO *const current = pEnc->current;   /*当前编码帧*/
FRAMEINFO *const reference = pEnc->reference;  /*重建参考帧*/
MBParam * const pParam = &pEnc->mbParam;    /*宏块编码结构信息*/
const uint32_t mb_width = pParam->mb_width;   /*宏块宽度*/
const uint32_t mb_height = pParam->mb_height;   /*宏块高度*/
const uint32_t edged_width = pParam->edged_width; /*扩展图像宽度*/
DECLARE_ALIGNED_MATRIX(dct_coeff , 6, 64,
int16_t, CACHE_LINE); /*DCT系数*/
DECLARE_ALIGNED_MATRIX(qnt_coeff , 6, 64,
int16_t, CACHE_LINE); /*DCT系数量化结果*/
pParam->m_rounding_type = 1 - pParam->m_rounding_type;
current->rounding_type = pParam->m_rounding_type;
current->fcode = pParam->m_fcode;
current->coding_type = P_VOP;
pEnc->current->mbs[0].quant = pEnc->current->quant;
SetMacroblockQuants(&pEnc->mbParam, current); /*初始化宏块量化步长*/
/*运动估计模块,把当前图像与上一帧的重建帧做最佳匹配,根据SAD抉择*/
iLimit = (mb_width * mb_height)>>1;   
/*当Intra块超过宏块的50%时,强制I帧编码*/
bIntra = MotionEstimation(&pEnc->mbParam,
current, reference,iLimit);
if (bIntra == 1)  return FrameCodeI(pEnc, bs);/*
bIntra为1则执行I帧编码,如场景切换*/
set_timecodes(current,reference,pParam->fbase);
/*写VOP头信息到码流*/
BitstreamWriteVopHeader(bs, &pEnc->mbParam,
current, 1, current->mbs[0].quant);
/*初始化状态统计变量*/
current->sStat.iTextBits = current->sStat.
iMvSum = current->sStat.iMvCount =
current->sStat.kblks = current->sStat.mblks =
current->sStat.ublks = 0;
/*以宏块为单位循环编码*/
for (y = 0; y < mb_height; y++) {
for (x = 0; x < mb_width; x++) {
MACROBLOCK *pMB = &current->mbs[x + y * mb_width];
bIntra = (pMB->mode == MODE_INTRA);
if (bIntra){                          
/*Intra块编码,即I帧编码的核心函数*/
CodeIntraMB(pEnc, pMB);
MBTransQuantIntra(&pEnc->mbParam, current,
pMB, x, y, dct_coeff, qnt_coeff);
MBPrediction(current, x, y, mb_width, qnt_coeff);
MBCoding(current, pMB, qnt_coeff, bs, &current->sStat);
continue;
}
/*运动补偿,根据运动向量确定具体的宏块参考位置,
并做差值计算,结果存放dct_coeff中*/
MBMotionCompensation(pMB, x, y, pRef, pCur,
dct_coeff, pParam-> edged_width);
/*差值做DCT、量化、反量化、IDCT,并更新到当前图像*/
if (pMB->mode != MODE_NOT_CODED)  
/*差值编码,即差值不全为零*/
pMB->cbp =MBTransQuantInter(&pEnc->mbParam, current,

pMB, x, y, dct_coeff, qnt_coeff);
skip_possible = (pMB->cbp == 0) && (pMB->mode == MODE_INTER);
if (current->coding_type == P_VOP)
skip_possible &= ( (pMB->mvs[0].x == 0) && (pMB->mvs[0].y == 0) );
/*宏块没有编码,跳过该宏块,继续编码*/
if ( (pMB->mode == MODE_NOT_CODED) || (skip_possible)) {
pMB->mode = MODE_NOT_CODED;
MBSkip(bs);
continue;
}
/*宏块编码,即差值的DCT系数量化值做VLC编码*/
MBCoding(current, pMB, qnt_coeff, bs, &pEnc->current->sStat);
}
}
pParam->m_fcode = 1;                       /*运动估计ME搜索窗口*/
pEnc->current->is_edged = 0;                /*没有做扩展*/
BitstreamPadAlways(bs);                      /*填充编码后的比特流*/
current->length = (BitstreamPos(bs) - bits) / 8;  /*编码后的码流长度*/
return 0;
在P帧编码前,首先对整帧做运动估计,确定每个宏块的编码模式:Intra、Inter、Not_coded。如果Intra块的数量超过宏块总数的一定比例,则强制该帧做I帧编码。在P帧编码中,既有Inter块的编码也有Intra块的编码,同时对零块不做编码not_coded,即跳过该宏块MBSkip。另外,为了提高编码效率,编码器中省略了图像扩展的模块。但是需要特别注意,为了保证解码器解码出的图像正确,在创建编、解码器的图像帧空间时(image_create函数),应都初始化为确定的相同的值,否则解码器重建的图像边缘会出错。
原创粉丝点击