|
14.2.1 MPEG-4编/解码设计与剖析(4)
① 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 = ¤t->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, ¤t->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函数),应都初始化为确定的相同的值,否则解码器重建的图像边缘会出错。
【责任编辑: 云霞 TEL:(010)68476606】
|