OpenCV 学习笔记(模板匹配)

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

在 OpenCV 中,提供了相应的函数完成这个操作。

matchTemplate 函数:在模板和输入图像之间寻找匹配,获得匹配结果图像
minMaxLoc 函数:在给定的矩阵中寻找最大和最小值,并给出它们的位置

在具体介绍这两个函数之前呢,我们还要介绍一个概念,就是如何来评价两幅图像是否“相似”。
OpenCV 提供了 6 种计算两幅图像相似度的方法。

  1. 差值平方和匹配 CV_TM_SQDIFF
  2. 标准化差值平方和匹配 CV_TM_SQDIFF_NORMED
  3. 相关匹配 CV_TM_CCORR
  4. 标准相关匹配 CV_TM_CCORR_NORMED
  5. 相关匹配 CV_TM_CCOEFF
  6. 标准相关匹配 CV_TM_CCOEFF_NORMED

下面就分别来介绍。首先,先给出几个符号:
T(x,y) T(x,y) 用来表示我们的模板。 I(x,y) I(x,y) 是我们的目标图像。 R(x,y) R(x,y) 是用来描述相似度的函数。

差值平方和匹配 CV_TM_SQDIFF

这类方法利用图像与模板各个像素差值的平方和来进行匹配,最好匹配为 0。 匹配越差,匹配值越大。

R(x,y)=∑x′,y′(T(x′,y′)−I(x+x′,y+y′))2

R(x,y) = \sum_{x',y'}\left(T(x',y')-I(x+x',y+y')\right)^2

标准化差值平方和匹配 CV_TM_SQDIFF_NORMED

这个方法其实和差值平方和算法是类似的。只不过对图像和模板进行了标准化操作。

R(x,y)=∑x′,y′(T(x′,y′)−I(x+x′,y+y′))2∑x′,y′T(x′,y′)2∑x′,y′I(x+x′,y+y′)2−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−√

R(x,y) = \frac{\sum_{x',y'}\left(T(x',y')-I(x+x',y+y')\right)^2}{\sqrt{\sum_{x',y'} T(x',y')^2 \sum_{x',y'}I(x+x',y+y')^2}}

这种标准化操作可以保证当模板和图像各个像素的亮度都乘上了同一个系数时,相关度不发生变化。

也就是说当 I(x,y) I(x,y)和 T(x,y) T(x,y) 变为 k×I(x,y) k\times I(x,y)和 k×T(x,y) k\times T(x,y) 时, R(x,y) R(x,y)不发生变化。

相关匹配 CV_TM_CCORR

这类方法采用模板和图像的互相关计算作为相似度的度量方法,所以较大的数表示匹配程度较高,0标识最坏的匹配效果。

R(x,y)=∑x′,y′(T(x′,y′)×I(x+x′,y+y′))

R(x,y) = \sum_{x',y'}\left(T(x',y')\times I(x+x',y+y')\right)

标准化相关匹配 CV_TM_CCORR_NORMED

这个方法和 标准化差值平方和匹配 类似,都是去除了亮度线性变化对相似度计算的影响。可以保证图像和模板同时变亮或变暗k倍时结果不变。

R(x,y)=∑x′,y′(T(x′,y′)×I(x+x′,y+y′))∑x′,y′T(x′,y′)2∑x′,y′I(x+x′,y+y′)2−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−√

R(x,y) = \frac{\sum_{x',y'}\left(T(x',y')\times I(x+x',y+y')\right)}{\sqrt{\sum_{x',y'} T(x',y')^2 \sum_{x',y'}I(x+x',y+y')^2}}

相关匹配 CV_TM_CCOEFF

这种方法也叫做相关匹配,但是和上面的 CV_TM_CCORR 匹配方法还是有不通过的。简单的说,这里是把图像和模板都减去了各自的平均值,使得这两幅图像都没有直流分量。

T′(x,y)=T(x,y)−∑x′,y′T(x′,y′)w×hI′(x,y)=I(x,y)−∑x′,y′I(x′,y′)w×hR(x,y)=∑x′,y′(T′(x′,y′)×I′(x+x′,y+y′))

T'(x,y) = T(x,y) - \frac{\sum_{x',y'} T(x',y')}{w\times h} \\ I'(x,y) = I(x,y) - \frac{\sum_{x',y'} I(x',y')}{w\times h} \\ R(x,y) = \sum_{x',y'}\left(T'(x',y')\times I'(x+x',y+y')\right)

