模版匹配
1、模板匹配概念
模板匹配是一項在一副圖像中尋找與另一幅模板圖像最匹配(相似)部分的技術。模板匹配不是基于直方圖的,而是通過在輸入圖像上滑動圖像塊(模板)同時對比相似度,來對模板和輸入圖像進行匹配的一種方法。
應用:
(1)目標查找定位
(2)運動物體跟蹤
2、模板匹配 — matchTemplate()
1 CV_EXPORTS_W void matchTemplate(InputArray image, InputArray temp1, OutputArray result, int method);
image:待搜索圖像(大圖)
temp1:搜索模板,需和原圖一樣數據類型且尺寸大小不能大于源圖像
reuslt:比較結果的映射圖像,其必須為單通道的,32位浮點型圖像,如果原圖(待搜索圖像)尺寸為W*H,二temp1的尺寸為w*h,則result的尺寸一定是(W-w+1)*(H-h+1)
method:指定的匹配方法,有如下六種:
CV_TM_SQDIFF --- 平方差匹配法(最好匹配0) CV_TM_SQDIFF_NORMED --- 歸一化平方差匹配法(最好匹配0) CV_TM_CCORR --- 相關匹配法(最壞匹配0) CV_TM_CCORR_NORMED ---歸一化相關匹配法(最壞匹配0) CV_TM_CCOEFF --- 系數匹配法(最好匹配1) CV_TM_CCOEFF_NORMED --- 歸一化系數匹配法(最好匹配1)
2、矩陣歸一化 — normalize()
void normalize(InputArray src,OutputArray dst, double alpha=1, double beta=0, int norm_type=NORM_L2, int dtype=-1, InputArray mask=noArray() )
src:輸入源圖像,Mat類型
dst:輸出結果圖像,需要和原圖一樣的尺寸和類型
alpha:歸一化后的最小值,默認為1
beta:歸一化后的最大值,默認為0
norm_type:歸一化類型,可選:NORM_INF, NORM_L1, NORM_L2(默認)等
dtype:默認值為-1,此參數為負值時,輸出矩陣和src有同樣的類型
mask:可選的掩碼操作
normallize()函數的作用是進行矩陣歸一化。
3、尋找最值 — minMaxLoc()
1void minMaxLoc(InputArray src, CV_OUT double* minVal, CV_OUT double* maxVal = 0, CV_OUT Point* minLoc=0, CV_OUT Point* maxLoc=0, InputArray mask=noArray());
src:輸入源圖像,單通道圖像
minVal:返回最小值的指針,若無需返回,則置為0
maxVal:返回最大值的指針,若無需返回,則置為0
minLoc:返回最小位置的指針,若無需返回,則置為0
maxLoc:返回最大位置的指針,若無需返回,則置為0
mask:可選的掩碼操作
minMaxLoc()函數的作用是在數組中找到全局最小值和最大值
4、單模板案例
//模板匹配 #include "opencv2/opencv.hpp" #include <iostream>using namespace std; using namespace cv;int main() {Mat temp=imread("mu.jpg");Mat src=imread("1.jpg");Mat dst=src.clone();imshow("temp",temp);int width=src.cols-temp.cols+1;//result寬度int height=src.rows-temp.rows+1;//result高度Mat result(height,width,CV_32FC1);//創建結果映射圖像//matchTemplate(srcImg, templateImg, resultImg, CV_TM_SQDIFF); //平方差匹配法(最好匹配0)//matchTemplate(srcImg, templateImg, resultImg, CV_TM_SQDIFF_NORMED); //歸一化平方差匹配法(最好匹配0)//matchTemplate(srcImg, templateImg, resultImg, CV_TM_CCORR); //相關匹配法(最壞匹配0)//matchTemplate(srcImg, templateImg, resultImg, CV_TM_CCORR_NORMED); //歸一化相關匹配法(最壞匹配0)//matchTemplate(srcImg, templateImg, resultImg, CV_TM_CCOEFF); //系數匹配法(最好匹配1)matchTemplate(src,temp,result,CV_TM_CCOEFF_NORMED);//化相關系數匹配,最佳值1imshow("result",result);normalize(result,result,0,1,NORM_MINMAX,-1);//歸一化到0-1范圍double minValue,maxValue;Point minLoc,maxLoc;minMaxLoc(result,&minValue,&maxValue,&minLoc,&maxLoc);cout<<"minValue="<<minValue<<endl;cout<<"maxValue="<<maxValue<<endl;rectangle(dst,maxLoc,Point(maxLoc.x+temp.cols,maxLoc.y+temp.rows),Scalar(0,255,0),2,8);imshow("dst",dst);waitKey(0);return 0; }
注意:result的長寬正好是(原圖-模板圖)的長寬,result圖中白亮程度表示匹配程度
5、視頻模板匹配
//視頻模板匹配 #include "opencv2/opencv.hpp" #include <iostream>using namespace std; using namespace cv;int main() {Mat frame,resultImg;Mat templateImg = imread("green.jpg");VideoCapture cap("1.mp4");if(!cap.isOpened())return;int resultImg_cols,resultImg_rows;while(1){cap>>frame;if(frame.empty()) break;Mat showImg = frame.clone();resultImg_cols = frame.cols - templateImg.cols + 1;resultImg_rows = frame.rows - templateImg.rows + 1;resultImg.create(resultImg_cols, resultImg_rows, CV_32FC1);matchTemplate(frame, templateImg, resultImg, CV_TM_CCOEFF_NORMED); //歸一化相關系數匹配法(最好匹配1)normalize(resultImg, resultImg, 0, 1, NORM_MINMAX);double minValue, maxValue;Point minLoc, maxLoc;Point matchLoc;minMaxLoc(resultImg, &minValue, &maxValue, &minLoc, &maxLoc);cout<<"max_value= "<<maxValue<<endl;//cout<<"min_value= "<<minValue<<endl;if(maxValue>=0.7)rectangle(showImg, maxLoc, Point(maxLoc.x + templateImg.cols, maxLoc.y + templateImg.rows), Scalar(0, 255, 0), 2);imshow("frame", frame);imshow("result", showImg);if(27 == waitKey(10))break;}destroyAllWindows();waitKey(0);return 0; }
6、多模板匹配
//多模板匹配 #include "opencv2/opencv.hpp" #include <iostream> #include <stdio.h>using namespace std; using namespace cv;int main() {Mat srcImg = imread("E://src.png");Mat templateImg = imread("E://temp.png");Mat resultImg;Mat showImg = srcImg.clone();int resultImg_cols = srcImg.cols - templateImg.cols + 1;int resultImg_rows = srcImg.rows - templateImg.rows + 1;resultImg.create(resultImg_cols, resultImg_rows, CV_32FC1);matchTemplate(srcImg, templateImg, resultImg, CV_TM_CCOEFF_NORMED); //化相關系數匹配法(最好匹配1)normalize(resultImg, resultImg, 0, 1, NORM_MINMAX);Mat midImg = resultImg.clone();//多目標模板匹配---方法一/*double matchValue;int count0=0;int tempW=0, tempH=0;char matchRate[10];for(int i=0; i<resultImg_rows; i++){for(int j=0; j<resultImg_cols; j++){matchValue = resultImg.at<float>(i, j);sprintf(matchRate, "%0.2f", matchValue);if(matchValue>=0.85 && (abs(j - tempW)>5) && (abs(i - tempH)>5) ){count0++;putText(showImg, matchRate, Point(j-5, i-5), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 1);rectangle(showImg, Point(j, i), Point(j + templateImg.cols, i + templateImg.rows), Scalar(0, 255, 0), 2);tempW = j;tempH = i;}}}cout<<"count="<<count0<<endl;imshow("resultImg", resultImg);imshow("dst", showImg);*///多目標模板匹配---方法二double minValue, maxValue;Point minLoc, maxLoc;Point matchLoc;char matchRate[10];for(int i=0; i<100; i++){int startX = maxLoc.x - 4;int startY = maxLoc.y - 4;int endX = maxLoc.x + 4;int endY = maxLoc.y + 4;if(startX<0 || startY<0){startX = 0;startY = 0;}if(endX > resultImg.cols - 1 || endY > resultImg.rows - 1){endX = resultImg.cols - 1;endY = resultImg.rows- 1;}Mat temp = Mat::zeros(endX - startX, endY - startY, CV_32FC1);//Mat ROI = resultImg(Rect(Point(startX, startY), temp.cols, temp.rows));temp.copyTo(resultImg(Rect(startX, startY, temp.cols, temp.rows)));minMaxLoc(resultImg, &minValue, &maxValue, &minLoc, &maxLoc);if(maxValue<0.8) break;cout<<"max_value= "<<maxValue<<endl;sprintf(matchRate, "%0.2f", maxValue);putText(showImg, matchRate, Point(maxLoc.x - 5, maxLoc.y - 5), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 1);rectangle(showImg, maxLoc, Point(maxLoc.x + templateImg.cols, maxLoc.y + templateImg.rows), Scalar(0, 255, 0), 2);}imshow("midImg", midImg);imshow("resultImg", resultImg);imshow("dst", showImg);waitKey(0);return 0; }
輪廓查找與繪制
1、輪廓的相關概念
1)什么是輪廓
輪廓可以簡單認為成將連續的點(連著邊界)連在一起的曲線,具有相同的顏色或者灰度,提取輪廓就是提取這些具有相同顏色或者灰度的曲線,或者說是連通域,輪廓在形狀分析和物體的檢測和識別中非常有用。
2)注意事項:
①為了更加準確,要使用二值化圖像。在尋找輪廓之前,要進行閾值化處理 或者 Canny 邊界檢測
②查找輪廓的函數會修改原始圖像。如果你在找到輪廓之后還想使用原始圖像的話,你應該將原始圖像存儲到其他變量中(clone(), copyTo())
③在OpenCV 中,查找輪廓就像在黑色背景中找白色物體。你應該記住,要找的物體應該是白色而背景應該是黑色。
3)常用函數:
findContours()—–查找輪廓
drawContours()—–繪制輪廓
2、查找輪廓
void findContours(InputArray image, OutputArrayofArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point());
image: 輸入圖像, Mat類型8位單通道圖像(一般為二值圖)
contours: 檢測到的輪廓, 每個輪廓存儲為一個點向量, 即Point類型的vector表示
hierarchy: 可選的輸出向量, 包含圖像的拓撲信息。其作為輪廓數量的表示, 包含了許多元素, 每個輪廓contours[i]對應4個hierarchy元素hierarchy[i][0]~hierarchy[i][3], 分別表示后一輪廓、前一輪廓、父輪廓、內嵌輪廓的索引編號, 如果沒有對應項, 設置為負數
mode: 輪廓檢索模式, 取值如下:
CV_RETR_EXTERNAL=0-----表示只檢測最外層輪廓 CV_RETR_LIST=1------提取所有輪廓并放置在list中, 輪廓不建立等級關系 CV_RETR_CCOMP=2------提取所有輪廓并組織為雙層結構 CV_RETR_TREE=3------提取所有輪廓并重新建立網狀輪廓結構
method: 輪廓的近似方法, 取值如下:
CV_CHAIN_APPROX_NONE ---連續存儲所有的輪廓點,任何兩個相鄰的點都是水平、垂直或者相鄰的。也就是說max(abs(x1-x2), abs(y2-y1)) == 1 CV_CHAIN_APPROX_SIMPLE --- 壓縮存儲,對于水平、垂直或者斜向的線段,比如一個四邊形,只會存儲四個頂點 CV_CHAIN_APPROX_TC89_L1/CV_CHAIN_APPROX_TC89_KCOS
offset: 每個輪廓的可選偏移量, 默認值Point()
3、繪制輪廓
1 CV_EXPORTS_W void drawContours(InputOutputArray image, InputArrayOfArrays contours, int contourIdx, const Scalar& color, int thickness=1, int lineType=8, InputArray hierarchy=noArray(), int maxLevel=INT_MAX, Point offset=Point());
image: 目標圖像, Mat類型對象即可
contours: 所有的輸入輪廓, 每個輪廓存儲為一個點向量
contourIdx: 輪廓繪制指示變量(索引), 若為負值, 則表示繪制所有輪廓
color: 繪制輪廓的顏色
thickness: 輪廓線條的粗細, 默認值1, 如果為負值, 則繪制輪廓內部, 可選宏 CV_FILLED
lineType: 線條類型, 默認值8
hierarcy: 可選的層次結構信息, 默認值noArray()
maxLevel: 表示用于繪制輪廓的最大等級, 默認值INT_MAX
offset: 可選的輪廓偏移參數, 默認值Point()
//輪廓查找 輪廓繪制 #include"opencv2/opencv.hpp" #include<iostream>using namespace std; using namespace cv;int main() {Mat srcImg = imread("2.png");Mat tempImg = srcImg.clone();//Mat draw(srcImg.rows, srcImg.cols, CV_8UC3);cvtColor(srcImg, srcImg, CV_BGR2GRAY); //轉為灰度圖threshold(srcImg, srcImg,100, 255, CV_THRESH_BINARY);//圖像二值化,value>threshold(即100)?255:0imshow("srcImg", srcImg); //輪廓查找前vector<vector<Point>> contours;vector<Vec4i> hierarchy;//findContours(srcImg, contours, hierarchy,RETR_EXTERNAL, CHAIN_APPROX_SIMPLE ); //查找外輪廓,壓縮存儲輪廓點findContours(srcImg, contours, hierarchy,RETR_LIST, CHAIN_APPROX_SIMPLE ); //查找所有輪廓//findContours(srcImg, contours, hierarchy,CV_RETR_CCOMP, CHAIN_APPROX_SIMPLE ); //查找所有輪廓//findContours(srcImg, contours, hierarchy,RETR_TREE, CHAIN_APPROX_NONE ); //查找所有輪廓,存儲所有輪廓點imshow("cont", srcImg); //輪廓查找后drawContours(tempImg, contours,-1, Scalar(0, 255, 0),2); //繪制輪廓:-1代表繪制所有輪廓cout<<"num="<<contours.size()<<endl; //輸出輪廓個數imshow("contours", tempImg);waitKey(0);return 0; }
附錄:
QT集合OpenCV庫
上面配置的磁盤路徑是你電腦編譯OpenCV的路徑
# 添加我們包含路徑 INCLUDEPATH += D:\Application\opencvdev\opencv3.4.6\rebuild_for_qt\install\include # 添加對應類庫文件 LIBS += D:\Application\opencvdev\opencv3.4.6\rebuild_for_qt\lib\libopencv_*.a