您的位置:首页 > 编程语言

HEVC代码学习14:motionCompensation函数

2017-05-24 17:27 288 查看
今天来看运动补偿,推荐两篇博文:

http://blog.csdn.net/hevc_cjl/article/details/8457642

http://blog.csdn.net/nb_vol_1/article/details/55253979

1、运动补偿整体介绍

运动补偿的功能是根据之前的局部图像来预测补偿当前的局部图像。而实际代码来看,主要完成的是亚像素插值补偿的工作。在运动估计中,得到的MV是亚像素精度的,而参考图像是整像素的,亚像素位置上是没有值的。如果通过MV找到的参考块是非整像素位置的,那么在参考图像上是没有这个参考块的。这就需要对参考图像进行插值,构造亚像素参考块。得到的这个参考块将用于计算残差。

实际运动补偿中的函数调用关系如下:



2、运动补偿入口函数motionCompensation

下面来看代码(没有具体看加权预测部分),运动补偿的入口函数是motionCompensation,在确定了MV之后被调用,用来构造参考块。

//运动补偿
Void TComPrediction::motionCompensation ( TComDataCU* pcCU, TComYuv* pcYuvPred, RefPicList eRefPicList, Int iPartIdx )
{
Int         iWidth;
Int         iHeight;
UInt        uiPartAddr;

//如果PU的索引是有效值,那么直接处理该PU,然后返回
if ( iPartIdx >= 0 )
{
pcCU->getPartIndexAndSize( iPartIdx, uiPartAddr, iWidth, iHeight );     //获取PU地址和大小
if ( eRefPicList != REF_PIC_LIST_X )    //指明了具体参考列表
{
if( pcCU->getSlice()->getPPS()->getUseWP())   //是否进行加权预测
{
xPredInterUni (pcCU, uiPartAddr, iWidth, iHeight, eRefPicList, pcYuvPred, true );   //最后的参数true标识双向
}
else
{
xPredInterUni (pcCU, uiPartAddr, iWidth, iHeight, eRefPicList, pcYuvPred );
}
if ( pcCU->getSlice()->getPPS()->getUseWP() )
{
xWeightedPredictionUni( pcCU, pcYuvPred, uiPartAddr, iWidth, iHeight, eRefPicList, pcYuvPred );
}
}
else    //没有指明明确的参考列表,那么判断PU两个方向上的参考帧是否相同
{
if ( xCheckIdenticalMotion( pcCU, uiPartAddr ) )      //检测PU与预测的CU是否匹配,匹配则进行单向补偿,否则进行双向补偿
{
xPredInterUni (pcCU, uiPartAddr, iWidth, iHeight, REF_PIC_LIST_0, pcYuvPred );
}
else
{
xPredInterBi  (pcCU, uiPartAddr, iWidth, iHeight, pcYuvPred );
}
}
return;
}

//PU索引无效则处理所有PU,处理同上。
for ( iPartIdx = 0; iPartIdx < pcCU->getNumPartitions(); iPartIdx++ )
{
pcCU->getPartIndexAndSize( iPartIdx, uiPartAddr, iWidth, iHeight );

if ( eRefPicList != REF_PIC_LIST_X )
{
if( pcCU->getSlice()->getPPS()->getUseWP())
{
xPredInterUni (pcCU, uiPartAddr, iWidth, iHeight, eRefPicList, pcYuvPred, true );
}
else
{
xPredInterUni (pcCU, uiPartAddr, iWidth, iHeight, eRefPicList, pcYuvPred );
}
if ( pcCU->getSlice()->getPPS()->getUseWP() )
{
xWeightedPredictionUni( pcCU, pcYuvPred, uiPartAddr, iWidth, iHeight, eRefPicList, pcYuvPred );
}
}
else
{
if ( xCheckIdenticalMotion( pcCU, uiPartAddr ) )
{
xPredInterUni (pcCU, uiPartAddr, iWidth, iHeight, REF_PIC_LIST_0, pcYuvPred );
}
else
{
xPredInterBi  (pcCU, uiPartAddr, iWidth, iHeight, pcYuvPred );
}
}
}
return;
}