标准相关匹配 CV_TM_CCOEFF_NORMED

这是 OpenCV 支持的最复杂的一种相似度算法。这里的相关运算就是数理统计学科的相关系数计算方法。具体的说,就是在减去了各自的平均值之外,还要各自除以各自的方差。经过减去平均值和除以方差这么两步操作之后,无论是我们的待检图像还是模板都被标准化了,这样可以保证图像和模板分别改变光照亮不影响计算结果。计算出的相关系数被限制在了 -1 到 1 之间,1 表示完全相同,-1 表示两幅图像的亮度正好相反,0 表示两幅图像之间没有线性关系。

T′(x,y)=T(x,y)−1w×h∑x′,y′T(x′,y′)∑x′,y′T(x′,y′)2−−−−−−−−−−−−−√I′(x,y)=I(x,y)−1w×h∑x′,y′I(x′,y′)∑x′,y′I(x′,y′)2−−−−−−−−−−−−−√R(x,y)=∑x′,y′(T′(x′,y′)×I′(x+x′,y+y′))

T'(x,y) = \frac{T(x,y) - \frac{1}{w\times h} \sum_{x',y'} T(x',y')}{ \sqrt{\sum_{x',y'} T(x',y')^2}} \\ I'(x,y) =\frac{ I(x,y) - \frac{1}{w\times h}\sum_{x',y'} I(x',y')}{ \sqrt{\sum_{x',y'} I(x',y')^2}} \\ R(x,y) = \sum_{x',y'}\left(T'(x',y')\times I'(x+x',y+y')\right)

下面给个例子,我们的测试图像如下:

我们的模板如下:

程序中会用到 OpenCV 的函数包括:

void matchTemplate( InputArray image, InputArray templ,OutputArray result, int method );

其中 result 是一个矩阵,返回每一个点匹配的结果。

void minMaxLoc(InputArray src, CV_OUT double* minVal,CV_OUT double* maxVal=0, CV_OUT Point* minLoc=0,CV_OUT Point* maxLoc=0, InputArray mask=noArray());

这个函数可以在一个矩阵中寻找最大点或最小点,并将位置返回回来。

下面是完整的测试程序。

#include <QCoreApplication>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"using namespace cv;int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);cv::Mat image = imread("D:/test.png", cv::IMREAD_COLOR );cv::Mat templateImage = imread("D:/template.png", cv::IMREAD_COLOR);int result_cols =  image.cols - templateImage.cols + 1;int result_rows = image.rows - templateImage.rows + 1;cv::Mat result = cv::Mat( result_cols, result_rows, CV_32FC1 );cv::matchTemplate( image, templateImage, result, CV_TM_SQDIFF );double minVal, maxVal;cv::Point minLoc, maxLoc, matchLoc;cv::minMaxLoc( result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );matchLoc = minLoc;cv::rectangle( image, cv::Rect(matchLoc, cv::Size(templateImage.cols, templateImage.rows) ), Scalar(0, 0, 255), 2, 8, 0 );imshow("", image);return a.exec();
}

输出结果是这样的。

其实上面的代码还可以封装一下。

double match(cv::Mat image, cv::Mat tepl, cv::Point &point, int method)
{int result_cols =  image.cols - tepl.cols + 1;int result_rows = image.rows - tepl.rows + 1;cv::Mat result = cv::Mat( result_cols, result_rows, CV_32FC1 );cv::matchTemplate( image, tepl, result, method );double minVal, maxVal;cv::Point minLoc, maxLoc;cv::minMaxLoc( result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );switch(method){case CV_TM_SQDIFF:case CV_TM_SQDIFF_NORMED:point = minLoc;return minVal;break;default:point = maxLoc;return maxVal;break;}
}

利用这个封装代码,我们可以把所有的匹配方法都实验一下。并且比较一下计算速度。

