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) 用来表示我们的模板。I(x,y) 是我们的目标图像。 R(x,y) 是用来描述相似度的函数。

差值平方和匹配 CV_TM_SQDIFF

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

R(x,y)=∑x′,y′(T(x′,y′)−I(x+x′,y+y′))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‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾√

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

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

相关匹配 CV_TM_CCORR

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

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

标准化相关匹配 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‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾√

相关匹配 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′))

标准相关匹配 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′))

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

我们的模板如下: 

程序中会用到 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
 
 原文 http://blog.csdn.net/liyuanbhu/article/details/49837661

OpenCV 学习笔记(模板匹配)的更多相关文章

  1. opencv学习笔记(三)基本数据类型

    opencv学习笔记(三)基本数据类型 类:DataType 将C++数据类型转换为对应的opencv数据类型 OpenCV原始数据类型的特征模版.OpenCV的原始数据类型包括unsigned ch ...

  2. opencv学习笔记(二)寻找轮廓

    opencv学习笔记(二)寻找轮廓 opencv中使用findContours函数来查找轮廓,这个函数的原型为: void findContours(InputOutputArray image, O ...

  3. OpenCV 学习笔记 07 目标检测与识别

    目标检测与识别是计算机视觉中最常见的挑战之一.属于高级主题. 本章节将扩展目标检测的概念,首先探讨人脸识别技术,然后将该技术应用到显示生活中的各种目标检测. 1 目标检测与识别技术 为了与OpenCV ...

  4. opencv学习笔记(七)SVM+HOG

    opencv学习笔记(七)SVM+HOG 一.简介 方向梯度直方图(Histogram of Oriented Gradient,HOG)特征是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子 ...

  5. opencv学习笔记(六)直方图比较图片相似度

    opencv学习笔记(六)直方图比较图片相似度 opencv提供了API来比较图片的相似程度,使我们很简单的就能对2个图片进行比较,这就是直方图的比较,直方图英文是histogram, 原理就是就是将 ...

  6. opencv学习笔记(五)镜像对称

    opencv学习笔记(五)镜像对称 设图像的宽度为width,长度为height.(x,y)为变换后的坐标,(x0,y0)为原图像的坐标. 水平镜像变换: 代码实现: #include <ios ...

  7. opencv学习笔记(四)投影

    opencv学习笔记(四)投影 任选了一张图片用于测试,图片如下所示: #include <cv.h> #include <highgui.h> using namespace ...

  8. opencv学习笔记(一)IplImage, CvMat, Mat 的关系

    opencv学习笔记(一)IplImage, CvMat, Mat 的关系 opencv中常见的与图像操作有关的数据容器有Mat,cvMat和IplImage,这三种类型都可以代表和显示图像,但是,M ...

  9. paper 93:OpenCV学习笔记大集锦

    整理了我所了解的有关OpenCV的学习笔记.原理分析.使用例程等相关的博文.排序不分先后,随机整理的.如果有好的资源,也欢迎介绍和分享. 1:OpenCV学习笔记 作者:CSDN数量:55篇博文网址: ...

随机推荐

  1. 201521123042 《java程序设计》 第八周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. ①泛型定义:泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展, ...

  2. 201521123006 《Java程序设计》第6周学习总结

    1. 本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图,对面向对象思想进行一个总结. 注1:关键词与内容不求多,但概念之间的联系要清晰,内容覆盖 ...

  3. 201521123077 《Java程序设计》第4周学习总结

    1. 本周学习总结 几种简单说明注释的使用 抽象类与抽象方法 super调用父类的方法 2. 书面作业 Q1.注释的应用使用类的注释与方法的注释为前面编写的类与方法进行注释,并在Eclipse中查看. ...

  4. 201521123075 《Java程序设计》第11周学习总结

    1. 本周学习总结 2. 书面作业 本次PTA作业题集多线程 1.互斥访问与同步访问 完成题集4-4(互斥访问)与4-5(同步访问) 1.1 除了使用synchronized修饰方法实现互斥同步访问, ...

  5. linux 下怎样查找一个文件夹在哪个目录下?

    如果只显示所在目录的路径: find 目录 -type d -name "查询目录名" -printf "%h\n" 如果同时显示目录名称和所在目录的路径: f ...

  6. [LeetCode] 349 Intersection of Two Arrays && 350 Intersection of Two Arrays II

    这两道题都是求两个数组之间的重复元素,因此把它们放在一起. 原题地址: 349 Intersection of Two Arrays :https://leetcode.com/problems/in ...

  7. 静态页面如何实现 include 引入公用代码

    一直以来,我司的前端都是用 php 的 include 函数来实现引入 header .footer 这些公用代码的,就像下面这样: <!-- index.php --> <!DOC ...

  8. 腾讯x5Webview取代原生android Webview

    一.官方地址: https://x5.tencent.com/tbs/ 二.不需要申请开发者,QQ直接登录,下载即可集成到项目中. 三.与原生的webview对比优势 1) 速度快:相比系统webvi ...

  9. jQuery: Callbacks

    jQuery 中提供了一个Callback的工具类Callbacks,它提供了一个Callback Chain.使用它可以在一个chain上来执行相关操作.它也是jQuery中的ajax, Defer ...

  10. 快速搭建属于自己的数据库——mongodb

    为了真实模拟一个项目上线,拥有前端后端数据库都具备的功能,我选择了mongodb作为项目的数据库支持,这里分享一些mongodb的经验心得和血的教训. mongoddb安装 在本地安装 直接通过官网下 ...