3、单向预测xPredInterUni和双向预测xPredInterBi

motionCompensation通过xPredInterUni(单向)和xPredInterBi(双向)调用xPredInterBlk函数来进行亚像素插值的操作,得到亚像素参考块。

//单向运动补偿
Void TComPrediction::xPredInterUni ( TComDataCU* pcCU, UInt uiPartAddr, Int iWidth, Int iHeight, RefPicList eRefPicList, TComYuv* pcYuvPred, Bool bi )
{
Int         iRefIdx     = pcCU->getCUMvField( eRefPicList )->getRefIdx( uiPartAddr ); //参考帧索引
assert (iRefIdx >= 0);
TComMv      cMv         = pcCU->getCUMvField( eRefPicList )->getMv( uiPartAddr );     //获取MV
pcCU->clipMv(cMv);

for (UInt comp=COMPONENT_Y; comp<pcYuvPred->getNumberValidComponents(); comp++)
{
const ComponentID compID=ComponentID(comp);
//亚像素块补偿
xPredInterBlk  (compID,  pcCU, pcCU->getSlice()->getRefPic( eRefPicList, iRefIdx )->getPicYuvRec(), uiPartAddr, &cMv, iWidth, iHeight, pcYuvPred, bi, pcCU->getSlice()->getSPS()->getBitDepth(toChannelType(compID)) );
}
}

//双向运动补偿,调用xPredInterUni
Void TComPrediction::xPredInterBi ( TComDataCU* pcCU, UInt uiPartAddr, Int iWidth, Int iHeight, TComYuv* pcYuvPred )
{
TComYuv* pcMbYuv;
Int      iRefIdx[NUM_REF_PIC_LIST_01] = {-1, -1};

for ( UInt refList = 0; refList < NUM_REF_PIC_LIST_01; refList++ )
{
RefPicList eRefPicList = (refList ? REF_PIC_LIST_1 : REF_PIC_LIST_0);
iRefIdx[refList] = pcCU->getCUMvField( eRefPicList )->getRefIdx( uiPartAddr );

if ( iRefIdx[refList] < 0 )
{
continue;
}

assert( iRefIdx[refList] < pcCU->getSlice()->getNumRefIdx(eRefPicList) );

pcMbYuv = &m_acYuvPred[refList];
if( pcCU->getCUMvField( REF_PIC_LIST_0 )->getRefIdx( uiPartAddr ) >= 0 && pcCU->getCUMvField( REF_PIC_LIST_1 )->getRefIdx( uiPartAddr ) >= 0 )
{
xPredInterUni ( pcCU, uiPartAddr, iWidth, iHeight, eRefPicList, pcMbYuv, true );
}
else
{
if ( ( pcCU->getSlice()->getPPS()->getUseWP()       && pcCU->getSlice()->getSliceType() == P_SLICE ) ||
( pcCU->getSlice()->getPPS()->getWPBiPred()    && pcCU->getSlice()->getSliceType() == B_SLICE ) )
{
xPredInterUni ( pcCU, uiPartAddr, iWidth, iHeight, eRefPicList, pcMbYuv, true );
}
else
{
xPredInterUni ( pcCU, uiPartAddr, iWidth, iHeight, eRefPicList, pcMbYuv );
}
}
}

if ( pcCU->getSlice()->getPPS()->getWPBiPred()    && pcCU->getSlice()->getSliceType() == B_SLICE  )
{
xWeightedPredictionBi( pcCU, &m_acYuvPred[REF_PIC_LIST_0], &m_acYuvPred[REF_PIC_LIST_1], iRefIdx[REF_PIC_LIST_0], iRefIdx[REF_PIC_LIST_1], uiPartAddr, iWidth, iHeight, pcYuvPred );
}
else if ( pcCU->getSlice()->getPPS()->getUseWP() && pcCU->getSlice()->getSliceType() == P_SLICE )
{
xWeightedPredictionUni( pcCU, &m_acYuvPred[REF_PIC_LIST_0], uiPartAddr, iWidth, iHeight, REF_PIC_LIST_0, pcYuvPred );
}
else
{
xWeightedAverage( &m_acYuvPred[REF_PIC_LIST_0], &m_acYuvPred[REF_PIC_LIST_1], iRefIdx[REF_PIC_LIST_0], iRefIdx[REF_PIC_LIST_1], uiPartAddr, iWidth, iHeight, pcYuvPred, pcCU->getSlice()->getSPS()->getBitDepths() );
}
}


