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
实际运动补偿中的函数调用关系如下:
这里简单介绍一下加权预测,对参考图像队列list0和list1加权得到预测像素,分为默认加权(w0=w1=0.5)预测和Explicit(w0和w1计算得到)加权预测。
单向加权预测xWeightedPredictionUni中调用了getWpScaling和addWeightUni函数,分别完成了计算权重列表和加权计算预测像素的工作。
双向加权预测xWeightedPredictionBi调用了getWpScaling、addWeightBi、addWeightUni分别完成计算权重列表、双向加权计算预测像素、单向加权计算预测像素的工作。值得注意的是,xWeightedPredictionBi只在双向预测xPredInterBi中被调用。
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); } }
相关文章推荐
- 代码大全学习-14-变量名的力量(Power of Variables Names)
- HEVC学习(八) —— 以SAO为例浅析跟踪代码方法
- Docker学习总结(14)——从代码到上线, 云端Docker化持续交付实践
- HEVC代码学习4:cross-component prediction代码实现
- HEVC代码学习3:TEncTop::encode函数
- Windows API 函数学习(14)---SN填写代码
- HEVC代码学习8:xMotionEstimation函数
- openHevc学习笔记:工程代码基本结构
- Android学习心得(14) --- Android代码混淆(2)
- HEVC代码学习6:filterHor和filterVer函数
- HEVC学习:HM-10.1-dev代码分析之TLibVideoIO库
- 【HEVC学习与研究】37、HM编码器的基本结构2:帧内编码部分的代码骨架
- HEVC学习(一)HM模型代码下载,HEVC协议下载,HM模型运行
- Docker学习总结(14)——从代码到上线, 云端Docker化持续交付实践
- Python菜鸟学习手册14----标准库+代码实例
- HEVC学习(八) —— 以SAO为例浅析跟踪代码方法
- Duanxx的HEVC学习(二) HEVC测试代码环境搭建
- 我的编程学习日志(14)--八数码问题(代码)
- HEVC代码学习1:TAppEncoder的main函数
- HEVC代码学习7:xPatternSearchFracDIF函数