OpenCV中的模板匹配方法及其应用

模板匹配(TemplateMatching)就是在一幅图像中寻找和模板图像(template)最相似的区域,该方法原理简单计算速度快,能够应用于目标识别,目标跟踪等多个领域。OpenCV中对应的函数为matchTemplate或cvMatchTemplate(参考opencvdoc),简单介绍下:

1、函数原型

C++: void matchTemplate(InputArray image, InputArray templ, OutputArray result, int method);
C: void cvMatchTemplate(const CvArr* image, const CvArr* templ, CvArr* result, int method);

2、参数解释

-image:输入图像。必须为8位或者32位的浮点型。

-templ:用于搜索的模板图像。必须小于输入图像并且是一样的数据类型。

-result:匹配结果图像。必须是单通道32位浮点型,且大小是(W-w+1)*(H-h+1),其中W,H分别为输入图像的宽和高,w,h分别为模板图像的宽和高。

-method:相似度衡量的方法。具体如下(这里T是templ,I是image,R是result,x’是从0到w-1,y’是从0到h-1):

(1)method=CV_TM_SQDIFF(Sum of SquaredDifference)

平方差匹配法,最好的匹配为0,值越大匹配越差;

(2)method=CV_TM_SQDIFF_NORMED

归一化平方差匹配法,第一种方法的归一化形式;

(3)method=CV_TM_CCORR(Cross Correlation)

相关匹配法,采用乘法操作,数值越大表明匹配越好;

(4)method=CV_TM_CCORR_NORMED

归一化相关匹配法,第三种方法的归一化形式;

(5)method=CV_TM_CCOEFF

,其中:

相关系数匹配法,最好的匹配为1,-1表示最差的匹配;

(6)method=CV_TM_CCOEFF_NORMED 

归一化相关系数匹配法,第五种方法的归一化形式;

最后需要注意:

(1)前面两种方法为越小的值表示越匹配,后四种方法值越大越匹配。原因在于我们把第一种方法CV_TM_SQDIFF的计算公式展开可以得到:

上式的第一项(模板图像T的能量)是一个常数,第三项(图像I局部的能量)也可以近似一个常数,剩下的第二项与第三种方法CV_TM_CCORR的计算公式一样。而我们知道互相关系数(Cross Correlation)越大相似度越大,所以第一、二种方法的数值越小相似度越高,而第三、四种方法的数值越大相似度越高,第五、六种方法的计算公式与第三、四种方法的类似,所以也是数值越大相似度越高。

(2)如果输入图像和模板图像都是彩色图像,则三个通道分别计算上述相似度,然后求平均值。

3、功能描述

函数通过在输入图像image中滑动(从左到右,从上到下),寻找各个位置的区块(搜索窗口)与模板图像templ的相似度,并将结果保存在结果图像result中。该图像中的每一个点的亮度表示该处的输入图像与模板图像的匹配程度,然后可以通过某方法(一般使用函数minMaxLoc)定位result中的最大值或者最小值得到最佳匹配点,最后根据匹配点和模板图像的矩形框标出匹配区域(如下图,红圈标出的亮点是最佳匹配点,黑框是模板图像矩形框)。

4、代码demo

(1)使用matchTemplate进行目标匹配

/**
 * Object matching using function matchTemplate
 */

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
using namespace std;
using namespace cv;

/// 全局变量 ///
Mat srcImg;			//原始图像
Mat templImg;		//模板图像
Mat resultImg;		//匹配结果图像

const char* imageWindow = "Source Image";		//原始图像显示窗口
const char* resultWindow = "Result Window";		//匹配结果图像显示窗口

int matchMethod;		//匹配方法index
int maxTrackbar = 5;	//滑动条范围(与匹配方法个数对应)

/// 函数声明 ///
void MatchingMethod( int, void* );		//匹配函数