4、生成预测块xPredInterBlk

xPredInterBlk用来生成运动补偿块,其实就是在对参考块进行插值。首先会判断MV具体指向的亚像素位置,然后插值得到该位置的参考块。

//生成运动补偿块
/**
* \brief Generate motion-compensated block
*
* \param compID     Colour component ID
* \param cu         Pointer to current CU  当前CU指针
* \param refPic     Pointer to reference picture   参考图像指针
* \param partAddr   Address of block within CU
* \param mv         Motion vector      运动矢量
* \param width      Width of block
* \param height     Height of block
* \param dstPic     Pointer to destination picture     目标图像指针
* \param bi         Flag indicating whether bipred is used     双向预测标识
* \param  bitDepth  Bit depth
*/
Void TComPrediction::xPredInterBlk(const ComponentID compID, TComDataCU *cu, TComPicYuv *refPic, UInt partAddr, TComMv *mv, Int width, Int height, TComYuv *dstPic, Bool bi, const Int bitDepth )
{
Int     refStride  = refPic->getStride(compID);       //参考图像跨度
Int     dstStride  = dstPic->getStride(compID);       //目标图像跨度
Int shiftHor=(2+refPic->getComponentScaleX(compID));
Int shiftVer=(2+refPic->getComponentScaleY(compID));

Int     refOffset  = (mv->getHor() >> shiftHor) + (mv->getVer() >> shiftVer) * refStride;

Pel*    ref     = refPic->getAddr(compID, cu->getCtuRsAddr(), cu->getZorderIdxInCtu() + partAddr ) + refOffset;   //参考块地址

Pel*    dst = dstPic->getAddr( compID, partAddr );    //目标块地址

Int     xFrac  = mv->getHor() & ((1<<shiftHor)-1);    //水平亚像素精度
Int     yFrac  = mv->getVer() & ((1<<shiftVer)-1);    //垂直亚像素精度
UInt    cxWidth  = width  >> refPic->getComponentScaleX(compID);
UInt    cxHeight = height >> refPic->getComponentScaleY(compID);

const ChromaFormat chFmt = cu->getPic()->getChromaFormat();

if ( yFrac == 0 )   //垂直亚像素精度为0,则进行水平亚像素精度插值
{
m_if.filterHor(compID, ref, refStride, dst,  dstStride, cxWidth, cxHeight, xFrac, !bi, chFmt, bitDepth);
}
else if ( xFrac == 0 )    //水平亚像素精度为0,则进行垂直亚像素精度插值
{
m_if.filterVer(compID, ref, refStride, dst, dstStride, cxWidth, cxHeight, yFrac, true, !bi, chFmt, bitDepth);
}
else    //否则水平垂直都进行插值
{
Int   tmpStride = m_filteredBlockTmp[0].getStride(compID);
Pel*  tmp       = m_filteredBlockTmp[0].getAddr(compID);

const Int vFilterSize = isLuma(compID) ? NTAPS_LUMA : NTAPS_CHROMA;

//根据亚像素位置进行插值
m_if.filterHor(compID, ref - ((vFilterSize>>1) -1)*refStride, refStride, tmp, tmpStride, cxWidth, cxHeight+vFilterSize-1, xFrac, false,      chFmt, bitDepth);
m_if.filterVer(compID, tmp + ((vFilterSize>>1) -1)*tmpStride, tmpStride, dst, dstStride, cxWidth, cxHeight,               yFrac, false, !bi, chFmt, bitDepth);
}
}


