柯兄写的文档,直接贴上来的。
调用处:
Void TEncSearch::xPatternSearchFast()èxTZSearch()
1. 相关结构
TZ_SEARCH_CONFIGURATION : 控制搜索过程
ntTZSearchStruct cStruct : 存放搜索过程的中间最优结果
搜索结构定义如下:
typedef struct
{
Pel* piRefY; //设置:cStruct.piRefY = piRefY;
Int iYStride; // 设置: cStruct.iYStride = iRefStride;
Int iBestX; //在xTZSearchHelp()中设置以下变量
Int iBestY;
UInt uiBestRound; // 菱形或方形搜索的轮数, 控制复杂度
UInt uiBestDistance;
UInt uiBestSad; //
UChar ucPointNr; // 存放搜索点的位置
} IntTZSearchStruct;
搜索点的位置说明:
12 3
40 5
67 8
2. 算法步骤
图1 8点Diamond搜索(半径<=8)
(1) 以当前位置为参考原点,在相对位置 为预测mv(如果bTestOtherPredictedMV开关打开)和相对位置 为0(如果bTestZeroVector开关打开)中,选择最佳(失真代价最小)的位置作为起始点.
(2) 在搜索范围内, 以2的指数次(幂次idx为0, 1, 2, .., 6),逐步扩大搜索步长:
for ( iDist = 1; iDist<= (Int)uiSearchRange;iDist*=2 ) // 例如uiSearchRange为64
{
if ( bFirstSearchDiamond == 1 ) // 控制先搜哪种模式(Diamond or Square)的开关
{
// pcPatternKey表示当前CU,即原始图像的CU, cStruct中的有参考图像的内容piRefY
// pcMvSrchRngLT是根据搜索起始点和搜索范围计算出来的左边和上边边界
// iDist为搜索步长
xTZ8PointDiamondSearch ( pcPatternKey, cStruct, pcMvSrchRngLT,
pcMvSrchRngRB, iStartX, iStartY, iDist );
//每次搜索, cStruct.uiBestRound++
}
else
{
xTZ8PointSquareSearch ( pcPatternKey,cStruct, pcMvSrchRngLT,
pcMvSrchRngRB, iStartX,iStartY, iDist);
//每次搜索, cStruct.uiBestRound++
}
// 在每个中心点,最多搜索uiFirstSearchRounds次, 默认初始化为3次
if( bFirstSearchStop&& ( cStruct.uiBestRound>= uiFirstSearchRounds ) ) // stop
{
break;
}
} // end for
循环第一步, iDist == 1 , xTZ8PointDiamondSearch在top, left, right, bottom四个点匹配最佳位置,这是通过调用xTZSearchHelp来实现的(搜索点序号为2, 4, 5, 7)
2
4 5
7
对应于图1中的
1
1 0 1
1
模板.
TEncSearch::xTZSearchHelp()函数通过计算原始图像与参考图像的误差(失真),寻找最小失真来得到当前最佳点.
{
uiSad = m_cDistParam.DistFunc(&m_cDistParam );
uiSad += m_pcRdCost->getCost( iSearchX, iSearchY );
if( uiSad< rcStruct.uiBestSad)
{
..
rcStruct.uiBestRound = 0;
}
}
循环第二步, iDist == 2, 在xTZ8PointDiamondSearch()中, iDist <= 8的时候匹配模板方法如下:
2
1 x 3
4 x x x 5
6 x 8
7
上图中,数字表示要匹配的点
在匹配中,把当前代价最小的点的位置存放到rcStruct结构中:
rcStruct.iBestX = iSearchX;
rcStruct.iBestY = iSearchY;
rcStruct.uiBestDistance = uiDistance;
rcStruct.ucPointNr = ucPointNr;
如果匹配轮数(每个步长为1轮)超过uiFirstSearchRounds,则停止.
if ( bFirstSearchStop&& ( cStruct.uiBestRound>= uiFirstSearchRounds ) ) // stop criterion
{
break;
}
(3) 循环出来后,为了弥补步长比较小时候,搜索遗漏,加上了两点搜索
if ( cStruct.uiBestDistance== 1 )
{
cStruct.uiBestDistance = 0;
xTZ2PointSearch( pcPatternKey, cStruct,pcMvSrchRngLT, pcMvSrchRngRB);
}
图2 两点搜索
其原理是,如果cStruct.uiBestDistance为1, 则为最佳匹配点为图2中除去中央0点之外的某个点.那么最佳点相邻两个点需要再搜索一下.(好像多余?, 因为在步长为2时已经匹配过.)
(4) 如果得出来的最佳点与起始点距离如果过大,大于iRaster,那么这个MV的准确性不太可靠,通过光栅扫描(raster search)的方式可以在大的范围内寻找可能更合适的MV.
扫描的间距为iRaster, 扫描分为为从[iSrchRngVerTop, iSrchRngVerBottom] X [iSrchRngHorLeft, iSrchRngHorRight].
// raster search if distance is too big
if ( bEnableRasterSearch&& ( ((Int)(cStruct.uiBestDistance) > iRaster)|| bAlwaysRasterSearch ) )
{
cStruct.uiBestDistance= iRaster;
for ( iStartY = iSrchRngVerTop;iStartY <= iSrchRngVerBottom;iStartY += iRaster)
{
for ( iStartX = iSrchRngHorLeft;iStartX <= iSrchRngHorRight;iStartX += iRaster)
{
xTZSearchHelp( pcPatternKey,cStruct, iStartX,iStartY, 0, iRaster);
}
}
}
(5) 由于光栅扫描也是比较粗糙的, 所以就有了修正的过程,raster refinement.
if ( bRasterRefinementEnable && cStruct.uiBestDistance> 0 )
{
while ( cStruct.uiBestDistance> 0 )
{
iStartX = cStruct.iBestX;
iStartY = cStruct.iBestY;
if( cStruct.uiBestDistance> 1 )
{
iDist = cStruct.uiBestDistance >>= 1;
if( bRasterRefinementDiamond== 1 )
{
xTZ8PointDiamondSearch(..)
}
else
{
xTZ8PointSquareSearch(..)
}
}
if ( cStruct.uiBestDistance == 1 )
{
cStruct.uiBestDistance = 0;
// 两点搜索
xTZ2PointSearch
}
}
}
修正的原理是,从最佳位置, 和最佳距离开始,按照2的指数级减少步长,进行搜索.每步都更新当前起始点位置. 直到cStruct.uiBestDistance为0.
(6) 这时候,MV基本比较准确了,为了精益求精, 在当前最佳位置附近, 迭代求最佳点.该步骤称为startrefinement,循环进行(步长以2的幂次增长到uiSearchRange)最佳点的搜索, 得到最佳位置和最佳距离.循环直到最佳距离为0.
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 < (Int)uiSearchRange + 1; iDist*=2)
{
if ( bStarRefinementDiamond == 1 )
{
xTZ8PointDiamondSearch(..)
}
else
{
xTZ8PointSquareSearch(..)
}
if( bStarRefinementStop&& (cStruct.uiBestRound>= uiStarRefinementRounds))
{
break;
}
}
if ( cStruct.uiBestDistance == 1 )
{
..
xTZ2PointSearch(..) // 两点搜索
}
}
}
(7) 保存结果
rcMv.set( cStruct.iBestX, cStruct.iBestY );
ruiSAD= cStruct.uiBestSad- m_pcRdCost->getCost(cStruct.iBestX,cStruct.iBestY);
3. 单步搜索
每个每一步搜索,都搜索8个按照菱形或者方形分布的点,点的编号如下:
1 2 3
4 0 5
6 7 8
3.18点菱形搜索
见函数
对于菱形的8个点,是由半径(边长的一半)不同的两个方形, 各取四个点得来.所以要定位8个点的坐标,需要计算两个方形的上下左右坐标,正如xTZ8PointDiamondSearch中所做的那样.
搜索半径为iDist,
如果iDist <= 8, 搜索过程见图1.
搜索2, 1, 3, 4, 5, 6, 8,7位置的点
否则,即iDist>8,则
(1)先搜索2, 4, 5, 7位置的点:
2
4 5
7
(2) 搜索相对2, 4 5, 7位置点的上,左,右,下四个点, 偏移量以(iDist>>2)为单位.
3.28点方形搜索
见函数xTZ8PointSquareSearch.
计算方形的左右上下的坐标, 搜索1,2, 3, 4, 5, 6, 7, 8位置的点,得出最优点.
4. 复杂度分析和结论
4.1开关复杂度控制
宏 TZ_SEARCH_CONFIGURATION 对下面的开关做了初始化,以控制TZSearch的搜索过程.
(1) bTestZeroVector
控制是否测试zero MV是否最佳起始匹配位置
(2) bTestOtherPredictedMV,默认0(false)
在搜索起始点的选择上, 以MV_P = pred(MV_A, MV_B, MV_C)为起始点, 这个开关若打开,则在集合{ MV_A, MV_B , MV_C }中都试探一下,选择最佳的起始点.
可以改变数值测试效果.
(3) bFirstSearchDiamond, 默认1(true)
控制搜索模式: Diamond or Square
在搜索半径比较大(大于8)的时候,Diamond匹配4x4个位置点,Square任然匹配8个位置点, 此时前者复杂度后后者的两倍.
可以改变数值测试效果.
(4) bFirstSearchStop, 默认为1(true)
控制以起始点为中心搜索时, 是否能够控制搜索轮数.
应该控制,不用改变数值测试了.
(5) uiFirstSearchRounds,默认为3
控制起始点为中心搜索时, 轮数阈值.
可以改变数值测试效果.不过3已经比较小了,在MV预测不准确的时候,还是不减小的好.
(6) bEnableRasterSearch,默认为1(true)
控制是否允许栅格扫描的方式搜索匹配, 如果从起始点搜索后最佳位置距离过大,则在大范围内进行栅格扫描匹配.
可以关掉试试.或者不关掉,而改变距离过大的阈值iRaster
(7) bAlwaysRasterSearch,默认为0(false)
控制是否无条件地进行栅格扫描.控制级别比bEnableRasterSearch要低.
不用修改.
(8) iRaster,默认为5
控制栅格扫描匹配的阈值.大于这个阈值才会有栅格扫描
建议改大点,为8或许比较好.
(9) bRasterRefinementEnable,默认为0(false)
控制是否进行栅格扫描修正.
(10)bRasterRefinementDiamond, 默认为0(false, 即用方形)
控制是否实用菱形来进行raster refine.
不用修正时不用改.
(11)bStarRefinementEnable,默认为1(true)
控制是否开启Star修正.
(12)bStarRefinementDiamond, 默认为1(true)
控制是否是否实用菱形搜索.
(13)bStarRefinementStop,默认为0(false)
控制Star(星型)修正是否可以提前结束.
不用改,既然使用了修正,在乎效果,似乎就不用改?
(14)uiStarRefinementRounds, 默认为2
控制星型修正的匹配轮数.
不用改
4.2参考文章的分析
(略)
4.3疑问
http://wenku.baidu.com/view/9fffb106cc17552707220806.html中指出,起始点的的候选者为中值预测矢量,左\上\右上位置宏块的矢量, 好像不对, 应该是当前帧的左\上\右上位置.