int main( int argc, char** argv )
{
  // 加载原始图像和模板图像
  srcImg = imread( "D:\\opencv_pic\\cat0.jpg", 1 );
  templImg = imread( "D:\\opencv_pic\\cat3d120.jpg", 1 );

  // 创建显示窗口
  namedWindow( imageWindow, CV_WINDOW_AUTOSIZE );
  namedWindow( resultWindow, CV_WINDOW_AUTOSIZE );

  // 创建滑动条
  char* trackbarLabel =
	  "Method: \n 	  0: SQDIFF \n 	  1: SQDIFF NORMED \n 	  2: TM CCORR \n 	  3: TM CCORR NORMED \n 	  4: TM COEFF \n 	  5: TM COEFF NORMED";
  //参数:滑动条名称 显示窗口名称 匹配方法index 滑动条范围 回调函数
  createTrackbar( trackbarLabel, imageWindow, &matchMethod, maxTrackbar, MatchingMethod );

  MatchingMethod( 0, 0 );

  waitKey(0);
  return 0;
}

/// 函数定义 ///
void MatchingMethod( int, void* )		//匹配函数
{
  // 深拷贝用于显示
  Mat displayImg;
  srcImg.copyTo( displayImg );

  // 创建匹配结果图像,为每个模板位置存储匹配结果
  // 匹配结果图像大小为:(W-w+1)*(H-h+1)
  int result_cols =  srcImg.cols - templImg.cols + 1;
  int result_rows = srcImg.rows - templImg.rows + 1;
  resultImg.create( result_cols, result_rows, CV_32FC1 );

  // 进行匹配并归一化
  matchTemplate( srcImg, templImg, resultImg, matchMethod );
  normalize( resultImg, resultImg, 0, 1, NORM_MINMAX, -1, Mat() );

  // 使用minMaxLoc找出最佳匹配
  double minVal, maxVal;
  Point minLoc, maxLoc, matchLoc;
  minMaxLoc( resultImg, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );

  // 对于CV_TM_SQDIFF和 CV_TM_SQDIFF_NORMED这两种方法,最小值为最佳匹配;对于别的方法最大值为最佳匹配
  if( matchMethod  == CV_TM_SQDIFF || matchMethod == CV_TM_SQDIFF_NORMED )
    { matchLoc = minLoc; }
  else
    { matchLoc = maxLoc; }

  // 在原始图像和匹配结果图像中以最佳匹配点为左上角标出最佳匹配框
  rectangle( displayImg, matchLoc, Point( matchLoc.x + templImg.cols , matchLoc.y + templImg.rows ), Scalar::all(0), 2, 8, 0 );
  rectangle( resultImg, matchLoc, Point( matchLoc.x + templImg.cols , matchLoc.y + templImg.rows ), Scalar::all(0), 2, 8, 0 ); 

  imshow( imageWindow, displayImg );
  imshow( resultWindow, resultImg );

  return;
}

模板图为:

运行结果:

这是第一种方法的结果,最暗处为最佳匹配;其他几种方法的结果是最亮处为最佳匹配。

(2)使用matchTemplate进行目标跟踪

/**
 * Object tracking using function matchTemplate
 */

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
using namespace std;
using namespace cv;

/// 全局变量 ///
Rect templRect;						//模板矩形框
bool drawing_rect = false;		//是否绘制矩形框
const char* windowName = "Object Tracker";	//窗口名称

/// 绘制模板矩形框 ///
void mouseHandler(int event, int x, int y, int flags, void *param)
{
	switch( event )
	{
	case CV_EVENT_LBUTTONDOWN:
		drawing_rect = true;
		//初始位置
		templRect = Rect( x, y, 0, 0 );
		break;
	case CV_EVENT_LBUTTONUP:
		drawing_rect = false;
		//从右往左
		if( templRect.width < 0 )
		{
			templRect.x += templRect.width;
			templRect.width *= -1;
		}
		//从下往上
		if( templRect.height < 0 )
		{
			templRect.y += templRect.height;
			templRect.height *= -1;
		}
		break;
	case CV_EVENT_MOUSEMOVE:
		if (drawing_rect)
		{
			//从左往右,从上往下
			templRect.width = x - templRect.x;
			templRect.height = y - templRect.y;
		}
		break;
	}
}