int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);cv::Mat image = imread("D:/test.png", cv::IMREAD_COLOR );cv::Mat tepl = imread("D:/template.png", cv::IMREAD_COLOR);cv::Point matchLoc;double value;int elapse;QTime t;t.start();value = match(image, tepl, matchLoc, CV_TM_SQDIFF);elapse = t.elapsed();qDebug("CV_TM_SQDIFF Time elapsed: %d ms", elapse);qDebug() << value;t.start();value = match(image, tepl, matchLoc, CV_TM_SQDIFF_NORMED);elapse = t.elapsed();qDebug("CV_TM_SQDIFF_NORMED Time elapsed: %d ms", elapse);qDebug() << value;t.start();value = match(image, tepl, matchLoc, CV_TM_CCORR);elapse = t.elapsed();qDebug("CV_TM_CCORR Time elapsed: %d ms", elapse);qDebug() << value;t.start();value = match(image, tepl, matchLoc, CV_TM_CCORR_NORMED);elapse = t.elapsed();qDebug("CV_TM_CCORR_NORMED Time elapsed: %d ms", elapse);qDebug() << value;t.start();value = match(image, tepl, matchLoc, CV_TM_CCOEFF);elapse = t.elapsed();qDebug("CV_TM_CCOEFF Time elapsed: %d ms", elapse);qDebug() << value;t.start();value = match(image, tepl, matchLoc, CV_TM_CCOEFF_NORMED);elapse = t.elapsed();qDebug("CV_TM_CCOEFF_NORMED Time elapsed: %d ms", elapse);qDebug() << value;cv::rectangle( image, cv::Rect(matchLoc, cv::Size(tepl.cols, tepl.rows) ), Scalar(0, 0, 255), 2, 8, 0 );imshow("", image);return a.exec();
}

输出结果如下:

CV_TM_SQDIFF Time elapsed: 734 ms
4
CV_TM_SQDIFF_NORMED Time elapsed: 699 ms
1.43391e-08
CV_TM_CCORR Time elapsed: 638 ms
2.78957e+08
CV_TM_CCORR_NORMED Time elapsed: 710 ms
1
CV_TM_CCOEFF Time elapsed: 721 ms
2.30675e+08
CV_TM_CCOEFF_NORMED Time elapsed: 759 ms
1

如果我们先将图像都转换为灰度图,那么计算速度会快很多。

CV_TM_SQDIFF Time elapsed: 249 ms
12
CV_TM_SQDIFF_NORMED Time elapsed: 246 ms
1.29052e-07
CV_TM_CCORR Time elapsed: 208 ms
9.29857e+07
CV_TM_CCORR_NORMED Time elapsed: 242 ms
1
CV_TM_CCOEFF Time elapsed: 246 ms
7.68916e+07
CV_TM_CCOEFF_NORMED Time elapsed: 281 ms
1

基本缩短到了 1/3 。所以,如果可以用灰度图来计算,就不要用彩色图。

我们还可以去掉模板大小对匹配度的影响:

double match(cv::Mat image, cv::Mat tepl, cv::Point &point, int method)
{int result_cols =  image.cols - tepl.cols + 1;int result_rows = image.rows - tepl.rows + 1;cv::Mat result = cv::Mat( result_cols, result_rows, CV_32FC1 );cv::matchTemplate( image, tepl, result, method );double minVal, maxVal;cv::Point minLoc, maxLoc;cv::minMaxLoc( result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );switch(method){case CV_TM_SQDIFF:point = minLoc;return minVal / (tepl.cols * tepl.cols);break;case CV_TM_SQDIFF_NORMED:point = minLoc;return minVal;break;case CV_TM_CCORR:case CV_TM_CCOEFF:point = maxLoc;return maxVal / (tepl.cols * tepl.cols);break;case CV_TM_CCORR_NORMED:case CV_TM_CCOEFF_NORMED:default:point = maxLoc;return maxVal;break;}
}

这时的结果如下:

CV_TM_SQDIFF Time elapsed: 705 ms
0.000609663
CV_TM_SQDIFF_NORMED Time elapsed: 682 ms
1.43391e-08
CV_TM_CCORR Time elapsed: 615 ms
42517.5
CV_TM_CCORR_NORMED Time elapsed: 698 ms
1
CV_TM_CCOEFF Time elapsed: 703 ms
35158.5
CV_TM_CCOEFF_NORMED Time elapsed: 757 ms
1

