VVC中进行运动估计时使用的快速搜素算法是TZ Search搜索算法,TZ Search算法常用的搜索模板如下图所示:
TZSearch算法的步骤:
①确定搜索起始点:VVC中采用AMVP技术来确定起始搜索点,AMVP会给出若干个候选预测MV,编码器从中选择率失真代价最小的作为预测MV,并用其所指向的位置作为起始搜索点。
②以步长1开始,按照上图所示的菱形模板(或正方形模板)在搜索范围内进行搜索,其中步长以2的整数次幂的形式进行递增,选出率失真代价最小的点作为该步骤搜索的结果。
③若步骤②中得到的最优点对应的步长为1,则需要在该点的周围进行两点搜索,其主要目的是补充搜索最优点周围尚未搜索的点。如下图所示,若步骤②使用的是菱形模板,则最优点可能是2、4、5、7;若步骤二使用的是正方形模板,则最优点可能为1~8。两点搜索将会搜索图中与当前最优点距离最近的两个点。例如,若最优点为2,则会搜索a,b两个点;若最优点为6,则会搜素e,g两个点。
④若步骤②中得到的最优点对应的步长大于某个阈值,则以该最优点为中心,在一定范围内做全搜索,即搜索该范围内的所有的点,选择率失真代价最小的作为该步骤的最优点。
⑤以步骤四得到的最优点为新的起始的搜索点,重复步骤二到步骤四,细化搜索,当相邻两次细化搜索得到的最优点一致时停止细化搜索,此时得到的MV即为最终的MV。
VTM中进行TZ Search算法的入口函数是xTZSearch函数。xTZSearch函数的流程如下所示:
- 初始化,将AMVP得到的最优预测MV设置为搜索起始点和最佳点
- 设置搜索起点和搜索范围
- 搜素过程分为两步:
- 第一步,围绕起点进行菱形(xTZ8PointDiamondSearch函数)或者正方形搜索(xTZ8PointSquareSearch函数),以步长为2的整数次幂进行递增
- 若第一次获得的最优搜索步长为1时,进行两点搜索
- 否则,第一次获得的最优搜索步长太大时,进行一定区域的全搜索
- 第二步,细化搜索
- 以上一次找到的最优点为起始点进行菱形搜索或者正方形搜索,步长以2的整数次幂进行递增,如果最优搜索步长为1,则会进行两点搜索
- 重复进行上述过程,直到搜索得到的最优点和起始搜索点相同,那么这个点就是最优匹配点
- 第一步,围绕起点进行菱形(xTZ8PointDiamondSearch函数)或者正方形搜索(xTZ8PointSquareSearch函数),以步长为2的整数次幂进行递增
在xTZSearch函数中进行搜索时,通过调用xTZSearchHelp函数进行像素匹配,计算相邻重建帧搜索点的像素和当前块原始像素的SAD,从而存储最优搜索点
代码及注释如下:
void InterSearch::xTZSearch( const PredictionUnit& pu,
RefPicList eRefPicList,
int iRefIdxPred,
IntTZSearchStruct& cStruct,
Mv& rcMv,
Distortion& ruiSAD,
const Mv* const pIntegerMv2Nx2NPred,
const bool bExtendedSettings,
const bool bFastSettings)
{
const bool bUseRasterInFastMode = true; //toggle this to further reduce runtime
const bool bUseAdaptiveRaster = bExtendedSettings;
const int iRaster = (bFastSettings && bUseRasterInFastMode) ? 8 : 5;
const bool bTestZeroVector = true && !bFastSettings;
const bool bTestZeroVectorStart = bExtendedSettings;
const bool bTestZeroVectorStop = false;
const bool bFirstSearchDiamond = true; // 1 = xTZ8PointDiamondSearch 0 = xTZ8PointSquareSearch
const bool bFirstCornersForDiamondDist1 = bExtendedSettings;
const bool bFirstSearchStop = m_pcEncCfg->getFastMEAssumingSmootherMVEnabled();
const uint32_t uiFirstSearchRounds = bFastSettings ? (bUseRasterInFastMode?3:2) : 3; // first search stop X rounds after best match (must be >=1)
const bool bEnableRasterSearch = bFastSettings ? bUseRasterInFastMode : true;
const bool bAlwaysRasterSearch = bExtendedSettings; // true: BETTER but factor 2 slower
const bool bRasterRefinementEnable = false; // enable either raster refinement or star refinement 启用光栅细化或星形细化
const bool bRasterRefinementDiamond = false; // 1 = xTZ8PointDiamondSearch 0 = xTZ8PointSquareSearch
const bool bRasterRefinementCornersForDiamondDist1 = bExtendedSettings;
const bool bStarRefinementEnable = true; // enable either star refinement or raster refinement
const bool bStarRefinementDiamond = true; // 1 = xTZ8PointDiamondSearch 0 = xTZ8PointSquareSearch
const bool bStarRefinementCornersForDiamondDist1 = bExtendedSettings;
const bool bStarRefinementStop = false || bFastSettings;
const uint32_t uiStarRefinementRounds = 2; // star refinement stop X rounds after best match (must be >=1)
const bool bNewZeroNeighbourhoodTest = bExtendedSettings;
int iSearchRange = m_iSearchRange;
if( m_pcEncCfg->getMCTSEncConstraint() )
{
MCTSHelper::clipMvToArea( rcMv, pu.Y(), pu.cs->picture->mctsInfo.getTileArea(), *pu.cs->sps );
}
else
{
clipMv( rcMv, pu.cu->lumaPos(), pu.cu->lumaSize(), *pu.cs->sps, *pu.cs->pps );
}
rcMv.changePrecision(MV_PRECISION_INTERNAL, MV_PRECISION_QUARTER);
rcMv.divideByPowerOf2(2); //rcMv右移两位
// init TZSearchStruct
cStruct.uiBestSad = std::numeric_limits<Distortion>::max();//最优失真设为最大
//
m_cDistParam.maximumDistortionForEarlyExit = cStruct.uiBestSad;
m_pcRdCost->setDistParam( m_cDistParam, *cStruct.pcPatternKey, cStruct.piRefY, cStruct.iRefStride, m_lumaClpRng.bd, COMPONENT_Y, cStruct.subShiftMode );
// distortion
// set rcMv (Median predictor) as start point and as best point 设置rcMv作为搜索起始点和最佳点
xTZSearchHelp( cStruct, rcMv.getHor(), rcMv.getVer(), 0, 0 );//测试rcMv点的匹配
// test whether zero Mv is better start point than Median predictor 测试0MV是否比初始MV好
if ( bTestZeroVector )
{
if ((rcMv.getHor() != 0 || rcMv.getVer() != 0) &&
(0 != cStruct.iBestX || 0 != cStruct.iBestY))
{
// only test 0-vector if not obviously previously tested. 如果之前没有明显测试,只测试0矢量。
xTZSearchHelp( cStruct, 0, 0, 0, 0 );
}
}
SearchRange& sr = cStruct.searchRange;
if (pIntegerMv2Nx2NPred != 0) //一般为0 测试pIntegerMv2Nx2NPred点的匹配
{
Mv integerMv2Nx2NPred = *pIntegerMv2Nx2NPred;
integerMv2Nx2NPred.changePrecision(MV_PRECISION_INT, MV_PRECISION_INTERNAL);
if( m_pcEncCfg->getMCTSEncConstraint() )
{
MCTSHelper::clipMvToArea( integerMv2Nx2NPred, pu.Y(), pu.cs->picture->mctsInfo.getTileArea(), *pu.cs->sps );
}
else
{
clipMv( integerMv2Nx2NPred, pu.cu->lumaPos(), pu.cu->lumaSize(), *pu.cs->sps, *pu.cs->pps );
}
integerMv2Nx2NPred.changePrecision(MV_PRECISION_INTERNAL, MV_PRECISION_QUARTER);
integerMv2Nx2NPred.divideByPowerOf2(2);
if ((rcMv != integerMv2Nx2NPred) &&
(integerMv2Nx2NPred.getHor() != cStruct.iBestX || integerMv2Nx2NPred.getVer() != cStruct.iBestY))
{
// only test integerMv2Nx2NPred if not obviously previously tested.
xTZSearchHelp( cStruct, integerMv2Nx2NPred.getHor(), integerMv2Nx2NPred.getVer(), 0, 0);
}
}
for (int i = 0; i < m_uniMvListSize; i++)
{
BlkUniMvInfo* curMvInfo = m_uniMvList + ((m_uniMvListIdx - 1 - i + m_uniMvListMaxSize) % (m_uniMvListMaxSize));
int j = 0;
for (; j < i; j++)
{
BlkUniMvInfo *prevMvInfo = m_uniMvList + ((m_uniMvListIdx - 1 - j + m_uniMvListMaxSize) % (m_uniMvListMaxSize));
if (curMvInfo->uniMvs[eRefPicList][iRefIdxPred] == prevMvInfo->uniMvs[eRefPicList][iRefIdxPred])
{
break;
}
}
if (j < i)
continue;
Mv cTmpMv = curMvInfo->uniMvs[eRefPicList][iRefIdxPred];
clipMv( cTmpMv, pu.cu->lumaPos(), pu.cu->lumaSize(), *pu.cs->sps, *pu.cs->pps );
cTmpMv.changePrecision(MV_PRECISION_INTERNAL, MV_PRECISION_INT);
m_cDistParam.cur.buf = cStruct.piRefY + (cTmpMv.ver * cStruct.iRefStride) + cTmpMv.hor;
Distortion uiSad = m_cDistParam.distFunc(m_cDistParam);
uiSad += m_pcRdCost->getCostOfVectorWithPredictor(cTmpMv.hor, cTmpMv.ver, cStruct.imvShift);
if (uiSad < cStruct.uiBestSad)
{
cStruct.uiBestSad = uiSad;
cStruct.iBestX = cTmpMv.hor;
cStruct.iBestY = cTmpMv.ver;
m_cDistParam.maximumDistortionForEarlyExit = uiSad;
}
}
{
// set search range
Mv currBestMv(cStruct.iBestX, cStruct.iBestY );//搜索起点
currBestMv <<= MV_FRACTIONAL_BITS_INTERNAL;
xSetSearchRange(pu, currBestMv, m_iSearchRange >> (bFastSettings ? 1 : 0), sr, cStruct);//设置搜索起点和搜索范围
}
// 哈希运动估计
if (m_pcEncCfg->getUseHashME() && (m_currRefPicList == 0 || pu.cu->slice->getList1IdxToList0Idx(m_currRefPicIndex) < 0))
{
int minSize = min(pu.cu->lumaSize().width, pu.cu->lumaSize().height);
if (minSize < 128 && minSize >= 4)
{
int numberOfOtherMvps = m_numHashMVStoreds[m_currRefPicList][m_currRefPicIndex];
for (int i = 0; i < numberOfOtherMvps; i++)
{
xTZSearchHelp(cStruct, m_hashMVStoreds[m_currRefPicList][m_currRefPicIndex][i].getHor(), m_hashMVStoreds[m_currRefPicList][m_currRefPicIndex][i].getVer(), 0, 0);
}
if (numberOfOtherMvps > 0)
{
// write out best match
rcMv.set(cStruct.iBestX, cStruct.iBestY);
ruiSAD = cStruct.uiBestSad - m_pcRdCost->getCostOfVectorWithPredictor(cStruct.iBestX, cStruct.iBestY, cStruct.imvShift);
m_skipFracME = true;
return;
}
}
}
// start search 开始搜索
int iDist = 0;
int iStartX = cStruct.iBestX;//搜索起点
int iStartY = cStruct.iBestY;
const bool bBestCandidateZero = (cStruct.iBestX == 0) && (cStruct.iBestY == 0);//搜索起点是否为0
// first search around best position up to now.
// The following works as a "subsampled/log" window search around the best candidate
//第一次围绕起点进行菱形或者正方形搜索,以步长为2的整数次幂进行递增,iDst为搜索步长
/*=====================以上面得到的起点为搜索起点,进行搜索=====================*/
for ( iDist = 1; iDist <= iSearchRange; iDist*=2 )
{
if ( bFirstSearchDiamond == 1 )
{
xTZ8PointDiamondSearch ( cStruct, iStartX, iStartY, iDist, bFirstCornersForDiamondDist1 );
}
else
{
xTZ8PointSquareSearch ( cStruct, iStartX, iStartY, iDist );
}
if ( bFirstSearchStop && ( cStruct.uiBestRound >= uiFirstSearchRounds ) ) // stop criterion
{
break;//第一次菱形搜索,最大搜索步长为8
}
}
/*=======================以0 MV作为搜索起点,进行搜索================*/
if (!bNewZeroNeighbourhoodTest) //一般为false
{
// test whether zero Mv is a better start point than Median predictor 测试0点是否比rcMv做起始点更好
if ( bTestZeroVectorStart && ((cStruct.iBestX != 0) || (cStruct.iBestY != 0)) )
{
xTZSearchHelp( cStruct, 0, 0, 0, 0 );
if ( (cStruct.iBestX == 0) && (cStruct.iBestY == 0) )
{
// test its neighborhood
for ( iDist = 1; iDist <= iSearchRange; iDist*=2 )
{
xTZ8PointDiamondSearch( cStruct, 0, 0, iDist, false );
if ( bTestZeroVectorStop && (cStruct.uiBestRound > 0) ) // stop criterion
{
break;
}
}
}
}
}
else
{
// Test also zero neighbourhood but with half the range
// It was reported that the original (above) search scheme using bTestZeroVectorStart did not
// make sense since one would have already checked the zero candidate earlier
// and thus the conditions for that test would have not been satisfied
if (bTestZeroVectorStart == true && bBestCandidateZero != true)//检查0点,必须满足之前的菱形搜索最优点不为0
{
for ( iDist = 1; iDist <= (iSearchRange >> 1); iDist*=2 )//以0点为起点,搜索范围减半
{
xTZ8PointDiamondSearch( cStruct, 0, 0, iDist, false );
if ( bTestZeroVectorStop && (cStruct.uiBestRound > 2) ) // stop criterion
{
break;
}
}
}
}
/*=================第一次获得的最优搜索步长为1时,进行两点搜索============*/
// calculate only 2 missing points instead 8 points if cStruct.uiBestDistance == 1
if ( cStruct.uiBestDistance == 1 )
{
cStruct.uiBestDistance = 0;
xTZ2PointSearch( cStruct );
}
/*===============第一次获得的最优搜索步长太大时,进行一定区域的全搜索========*/
// raster search if distance is too big
if (bUseAdaptiveRaster)//一般为false
{
int iWindowSize = iRaster;//步长阈值
SearchRange localsr = sr;
if (!(bEnableRasterSearch && ( ((int)(cStruct.uiBestDistance) >= iRaster))))//步长没有大于阈值,缩小搜索范围全搜索一次
{
iWindowSize ++;
localsr.left /= 2;
localsr.right /= 2;
localsr.top /= 2;
localsr.bottom /= 2;
}
cStruct.uiBestDistance = iWindowSize;
for ( iStartY = localsr.top; iStartY <= localsr.bottom; iStartY += iWindowSize )//搜索范围localsr内全搜索
{
for ( iStartX = localsr.left; iStartX <= localsr.right; iStartX += iWindowSize )
{
xTZSearchHelp( cStruct, iStartX, iStartY, 0, iWindowSize );
}
}
}
else
{
//第一次获得的最优搜索步长太大时,进行一定区域的全搜索
if ( bEnableRasterSearch && ( ((int)(cStruct.uiBestDistance) >= iRaster) || bAlwaysRasterSearch ) )
{
cStruct.uiBestDistance = iRaster;
for ( iStartY = sr.top; iStartY <= sr.bottom; iStartY += iRaster )//全搜索
{
for ( iStartX = sr.left; iStartX <= sr.right; iStartX += iRaster )
{
xTZSearchHelp( cStruct, iStartX, iStartY, 0, iRaster );
}
}
}
}
/*===================细化搜索======================*/
// raster refinement 光栅细化
if ( bRasterRefinementEnable && cStruct.uiBestDistance > 0 ) //bRasterRefinementEnable为false
{
while ( cStruct.uiBestDistance > 0 )//重复进行菱形搜索和两点搜索,找最佳匹配点
{
iStartX = cStruct.iBestX; //以上次得到的最佳匹配点为起始点
iStartY = cStruct.iBestY;
if ( cStruct.uiBestDistance > 1 )
{
iDist = cStruct.uiBestDistance >>= 1;
if ( bRasterRefinementDiamond == 1 )
{
xTZ8PointDiamondSearch ( cStruct, iStartX, iStartY, iDist, bRasterRefinementCornersForDiamondDist1 );
}
else
{
xTZ8PointSquareSearch ( cStruct, iStartX, iStartY, iDist );
}
}
// calculate only 2 missing points instead 8 points if cStruct.uiBestDistance == 1
if ( cStruct.uiBestDistance == 1 ) //得到的最优点的搜索步长为1时,进行两点搜索
{
cStruct.uiBestDistance = 0;
if ( cStruct.ucPointNr != 0 )
{
xTZ2PointSearch( cStruct );
}
}
}//while循环内重复进行以选出的最优点为起点的菱形搜索和两点搜索,直到菱形搜索得到的最优点和起始搜索点相同,那么这个点就是最优匹配点了
}
// star refinement
if ( bStarRefinementEnable && cStruct.uiBestDistance > 0 )
{
while ( cStruct.uiBestDistance > 0 )
{
iStartX = cStruct.iBestX;
iStartY = cStruct.iBestY;
cStruct.uiBestDistance = 0;
cStruct.ucPointNr = 0;
for ( iDist = 1; iDist < iSearchRange + 1; iDist*=2 )
{
if ( bStarRefinementDiamond == 1 )
{
xTZ8PointDiamondSearch ( cStruct, iStartX, iStartY, iDist, bStarRefinementCornersForDiamondDist1 );
}
else
{
xTZ8PointSquareSearch ( cStruct, iStartX, iStartY, iDist );
}
if ( bStarRefinementStop && (cStruct.uiBestRound >= uiStarRefinementRounds) ) // stop criterion
{
break;
}
}
// calculate only 2 missing points instead 8 points if cStrukt.uiBestDistance == 1
if ( cStruct.uiBestDistance == 1 )
{
cStruct.uiBestDistance = 0;
if ( cStruct.ucPointNr != 0 )
{
xTZ2PointSearch( cStruct );
}
}
}
}
// write out best match
rcMv.set( cStruct.iBestX, cStruct.iBestY );
ruiSAD = cStruct.uiBestSad - m_pcRdCost->getCostOfVectorWithPredictor( cStruct.iBestX, cStruct.iBestY, cStruct.imvShift );
}
xTZ8PointDiamondSearch函数是用菱形模板进行搜索,如下图所示:
当搜索步长为1时,仅搜索上、下、左、右的4个点
当搜索步长大于1小于等于8时,进行如上图所示的8点搜索,搜索菱形边上的8个点
当搜索步长大于8时,进行如上图最外围的16点的菱形搜索,搜索菱形边上的16个点
代码及注释如下:
inline void InterSearch::xTZ8PointDiamondSearch( IntTZSearchStruct& rcStruct,
const int iStartX,
const int iStartY,
const int iDist,
const bool bCheckCornersAtDist1 )
{
const SearchRange& sr = rcStruct.searchRange; //搜索范围
// 8 point search, // 1 2 3
// search around the start point // 4 0 5
// with the required distance // 6 7 8
CHECK( iDist == 0, "Invalid distance" );
const int iTop = iStartY - iDist;//菱形顶点 2
const int iBottom = iStartY + iDist;//菱形底点 7
const int iLeft = iStartX - iDist;//菱形左点 4
const int iRight = iStartX + iDist;//菱形右点 5
rcStruct.uiBestRound += 1;//搜索圈数+1
if ( iDist == 1 ) //搜索步长为1时,搜索2,4,5,7四个点
{
if ( iTop >= sr.top ) // check top 菱形顶点在搜索范围内
{
if (bCheckCornersAtDist1)
{
if ( iLeft >= sr.left) // check top-left
{
xTZSearchHelp( rcStruct, iLeft, iTop, 1, iDist ); //检查菱形点1
}
xTZSearchHelp( rcStruct, iStartX, iTop, 2, iDist );//检查菱形点2
if ( iRight <= sr.right ) // check middle right
{
xTZSearchHelp( rcStruct, iRight, iTop, 3, iDist );//检查菱形点3
}
}
else
{
xTZSearchHelp( rcStruct, iStartX, iTop, 2, iDist ); //检查菱形点2
}
}
if ( iLeft >= sr.left ) // check middle left
{
xTZSearchHelp( rcStruct, iLeft, iStartY, 4, iDist ); // 4
}
if ( iRight <= sr.right ) // check middle right
{
xTZSearchHelp( rcStruct, iRight, iStartY, 5, iDist ); // 5
}
if ( iBottom <= sr.bottom ) // check bottom
{
if (bCheckCornersAtDist1)
{
if ( iLeft >= sr.left) // check top-left
{
xTZSearchHelp( rcStruct, iLeft, iBottom, 6, iDist );
}
xTZSearchHelp( rcStruct, iStartX, iBottom, 7, iDist ); // 7
if ( iRight <= sr.right ) // check middle right
{
xTZSearchHelp( rcStruct, iRight, iBottom, 8, iDist );
}
}
else
{
xTZSearchHelp( rcStruct, iStartX, iBottom, 7, iDist );
}
}
}
else
{
if ( iDist <= 8 ) // 搜索步长大于1,小于等于8时,8点菱形搜索;
{
const int iTop_2 = iStartY - (iDist>>1);
const int iBottom_2 = iStartY + (iDist>>1);
const int iLeft_2 = iStartX - (iDist>>1);
const int iRight_2 = iStartX + (iDist>>1);
if ( iTop >= sr.top && iLeft >= sr.left &&
iRight <= sr.right && iBottom <= sr.bottom ) // check border
{
xTZSearchHelp( rcStruct, iStartX, iTop, 2, iDist );
xTZSearchHelp( rcStruct, iLeft_2, iTop_2, 1, iDist>>1 );
xTZSearchHelp( rcStruct, iRight_2, iTop_2, 3, iDist>>1 );
xTZSearchHelp( rcStruct, iLeft, iStartY, 4, iDist );
xTZSearchHelp( rcStruct, iRight, iStartY, 5, iDist );
xTZSearchHelp( rcStruct, iLeft_2, iBottom_2, 6, iDist>>1 );
xTZSearchHelp( rcStruct, iRight_2, iBottom_2, 8, iDist>>1 );
xTZSearchHelp( rcStruct, iStartX, iBottom, 7, iDist );
}
else // check border
{
if ( iTop >= sr.top ) // check top
{
xTZSearchHelp( rcStruct, iStartX, iTop, 2, iDist );
}
if ( iTop_2 >= sr.top ) // check half top
{
if ( iLeft_2 >= sr.left ) // check half left
{
xTZSearchHelp( rcStruct, iLeft_2, iTop_2, 1, (iDist>>1) );
}
if ( iRight_2 <= sr.right ) // check half right
{
xTZSearchHelp( rcStruct, iRight_2, iTop_2, 3, (iDist>>1) );
}
} // check half top
if ( iLeft >= sr.left ) // check left
{
xTZSearchHelp( rcStruct, iLeft, iStartY, 4, iDist );
}
if ( iRight <= sr.right ) // check right
{
xTZSearchHelp( rcStruct, iRight, iStartY, 5, iDist );
}
if ( iBottom_2 <= sr.bottom ) // check half bottom
{
if ( iLeft_2 >= sr.left ) // check half left
{
xTZSearchHelp( rcStruct, iLeft_2, iBottom_2, 6, (iDist>>1) );
}
if ( iRight_2 <= sr.right ) // check half right
{
xTZSearchHelp( rcStruct, iRight_2, iBottom_2, 8, (iDist>>1) );
}
} // check half bottom
if ( iBottom <= sr.bottom ) // check bottom
{
xTZSearchHelp( rcStruct, iStartX, iBottom, 7, iDist );
}
} // check border
}
else // iDist > 8 搜索步长大于8时,16点菱形搜索
{
if ( iTop >= sr.top && iLeft >= sr.left &&
iRight <= sr.right && iBottom <= sr.bottom ) // check border
{
xTZSearchHelp( rcStruct, iStartX, iTop, 0, iDist );
xTZSearchHelp( rcStruct, iLeft, iStartY, 0, iDist );
xTZSearchHelp( rcStruct, iRight, iStartY, 0, iDist );
xTZSearchHelp( rcStruct, iStartX, iBottom, 0, iDist );
for ( int index = 1; index < 4; index++ )
{
const int iPosYT = iTop + ((iDist>>2) * index);
const int iPosYB = iBottom - ((iDist>>2) * index);
const int iPosXL = iStartX - ((iDist>>2) * index);
const int iPosXR = iStartX + ((iDist>>2) * index);
xTZSearchHelp( rcStruct, iPosXL, iPosYT, 0, iDist );
xTZSearchHelp( rcStruct, iPosXR, iPosYT, 0, iDist );
xTZSearchHelp( rcStruct, iPosXL, iPosYB, 0, iDist );
xTZSearchHelp( rcStruct, iPosXR, iPosYB, 0, iDist );
}
}
else // check border
{
if ( iTop >= sr.top ) // check top
{
xTZSearchHelp( rcStruct, iStartX, iTop, 0, iDist );
}
if ( iLeft >= sr.left ) // check left
{
xTZSearchHelp( rcStruct, iLeft, iStartY, 0, iDist );
}
if ( iRight <= sr.right ) // check right
{
xTZSearchHelp( rcStruct, iRight, iStartY, 0, iDist );
}
if ( iBottom <= sr.bottom ) // check bottom
{
xTZSearchHelp( rcStruct, iStartX, iBottom, 0, iDist );
}
for ( int index = 1; index < 4; index++ )
{
const int iPosYT = iTop + ((iDist>>2) * index);
const int iPosYB = iBottom - ((iDist>>2) * index);
const int iPosXL = iStartX - ((iDist>>2) * index);
const int iPosXR = iStartX + ((iDist>>2) * index);
if ( iPosYT >= sr.top ) // check top
{
if ( iPosXL >= sr.left ) // check left
{
xTZSearchHelp( rcStruct, iPosXL, iPosYT, 0, iDist );
}
if ( iPosXR <= sr.right ) // check right
{
xTZSearchHelp( rcStruct, iPosXR, iPosYT, 0, iDist );
}
} // check top
if ( iPosYB <= sr.bottom ) // check bottom
{
if ( iPosXL >= sr.left ) // check left
{
xTZSearchHelp( rcStruct, iPosXL, iPosYB, 0, iDist );
}
if ( iPosXR <= sr.right ) // check right
{
xTZSearchHelp( rcStruct, iPosXR, iPosYB, 0, iDist );
}
} // check bottom
} // for ...
} // check border
} // iDist <= 8
} // iDist == 1
}
xTZSearchHelp函数是对给定的搜索点,进行像素匹配,计算SAD,并将更优的搜索点存储到rcStruct中
代码及注释如下:
/*
* 对给定的需要测试的搜索点,进行像素匹配,计算SAD,并将更优匹配点的数据存储到rcStruct
* iSearchX、iSearchY即需要搜索匹配的点的坐标
* ucPointNr表示搜索点与起始点的位置关系的标号
* uiDistance是搜索点距离起始点的搜索步长
*/
inline void InterSearch::xTZSearchHelp( IntTZSearchStruct& rcStruct, const int iSearchX, const int iSearchY, const uint8_t ucPointNr, const uint32_t uiDistance )
{
Distortion uiSad = 0;
// CHECK(!( !( rcStruct.searchRange.left > iSearchX || rcStruct.searchRange.right < iSearchX || rcStruct.searchRange.top > iSearchY || rcStruct.searchRange.bottom < iSearchY )), "Unspecified error");
//根据搜索点坐标,获取从搜索点开始的参考像素地址
const Pel* const piRefSrch = rcStruct.piRefY + iSearchY * rcStruct.iRefStride + iSearchX;
m_cDistParam.cur.buf = piRefSrch; //参考像素搜索起始点地址赋给m_cDistParam,用来计算失真
//搜索模式为MESEARCH_SELECTIVE时 该模式不是一次性计算像素匹配失真的,而是亚采样逐步计算的
if( 1 == rcStruct.subShiftMode )
{
// motion cost 这种模式时,m_cDistParam.subShift不为0,即匹配求失真时,采用了像素行的亚采样
Distortion uiBitCost = m_pcRdCost->getCostOfVectorWithPredictor( iSearchX, iSearchY, rcStruct.imvShift );//mv的bit cost
// Skip search if bit cost is already larger than best SAD
if (uiBitCost < rcStruct.uiBestSad)
{
Distortion uiTempSad = m_cDistParam.distFunc( m_cDistParam );//像素行亚采样的像素匹配
if((uiTempSad + uiBitCost) < rcStruct.uiBestSad)
{
// it's not supposed that any member of DistParams is manipulated beside cur.buf
int subShift = m_cDistParam.subShift;
const Pel* pOrgCpy = m_cDistParam.org.buf;
uiSad += uiTempSad >> m_cDistParam.subShift;
while( m_cDistParam.subShift > 0 )//像素行亚采样
{
int isubShift = m_cDistParam.subShift -1;
m_cDistParam.org.buf = rcStruct.pcPatternKey->buf + (rcStruct.pcPatternKey->stride << isubShift);
m_cDistParam.cur.buf = piRefSrch + (rcStruct.iRefStride << isubShift);//对原始像素和参考像素的像素行亚采样
uiTempSad = m_cDistParam.distFunc( m_cDistParam );//计算失真
uiSad += uiTempSad >> m_cDistParam.subShift;
if(((uiSad << isubShift) + uiBitCost) > rcStruct.uiBestSad)
{
break;
}
m_cDistParam.subShift--; //4行亚采样->偶数行亚采样->没有亚采样
}
if(m_cDistParam.subShift == 0)
{
uiSad += uiBitCost;
if( uiSad < rcStruct.uiBestSad ) //如果测试点更优,将最优信息保存到rcStruct
{
rcStruct.uiBestSad = uiSad;
rcStruct.iBestX = iSearchX;
rcStruct.iBestY = iSearchY;
rcStruct.uiBestDistance = uiDistance;
rcStruct.uiBestRound = 0;
rcStruct.ucPointNr = ucPointNr;
m_cDistParam.maximumDistortionForEarlyExit = uiSad;
}
}
// restore org ptr
m_cDistParam.org.buf = pOrgCpy; //由于上面进行了亚采样求失真,现在将信息恢复到函数开始时那样
m_cDistParam.subShift = subShift;
}
}
}
else
{
//除了MESEARCH_SELECTIVE之外的其它搜索模式时
//这时的m_cDistParam.subShift为0
uiSad = m_cDistParam.distFunc( m_cDistParam );
// only add motion cost if uiSad is smaller than best. Otherwise pointless
// to add motion cost.只有当uiSad小于best时,才会增加运动成本。否则增加运动成本是毫无意义的。
if( uiSad < rcStruct.uiBestSad )
{
// motion cost 像素失真再加上运动信息的cost,得到总失真
uiSad += m_pcRdCost->getCostOfVectorWithPredictor( iSearchX, iSearchY, rcStruct.imvShift );
if( uiSad < rcStruct.uiBestSad )
{
rcStruct.uiBestSad = uiSad;
rcStruct.iBestX = iSearchX;//更新最优匹配点
rcStruct.iBestY = iSearchY;
rcStruct.uiBestDistance = uiDistance;
rcStruct.uiBestRound = 0;
rcStruct.ucPointNr = ucPointNr;
m_cDistParam.maximumDistortionForEarlyExit = uiSad;
}
}
}
}