/// 函数声明 ///
void tracking(Mat frame, Mat &templ, Rect &rect);

int main(int argc, char * argv[])
{
	//读取视频
	VideoCapture capture;
	capture.open("D:\\opencv_video\\PETS_test\\View_001.avi");
	if (!capture.isOpened())
	{
		cout << "capture device failed to open!" << endl;
		return -1;
	}

	//注册鼠标事件回调函数
	namedWindow(windowName, CV_WINDOW_AUTOSIZE);
	setMouseCallback(windowName, mouseHandler, NULL ); 

	//显示第一帧
	Mat frame, grayFrame;
	capture >> frame;
	imshow(windowName, frame);
	//等待绘制模板矩形框
	waitKey(0);

	//提取模板矩形框区域作为模板图像
	cvtColor(frame, grayFrame, CV_RGB2GRAY);	//灰度化
	Mat templ = grayFrame(templRect);

	//持续跟踪
	int countFrame = 0;
	while (1)
	{
		capture >> frame;
		if (frame.empty())	break;
		countFrame++;
		//计时
		double start = (double)getTickCount();

		//基于模板匹配的跟踪过程
		//参数:帧图像,模板图像,模板矩形框
		tracking(frame, templ, templRect);

		//显示
		rectangle(frame, templRect, Scalar(0, 0, 255), 3);
		imshow(windowName, frame);

		//计时
		double end = (double)getTickCount();
		cout << "Frame: "<<countFrame<<"\tTime: " << (end - start)*1000 / (getTickFrequency()) <<"ms"<< endl;
		if (waitKey(1) == 27)	break;
	}
	return 0;
}

/// 函数定义 ///
//根据上一个模板矩形框rect来设定当前搜索窗口searchWindow,
//并通过匹配(matchTemplate)搜索窗口searchWindow与当前模板图像templ来找到最佳匹配,
//最后更新模板矩形框rect与模板图像templ。
void tracking(Mat frame, Mat &templ, Rect &rect)
{
	Mat grayFrame;
	cvtColor(frame, grayFrame, CV_RGB2GRAY);

	//选取搜索窗口
	Rect searchWindow;
	searchWindow.width = rect.width * 3;
	searchWindow.height = rect.height * 3;
	searchWindow.x = rect.x + rect.width * 0.5 - searchWindow.width * 0.5;
	searchWindow.y = rect.y + rect.height * 0.5 - searchWindow.height * 0.5;
	searchWindow &= Rect(0, 0, frame.cols, frame.rows);

	//模板匹配
	Mat similarity;
	matchTemplate(grayFrame(searchWindow), templ, similarity, CV_TM_CCOEFF_NORMED);
	//找出最佳匹配的位置
	double maxVal;
	Point maxLoc;
	minMaxLoc(similarity, 0, &maxVal, 0, &maxLoc);

	//更新模板矩形框
	rect.x = maxLoc.x + searchWindow.x;
	rect.y = maxLoc.y + searchWindow.y;
	//更新模板
	templ = grayFrame(rect);
}

测试视频是PETS库里面的:View_001.avi

运行结果:

时间: 2024-11-05 11:24:31

OpenCV中的模板匹配方法及其应用的相关文章

halcon三种模板匹配方法

转自 : http://blog.csdn.net/hust1900/article/details/8843270 halcon有三种模板匹配方法:即Component-Based.Gray-Value-Based.Shaped_based,分别是基于组件(或成分.元素)的匹配,基于灰度值的匹配和基于形状的匹配,此外还有变形匹配和三维模型匹配也是分属于前面的大类 本文只对形状匹配做简要说明和补充: Shape_Based匹配方法: 上图介绍的是形状匹配做法的一般流程及模板制作的两种方法. 先要

Halcon中模板匹配方法的总结归纳

基于组件的模板匹配: 应用场合:组件匹配是形状匹配的扩展,但不支持大小缩放匹配,一般用于多个对象(工件)定位的场合. 算法步骤: 1.获取组件模型里的初始控件 gen_initial_components() 参数: ModelImage [Input] 初始组件的图片 InitialComponents [Output] 初始组件的轮廓区域 ContrastLow [Input] 对比度下限 ContrastHigh [Input] 对比度上限 MinSize [Input] 初始组件的最小尺

