基于OpenCV和图像金字塔的模板匹配

您所在的位置:网站首页 图像尺寸测量 基于OpenCV和图像金字塔的模板匹配

基于OpenCV和图像金字塔的模板匹配

2023-07-24 10:54| 来源: 网络整理| 查看: 265

前言

本项目是机器视觉课程的大作业,写的并不是很明晰,如有问题欢迎提出。

介绍

本项目为基于OpenCV的火花塞间隙尺寸测量,主要是运用模板匹配技术定位火花塞间隙尺寸,然后寻找火花塞间隙并测量其尺寸。

算法说明

在这里插入图片描述

程序主要分成四个部分:

预处理:

在此部分中,将完成模板的创建和待处理图片的路径读取。程序将从文件夹中读取model.png图片作为模板。如果文件夹中没有该图片,则将从第一张待处理图片中截取所需区域作为模板。

匹配:

此函数主要调用OpenCV库中的matchTemplate函数以及相关函数寻找各种角度的图片中与模板的最佳匹配点,并且根据该点找寻区域的中心位置。模板匹配算法根据需要决定,本项目使用平均方差。

旋转图像

为了匹配旋转的图像,所以在匹配时要将图像进行旋转。 旋转使用的是仿射变换,仿射矩阵可用getRotationMatrix2D()得到M1。 由于在旋转图像后,图像的大小也会改变,所以不能直接使用M1进行变换,而是要用getAffineTransform()函数再获得一个大小变换矩阵M2 最后将M1的旋转部分和M2的大小变换部分相结合变成一个新的变换矩阵M,使用warpAffine()函数就能完成图像的仿射变换。 由于代码需要,还需要写坐标变换的函数,公式如下 在这里插入图片描述 M就是一个3*2的矩阵

图像金字塔

如果单纯使用模板匹配,时间复杂度将爆炸,但我们可以使用图像金字塔来进行优化

建立n层图像金字塔,对于每一层金字塔,将图像向下采样,每 2 n 2^n 2n行 2 n 2^n 2n列取一个像素。由此获得 1 2 n \frac{1}{2^n} 2n1​倍的图像和模板。采样完后的图像可用高斯滤波器滤波(本项目没有)

从高层开始将该层模板对该层图像进行模板匹配,所有匹配度高于某一阈值的点都将用一个矩形框住,并记录下旋转角度变换范围。

下一层在上一层截取的区域和角度中进行模板匹配,依次类推逐渐获得一个比较小的匹配区域和角度范围。

对n-1层图像金字塔依次进行匹配后,在源图像中找到得到的区域,并在相应角度范围中进行最终的模板匹配,获得中心点的位置。

尺寸计算:

当找到匹配区域的中心点位置后,垂直向下寻找火花塞间隙。将图片变换成灰度图之后,就可以发现向下直线上的两处灰度值突变的点便是间隙边缘上的点,计算两点间距离就是火花塞间隙的开度。

绘制输出

测量处相关函数之后就可以在图片上绘制测量区域,并且显示开度的数值