5、加权预测xWeightedPredictionUni和xWeightedPredictionBi

加权预测部分分为单向加权预测xWeightedPredictionUni和双向加权预测xWeightedPredictionBi。

这里简单介绍一下加权预测,对参考图像队列list0和list1加权得到预测像素,分为默认加权(w0=w1=0.5)预测和Explicit(w0和w1计算得到)加权预测。

单向加权预测xWeightedPredictionUni中调用了getWpScaling和addWeightUni函数,分别完成了计算权重列表和加权计算预测像素的工作。

//! weighted prediction for uni-pred
Void TComWeightPrediction::xWeightedPredictionUni(       TComDataCU *const pcCU,
const TComYuv    *const pcYuvSrc,
const UInt              uiPartAddr,
const Int               iWidth,
const Int               iHeight,
const RefPicList        eRefPicList,
TComYuv          *pcYuvPred,
const Int               iRefIdx_input)
{
WPScalingParam  *pwp, *pwpTmp;

Int iRefIdx=iRefIdx_input;
if ( iRefIdx < 0 )
{
iRefIdx   = pcCU->getCUMvField( eRefPicList )->getRefIdx( uiPartAddr );
}
assert (iRefIdx >= 0);

if ( eRefPicList == REF_PIC_LIST_0 )      //list0
{
getWpScaling(pcCU, iRefIdx, -1, pwp, pwpTmp);       //计算list0的权重列表
}
else
{
getWpScaling(pcCU, -1, iRefIdx, pwpTmp, pwp);       //计算list1的权重列表
}
addWeightUni( pcYuvSrc, pcCU->getSlice()->getSPS()->getBitDepths(), uiPartAddr, iWidth, iHeight, pwp, pcYuvPred );        //进行加权计算预测像素
}


双向加权预测xWeightedPredictionBi调用了getWpScaling、addWeightBi、addWeightUni分别完成计算权重列表、双向加权计算预测像素、单向加权计算预测像素的工作。值得注意的是,xWeightedPredictionBi只在双向预测xPredInterBi中被调用。

//! weighted prediction for bi-pred
Void TComWeightPrediction::xWeightedPredictionBi(       TComDataCU *const pcCU,
const TComYuv    *const pcYuvSrc0,
const TComYuv    *const pcYuvSrc1,
const Int               iRefIdx0,
const Int               iRefIdx1,
const UInt              uiPartIdx,
const Int               iWidth,
const Int               iHeight,
TComYuv          *rpcYuvDst )
{
WPScalingParam  *pwp0;
WPScalingParam  *pwp1;

assert(pcCU->getSlice()->getPPS()->getWPBiPred());

getWpScaling(pcCU, iRefIdx0, iRefIdx1, pwp0, pwp1);       //计算list0和list1的权重列表

if( iRefIdx0 >= 0 && iRefIdx1 >= 0 )
{
addWeightBi(pcYuvSrc0, pcYuvSrc1, pcCU->getSlice()->getSPS()->getBitDepths(), uiPartIdx, iWidth, iHeight, pwp0, pwp1, rpcYuvDst );        //进行双向加权计算预测像素

}
else if ( iRefIdx0 >= 0 && iRefIdx1 <  0 )
{
addWeightUni( pcYuvSrc0, pcCU->getSlice()->getSPS()->getBitDepths(), uiPartIdx, iWidth, iHeight, pwp0, rpcYuvDst );       //进行单向加权计算预测像素

}
else if ( iRefIdx0 <  0 && iRefIdx1 >= 0 )
{
addWeightUni( pcYuvSrc1, pcCU->getSlice()->getSPS()->getBitDepths(), uiPartIdx, iWidth, iHeight, pwp1, rpcYuvDst );       //进行单向加权计算预测像素

}
else
{
assert (0);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: