音效素材网提供各类素材,打造精品素材网站!

站内导航 站长工具 投稿中心 手机访问

音效素材

OpenCV-Python实现多模板匹配
日期:2021-09-08 14:39:31   来源:脚本之家

模板匹配的作用在图像识别领域作用可大了。那什么是模板匹配?

模板匹配,就是在一幅图像中寻找另一幅模板图像最匹配(也就是最相似)的部分的技术。

多模板匹配

在上一篇的实战中,我们通过人物眼睛的子图,找出了其在图像中出现位置。但是,有些情况下,并不仅仅只有一次,比如我们讲解傅里叶变换时,曾介绍一张草原的狮子图。如果匹配某个草,可能单个图像内会有很多,这个时候就要找出多个匹配结果。

而函数cv2.minMaxLoc()仅仅能找出最值,无法给出所有匹配区域的位置信息。所以,要想匹配多个结果,就需要进行如下4个步骤:

获取匹配位置的集合

首先,Numpy库中的函数where()能够获取模板匹配位置的集合。对于不同的输入,其返回值是不同的。

  • 当输入是一维数组时,返回值是一维索引,只是一组索引数组。
  • 当输入是二维数组时,返回的是匹配值的位置索引,因此会有两组索引数组表示返回值的位置。

比如,我们的灰度图像一般都是二维数组。下面,我们来查找一个二维数组中,值大于8的元素索引:

import numpy as np

img = np.array([[2, 4, 6, 8, 10], [9, 60, 10, 30, 4], [55, 21, 11, 7, 5]])
result = np.where(img > 5)
print(result)

运行之后,控制台会输出如下内容:

输出结果

如果你对Numpy不是很了解的化。下面博主在将数据转换以下,基本上都能看懂了。转换之后,格式如下:

转换

第一行为大于5的值的X坐标,第二行为大于5的值的Y坐标。那么上面大于5的数组索引为:[0,2],[0,3],[0,4],[1,0],[1,1],[1,2],[1,3],[2,0],[2,1],[2,2],[2,3]。你可以回溯对比看看是不是一致的。

通过np.where()函数可以找出在cv2.matchTemplate()函数的返回值中,哪些位置上的值是大于阈值threshold的。具体操作代码如下:

loc=np.where(res>threshold)

循环

因为我们找到的原图对应的模板图像不止一个,要处理多个值,肯定会用到循环。因此,在获取匹配值的索引后,可以采用如下语句遍历所有匹配的位置,对这些位置做标记:

for i in 匹配位置集合:
	标记匹配位置

在循环中使用zip()

函数zip()用可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。

例如,我们获取的索引为x,y,z。下面我们使用zip()将它们打包成元组。代码如下:

import cv2
import numpy as np
import matplotlib.pyplot as plt

img = np.array([[2, 4, 6, 8, 10], [9, 60, 10, 30, 4], [55, 21, 11, 7, 5]])
result = np.where(img > 5)
for i in zip(*result):
    print(i)

这里我们还是使用上面的值,输出结果如下:

符合条件

这里自动将我们刚才满足条件的索引打包成了元素格式。是不是比刚才的控制台输出结果更加的直观呢?

替换坐标

我们上面得到的结果是符合条件的索引:(行号,列号),但我们需要绘制匹配位置的矩形,需要的是(列号,行号)。

所以,在使用cv2.rectangle()绘制矩形前,要先将函数numpy.where()得到的位置索引行列互换,行列互换可以通过如下代码实现:

import numpy as np

img = np.array([[2, 4, 6, 8, 10], [9, 60, 10, 30, 4], [55, 21, 11, 7, 5]])
result = np.where(img > 5)
for i in zip(*result[::-1]):
    print(i)

运行之后,输出结果如下:

行列互换

实战多模板匹配

既然我们已经了解了标记绘制多个模板位置的4个步骤。下面,我们直接将上面的代码整理以下,即可完成多模板的匹配。具体代码如下所示:

import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread("34.jpg", 0)
template = cv2.imread("4_1.jpg", 0)
w, h = template.shape[::-1]
res = cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED)
threshold = 0.9
loc = np.where(res >= 0.9)
for i in zip(*loc[::-1]):
    cv2.rectangle(img, i, (i[0] + w, i[1] + h), 255, 1)
plt.imshow(img, cmap="gray")
plt.axis("off")
plt.show()

这里的代码与上面4个步骤一摸一样,具体就不做过多的讲解了。运行之后,多个模板也就匹配完成。

匹配多个模板

附录:

模板图

模板图

原图

原图

实例:基于opencv的多目标模板匹配

利用opencv进行多目标模板匹配,只要是利用其matchTemplate函数,但在多目标(这里是讨论目标图片中不同大小模板的匹配),以下贴出代码和图片,供大家参考:

#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <iostream>
#include <math.h>

using namespace std;
using namespace cv;

Point getNextMinLoc(Mat &result, Point minLoc, int maxValue, int templatW, int templatH);

int main(void)
{
 Mat src = imread("1_2.png");
 Mat srcCopy = src.clone();
 
 Mat temp = imread("1_4.png");
 Mat result;

 if (src.empty() || temp.empty())
 {
  cout << "打开图片失败" << endl;
  return 0;
 }

 vector<Mat> templat;
 vector<float> minV;
 vector<Point> minL;

 int srcW, srcH, templatW, templatH, resultH, resultW;
 srcW = src.cols;
 srcH = src.rows;
 templat.push_back(temp);
 double minValue, maxValue;
 Point minLoc, maxLoc;

 for (int i=0;i<10;i++)
 {
  cout << i << ": ";
  templatW = templat[i].cols;
  templatH = templat[i].rows;

  if (srcW < templatW || srcH < templatH)
  {
   cout << "模板不能比原图大" << endl;
   return 0;
  }

  resultW = srcW - templatW + 1;
  resultH = srcH - templatH + 1;

  result.create(Size(resultW, resultH), CV_32FC1);
  matchTemplate(src, templat[i], result, CV_TM_SQDIFF_NORMED);

  minMaxLoc(result, &minValue, &maxValue, &minLoc, &maxLoc);

  cout << "min1: " << minValue << endl;
  if (minValue<=0.070055)
  {
   rectangle(srcCopy, minLoc, Point(minLoc.x + templatW, minLoc.y + templatH), Scalar(0, 0, 255), 2, 8, 0);

   Point new_minLoc;
   new_minLoc = getNextMinLoc(result, minLoc, maxValue, templatW, templatH);
   
   float *data = result.ptr<float>(new_minLoc.y);

   cout << "min2: " << data[new_minLoc.x] << " ";
   if (data[new_minLoc.x]<=0.5)
   {
    cout << "进这个函数了:" << i << ":" << new_minLoc.x;
    cout << " " << new_minLoc.y;
    rectangle(srcCopy, new_minLoc, Point(new_minLoc.x + templatW, new_minLoc.y + templatH),
     Scalar(0, 255, 0), 2, 8, 0);
    new_minLoc = getNextMinLoc(result, new_minLoc, maxValue, templatW, templatH);
   }

   float *data1 = result.ptr<float>(new_minLoc.y);
   cout << "min3: " << data1[new_minLoc.x] << " " << endl;
   if (data1[new_minLoc.x] <= 0.4)
   {
    
    rectangle(srcCopy, new_minLoc, Point(new_minLoc.x + templatW, new_minLoc.y + templatH),
     Scalar(255, 0, 0), 2, 8, 0);
   }
  }
  cout << "#" << endl;
  Mat temp_templat;
  resize(templat[i], temp_templat, Size(templat[i].cols / 1.1, templat[i].rows / 1.1));
  templat.push_back(temp_templat);
 }

 imshow("结果", srcCopy);
 waitKey(0);
 return 0;
}

Point getNextMinLoc(Mat &result, Point minLoc, int maxValue, int templatW, int templatH)
{
 //imshow("result", result);
 //cout << "maxvalue: " << maxValue << endl;
 int startX = minLoc.x - templatW / 3;
 int startY = minLoc.y - templatH / 3;
 int endX = minLoc.x + templatW / 3;
 int endY = minLoc.y + templatH / 3;
 if (startX < 0 || startY < 0)
 {
  startX = 0;
  startY = 0;
 }
 if (endX > result.cols - 1 || endY > result.rows - 1)
 {
  endX = result.cols - 1;
  endY = result.rows - 1;
 }
 int y, x;
 for (y = startY; y < endY; y++)
 {
  for (x = startX; x < endX; x++)
  {
   float *data = result.ptr<float>(y);
   
   data[x] = maxValue;
  }
 }
 double new_minValue, new_maxValue;
 Point new_minLoc, new_maxLoc;
 minMaxLoc(result, &new_minValue, &new_maxValue, &new_minLoc, &new_maxLoc);
 //imshow("result_end", result);
 return new_minLoc;
}

