引自:http://blog.csdn.net/liyuanbhu/article/details/49837661

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
 
 

opencv----模板匹配的更多相关文章

  1. Atitit opencv模板匹配attilax总结

    Atitit opencv模板匹配attilax总结 找一幅图像的匹配的模板,可以在一段视频里寻找出我们感兴趣的东西,比如条形码的识别就可能需要这样类似的一个工作提取出条形码区域(当然这样的方法并不鲁 ...

  2. Atitit opencv 模板匹配

    Atitit opencv 模板匹配 1.1. 图片1 1.2. Atitit opencv 模板匹配  6中匹配算法貌似效果区别不大1 1.3. 对模板缩放的影响 一般的缩放可以,太大了就歇菜了.. ...

  3. opencv 模板匹配与滑动窗口(单匹配) (多匹配)

    1单匹配: 测试图片:   code: #include <opencv\cv.h> #include <opencv\highgui.h> #include <open ...

  4. OpenCV模板匹配函数matchTemplate详解

    参考文档:http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/histograms/template_matchin ...

  5. OpenCV——模板匹配

    minMaxLoc函数: void minMaxLoc( const Mat& src, double* minVal, double* maxVal=0, Point* minLoc=0, ...

  6. opencv::模板匹配(Template Match)

    模板匹配介绍 模板匹配就是在整个图像区域发现与给定子图像匹配的小块区域. 所以模板匹配首先需要一个模板图像T(给定的子图像) 另外需要一个待检测的图像-源图像S 工作方法,在带检测图像上,从左到右,从 ...

  7. 关于opencv模板匹配功能的项目测试记录

    模板匹配功能介绍的很好的一篇博客:https://www.cnblogs.com/XJT2018/p/9934139.html 就如上述博客所言:“若原图像中的匹配目标发生旋转或大小变化,该算法无效. ...

  8. opencv模板匹配查找图像(python)

    #!/usr/bin/env python3 # -*- coding: utf-8 -*- import cv2 import numpy as np from cv2 import COLOR_B ...

  9. opencv 模板匹配, 已解决模板过大程序不工作的bug

    #include <opencv2/opencv.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv ...

  10. opencv模板匹配有趣的链接

    https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_imgproc/py_template_matching/py_template_matchi ...

随机推荐

  1. 7.翻译:EF基础系列---EF中的实体类型

    原文地址:http://www.entityframeworktutorial.net/Types-of-Entities.aspx 在Entity Framework中有两种实体类型:一种是POCO ...

  2. 简简单单搞掂恼人的Laravel 5安装

    想折腾下Laravel 5了.Laravel是这世界上最好且没有之一的语言──PHP──的众多框架中的一个,是我比较感兴趣的PHP Web Framework. 但是安装Laravel可不是件容易的事 ...

  3. MySQL 死锁与日志二三事

    最近线上 MySQL 接连发生了几起数据异常,都是在凌晨爆发,由于业务场景属于典型的数据仓库型应用,白天压力较小无法复现.甚至有些异常还比较诡异,最后 root cause 分析颇费周折.那实际业务当 ...

  4. PCIE xilinx v5 IP核使用前的研究

    外带一个月前啃的一个星期,加本星期心无旁骛,啃出些心得,广惠后人.但愿有用. trn信号是数据链路层的信号 TLP包是数据链路层传给transaction层的包 解包需要一个transaction的协 ...

  5. Linux设备驱动开发详解

    Linux设备驱动开发详解 http://download.csdn.net/detail/wuyouzi067/9581380

  6. scikit

    http://scikit-learn.org/dev/_downloads/scikit-learn-docs.pdf http://scikit-learn.org/stable/tutorial ...

  7. 用NPOI创建Excel、合并单元格、设置单元格样式、边框的方法

    本篇文章小编为大家介绍,用NPOI创建Excel.合并单元格.设置单元格样式.边框的方法.需要的朋友参考下 今天在做项目中,遇到使用代码生成具有一定样式的Excel,找了很多资料,最后终于解决了,Ex ...

  8. django rest_framework入门四-类视图APIView

    上节,我们使用函数视图,用了@api_view装饰器来修饰,这一节,我们介绍类视图APIView,显然,类视图更符合面向对象的原则. 1.使用类视图APIView重写API 类视图APIView,取代 ...

  9. JMeter测试工具中的参数化使用[函数助手]

    下面是我使用JMeter测试工具对某项目中的一个http接口进行测试时的配置参数,使用到了JMeter的随机函数: 在发送POST请求时,需要发送json格式的数据,其中使用到JMeter的" ...

  10. 使用TCP协议的NAT穿透技术(转)

    其实很早我就已经实现了使用TCP协议穿透NAT了,但是苦于一直没有时间,所以没有写出来,现在终于放假有一点空闲,于是写出来共享之. 一直以来,说起NAT穿透,很多人都会被告知使用UDP打孔这个技术,基 ...