OpenCV实现多角度模板匹配:详细步骤与代码解析
创作时间:
作者:
@小白创作中心
OpenCV实现多角度模板匹配:详细步骤与代码解析
引用
CSDN
1.
https://blog.csdn.net/qq_42722197/article/details/131447920
本文将介绍使用OpenCV实现多角度模板匹配的详细步骤和代码。
背景介绍
熟悉OpenCV的朋友肯定都知道OpenCV自带的模板匹配matchTemplate
方法是不支持旋转的,也就是说当目标和模板有角度差异时匹配常常会失败,可能目标只是轻微的旋转,匹配分数就会下降很多,导致匹配精度下降甚至匹配出错。另一个方法是matchShape
(形状匹配),匹配时需要轮廓分明才容易匹配成功,但无法得到匹配角度,也不方便使用。本文介绍基于matchTemplate
+旋转+金字塔下采样实现多角度的模板匹配,返回匹配结果(斜矩形、角度、方向)。
实现效果
如上面视频所示,本方法可以对不同角度的元件做匹配并标注元件方向。
实现思路
- 如何适应目标的角度变化?我们可以将模板旋转,从0~360°依次匹配找到最佳的匹配位置;
- 如何提高匹配速度?使用金字塔下采样,将模板和待匹配图均缩小后匹配;加大匹配搜寻角度的步长,比如从每1°匹配一次改为每5°匹配一次等。
实现步骤
- 旋转模板图像。旋转图像本身比较简单,下面是代码:
//旋转图像
Mat ImageRotate(Mat image, double angle)
{
Mat newImg;
Point2f pt = Point2f((float)image.cols / 2, (float)image.rows / 2);
Mat M = getRotationMatrix2D(pt, angle, 1.0);
warpAffine(image, newImg, M, image.size());
return newImg;
}
但需要注意,很多时候按照上面方法旋转时,会丢失模板信息产生黑边,这里提供两种方法供大家参考尝试:
- 旋转时放大目标图像尺寸,保证模板图像上信息不丢失,然后模板匹配时使用mask,如何使用mask掩码有什么用?看下面链接文章介绍:实战 | OpenCV带掩码(mask)的模板匹配使用技巧与演示(附源码)
- 旋转时不放大目标图像尺寸,剔除黑边剩余部分做mask来匹配。
- 图像金字塔下采样。什么是图像金字塔?什么是上下采样?直接百度。
下采样的目的前面已介绍,减小图像分辨率提高图像匹配速度,代码如下:
//对模板图像和待检测图像分别进行图像金字塔下采样
for (int i = 0; i < numLevels; i++)
{
pyrDown(src, src, Size(src.cols / 2, src.rows / 2));
pyrDown(model, model, Size(model.cols / 2, model.rows / 2));
}
- **0~360°各角度匹配。**旋转模板图像,依次调用
matchTemplate
在目标图中匹配,记录最佳匹配分数,以及对应的角度。
模板匹配详细使用说明:https://docs.opencv.org/4.x/df/dfb/group__imgproc__object.html#ga586ebfb0a7fb604b35a23d85391329be
旋转匹配代码:
TemplateMatchModes matchMode = TM_CCOEFF_NORMED;
switch (nccMethod)
{
case 0:
matchMode = TM_SQDIFF;
break;
case 1:
matchMode = TM_SQDIFF_NORMED;
break;
case 2:
matchMode = TM_CCORR;
break;
case 3:
matchMode = TM_CCORR_NORMED;
break;
case 4:
matchMode = TM_CCOEFF;
break;
case 5:
matchMode = TM_CCOEFF_NORMED;
break;
}
//在没有旋转的情况下进行第一次匹配
double minVal, maxVal;
Point minLoc, maxLoc;
matchTemplate(src, model, result, matchMode);
minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc);
Point location = maxLoc;
double temp = maxVal;
double angle = 0;
Mat newImg;
//以最佳匹配点左右十倍角度步长进行循环匹配,直到角度步长小于参数角度步长
if (nccMethod == 0 || nccMethod == 1)
{
do
{
for (int i = 0; i <= (int)range / step; i++)
{
newImg = ImageRotate(model, start + step * i);
matchTemplate(src, newImg, result, matchMode);
double minval, maxval;
Point minloc, maxloc;
minMaxLoc(result, &minval, &maxval, &minloc, &maxloc);
if (maxval < temp)
{
location = maxloc;
temp = maxval;
angle = start + step * i;
}
}
range = step * 2;
start = angle - step;
step = step / 10;
} while (step > angleStep);
return ResultPoint(location.x * pow(2, numLevels) + modelImage.cols / 2, location.y * pow(2, numLevels) + modelImage.rows / 2, -angle, temp);
}
else
{
do
{
for (int i = 0; i <= (int)range / step; i++)
{
newImg = ImageRotate(model, start + step * i);
imshow("rotate", newImg);
imshow("src-pyrDown", src);
waitKey();
matchTemplate(src, newImg, result, matchMode);
double minval, maxval;
Point minloc, maxloc;
minMaxLoc(result, &minval, &maxval, &minloc, &maxloc);
if (maxval > temp)
{
location = maxloc;
temp = maxval;
angle = start + step * i;
}
}
range = step * 2;
start = angle - step;
step = step / 10;
} while (step > angleStep);
if (temp > thresScore)
{
return ResultPoint(location.x * pow(2, numLevels), location.y * pow(2, numLevels), -angle, temp);
}
}
return ResultPoint(-1, -1, 0, 0);
- 标注匹配结果。根据模板图大小、匹配结果角度计算出匹配后的矩形四个角点,根据角点关系即可绘制方向:
//获取旋转后矩形对应的端点坐标
vector<Point> GetRotatePoints(Mat img, Rect inRect, double angle)
{
Rect rect = inRect;
vector<Point>pts;
Point2f center = Point2f(img.cols / 2, img.rows / 2);
Mat M = getRotationMatrix2D(center, angle, 1.0);
//cout << M << endl;
Mat ptMat = Mat::ones(3, 4, CV_32FC1);
ptMat.at<float>(0, 0) = 0;
ptMat.at<float>(0, 1) = (float)rect.width - 1;
ptMat.at<float>(0, 2) = (float)rect.width - 1;
ptMat.at<float>(0, 3) = 0;
ptMat.at<float>(1, 0) = 0;
ptMat.at<float>(1, 1) = 0;
ptMat.at<float>(1, 2) = (float)rect.height - 1;
ptMat.at<float>(1, 3) = (float)rect.height - 1;
M.convertTo(M, CV_32F);
Mat result = M * ptMat;
//cout << result << endl;
pts.push_back(Point((int)result.at<float>(0, 0), (int)result.at<float>(1, 0)));
pts.push_back(Point((int)result.at<float>(0, 1), (int)result.at<float>(1, 1)));
pts.push_back(Point((int)result.at<float>(0, 2), (int)result.at<float>(1, 2)));
pts.push_back(Point((int)result.at<float>(0, 3), (int)result.at<float>(1, 3)));
return pts;
}
- 举例演示。模板图从下图中截取并保存
template.png
:
测试图像12张,来自Halcon例程图片,路径如下:C:\Users\Public\Documents\MVTec\HALCON-20.11-Steady\examples\images\modules
匹配结果:
后记
可以添加匹配分数阈值和NMS实现多目标匹配,后续还会介绍其他匹配方法的实现,敬请期待。
热门推荐
肌肉急性缺血 快速治疗存活率高
一步步组装高性能游戏主机:从选购配件到最终调试的完整指南
冷漠女性的三大特征:上进、高效与断舍离能力
迷你主机选购避坑指南:2024年最新攻略
10本哲学入门好书,认真读一本就受益终生
电动车托运怎样收费
书海中的新视角:探讨《人工智能时代的审计》如何引领审计创新
跨海工程的智慧内核——深中通道设计、制造和管理数字化关键技术研究与应用
科普园地|人类肠道为何“喜爱”膳食纤维
龙里公证:探索公证服务在现代社会的应用与价值
电饼铛如何烤红薯,家居生活小妙招
合同违约起诉费用由谁承担
遏制妖股唯有价值投资
孩子近视可以通过中治疗疗吗?探讨中医在儿童近视预防中的有效性与方法
羊肉火锅,除了羊肉,还有什么食材可以一起涮更美味呢?
中国粮食安全现状与未来发展趋势
深入探讨科技历程:从古代科学到现代人工智能的演进与展望
匠人教育:2025年二建难考吗?主客观解读考试难度及备考策略
山梨酸钾:食品添加剂的合法性与风险
科普小知识丨如何区分“心理正常”与“心理异常”?
“数字+” 陕西文旅故事这样讲
机器人柔性执行机构与驱动技术
成排的东风-26,是破解美国亚太战略的利剑和矛头
国内种植牙技术水平如何?种植牙的优势与发展趋势分析
软软糯糯排骨炖萝卜
男性胡子生长速度的影响因素及促进方法
一种创新的锂电池回收处理设备破碎生产线技术
“请您雇佣我!”19岁贫困大学生的求职视频,让人看得笑着含泪
郇阳社区网格员李秋霞:坚守网格责任 绽放服务光芒
婚介所里的婚托诈骗罪:如何识别和防范?