声明:本篇仅仅是分享网上的开源项目,算法非本人原创。

〇.算法效果展示

0.1要定位的模板一

找到的匹配

在有污损情况下找到的匹配

0.2要定位的模板2

找到的匹配

一. 理论部分

模板匹配的算法包括基于灰度的匹配,基于特征的匹配,基于组件的匹配,基于相关性的匹配以及局部变形匹配。基于灰度的匹配一般应用在没有缩放和旋转,颜色变化不大的场合。基于特征的匹配一般应用在具有缩放和旋转,颜色变化较大的场合。在模板各个组件有相对位移的情况下,使用基于组件的匹配算法。在图像模糊,目标定位不依赖于边缘的情况下一般使用基于相关性的匹配。目标有局部变形的情况使用局部变形匹配算法。基于特征的匹配算法通常使用线特征即轮廓特征,也有使用点特征(ICP算法和RANSAC算法)和面特征(基于区域,基于纹理等)的方法。本文主要介绍基于轮廓特征的模板匹配。

基于轮廓的模板匹配其相似度分布比基于灰度的模板匹配更加有区分度,因而可以进行较高精度的定位。同时,由于只对轮廓进行处理,因而提高了处理速度,因此在处理带有旋转和缩放的情况时具有优势。另外,基于轮廓特征的模板可以不从现场获取,而从CAD数据生成。

二.实验部分:

2.1实验一:边缘模板创建

(Canny算法+非极大值已知+滞后阈值)

在opencv中有函数findcontour可以找到二值图中的边缘,但需要人为控制二值化参数并观察结果,对于灰度区域较多的模板,并不能找到所有边缘,因此使用Canny边缘检测方法,然后进行非极大值抑制和滞后阈值处理得到的边缘。

原图:

找到的边缘点

代码

C++

int myfindcontour(Mat templateArr, double maxContrast, double minContrast)
{Mat src = templateArr.clone();//变量定义Size Ssize;Ssize.width = src.cols;Ssize.height = src.rows;modelHeight = src.rows;  modelWidth = src.cols;noOfCordinates = 0;cordinates = new Point[modelWidth *modelHeight];edgeMagnitude = new double[modelWidth *modelHeight];edgeDerivativeX = new double[modelWidth *modelHeight];edgeDerivativeY = new double[modelWidth *modelHeight];//使用Sobel算子计算梯度GaussianBlur(src, src, Size(5,5), 1); Mat gx, gy;gx = Mat(Ssize.height, Ssize.width, CV_16SC1);gy = Mat(Ssize.height, Ssize.width, CV_16SC1);Sobel(src, gx, CV_16S, 1, 0, 3);       Sobel(src, gy, CV_16S, 0, 1, 3);Mat nmsEdges = Mat(Ssize.height, Ssize.width, CV_32F); const short* _sdx;const short* _sdy;double fdx, fdy;double MagG, DirG;double MaxGradient = -99999.99;double direction;int *orients = new int[Ssize.height *Ssize.width];int count = 0, i, j; double **magMat;CreateDoubleMatrix(magMat, Ssize);for (i = 1; i < Ssize.height - 1; i++){for (j = 1; j < Ssize.width - 1; j++){fdx = gx.at<short>(i, j);fdy = gy.at<short>(i, j);MagG = sqrt((float)(fdx*fdx) + (float)(fdy*fdy));direction = fastAtan2((float)fdy, (float)fdx);   magMat[i][j] = MagG;if (MagG > MaxGradient)MaxGradient = MagG; 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;elsedirection = 0;orients[count] = (int)direction;count++;}}//非极大值抑制count = 0;double leftPixel, rightPixel;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;}if ((magMat[i][j] < leftPixel) || (magMat[i][j] < rightPixel)){nmsEdges.at<int>(i,j) = 0;}else{nmsEdges.at<int>(i,j) = (uchar)(magMat[i][j] / MaxGradient * 255);}count++;}}//滞后阈值int RSum = 0, CSum = 0;int curX, curY;int flag = 1;for (i = 1; i < Ssize.height - 1; i++){for (j = 1; j < Ssize.width; j++){fdx = gx.at<short>(i, j);fdy = gy.at<short>(i, j);MagG = sqrt(fdx*fdx + fdy*fdy);DirG = fastAtan2((float)fdy, (float)fdx);flag = 1;if(nmsEdges.at<int>(i, j) < maxContrast){if(nmsEdges.at<int>(i, j) < minContrast){nmsEdges.at<int>(i, j) = 0;flag = 0; //移除}else{//8邻域中任意一点if (nmsEdges.at<int>(i - 1, j - 1) < maxContrast &&nmsEdges.at<int>(i - 1, j) < maxContrast &&nmsEdges.at<int>(i - 1, j + 1) < maxContrast &&nmsEdges.at<int>(i, j - 1) < maxContrast &&nmsEdges.at<int>(i, j + 1) < maxContrast &&nmsEdges.at<int>(i + 1, j - 1) < maxContrast &&nmsEdges.at<int>(i + 1, j) < maxContrast &&nmsEdges.at<int>(i + 1, j + 1) < maxContrast){nmsEdges.at<int>(i, j) = 0;flag = 0;}}}//保存找到的边缘点curX = i;    curY = j;if (flag != 0){if (fdx != 0 || fdy != 0){RSum = RSum + curX; CSum = CSum + curY; //累加准备计算重心cordinates[noOfCordinates].x = curX;cordinates[noOfCordinates].y = curY;edgeDerivativeX[noOfCordinates] = fdx;edgeDerivativeY[noOfCordinates] = fdy;if (MagG != 0)edgeMagnitude[noOfCordinates] = 1 / MagG;  elseedgeMagnitude[noOfCordinates] = 0;noOfCordinates++;}}}}//计算重心centerOfGravity.x = RSum / noOfCordinates;centerOfGravity.y = CSum / noOfCordinates;//偏移重心for (int m = 0; m < noOfCordinates; m++){int temp;temp = cordinates[m].x;cordinates[m].x = temp - centerOfGravity.x;temp = cordinates[m].y;cordinates[m].y = temp - centerOfGravity.y;}delete[] orients;modelDefined = true;return 1;
}

