模版匹配
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);//化相关系数匹配,最佳值1 imshow("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:0 imshow("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