使用OpenCV&amp;&amp;C++进行模板匹配

一:课程介绍 1.1:学习目标 学会用imread载入图像,和imshow输出图像. 用nameWindow创建窗口,用createTrackbar加入滚动条和其回调函数的写法. 熟悉OpenCV函数matchTemplate并学会通过该函数实现模板匹配. 1.2:什么是模板匹配? 在一副图像中寻找和另一幅图像最相似(匹配)部分的技术. 二:实验原理 让模板图片在原图片上的一次次滑动(从左到右,从上到下一个像素为单位的移动),然后将两张图片的像素值进行比对,然后选择相似度最高的部分进行标记,当遇

基于HALCON的模板匹配方法总结 (转)

很早就想总结一下前段时间学习HALCON的心得,但由于其他的事情总是抽不出时间.去年有过一段时间的集中学习,做了许多的练习和实验,并对基于HDevelop的形状匹配算法的参数优化进行了研究,写了一篇<基于HDevelop的形状匹配算法参数的优化研究>文章,总结了在形状匹配过程中哪些参数影响到模板的搜索和匹配,又如何来协调这些参数来加快匹配过程,提高匹配的精度,这篇paper放到了中国论文在线了,需要可以去下载. 德国MVTec公司开发的HALCON机器视觉开发软件,提供了许多的功能,在这里我主

Python+OpenCV图像处理之模板匹配

模板匹配就是在整个图像区域中发现与给定子图像匹配的小块区域 在OpenCV中,提供了相应的函数完成这个操作: matchTemplate 函数:在模板和输入图像之间寻找匹配,获得匹配结果图像 minMaxLoc 函数:在给定的矩阵中寻找最大和最小值,并给出它们的位置 几种常见的模板匹配算法: ①TM_SQDIFF是平方差匹配:TM_SQDIFF_NORMED是标准平方差匹配.利用平方差来进行匹配,最好匹配为0.匹配越差,匹配值越大. ②TM_CCORR是相关性匹配:TM_CCORR_NORMED

SSM-Spring-12:Spring中NameMatchMethodPointcutAdvisor名称匹配方法切入点顾问

------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- advice 是通知advisor 是顾问 顾问(Advisor) 通知Advice是Spring提供的一种切面(Aspect).但其功能过于简单,只能将切面织入到目标类的所有目标方法中,无法完成将切面织入到指定目标方法中. 顾问Advisor是Spring提供的另一种切面.其可以完成更为复杂的切面织入功能,能选择性的将增强切面中的部分方法. PointcutAdvisor是顾问的一种,可以指定具体的切入点

基于opencv的视频流 模板匹配 (灰度化 二值化 )

#include <cv.h> #include <highgui.h> #include <windows.h> // 获取直方图 // 1. pImageData 图像数据 // 2. nWidth 图像宽度 // 3. nHeight 图像高度 // 4. nWidthStep 图像行大小 // 5. pHistogram 直方图 BOOL GetHistogram(unsigned char *pImageData, int nWidth, int nHeigh

Opencv for android 模板匹配

因为有这方面的需要所以,对模板查找搜寻了相关资料,只是对于算法的东西很难看得动,特别是opencv涉及的很多的数学方法. 所以只为了实现这个功能,因为需求比较简单,在网上也搜寻到了相关代码,就直接拿来用了,这里也相当于转载一下: 代码上,亲测可以用的,效果也不错,确实将嘴巴给找出来了. 原文:http://www.itstrike.cn/Question/645ffff0-2862-46b6-a421-b76a37dfc660.html class MatchingDemo { public v

使用Python+OpenCV进行图像模板匹配(Match Template)

http://bluewhale.cc/2017-09-22/use-python-opencv-for-image-template-matching-match-template.html 原文地址:https://www.cnblogs.com/eustoma/p/9403582.html