以下是结果图:

到此这篇关于OpenCV-Python实现多模板匹配的文章就介绍到这了,更多相关OpenCV 多模板匹配内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

    您感兴趣的教程

    在docker中安装mysql详解

    本篇文章主要介绍了在docker中安装mysql详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编...

    详解 安装 docker mysql

    win10中文输入法仅在桌面显示怎么办?

    win10中文输入法仅在桌面显示怎么办?

    win10系统使用搜狗,QQ输入法只有在显示桌面的时候才出来,在使用其他程序输入框里面却只能输入字母数字,win10中...

    win10 中文输入法

    一分钟掌握linux系统目录结构

    这篇文章主要介绍了linux系统目录结构,通过结构图和多张表格了解linux系统目录结构,感兴趣的小伙伴们可以参考一...

    结构 目录 系统 linux

    PHP程序员玩转Linux系列 Linux和Windows安装

    这篇文章主要为大家详细介绍了PHP程序员玩转Linux系列文章,Linux和Windows安装nginx教程,具有一定的参考价值,感兴趣...

    玩转 程序员 安装 系列 PHP

    win10怎么安装杜比音效Doby V4.1 win10安装杜

    第四代杜比®家庭影院®技术包含了一整套协同工作的技术,让PC 发出清晰的环绕声同时第四代杜比家庭影院技术...

    win10杜比音效

    纯CSS实现iOS风格打开关闭选择框功能

    这篇文章主要介绍了纯CSS实现iOS风格打开关闭选择框,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作...

    css ios c

    Win7如何给C盘扩容 Win7系统电脑C盘扩容的办法

    Win7如何给C盘扩容 Win7系统电脑C盘扩容的

    Win7给电脑C盘扩容的办法大家知道吗?当系统分区C盘空间不足时,就需要给它扩容了,如果不管,C盘没有足够的空间...

    Win7 C盘 扩容

    百度推广竞品词的投放策略

    SEM是基于关键词搜索的营销活动。作为推广人员,我们所做的工作,就是打理成千上万的关键词,关注它们的质量度...

    百度推广 竞品词

    Visual Studio Code(vscode) git的使用教程

    这篇文章主要介绍了详解Visual Studio Code(vscode) git的使用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。...

    教程 Studio Visual Code git

    七牛云储存创始人分享七牛的创立故事与

    这篇文章主要介绍了七牛云储存创始人分享七牛的创立故事与对Go语言的应用,七牛选用Go语言这门新兴的编程语言进行...

    七牛 Go语言

    Win10预览版Mobile 10547即将发布 9月19日上午

    微软副总裁Gabriel Aul的Twitter透露了 Win10 Mobile预览版10536即将发布,他表示该版本已进入内部慢速版阶段,发布时间目...

    Win10 预览版

    HTML标签meta总结,HTML5 head meta 属性整理

    移动前端开发中添加一些webkit专属的HTML5头部标签,帮助浏览器更好解析HTML代码,更好地将移动web前端页面表现出来...

    移动端html5模拟长按事件的实现方法

    这篇文章主要介绍了移动端html5模拟长按事件的实现方法的相关资料,小编觉得挺不错的,现在分享给大家,也给大家...

    移动端 html5 长按

    HTML常用meta大全(推荐)

    这篇文章主要介绍了HTML常用meta大全(推荐),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参...

    cdr怎么把图片转换成位图? cdr图片转换为位图的教程

    cdr怎么把图片转换成位图? cdr图片转换为

    cdr怎么把图片转换成位图?cdr中插入的图片想要转换成位图,该怎么转换呢?下面我们就来看看cdr图片转换为位图的...

    cdr 图片 位图

    win10系统怎么录屏?win10系统自带录屏详细教程

    win10系统怎么录屏?win10系统自带录屏详细

    当我们是使用win10系统的时候,想要录制电脑上的画面,这时候有人会想到下个第三方软件,其实可以用电脑上的自带...

    win10 系统自带录屏 详细教程

    + 更多教程 +
    ASP编程JSP编程PHP编程.NET编程python编程