opencv 多角度模板匹配

article/2025/11/9 17:01:50

总结一下实现多角度模板匹配踩的坑
一 、多角度匹配涉及到要使用mask,首先opencv matchTemplateMask自带的源码如下:

static void matchTemplateMask( InputArray _img, InputArray _templ, OutputArray _result, int method, InputArray _mask )
{CV_Assert(_mask.depth() == CV_8U || _mask.depth() == CV_32F);CV_Assert(_mask.channels() == _templ.channels() || _mask.channels() == 1);CV_Assert(_templ.size() == _mask.size());CV_Assert(_img.size().height >= _templ.size().height &&_img.size().width >= _templ.size().width);Mat img = _img.getMat(), templ = _templ.getMat(), mask = _mask.getMat();if (img.depth() == CV_8U){img.convertTo(img, CV_32F);}if (templ.depth() == CV_8U){templ.convertTo(templ, CV_32F);}if (mask.depth() == CV_8U){// To keep compatibility to other masks in OpenCV: CV_8U masks are binary masksthreshold(mask, mask, 0/*threshold*/, 1.0/*maxVal*/, THRESH_BINARY);mask.convertTo(mask, CV_32F);}Size corrSize(img.cols - templ.cols + 1, img.rows - templ.rows + 1);_result.create(corrSize, CV_32F);Mat result = _result.getMat();// If mask has only one channel, we repeat it for every image/template channelif (templ.type() != mask.type()){// Assertions above ensured, that depth is the same and only number of channel differstd::vector<Mat> maskChannels(templ.channels(), mask);merge(maskChannels.data(), templ.channels(), mask);}if (method == CV_TM_SQDIFF || method == CV_TM_SQDIFF_NORMED){Mat temp_result(corrSize, CV_32F);Mat img2 = img.mul(img);Mat mask2 = mask.mul(mask);// If the mul() is ever unnested, declare MatExpr, *not* Mat, to be more efficient.// NORM_L2SQR calculates sum of squaresdouble templ2_mask2_sum = norm(templ.mul(mask), NORM_L2SQR);crossCorr(img2, mask2, temp_result, Point(0,0), 0, 0);crossCorr(img, templ.mul(mask2), result, Point(0,0), 0, 0);// result and temp_result should not be switched, because temp_result is potentially needed// for normalization.result = -2 * result + temp_result + templ2_mask2_sum;if (method == CV_TM_SQDIFF_NORMED){sqrt(templ2_mask2_sum * temp_result, temp_result);result /= temp_result;}}else if (method == CV_TM_CCORR || method == CV_TM_CCORR_NORMED){// If the mul() is ever unnested, declare MatExpr, *not* Mat, to be more efficient.Mat templ_mask2 = templ.mul(mask.mul(mask));crossCorr(img, templ_mask2, result, Point(0,0), 0, 0);if (method == CV_TM_CCORR_NORMED){Mat temp_result(corrSize, CV_32F);Mat img2 = img.mul(img);Mat mask2 = mask.mul(mask);// NORM_L2SQR calculates sum of squaresdouble templ2_mask2_sum = norm(templ.mul(mask), NORM_L2SQR);crossCorr( img2, mask2, temp_result, Point(0,0), 0, 0 );sqrt(templ2_mask2_sum * temp_result, temp_result);result /= temp_result;}}else if (method == CV_TM_CCOEFF || method == CV_TM_CCOEFF_NORMED){// Do mul() inline or declare MatExpr where possible, *not* Mat, to be more efficient.Scalar mask_sum = sum(mask);// T' * M where T' = M * (T - 1/sum(M)*sum(M*T))Mat templx_mask = mask.mul(mask.mul(templ - sum(mask.mul(templ)).div(mask_sum)));Mat img_mask_corr(corrSize, img.type()); // Needs separate channels// CCorr(I, T'*M)crossCorr(img, templx_mask, result, Point(0, 0), 0, 0);// CCorr(I, M)crossCorr(img, mask, img_mask_corr, Point(0, 0), 0, 0);// CCorr(I', T') = CCorr(I, T'*M) - sum(T'*M)/sum(M)*CCorr(I, M)// It does not matter what to use Mat/MatExpr, it should be evaluated to perform assign subtractionMat temp_res = img_mask_corr.mul(sum(templx_mask).div(mask_sum));if (img.channels() == 1){result -= temp_res;}else{// Sum channels of expressiontemp_res = temp_res.reshape(1, result.rows * result.cols);// channels are now columnsreduce(temp_res, temp_res, 1, REDUCE_SUM);// transform back, but now with only one channelresult -= temp_res.reshape(1, result.rows);}if (method == CV_TM_CCOEFF_NORMED){// norm(T')double norm_templx = norm(mask.mul(templ - sum(mask.mul(templ)).div(mask_sum)),NORM_L2);// norm(I') = sqrt{ CCorr(I^2, M^2) - 2*CCorr(I, M^2)/sum(M)*CCorr(I, M)//                  + sum(M^2)*CCorr(I, M)^2/sum(M)^2 }//          = sqrt{ CCorr(I^2, M^2)//                  + CCorr(I, M)/sum(M)*{ sum(M^2) / sum(M) * CCorr(I,M)//                  - 2 * CCorr(I, M^2) } }Mat norm_imgx(corrSize, CV_32F);Mat img2 = img.mul(img);Mat mask2 = mask.mul(mask);Scalar mask2_sum = sum(mask2);Mat img_mask2_corr(corrSize, img.type());crossCorr(img2, mask2, norm_imgx, Point(0,0), 0, 0);crossCorr(img, mask2, img_mask2_corr, Point(0,0), 0, 0);temp_res = img_mask_corr.mul(Scalar(1.0, 1.0, 1.0, 1.0).div(mask_sum)).mul(img_mask_corr.mul(mask2_sum.div(mask_sum)) - 2 * img_mask2_corr);if (img.channels() == 1){norm_imgx += temp_res;}else{// Sum channels of expressiontemp_res = temp_res.reshape(1, result.rows*result.cols);// channels are now columns// reduce sums columns (= channels)reduce(temp_res, temp_res, 1, REDUCE_SUM);// transform back, but now with only one channelnorm_imgx += temp_res.reshape(1, result.rows);}sqrt(norm_imgx, norm_imgx);result /= norm_imgx * norm_templx;}}
}

可以看到使用用了四次dft来计算卷积,目标图像要与mask卷三次,来计算目标图像在模板区域内的和,平方和。其中最后一次CCorr(I, mask2)可以省略掉,它跟CCorr(I, mask)是一样的,因为mask是二值。

 // CCorr(I, T'*M)crossCorr(img, templx_mask, result, Point(0, 0), 0, 0);// CCorr(I, M)crossCorr(img, mask, img_mask_corr, Point(0, 0), 0, 0);crossCorr(img2, mask2, norm_imgx, Point(0,0), 0, 0);crossCorr(img, mask2, img_mask2_corr, Point(0,0), 0, 0);

所以耗时的部分就是这三次卷积,可以用simd加速。opencv以及封装了simd指令,怎么用看这位博主OpenCV 4.x3.4.x版本以上也有中提供了强大的统一向量指令
实测,在高金字塔进行全局匹配的时候,用crossCorr来计算卷积,而用simd计算局部卷积,这样更快。
二、模板的旋转

  1. 创建一个paddingImg,其尺寸是模板的对角线长+1,然后再将模板图像拷贝到paddingImg中间去,这样旋转就不会超出边界,代码如下。
  2. 还有一个点,就是旋转的插值是最好使用INTER_NEAREST,试过其他几种,在比较高的金字塔层中匹配,会出现匹配不到的情况。
void NccMatch::RotateImg(Mat mImg, int nAngle, Mat& outImg, Mat& mask,RotatedRect* ptrMinRect, Point2d pRC)
{if (mImg.depth()    != CV_32F) { mImg.convertTo(mImg, CV_32F); }int nDiagonal       = sqrt(pow(mImg.cols, 2) + pow(mImg.rows, 2)) + 1;Mat paddingImg      = -1 * Mat::ones(Size(nDiagonal, nDiagonal), mImg.type());Rect roi(Point((nDiagonal - mImg.cols) * 0.5, (nDiagonal - mImg.rows) * 0.5), Size(mImg.cols, mImg.rows));mImg.copyTo(paddingImg(roi));Mat M = getRotationMatrix2D(Point2d(paddingImg.cols * 0.5, paddingImg.rows * 0.5), nAngle, 1.0);warpAffine(paddingImg, outImg, M, paddingImg.size(), INTER_NEAREST, 0, Scalar::all(-2));mask = outImg.clone();threshold(mask, mask, -1, 1, THRESH_BINARY);//RotatedRect rRect(Point2d(paddingImg.cols * 0.5, paddingImg.rows * 0.5), Size2f(mImg.cols, mImg.rows), -nAngle);ptrMinRect->center  = Point2d(paddingImg.cols * 0.5, paddingImg.rows * 0.5);ptrMinRect->size    = Size2f(mImg.cols, mImg.rows);ptrMinRect->angle   = -nAngle;return;
}

三,匹配
一定要把目标图像进行padding,确保模板能滑过每一个像素,不然会发现有些图,死活都匹配不上了。

最后,实现的效果如下,有些测试图是用的这位大佬的https://github.com/DennisLiu1993/Fastest_Image_Pattern_Matching
步长和亚像素的计算也是参考这位大佬。
在这里插入图片描述
目标图像2592x1944,模板149x150,匹配角度[0,360],耗时约为:34ms
在这里插入图片描述
目标图像646x492,模板214x98,匹配角度[0,360],金字塔层数=4,耗时约为:14ms
在这里插入图片描述
目标图像2592x1944,模板466x135,匹配角度[0,360],金字塔层数=5,耗时约为:58ms
在这里插入图片描述目标图像830x822,模板209x95,匹配角度[0,360],金字塔层数=4,耗时约为:45ms
更新:
增加了模板文件的序列化 ,把ncc match相关功能封装成了dll,用WPF做了个简单的Demo,如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
Demo里面展现了调用dll的接口,下载在这里Demo下载(不包含Ncc match的源码


http://chatgpt.dhexx.cn/article/qHE5lPbx.shtml

相关文章

用OpenCV进行模板匹配

1. 引言 今天我们来研究一种传统图像处理领域中对象检测和跟踪不可或缺的方法——模板匹配&#xff0c;其主要目的是为了在图像上找到我们需要的图案&#xff0c;这听起来十分令人兴奋。 所以&#xff0c;事不宜迟&#xff0c;让我们直接开始吧&#xff01; 2. 概念 模板匹…

OpenCV之模板匹配

模板匹配的概念与原理 模板匹配是在一幅图像中寻找与另一幅模板图像最匹配&#xff08;相似&#xff09;部分的技术&#xff0c;在OpenCV中&#xff0c;模板匹配由函数MatchTemplate()函数实现。需要注意的是&#xff0c;模板匹配不是基于直方图的&#xff0c;而是通过在输入图…

基于OpenCV的模板匹配

模板匹配是一项在一幅图像中寻找与另一幅模板图像最匹配(相似)部分的技术。模板匹配不是基于直方图的, 而是通过在输入图像上滑动图像块(模板)同时比对相似度, 来对模板和输入图像进行匹配的一种方法。 image: 待搜索图像(大图)templ: 搜索模板, 需和原图一样的数据类型且尺寸…

【OpenCV系列】模板匹配

原理 什么是模板匹配? 模板匹配是一项在一幅图像中寻找与另一幅模板图像最匹配(相似)部分的技术. 它是怎么实现的? 我们需要2幅图像: 我们的目标是检测最匹配的区域: 原图像 (I): 在这幅图像里,我们希望找到一块和模板匹配的区域模板 (T): 将和原图像比照的图像块 为了…

python opencv 模板匹配

模板匹配是在一个大图里搜索和找模板图像位置的方法。OpenCV有个函数cv2.matchTemplate()来做这个。它吧模板图像在输入图像上滑动&#xff0c;对比模板和在模板图像下的输入图像块。它返回了一个灰度图像&#xff0c;每个像素表示那个像素周围和模板匹配的情况。 如果输入图像…

基于OpenCV的图像匹配----模板匹配(一)

我先介绍一下模板匹配的原理 原图像&#xff1a;我们期望找到与模板图像匹配的图像 模板图像&#xff1a;将于模板图像进行比较的图像 一次移动一个像素&#xff08;从左到右&#xff0c;从上到下&#xff09;。在每个位置&#xff0c;计算相似度度量&#xff0c;以便它表示在…

OpenCV图像处理——模板匹配

总目录 图像处理总目录←点击这里 十一、模板匹配 11.1、原理 找一个图片上有的模板&#xff0c;和原图像进行模板匹配模板图像在待匹配图像上滑动&#xff0c;记录下每次滑动后的参数值&#xff08;如平方差&#xff0c;相关性&#xff09;。寻找最小值或最大值&#xff0…

OpenCV模板匹配算法详解

本博客在https://www.cnblogs.com/zhaoweiwei/p/OpenVC_matchTemplate.html基础上进行更加详细的注解。当初有几个地方看的比较费劲&#xff0c;但是里面没有注释&#xff0c;现给加上&#xff0c;主要是那些带黄色及红色部分的注释。 在此向weiwei22844致敬。 模板匹配是在一…

OpenCV 模板匹配(Template Match)

文章目录 模板匹配介绍模板匹配定义匹配算法平方差归一化的平方差相关性归一化的相关性相关性系数归一化的相关性系数 相关API代码示例 模板匹配介绍 模板匹配定义 模板匹配就是在整个图像区域发现与给定子图像匹配的小块区域。 所以模板匹配首先需要一个模板图像T&#xff…

OpenCV 模板匹配

模板匹配就是在大图中找小图&#xff0c;也就说在一幅图像中寻找另一幅模板图像的位置。 案例来源于傅老师。 模板匹配的操作方法是将模板图像B在图像A上滑动&#xff0c;遍历所有像素以完成匹配。 工作原理&#xff1a;在带检测图像上&#xff0c;从左到右&#xff0c;从上…

13 -- OpenCV学习—模板匹配

模板匹配 1.模板匹配 模板匹配&#xff1a;在给定的模板中查找最相似的区域 实现流程&#xff1a; 准备两幅图像 &#xff08;1&#xff09;原图 &#xff08;2&#xff09;模板图滑动模板块和原图像进行比对对于每个像素位置。将计算结果存在矩阵中&#xff0c;输入图像大小…

opencv模板匹配步骤及Code

opencv模板匹配步骤及Code 首先介绍一下模板匹配的适用场景&#xff1a; 1、图像检索 2、目标跟踪 简单的说&#xff0c;模板匹配最主要的功能就是在一幅图像中去寻找和另一幅模板图像中相似度最高的部分&#xff0c;这就是模板匹配。 比如&#xff0c;在下面这图片中: 我们要…

OpenCV 学习笔记(模板匹配)

OpenCV 学习笔记&#xff08;模板匹配&#xff09; 模板匹配是在一幅图像中寻找一个特定目标的方法之一。这种方法的原理非常简单&#xff0c;遍历图像中的每一个可能的位置&#xff0c;比较各处与模板是否“相似”&#xff0c;当相似度足够高时&#xff0c;就认为找到了我们的…

OpenCV-模板匹配 单个对象匹配、多个对象匹配

目录 概念步骤单个对象匹配代码实现一代码实现二 多个对象匹配代码实现 概念 模板匹配与剪辑原理很像&#xff0c;模板在原图像上从原点开始浮动&#xff0c;计算模板&#xff08;图像被模板覆盖的地方&#xff09;的差别程度&#xff0c;这个差别程度的计算方法在opencv里有六…

openCV——模板匹配

单模板匹配 import cv2 #opencv读取的格式是BGR import numpy as np import matplotlib.pyplot as plt#Matplotlib是RGB %matplotlib inline def cvshow(name, ndarray):img cv2.imshow(name, ndarray)cv2.waitKey(0)cv2.destroyAllWindows()模板匹配是指在当前图像 A 内寻找…

Python+Opencv实现模板匹配

目录 一、模板匹配简介二、传统模板匹配算法不足之处三、多尺度模板匹配实现步骤四、多尺度模板匹配实现代码五、多尺度模板匹配效果展示和分析六、思维扩展参考资料注意事项 一、模板匹配简介 所谓的模板匹配&#xff0c;即在给定的图片中查找和模板最相似的区域&#xff0c;该…

OpenCV数字图像处理实战二:模板匹配(C++)

OpenCV数字图像处理实战二&#xff1a;模板匹配&#xff08;C&#xff09; 1、模板匹配原理 模板匹配&#xff08;TemplateMatching&#xff09;就是在一幅图像中寻找和模板图像&#xff08;template&#xff09;最相似的区域&#xff0c;模板匹配不是基于直方图的&#xff0…

opencv 模板匹配形状匹配

文章目录 1. 找圆垫子1.1 得到模板1.2 形状匹配 2. 找瓜子 这是第四次作业要求 所以今天就趁机会讲讲模板匹配&#xff0c;正好之前的项目有一部分重要工作就是和模板匹配紧密相关&#xff0c;对于今天作业来说&#xff0c;之前的项目难度更大&#xff0c;因为涉及到许多要考虑…

Opencv——图像模板匹配

引言 什么是模板匹配呢&#xff1f; 看到这里大家是否会觉得很熟悉的感觉涌上心头&#xff01;在人脸识别是不是也会看见 等等。 模板匹配可以看作是对象检测的一种非常基本的形式。使用模板匹配&#xff0c;我们可以使用包含要检测对象的“模板”来检测输入图像中的对象。 …

OpenCV-模板匹配cv::matchTemplate

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 函数原型 void matchTemplate( InputArray image, InputArray templ,OutputArray result, int method, InputArray mask noArr…