介紹
模板匹配是一個圖像處理問題,當對象的姿勢(X、Y、+)未知時,它使用模板圖像在另一個搜索圖像中查找其位置。在這篇文章中,我們實現一個算法,該算法使用對象的邊緣信息來識別搜索圖像中的對象。
背景
由於模板匹配的速度和可靠性問題,模板匹配本質上是一個棘手的問題。當對象部分可見或與其他對象混合時,該解決方案應針對亮度變化保持穩健,最重要的是,該算法的計算效率應高。解決這個問題主要有兩種方法,基於灰值的匹配(或基於區域的匹配)和基於特征的匹配(非基於區域的匹配)。
基於灰值的方法:在基於灰值的匹配中,規范化交叉關聯(NCC)算法是從過去開始認識的。這通常通過減去均值和除以標准差來在每個步驟中完成。模板 t(x, y) 與子圖像 f(x, y) 的交叉相關性是:

其中 n 是 t(x、y) 和 f(x, y) 中的像素數。•維基]
盡管此方法針對線性照明變化是健壯的,但當對象部分可見或對象與其他對象混合時,算法將失敗。此外,該算法的計算成本很高,因為它需要計算模板圖像中所有像素與搜索圖像之間的相關性。
基於特征的方法:在圖像處理領域采用幾種基於特征的模板匹配方法。與基於邊的對象識別一樣,對象邊緣是用於匹配的要素,在通用霍夫變換中,對象幾何特征將用於匹配。
在這篇文章中,我們實現一個算法,該算法使用對象的邊緣信息來識別搜索圖像中的對象。此實現使用開源計算機視覺庫作為平台。
編譯示例代碼
我們使用 OpenCV 2.0 和 Visual studio 2008 來開發此代碼。要編譯示例代碼,我們需要安裝 OpenCV。
OpenCV可以從這里免費下載。OpenCV(開源Computer Vision)是一個用於實時計算機視覺的編程函數庫。下載 OpenCV 並將其安裝到您的系統中。安裝信息可從這里讀取。
我們需要配置我們的可視化工作室環境。此信息可在此處閱讀。
算法
在這里,我們解釋一種基於邊緣的模板匹配技術。邊緣可以定義為數字圖像中的點,其中圖像亮度會急劇變化或具有不連續性。從技術上講,它是一種離散的分化操作,計算圖像強度函數梯度的近似值。
邊緣檢測的方法有很多,但大多數方法可以分為兩類:基於搜索和基於零交叉。基於搜索的方法首先計算邊強度的度量,通常是一階導數表達式(如梯度幅度)來檢測邊緣,然后使用對邊緣的局部方向(通常是梯度方向)的計算估計來搜索梯度幅度的局部方向最大值。在這里,我們使用這樣的方法實現由索貝爾稱為索貝爾運算符。操作員計算每個點的圖像強度的漸變,給出從淺到暗的最大可能增加方向以及該方向的變化速率。
我們使用這些梯度或導數在X方向和Y方向進行匹配。
此算法涉及兩個步驟。首先,我們需要創建模板圖像的基於邊緣的模型,然后使用此模型在搜索圖像中搜索。
創建基於邊的模板模型
我們首先從模板圖像的邊緣創建一個數據集或模板模型,用於在搜索圖像中查找該對象的姿勢。
在這里,我們使用 Canny 邊緣檢測方法的變體來查找邊緣。你可以在這里閱讀更多關於坎尼的邊緣檢測。對於邊緣提取,Canny 使用以下步驟:
第 1 步:查找圖像的強度漸變
在模板圖像上使用 Sobel 篩選器,該篩選器返回 X (Gx) 和 Y (Gy) 方向的漸變。在此梯度中,我們將使用以下公式計算邊緣幅度和方向:
![]()
我們使用 OpenCV 函數來查找這些值。
cvSobel( src, gx, 1,0, 3 ); //gradient in X direction cvSobel( src, gy, 0, 1, 3 ); //gradient in Y direction for( i = 1; i < Ssize.height-1; i++ ) { for( j = 1; j < Ssize.width-1; j++ ) { _sdx = (short*)(gx->data.ptr + gx->step*i); _sdy = (short*)(gy->data.ptr + gy->step*i); fdx = _sdx[j]; fdy = _sdy[j]; // read x, y derivatives //Magnitude = Sqrt(gx^2 +gy^2) MagG = sqrt((float)(fdx*fdx) + (float)(fdy*fdy)); //Direction = invtan (Gy / Gx) direction =cvFastArctan((float)fdy,(float)fdx); magMat[i][j] = MagG; if(MagG>MaxGradient) MaxGradient=MagG; // get maximum gradient value for normalizing. // get closest angle from 0, 45, 90, 135 set if ( (direction>0 && direction < 22.5) || (direction >157.5 && direction < 202.5) || (direction>337.5 && direction<360) ) direction = 0; else if ( (direction>22.5 && direction < 67.5) || (direction >202.5 && direction <247.5) ) direction = 45; else if ( (direction >67.5 && direction < 112.5)|| (direction>247.5 && direction<292.5) ) direction = 90; else if ( (direction >112.5 && direction < 157.5)|| (direction>292.5 && direction<337.5) ) direction = 135; else direction = 0; orients[count] = (int)direction; count++; } }
找到邊緣方向后,下一步是關聯圖像中可跟蹤的邊緣方向。描述周圍像素有四種可能的方向:0 度、45 度、90 度和 135 度。我們指定所有方向到這些角度。
第 2 步:應用非最大抑制
找到邊緣方向后,我們將進行非最大抑制算法。非最大抑制沿邊緣方向跟蹤左右像素,如果當前像素幅度小於左右像素幅度,則禁止抑制。這將導致圖像變薄。
for( i = 1; i < Ssize.height-1; i++ ) { for( j = 1; j < Ssize.width-1; j++ ) { switch ( orients[count] ) { case 0: leftPixel = magMat[i][j-1]; rightPixel = magMat[i][j+1]; break; case 45: leftPixel = magMat[i-1][j+1]; rightPixel = magMat[i+1][j-1]; break; case 90: leftPixel = magMat[i-1][j]; rightPixel = magMat[i+1][j]; break; case 135: leftPixel = magMat[i-1][j-1]; rightPixel = magMat[i+1][j+1]; break; } // compare current pixels value with adjacent pixels if (( magMat[i][j] < leftPixel ) || (magMat[i][j] < rightPixel ) ) (nmsEdges->data.ptr + nmsEdges->step*i)[j]=0; Else (nmsEdges->data.ptr + nmsEdges->step*i)[j]= (uchar)(magMat[i][j]/MaxGradient*255); count++; } }
第 3 步:執行滯后閾值
使用滯后設置閾值需要兩個閾值:高和低。我們應用一個高閾值來標出那些邊緣, 我們可以相當肯定是真實的。從這些開始,使用之前派生的方向信息,其他邊緣可以通過圖像進行跟蹤。在跟蹤邊時,我們應用較低的閾值,允許我們跟蹤邊緣的微弱部分,只要我們找到一個起點。
_sdx = (short*)(gx->data.ptr + gx->step*i); _sdy = (short*)(gy->data.ptr + gy->step*i); fdx = _sdx[j]; fdy = _sdy[j]; MagG = sqrt(fdx*fdx + fdy*fdy); //Magnitude = Sqrt(gx^2 +gy^2) DirG =cvFastArctan((float)fdy,(float)fdx); //Direction = tan(y/x) ////((uchar*)(imgGDir->imageData + imgGDir->widthStep*i))[j]= MagG; flag=1; if(((double)((nmsEdges->data.ptr + nmsEdges->step*i))[j]) < maxContrast) { if(((double)((nmsEdges->data.ptr + nmsEdges->step*i))[j])< minContrast) { (nmsEdges->data.ptr + nmsEdges->step*i)[j]=0; flag=0; // remove from edge ////((uchar*)(imgGDir->imageData + imgGDir->widthStep*i))[j]=0; } else { // if any of 8 neighboring pixel is not greater than max contraxt remove from edge if( (((double)((nmsEdges->data.ptr + nmsEdges->step*(i-1)))[j-1]) < maxContrast) && (((double)((nmsEdges->data.ptr + nmsEdges->step*(i-1)))[j]) < maxContrast) && (((double)((nmsEdges->data.ptr + nmsEdges->step*(i-1)))[j+1]) < maxContrast) && (((double)((nmsEdges->data.ptr + nmsEdges->step*i))[j-1]) < maxContrast) && (((double)((nmsEdges->data.ptr + nmsEdges->step*i))[j+1]) < maxContrast) && (((double)((nmsEdges->data.ptr + nmsEdges->step*(i+1)))[j-1]) < maxContrast) && (((double)((nmsEdges->data.ptr + nmsEdges->step*(i+1)))[j]) < maxContrast) && (((double)((nmsEdges->data.ptr + nmsEdges->step*(i+1)))[j+1]) < maxContrast)) { (nmsEdges->data.ptr + nmsEdges->step*i)[j]=0; flag=0; ////((uchar*)(imgGDir->imageData + imgGDir->widthStep*i))[j]=0; } } }
第 4 步:保存數據集
提取邊后,我們將所選邊的 X 和 Y 導數以及坐標信息作為模板模型。這些坐標將被重新排列,以反映起點作為重心。
查找基於邊的模板模型
算法中的下一個任務是使用模板模型在搜索圖像中查找對象。我們可以看到我們從包含一組點的模板圖像創建的模型:,
及其在 X 和 Y 方向的漸變
,其中 i = 1 ...n,n是模板 (T) 數據集中的元素數。
我們還可以在搜索圖像 (S) 中找到
漸變,其中 u = 1...搜索圖像中的列數。
在匹配過程中,應使用相似性度量度將模板模型與所有位置的搜索圖像進行比較。相似性度量背后的理念是采取模板圖像的梯度矢量的所有規范化點乘量的總和,並在模型數據集的所有點上搜索圖像。這將導致搜索圖像中每個點的分數。這可表述如下:

如果模板模型和搜索圖像之間完全匹配,則此函數將返回分數 1。分數對應於搜索圖像中可見的對象部分。如果搜索圖像中不存在對象,則分數將為 0。
cvSobel( src, Sdx, 1, 0, 3 ); // find X derivatives cvSobel( src, Sdy, 0, 1, 3 ); // find Y derivatives for( i = 0; i < Ssize.height; i++ ) { for( j = 0; j < Ssize.width; j++ ) { partialSum = 0; // initilize partialSum measure for(m=0;m<noOfCordinates;m++) { curX = i + cordinates[m].x ; // template X coordinate curY = j + cordinates[m].y ; // template Y coordinate iTx = edgeDerivativeX[m]; // template X derivative iTy = edgeDerivativeY[m]; // template Y derivative if(curX<0 ||curY<0||curX>Ssize.height-1 ||curY>Ssize.width-1) continue; _Sdx = (short*)(Sdx->data.ptr + Sdx->step*(curX)); _Sdy = (short*)(Sdy->data.ptr + Sdy->step*(curX)); iSx=_Sdx[curY]; // get curresponding X derivative from source image iSy=_Sdy[curY];// get curresponding Y derivative from source image if((iSx!=0 || iSy!=0) && (iTx!=0 || iTy!=0)) { //partial Sum = Sum of(((Source X derivative* Template X drivative) //+ Source Y derivative * Template Y derivative)) / Edge //magnitude of(Template)* edge magnitude of(Source)) partialSum = partialSum + ((iSx*iTx)+(iSy*iTy))* (edgeMagnitude[m] * matGradMag[curX][curY]); }
在實際情況下,我們需要加快搜索過程。這可以通過各種方法實現。第一個方法是使用平均的屬性。找到相似性度量時,如果我們可以為相似性度量值設置最低分數 (Smin),則無需評估模板模型中的所有點。要檢查特定點 J 的部分分數 Su,v,我們必須找到點 m. Sm 的部分總和,可以定義如下:

顯然,該金額的剩余條款較小或等於 1。因此,如果 ,我們可以停止
評估。
另一個標准可能是,任何時間點的部分分數應大於最低分數。即
.使用此條件時,匹配速度將非常快。但問題是,如果首先檢查對象的缺失部分,則部分總和將很低。在這種情況下,該對象的實例將不被視為匹配項。我們可以用另一個條件修改這一點,其中我們檢查模板模型的第一部分與安全停止標准,其余與硬條件 ,
。用戶可以指定貪婪參數 (g),其中使用硬條件檢查模板模型的分數。因此,如果 g=1,模板模型中的所有點都使用硬條件進行檢查,如果 g=0,則所有點將僅使用安全條件進行檢查。我們可以制定如下程序。
部分分數的評估可以停止在以下:
// stoping criterias to search for model double normMinScore = minScore /noOfCordinates; // precompute minumum score double normGreediness = ((1- greediness * minScore)/(1-greediness)) /noOfCordinates; // precompute greedniness sumOfCoords = m + 1; partialScore = partialSum /sumOfCoords ; // check termination criteria // if partial score score is less than the score than // needed to make the required score at that position // break serching at that coordinate. if( partialScore < (MIN((minScore -1) + normGreediness*sumOfCoords,normMinScore* sumOfCoords))) break;
這種相似性度量具有以下幾個優點:相似性度量與非線性照明變化是不變的,因為所有梯度矢量都已規范化。由於邊緣濾波上沒有分段,因此將顯示對照明任意變化的真不變性。更重要的是,當對象部分可見或與其他對象混合時,這種相似性度量是健壯的。
增強
此算法可能提供各種增強功能。為了進一步加快搜索過程,可以使用金字塔式方法。在這種情況下,搜索以小圖像大小的低分辨率開始。這對應於金字塔的頂部。如果搜索在此階段成功,則搜索將繼續在金字塔的下一個級別,該級別表示更高分辨率的圖像。以這種方式,搜索繼續,因此,結果被優化,直到原始圖像大小,即,到達金字塔的底部。
另一個增強是可能的,通過擴展旋轉和縮放算法。這可以通過創建用於旋轉和縮放的模板模型以及使用所有這些模板模型執行搜索來完成。
引用
- 機器視覺算法和應用 [卡斯滕·斯特格、馬庫斯·烏爾里希、克里斯蒂安·維德曼]
- 數字圖像處理 [拉斐爾·岡薩雷斯,理查德·尤金·伍茲]
- http://dasl.mem.drexel.edu/alumni/bGreen/www.pages.drexel.edu/_weg22/can_tut.html
- https://www.codeproject.com/articles/99457/edge-based-template-matching