2.2实验二:使用滑动窗口的边缘模板匹配

接上述实验一,在找到模板的边缘后,使用同样的方法找到待检图片中边缘。然后使模板在待检测图片中滑动,只对模板中找到的边缘点进行计算,进行基于边缘的匹配。
待检测图片:

检测结果:

代码:

C++

 for (i = 0; i < Ssize.height; i++){for (j = 0; j < Ssize.width; j++){partialSum = 0; for (m = 0; m < noOfCordinates; m++){curX = i + cordinates[m].x;  curY = j + cordinates[m].y;iTx = edgeDerivativeX[m];iTy = edgeDerivativeY[m];if (curX<0||curY<0 || curX>Ssize.height - 1 || curY>Ssize.width - 1)continue;iSx = Sdx.at<short>(curX, curY);iSy = Sdy.at<short>(curX, curY);if ((iSx != 0 || iSy != 0) && (iTx != 0 || iTy != 0)){partialSum = partialSum + ((iSx*iTx) + (iSy*iTy))*(edgeMagnitude[m] * matGradMag[curX][curY]);}sumOfCoords = m + 1;partialScore = partialSum / sumOfCoords;if (partialScore < (MIN((minScore - 1) + normGreediness*sumOfCoords, normMinScore*  sumOfCoords)))break;}if (partialScore > resultScore) //更新结果{resultScore = partialScore;resultPoint->x = i;resultPoint->y = j;}}}

该算法对待检图片有污损的情况下仍能稳定检测:

如上图待检图片右上部分轮廓不可见,在进过处理后得到如下结果:

2.3实验三:基于Hu矩的边缘模板匹配

在模板情况简单,只用外轮廓就可精确定位的前提下,可以使用Hu矩轮廓匹配方法matchShapes,使用匹配到的重心进行定位,对模板轮廓进行缩放和旋转参数搜索,得到目标相对模板旋转缩放参数。
模板:

待搜索图中目标进行的相应的缩放和旋转:

搜索结果:

代码:

C++

 Mat searchImage = imread("pic/contour/Search2.jpg");Mat searchImageResult = searchImage.clone();Mat graySearchImg;cvtColor(searchImage, graySearchImg, COLOR_RGB2GRAY);Mat templateImage = imread("pic/contour/Template2.jpg");Mat grayTemplateImg;cvtColor(templateImage, grayTemplateImg, COLOR_RGB2GRAY);//在Template2和Search2中找外轮廓Mat bin_search;threshold(graySearchImg, bin_search, 128, 255, THRESH_BINARY);vector<vector<Point>> contours_search;vector<Vec4i> hierarchy_search;findContours(bin_search, contours_search, hierarchy_search, RETR_TREE, CHAIN_APPROX_NONE, Point(0, 0));Mat bin_template;threshold(grayTemplateImg, bin_template, 128, 255, THRESH_BINARY);vector<vector<Point>> contours_template;vector<Vec4i> hierarchy_template;findContours(bin_template, contours_template, hierarchy_template, RETR_TREE, CHAIN_APPROX_NONE, Point(0, 0));drawContours(searchImage, contours_search, -1, Scalar(255, 0, 0));drawContours(templateImage, contours_template, -1, Scalar(255, 0, 0));//对待检图中的每个轮廓进行判断,可以事先筛选再判断for (int i = 0; i < contours_search.size(); i++){double score = matchShapes(contours_template[0], contours_search[i], CONTOURS_MATCH_I3,0);cout << "score" << score << endl;if (score < 0.1)  //认为匹配成功{//计算重心偏移,缩放和旋转double area_template = contourArea(contours_template[0]);double area_search = contourArea(contours_search[i]);float scale = sqrt(area_search/ area_template);cout << "scale:" << scale << endl;//由于先验目标最小包围矩形是长方形//因此最小包围矩形的中心和重心的向量夹角为旋转RotatedRect rect_template = minAreaRect(contours_template[0]);RotatedRect rect_search = minAreaRect(contours_search[i]);//两个旋转矩阵是否同向int sign = (rect_template.size.width - rect_template.size.height) *(rect_search.size.width - rect_search.size.height);float angle;if (sign > 0) //可以直接相减angle = rect_search.angle - rect_template.angle;elseangle = (90 + rect_search.angle) - rect_template.angle;cout << "angle:" << angle << endl;//对contours_template基于重心进行旋转和缩放,//然后偏移contours_search[i],绘制到待检图中Moments mom_template = moments(contours_template[0]);Moments mom_search = moments(contours_search[i]);Point gravity_template = Point(mom_template.m10 / mom_template.m00, mom_template.m01 / mom_template.m00);Point gravity_search = Point(mom_search.m10 / mom_search.m00, mom_search.m01 / mom_search.m00);//对模板轮廓进行平移,旋转和缩放for (int j = 0; j < contours_template[0].size(); j++){//重心平移到原点int x = contours_template[0][j].x;int y = contours_template[0][j].y;x -= gravity_template.x;y -= gravity_template.y;//缩放x *= scale;y *= scale;//旋转float rad = angle / 180.0*3.1416;contours_template[0][j].x = x*cos(rad) - y * sin(rad);contours_template[0][j].y = x*sin(rad) + y * cos(rad);}//画带有偏移的从模板经过缩放和旋转生成的轮廓drawContours(searchImageResult, contours_template, -1, Scalar(0, 255, 0),4,8,noArray(),LONG_MAX, gravity_search);}}imshow("templateImage", templateImage);imshow("searchImage", searchImage);imshow("searchImageResult", searchImageResult);waitKey();

总结
该方法由于要找到反映目标外轮廓的曲线,因此对背景干扰较为敏感。但由于不使用滑动窗口的方法,算法复杂度降低,处理时间减少

————————————————

版权声明:本文为CSDN博主「仟人斩」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:基于轮廓的模板匹配_千人斩的博客-CSDN博客_轮廓匹配和模板匹配https://blog.csdn.net/iamqianrenzhan/article/details/105040587

-----

参考文献:

其他人写的模板匹配源码:

基于OpenCV的的图像旋转匹配算法模板(C++实现)_opencv旋转匹配,opencv带旋转的模板匹配-C++代码类资源-CSDN下载https://download.csdn.net/download/zxt1484675627/12074584

使用OpenCV实现Halcon算法(3)基于轮廓的模板匹配相关推荐

  1. MFC与Halcon混合编程--基于相关性的模板匹配

    文章目录 前言 一.基于相关性的模板匹配 二.基于相关性的模板匹配的代码实现 1.Halcon中完成基于相关性的模板匹配 2.MFC实现与Halcon混合编程 3.实现效果 前言 正在学习Halcon ...

  2. 基于Halcon学习的基于灰度值模板匹配【一】exhaustive_match.hdev例程

    * 模板与图像的匹配 read_image (Image, 'fabrik') gen_rectangle1 (Rectangle, 365, 300, 390, 330) *将图像的定义域缩小为创建 ...

  3. 使用OpenCV实现Halcon算法(4)OpenCV实现边缘模板匹配算法

    声明:本篇仅仅是分享网上的开源项目,算法非本人原创. 本文转自:OpenCV研习社 干货 | OpenCV实现边缘模板匹配算法 - 云+社区 - 腾讯云 干货 | OpenCV实现边缘模板匹配算法 - ...

  4. OpenCV 实现基于边界的模板匹配-适用部分覆盖和光照变化情况

    介绍 模板匹配是一个图像处理问题,当其姿态(X,Y,θ)未知时,使用另一张搜索图像中的模板图像找到对象的位置.在本文中,我们实现了一种算法,该算法使用对象的边缘信息来识别搜索图像中的对象. 背景 由于 ...

  5. OpenCV基于形状的模板匹配

    OpenCV基于形状的模板匹配 引言 基于形状的匹配算法 具体代码 KcgMatch.h KcgMatch.cpp main.cpp 匹配的结果 引言 在OpenCV中有个用于模板匹配的基本函数mat ...

  6. Qt与halcon联合开发实现基于形状的模板匹配

    目录 前言 一.基于形状的模板匹配是什么? 二.具体实现 1.算子介绍 2.关键代码实现 总结 前言 第一次在CSDN写博客,准备写一个简单的形状匹配算子的用法及实现的介绍. 一.基于形状的模板匹配是 ...

  7. HALCON基于形变的模板匹配实现

    基于形变的模板匹配 先看匹配结果: 下面是HALCON的代码,用匹配助手生成的,现在得到的就是模板上61个点的坐标,还有仿射矩阵,利用仿射矩阵可以算出61个点匹配上的像素坐标. * * Matchin ...

  8. Halcon 第五章『模板匹配Matching』◆第5节:基于组件的模板匹配|Component-Based

    一.介绍 基于组件的模板匹配是基于形状匹配的一种应用,也可以说是基于形状的模板匹配的加强版,加强的地方在于,这种方法允许模板中包含多个目标,并且允许目标之间存在相对运动(位移和旋转).区别在于基于形状 ...

  9. OpenCV实现基于形状的模板匹配(附源码)

    效果预览 OpenCV实现基于形状的模板匹配(多角度+不同亮度) 实例演示一: 实例演示二: 实例演示三: 实例演示四: 实例演示五: 实例演示六ÿ

最新文章

  1. html带颜色表格怎么做,使用HTML添加表格4(行颜色与表格嵌套)——零基础自学网页制作...
  2. Untiy3D(4.5) 中应用 MovieTexture,无法关联资源文件可能的原因
  3. 逆向 EasyBase64
  4. linux7做服务器,centos7 搭建yum服务器
  5. Linux下那些查找命令
  6. CCF201703-2 学生排队
  7. 1.redis单机部署
  8. Volley源码解析(一)
  9. 汽车零部件开发工具巨头V公司全套应用层UDS协议栈源代码,包括10,11,14,19,22,27,28,31,34,35,36,37,85,2e,2f,3e服务、配置及抽象层,可以自己集成
  10. oracle字段类型number默认值,Oracle 字段类型 | 学步园
  11. 漂浮广告代码、漂浮代码分析(讲解)
  12. DeviceManager--Device administration设备管理器
  13. The bean sellergoods.FeignClientSpecification could not be registered. A bean with that name has a
  14. mysql sql 除法运算_SQL语句怎么表示除法运算?
  15. 快速开始keras 教程
  16. 离散数学在计算机科学中的应用
  17. 同步与异步通信的区别
  18. python pandas dropna 删除空值/缺失值(DataFrame)
  19. Element 单元格合计(多行合计、合计列合并)
  20. snowboy嵌入式_jetson nano 安装 snowboy 遇到的问题及处理

热门文章

  1. 画不同阻尼比(欠阻尼系统)下二阶系统的幅频相频
  2. 经纬度值绘制GPS轨迹图
  3. 在职计算机工程博士,在职读工程博士是否有双证
  4. stm32 笔记 PWM输入模式测量脉宽和占空比原理
  5. 我这款是移动光猫电视一体机,海思芯片,网上说短接点,怎样找短接点刷机
  6. 如何解决C盘空间不足的问题?
  7. 全套恒压供水一拖三程序图纸(看描述)恒压供水一拖三图纸程序
  8. DeepMind创始人自述:我们的算法可以横扫一切棋类博弈
  9. 计算机术语ap,各种计算机术语KVM
  10. vivo 首发手语识别技术 + AI手语开放平台