OpenCV輪廓匹配原理介紹與使用
1. 輪廓匹配的基本概念
輪廓匹配(Contour Matching)是計算機視覺中的一種重要方法,主要用于比較兩個輪廓的相似性。它廣泛應用于目標識別、形狀分析、手勢識別等領域。
在 OpenCV 中,輪廓匹配主要基于形狀匹配算法,其中 matchShapes
是核心函數。該函數用于計算兩個輪廓之間的相似度,返回一個數值,該數值越小表示兩個輪廓越相似。
2. 輪廓匹配的算法原理
Hu矩(Hu Moments)是由Ming-Kuei Hu在1962年提出。OpenCV 采用 Hu 矩(Hu Moments)進行輪廓匹配。Hu 矩是一組不變矩,可以用于描述圖像的形狀特征,并且具有旋轉、縮放和平移不變性。Hu矩是通過對圖像的歸一化中心矩進行特定的線性組合得到的。具體而言,它們是基于二階和三階的歸一化中心矩計算的。
Hu 矩由 7 個不變矩組成:
I 1 = η 20 + η 02 I 2 = ( η 20 ? η 02 ) 2 + 4 η 11 2 I 3 = ( η 30 ? 3 η 12 ) 2 + ( 3 η 21 ? η 03 ) 2 I 4 = ( η 30 + η 12 ) 2 + ( η 21 + η 03 ) 2 I 5 = ( η 30 ? 3 η 12 ) ( η 30 + η 12 ) [ ( η 30 + η 12 ) 2 ? 3 ( η 21 + η 03 ) 2 ] + ( 3 η 21 ? η 03 ) ( η 21 + η 03 ) [ 3 ( η 30 + η 12 ) 2 ? ( η 21 + η 03 ) 2 ] I 6 = ( η 20 ? η 02 ) [ ( η 30 + η 12 ) 2 ? ( η 21 + η 03 ) 2 ] + 4 η 11 ( η 30 + η 12 ) ( η 21 + η 03 ) I 7 = ( 3 η 21 ? η 03 ) ( η 30 + η 12 ) [ ( η 30 + η 12 ) 2 ? 3 ( η 21 + η 03 ) 2 ] ? ( η 30 ? 3 η 12 ) ( η 21 + η 03 ) [ 3 ( η 30 + η 12 ) 2 ? ( η 21 + η 03 ) 2 ] \begin{align*}I_1 &= \eta_{20} + \eta_{02} \\I_2 &= (\eta_{20} - \eta_{02})^2 + 4\eta_{11}^2 \\I_3 &= (\eta_{30} - 3\eta_{12})^2 + (3\eta_{21} - \eta_{03})^2 \\I_4 &= (\eta_{30} + \eta_{12})^2 + (\eta_{21} + \eta_{03})^2 \\I_5 &= (\eta_{30} - 3\eta_{12})(\eta_{30} + \eta_{12})\left[(\eta_{30} + \eta_{12})^2 - 3(\eta_{21} + \eta_{03})^2\right] \\&\quad + (3\eta_{21} - \eta_{03})(\eta_{21} + \eta_{03})\left[3(\eta_{30} + \eta_{12})^2 - (\eta_{21} + \eta_{03})^2\right] \\I_6 &= (\eta_{20} - \eta_{02})\left[(\eta_{30} + \eta_{12})^2 - (\eta_{21} + \eta_{03})^2\right] \\&\quad + 4\eta_{11}(\eta_{30} + \eta_{12})(\eta_{21} + \eta_{03}) \\I_7 &= (3\eta_{21} - \eta_{03})(\eta_{30} + \eta_{12})\left[(\eta_{30} + \eta_{12})^2 - 3(\eta_{21} + \eta_{03})^2\right] \\&\quad - (\eta_{30} - 3\eta_{12})(\eta_{21} + \eta_{03})\left[3(\eta_{30} + \eta_{12})^2 - (\eta_{21} + \eta_{03})^2\right]\end{align*} I1?I2?I3?I4?I5?I6?I7??=η20?+η02?=(η20??η02?)2+4η112?=(η30??3η12?)2+(3η21??η03?)2=(η30?+η12?)2+(η21?+η03?)2=(η30??3η12?)(η30?+η12?)[(η30?+η12?)2?3(η21?+η03?)2]+(3η21??η03?)(η21?+η03?)[3(η30?+η12?)2?(η21?+η03?)2]=(η20??η02?)[(η30?+η12?)2?(η21?+η03?)2]+4η11?(η30?+η12?)(η21?+η03?)=(3η21??η03?)(η30?+η12?)[(η30?+η12?)2?3(η21?+η03?)2]?(η30??3η12?)(η21?+η03?)[3(η30?+η12?)2?(η21?+η03?)2]?
通過計算 Hu 矩的值,OpenCV 使用 matchShapes
進行輪廓匹配.
需要注意的是,雖然Hu矩對常見的幾何變換具有不變性,但在實際應用中,噪聲、遮擋和分割質量等因素可能影響其穩定性。因此,在處理實際問題時,需綜合考慮這些因素對Hu矩計算的影響。
3. matchShapes
函數介紹
3.1 函數原型
double matchShapes(InputArray contour1, InputArray contour2, int method, double parameter);
3.2 參數說明
contour1
:第一個輪廓(vector<Point>
格式)。contour2
:第二個輪廓(vector<Point>
格式)。method
:匹配方法,可選值:CONTOURS_MATCH_I1
: d ( I ) = ∑ ∣ 1 I i ( 1 ) ? 1 I i ( 2 ) ∣ d(I)=\sum\begin{vmatrix} \frac{1}{I_i^{(1)}}-\frac{1}{I_i^{(2)}} \end{vmatrix} d(I)=∑ ?Ii(1)?1??Ii(2)?1?? ?CONTOURS_MATCH_I2
: d ( I ) = ∣ I i ( 1 ) ? I i ( 2 ) ∣ d(I)=\begin{vmatrix}I_i^{(1)}-I_i^{(2)}\end{vmatrix} d(I)= ?Ii(1)??Ii(2)?? ?CONTOURS_MATCH_I3
: d ( I ) = ∑ ∣ 1 i 1 ( 1 ) ? 1 i i ( 2 ) ∣ d(I)=\sum\begin{vmatrix} \frac{1}{i_1^{(1)}}-\frac{1}{i_i^{(2)}} \end{vmatrix} d(I)=∑ ?i1(1)?1??ii(2)?1?? ?
parameter
:該參數在當前版本未使用,通常填0
。
3.3 返回值
返回兩個輪廓之間的相似性分數,數值越小,輪廓越相似
。
4. 輪廓匹配
4.1示例代碼1:直接匹配
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>using namespace cv;
using namespace std;int main()
{// 1. 讀取輸入圖像和模板圖像,并轉換為灰度圖Mat inputImg = imread("E:/image/pic1.png");Mat templateImg = imread("E:/image/templ.png");if (inputImg.empty() || templateImg.empty()) {cerr << "圖像加載失敗!" << endl;return -1;}Mat grayInput, grayTemplate;cvtColor(inputImg, grayInput, COLOR_BGR2GRAY);cvtColor(templateImg, grayTemplate, COLOR_BGR2GRAY);// 2. 對圖像應用閾值處理得到二值圖像Mat binaryInput, binaryTemplate;threshold(grayInput, binaryInput, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);threshold(grayTemplate, binaryTemplate, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);// 3. 檢測輪廓vector<vector<Point>> contoursInput, contoursTemplate;vector<Vec4i> hierarchy;findContours(binaryInput, contoursInput, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);findContours(binaryTemplate, contoursTemplate, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);// 4. 假設模板圖像只包含一個主要輪廓,取第一個輪廓作為模板if (contoursTemplate.empty()) {cerr << "模板輪廓檢測失敗!" << endl;return -1;}cout << contoursTemplate.size();vector<Point> templateContour = contoursTemplate[0];// 5. 遍歷輸入圖像中的所有輪廓,計算與模板輪廓的匹配度for (size_t i = 0; i < contoursInput.size(); i++) {double matchScore = matchShapes(templateContour, contoursInput[i], CONTOURS_MATCH_I1, 0);cout << "輪廓 " << i << " 匹配分數: " << matchScore << endl;// 在輸入圖像上繪制輪廓并標注匹配分數if (matchScore<0.05){drawContours(inputImg, contoursInput, static_cast<int>(i), Scalar(0, 255, 0), 2);}Moments m = moments(contoursInput[i]);int cx = static_cast<int>(m.m10 / m.m00);int cy = static_cast<int>(m.m01 / m.m00);putText(inputImg, format("%.2f", matchScore), Point(cx, cy), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 255), 1);}//6. 顯示結果\n imshow("輸入圖像輪廓匹配", inputImg);imshow("模板圖像", templateImg);waitKey(0);return 0;
}
綠色為找到的輪廓
4. 2示例代碼2:hu距匹配
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>using namespace cv;
using namespace std;int main()
{// 1. 讀取輸入圖像和模板圖像,并轉換為灰度圖Mat inputImg = imread("E:/image/pic1.png");Mat templateImg = imread("E:/image/templ.png");if (inputImg.empty() || templateImg.empty()) {cerr << "圖像加載失敗!" << endl;return -1;}Mat grayInput, grayTemplate;cvtColor(inputImg, grayInput, COLOR_BGR2GRAY);cvtColor(templateImg, grayTemplate, COLOR_BGR2GRAY);// 2. 對圖像應用閾值處理得到二值圖像Mat binaryInput, binaryTemplate;threshold(grayInput, binaryInput, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);threshold(grayTemplate, binaryTemplate, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);// 3. 檢測輪廓vector<vector<Point>> contoursInput, contoursTemplate;vector<Vec4i> hierarchy;findContours(binaryInput, contoursInput, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);findContours(binaryTemplate, contoursTemplate, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);// 4. 假設模板圖像只包含一個主要輪廓,取第一個輪廓作為模板if (contoursTemplate.empty()) {cerr << "模板輪廓檢測失敗!" << endl;return -1;}cout << contoursTemplate.size();vector<Point> templateContour = contoursTemplate[0];Moments mTemplate = moments(contoursTemplate[0]);Mat huTemplate;HuMoments(mTemplate, huTemplate);// 5. 遍歷輸入圖像中的所有輪廓,計算與模板輪廓的匹配度for (size_t i = 0; i < contoursInput.size(); i++) {// 在輸入圖像上繪制輪廓并標注匹配分數Moments m = moments(contoursInput[i]);Mat hu;HuMoments(m, hu);double matchScore = matchShapes(hu, huTemplate, CONTOURS_MATCH_I1, 0);cout << "輪廓 " << i << " 匹配分數: " << matchScore << endl;if (matchScore < 0.005){drawContours(inputImg, contoursInput, static_cast<int>(i), Scalar(0, 255, 0), 2);}int cx = static_cast<int>(m.m10 / m.m00);int cy = static_cast<int>(m.m01 / m.m00);putText(inputImg, format("%.2f", matchScore), Point(cx, cy), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 255), 1);}//6. 顯示結果\n imshow("輸入圖像輪廓匹配", inputImg);imshow("模板圖像", templateImg);waitKey(0);return 0;
}
5. 輪廓匹配的應用場景
OpenCV 的 matchShapes
通過 Hu 矩計算輪廓的相似性,是一種高效的輪廓匹配方法。適用于各種形狀分析任務,在實際應用中,可以結合其他特征進一步優化匹配結果。
常用場景
- 目標識別:如手寫字符識別、手勢識別,車牌識別等。
- 工業檢測:用于檢測物品形狀偏差。
- 醫學影像分析:對比醫學影像中的病變輪廓。
- 形狀檢索:在數據庫中尋找相似形狀的對象。