代码实现 #include #include #include using namespace cv; using namespace std; 其他函数/// void Min(int &a,int b) { if(a>b)a = b; } void Max(int &a,int b) { if(a Point2f center(cols / 2, rows/2);//中心 Mat M1 = getRotationMatrix2D(center, degree, 1);//计算旋转的仿射变换矩阵 Point2f srcPoints1[3]; Point2f dstPoints1[3]; srcPoints1[0] = Point2i(0, 0); srcPoints1[1] = Point2i(0, rows); srcPoints1[2] = Point2i(cols, 0); dstPoints1[0] = Point2i((newCols - cols)/2 , (newRows - rows)/2); dstPoints1[1] = Point2i((newCols - cols)/2 , (newRows + rows)/2); dstPoints1[2] = Point2i((newCols + cols)/2, (newRows - rows)/2); Mat M2 = getAffineTransform(srcPoints1, dstPoints1); M1.at(0, 2) = M1.at(0, 2) + M2.at(0, 2); M1.at(1, 2) = M1.at(1, 2) + M2.at(1, 2); return M1; } //旋转图像内容不变,尺寸相应变大 Mat Rotate(Mat src, int degree) { double angle = degree * CV_PI / 180.; double a = sin(angle), b = cos(angle); int rows=src.rows; int cols=src.cols; //旋转后的新图尺寸 int width_rotate= int(rows * fabs(a) + cols * fabs(b)); int height_rotate=int(cols * fabs(a) + rows * fabs(b)); Mat M = GetMatrix(cols,rows,width_rotate,height_rotate,degree); Mat dst= Mat::zeros(width_rotate, height_rotate, src.type()); warpAffine(src, dst, M, Size(width_rotate, height_rotate));//仿射变换 return dst; } /* 坐标变换 */ void TransCoor(Point &point,int degree,int rows,int cols,bool dir = 1) { double angle = degree * CV_PI / 180.; double a = sin(angle), b = cos(angle); //旋转后的新图尺寸 int width_rotate= ceil(rows * fabs(a) + cols * fabs(b)); int height_rotate=ceil(cols * fabs(a) + rows * fabs(b)); Mat M; if(dir == 0)//顺时针 { M = GetMatrix(cols,rows,width_rotate,height_rotate,degree); } else //逆时针 { M = GetMatrix(width_rotate,height_rotate,cols,rows,-degree); } point = Point(point.x*M.at(0, 0)+point.y*M.at(0,1)+M.at(0,2), point.x*M.at(1, 0)+point.y*M.at(1, 1)+M.at(1,2)); } int level = 3;//金字塔层级 int degreeMin,degreeMax;//旋转范围 int degree;//最终的旋转角度 /* 在ROI图像中匹配模板model */ Point Match(Mat src,Mat model) { Point temLoc;//最佳匹配点 double Min = 1; for(int d = degreeMin;d temLoc = minLoc;//TM_SQDIFF_NORMED是最小值 degree = d;//最佳匹配角度 Min = min; } } return GetCenter(temLoc.x, temLoc.y, model.cols, model.rows); } /* 获得第level层金字塔图像 */ Mat GetPyramid(Mat src,int level) { int sample = 1 Mat pyramidSrc = GetPyramid(src,level); Mat pyramidModel = GetPyramid(model,level); Point topLeftP = Point(pyramidSrc.cols,pyramidSrc.rows); Point ButtonRightP = Point(0,0); bool isMatch = false; int dmin = 359,dmax = 0; for(int d = degreeMin;d isMatch = true; //匹配到的四个点 Point point[4] = { minLoc, Point(minLoc.x,minLoc.y+pyramidModel.rows), Point(minLoc.x+pyramidModel.cols,minLoc.y), Point(minLoc.x+pyramidModel.cols,minLoc.y+pyramidModel.rows) }; //寻找左上角和右下角 for(int i = 0;i Rect ROI = Rect(0,0,src.cols,src.rows); degreeMin = 0,degreeMax = 359; for(int i=level;i>=1;i--) { FindROI(src(ROI),ROI,i,model); } cout int deri = (int)gray.at(y,startP.x)- (int)gray.at(y+1,startP.x); if( deri secondP = Point(startP.x,y+1); break; } y++; } dis = abs(firstP.y-secondP.y);//计算尺寸 return ; } void Deal(Mat src,Mat model) { //寻找匹配到的区域的中心点 Point center = FindTemplate(src,model); //寻找火花塞间隙的两个端点,并计算尺寸 Point firstP,secondP; int dis; FindLinePoint(Rotate(src,degree),center,firstP,secondP,dis); // //标记并输出图像 Mat dst; src.copyTo(dst); // //将坐标变换为原图 TransCoor(center,degree,src.rows,src.cols); TransCoor(firstP,degree,src.rows,src.cols); TransCoor(secondP,degree,src.rows,src.cols); circle(dst,center,3,Scalar(0,0,255)); line(dst,firstP,secondP,Scalar(0,0,255)); putText(dst,"d:"+to_string(dis),(secondP+firstP)/2+Point(10,0), FONT_HERSHEY_COMPLEX,0.5,Scalar(0,0,255)); imshow("dst",dst); } int main() { string pattern_jpg; vector image_files; pattern_jpg = ".\\img\\*.png"; glob(pattern_jpg, image_files);//读取图片路径 //创建模板 string mode_path = ".\\img\\model.png"; Mat model = imread(mode_path);//模板 if(model.empty())//读取不到图片 { Mat img=imread(image_files[0]); model = img(Rect(250,150,120,80)); } //处理图片 for(int i=0;i


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3