OpenCV 学习笔记(模板匹配)相关推荐

  1. Opencv(C++)笔记--模板匹配cv::matchTemplate()和最值计算cv::minMaxLoc()

    目录 1--模板匹配 1-1--OpenCV API 1-2--六种匹配方法 1-3--代码实例 2--最值计算 2-1--OpenCV API 1--模板匹配 使用模板图像与原图像进行匹配,Open ...

  2. OpenCV学习之模板匹配

    模板匹配的概念与原理就不说了,<OpenCV3编程入门>这本书第9.5节有介绍,不过比较简洁,至于看不看得懂就NBCS了嘻嘻,毕竟本人也是看得一知半解.那么本次呢,主要就是针对该书中的9. ...

  3. opencv学习_11 (模板匹配(包括单模板和多模板))

    模板匹配--在一幅图像中匹配与模板相似的单个或者多个目标 (1)目标匹配函数: cvMatchTemplate( const CvArr* image, constCvArr* templ, CvAr ...

  4. 12月19日 OpenCV 实战基础学习笔记——特征匹配

    文章目录 前言 一.特征匹配 1.Brute-force 蛮力匹配 2.1 对 1 匹配 3.k 对最佳匹配 二.答题卡识别 前言 本文为12月19日 OpenCV 实战基础学习笔记--特征匹配,分为 ...

  5. opencv学习笔记(二):基于肤色的人手检测

    opencv学习笔记(二):基于肤色的人手检测 原文:http://blog.csdn.net/wzmsltw/article/details/50849810 先写了人手的检测程序,下一步基于检测程 ...

  6. OpenCV学习笔记(十一)(十二)(十三)(十四)(十五)

    OpenCV学习笔记(十一)--谈谈像素的类型和对Templates的限制使用 Templates是c++的一个很强大的特征,可以是数据结构更加安全高效.但也会增加编译时间和代码的长度,当函数被频繁调 ...

  7. OpenCV学习笔记(一)(二)(三)(四)(五)

    OpenCV学习笔记(一) 决心开始研究OpenCV.闲言少叙,sourceforge网站最近的版本是2011年8月的OpenCV2.3.1,下载安装,我这里使用的开发环境是vs2008,网上搜了一下 ...

  8. OpenCV中使用模板匹配识别空闲的货架空间

    但是点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 假设你是一名在超市工作的员工,被要求在商店里四处走动,检查需要 ...

  9. 百度计算广告学沙龙学习笔记 - 内容匹配广告

    百度计算广告学沙龙学习笔记 - 内容匹配广告 时间 2014-02-05 18:53:55 CSDN博客 原文  http://blog.csdn.net/three_body/article/det ...

最新文章

  1. stdthread(9)死锁deadlock
  2. c语言的运算符表格,C语言教案(运算符和表格达式).ppt
  3. mysql mybatis时间_MySQLMyBatis 时间处理的配合
  4. php 未定义数组索引_如何删除PHP数组元素键值并重新排序
  5. JS学习总结(8)——数组
  6. php 大excel,如何用php生成一个大的Excel文件?
  7. java AES 32位加密解密
  8. 模型预测控制器(MPC)系列: 3.车辆横向MPC控制中的前馈设计
  9. 关于阿里矢量图标的三种使用方法
  10. 佳博GP1124T的安装向导
  11. 公众号滑动图代码_微信公众号文章滑动图片怎么做的呢?
  12. linux limits.conf 生效,limits.conf生效问题
  13. blender2.8 bpy.data.images.new创建的图片返回值撤回操作后丢失
  14. Android 自带工具生成图标
  15. 6月16-17参加公司团建-军事化拓展训练总结
  16. 【从零学习openCV】IOS7下的人脸检测
  17. 汽车电子功能安全标准ISO26262解析(十二)——HARA分析
  18. IntelliJ IDEA中的神仙插件 写代码必备!
  19. qt 设置背景图片方法
  20. 淘宝店铺涨价如何才能把对店铺的影响降低到最低,涨价小技巧分享

热门文章

  1. 轨道姿态常用编程缩写
  2. TortoiseGit基本操作
  3. 自考计算机网络原理笔记,自考计算机网络原理笔记(典藏版).pdf
  4. 成都领君国土三调数据建库与管理系统总体设计方案
  5. 经验总结 | 如何使用服务器上传下载文件
  6. 高清Apriltag图片生成和制作
  7. [core]-ARM-A系列Core的分类快速参考
  8. 高考计算机会考等级划分标准,河北高中会考等级划分标准
  9. 郑厂长系列故事——逃离迷宫(C语言)
  10. 掌上单片机实验室 - 实现运动功能(10)