2019-08-07 22:19:52 weixin_44678052 阅读数 618

该说图像匹配了,图像匹配在图片处理中的作用举重若轻,今天就带大家入门图像匹配,简单介绍一下基础的图像匹配吧。

何为图像匹配呢?在原图片中扣出一块区域,然后通过这块区域的像素找到它在原始图片的位置,在今天所讲的是如此。

来看看百度百科给下的定义吧: 通过对影像内容、特征、结构、关系、纹理及灰度等的对应关系,相似性和一致性的分析,寻求相似影像目标的方法。

**1.**通过欧式距离来匹配图像。

将所有像素值看成一个坐标(不用管它是多少维的),通过最小化这两个坐标的欧式距离来确定最终在原始图像上匹配的位置。
欧式距离公式为:
在这里插入图片描述
下面通过代码来演示如何实现

import cv2 
import numpy as np

def Distance(x,y):   #计算欧式距离
	x = np.array(x)
	y = np.array(y)
	return np.sqrt(np.sum((x-y)*(x-y)))
img1 = cv2.imread('1.jpg')  #原始图片
img2 = img1[300:400,300:400]  #要匹配的区域

h = img2.shape[0]
w = img2.shape[1]
mins = le5   距离的最小值
for i in range(0,img1.shape[0]-h):  #遍历图片
	for j in range(0,img1.shape[1]-w):
			dist = img1[i:i+h,j:j+w]
			loss = Distance(dist,img2)
			if loss < mins:
				now_h = i
				now_w = j     #记住当前目标区域左上角的位置
				mins = loss   #更新最小值

cv2.rectangle(img1,(now_w,now_h),(now_w+w,now_h+h),(255,0,0))
cv2,imshow('dist',img1)
cv2,waitKey(0)


2.通过差分矩阵求和 均值来匹配

原理: 目标区域的像素值 - 模板区域的像素值,然后根据其和的大小 或者 其均值的大小来确定目标区域。

下面直接通过代码来演示

import cv2
import numpy as np
img1 = cv2.imread("1.jpg")
img2 = img1[100:250,200:350]
h , w = img2.shape[:2]
findpt = []
for i in range(0,img1.shape[0]-h):
    for j in range(0,img1.shape[1]-h):
    	com = img1[i:i+h,j:j+w] - img2
    	if np.mean(com)<20:    #np.sum(com) <1
    	findpt.append(i)
    	findpt.append(j)
    	break
 if len(findpt)>0:
    cv.rectangle(img1,(findpt[1],findpt[0]),(findpt[1]+w,findpt[0]+h),(255,0,0))

cv.imshow('dsts',img1)
cv.waitKey(0) 

今天的图像匹配的入门基础讲解就说到这里了,下次再见。

Thank for your reading !!!!

2019-01-20 17:04:18 qq_42887760 阅读数 210

模板匹配介绍

我们需要2幅图像:

  • 原图像 (I): 在这幅图像里,我们希望找到一块和模板匹配的区域
  • 模板 (T): 将和原图像比照的图像块
    在这里插入图片描述
  • 模板匹配就是在整个图像区域发现与给定子图像匹配的小块区域。
  • 所以模板匹配首先需要一个模板图像T(给定的子图像)
  • 另外需要一个待检测的图像-源图像S
  • 工作方法,在带检测图像上,从左到右,从上向下计算模板图像与重叠子图像的匹配度,匹配程度越大,两者相同的可能性越大。

模板匹配原理

  • 我们的目标是检测最匹配模板的原图像的区域:
    在这里插入图片描述
  • 为了确定匹配模板区域, 我们不得不滑动模板图像和原图像进行比较 :
    在这里插入图片描述
  • 对于 模板(T) 覆盖在 原图像 (I) 上的每个位置,你把度量值保存 到 结果图像矩阵 ( R ) 中. 在 R 中的每个位置 (x,y) 都包含匹配度量值:
    在这里插入图片描述
  • 上图(右)就是 TM_CCORR_NORMED 方法处理后的结果图像 R . 最白的位置代表最高的匹配. 正如您所见, 黑色框住的位置很可能是结果图像矩阵中的最大数值, 所以这个区域 (以这个点为顶点,长宽和模板图像一样大小的矩阵) 被认为是匹配的.
  • 实际上, 我们使用函数 minMaxLoc 来定位在矩阵 R 中的最大值点 (或者最小值, 根据函数输入的匹配参数) .

void minMaxLoc(InputArray src, double* minVal, double* maxVal=0, Point* minLoc=0, Point* maxLoc=0, InputArray mask=noArray())
src:输入图像。
minVal:在矩阵 src中存储的最小值,可输入NULL表示不需要。
maxVal :在矩阵 src中存储的最大值,可输入NULL表示不需要。
minLoc:在结果矩阵中最小值的坐标,可输入NULL表示不需要,Point类型。
maxLoc:在结果矩阵中最大值的坐标,可输入NULL表示不需要,Point类型。
mask:可选的掩模

模板匹配介绍 – 匹配算法介绍

OpenCV中提供了六种常见的匹配算法如下:

  • 计算平方不同 : 计算出来的值越小,越相关 TM_SQDIFF = 0
    在这里插入图片描述
  • 计算相关性: 计算出来的值越大,越相关 TM_CCORR = 2
    在这里插入图片描述
  • 计算相关系数: 计算出来的值越大,越相关 TM_CCOEFF = 4
    在这里插入图片描述
  • 计算归一化平方不同 : 计算出来的值越接近0,越相关 TM_SQDIFF_NORMED = 1
    在这里插入图片描述
  • 计算归一化相关性: 计算出来的值越接近1,越相关 TM_CCORR_NORMED = 3
    在这里插入图片描述
  • 计算归一化相关系数: 计算出来的值越接近1,越相关 TM_CCOEFF_NORMED = 5
    在这里插入图片描述

在这里插入图片描述

相关API介绍cv::matchTemplate

matchTemplate(
InputArray image,// 源图像,必须是8-bit或者32-bit浮点数图像
InputArray templ,// 模板图像,类型与输入图像一致
OutputArray result,// 输出结果,必须是单通道32位浮点数,假设源图像WxH,模板图像wxh, 则结果必须为W-w+1, H-h+1的大小。
int method,//使用的匹配方法
InputArray mask=noArray()//(optional)
)

在这里插入图片描述

程序代码

#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
 
// 定义一些全局变量, 例如原图像(img), 模板图像(templ) 和结果图像(result) , 
// 还有匹配方法以及窗口名称:
Mat img, templ, result;
char* image_window = "Source Image";
char* result_window = "Result window";
 
int match_method = TM_SQDIFF;
int max_Trackbar = 5;

void MatchingMethod( int, void* );
 
int main( int argc, char** argv )
{
	// 1. 载入原图像和模板块
	img = imread("E:/Experiment/OpenCV/Pictures/TargetSearch.jpg");
	templ = imread("E:/Experiment/OpenCV/Pictures/Target.jpg");
	imshow("模板图像",templ);
 
	// 创建窗口
	namedWindow( image_window, CV_WINDOW_AUTOSIZE );
	namedWindow( result_window, CV_WINDOW_AUTOSIZE );
 
	// 2. 创建滑动条并输入将被使用的匹配方法. 一旦滑动条发生改变,回调函数 MatchingMethod 就会被调用.
	char* trackbar_label = "模板匹配方式";
	createTrackbar( trackbar_label, image_window, &match_method, max_Trackbar, MatchingMethod );
 
	MatchingMethod( 0, 0 );
 
	waitKey(0);
	return 0;
}

void MatchingMethod( int, void* )
{
	// 将被显示的原图像
	Mat img_display;
	img.copyTo( img_display );
 
	// 创建输出结果的矩阵
	int result_cols =  img.cols - templ.cols + 1;
	int result_rows = img.rows - templ.rows + 1;
	// 创建了一幅用来存放匹配结果的输出图像矩阵. 仔细看看输出矩阵的大小(它包含了所有可能的匹配位置)
	result.create( result_cols, result_rows, CV_32FC1 );
 
	// 执行模板匹配操作,并对结果进行归一化:
	matchTemplate( img, templ, result, match_method );
	normalize( result, result, 0, 1, NORM_MINMAX, -1, Mat() );
 
	// 通过函数 minMaxLoc 定位最匹配的位置
	double minVal, maxVal; 
	Point minLoc, maxLoc;
	Point matchLoc;
	
	//通过使用函数 minMaxLoc ,我们确定结果矩阵 R 的最大值和最小值的位置.
	minMaxLoc( result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );//寻找result中最大值,最小值,及它们所在的位置
 
	// 对于方法 SQDIFF 和 SQDIFF_NORMED, 越小的数值代表更高的匹配结果. 而对于其他方法, 数值越大匹配越好
	if( match_method  == CV_TM_SQDIFF || match_method == CV_TM_SQDIFF_NORMED ){ 
		matchLoc = minLoc; 
	}else{ 
		matchLoc = maxLoc; 
	}
 
	// 绘制矩形
	rectangle( img_display, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar::all(0), 2, 8, 0 );
	rectangle( result, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar::all(0), 2, 8, 0 );
 
	imshow( image_window, img_display );
	imshow( result_window, result );
 
	return;
}


运行结果

在这里插入图片描述
在这里插入图片描述

参考博客

  1. https://blog.csdn.net/LYKymy/article/details/83210432 (我觉得他写的比我好,推荐阅读)
  2. https://blog.csdn.net/huanghuangjin/article/details/81180568
2019-06-02 17:00:00 weixin_40063211 阅读数 648

模板匹配法

       模板匹配是一种用于在源图像S中寻找定位给定目标图像T(即模板图像)的技术。其原理很简单,就是通过一些相似度准则来衡量两个图像块之间的相似度Similarity(S,T)。

模板匹配的工作方式

       模板匹配的工作方式跟直方图的反向投影基本一样,大致过程是这样的:通过在输入图像上滑动图像块对实际的图像块和输入图像进行匹配。假设我们有一张100x100的输入图像,有一张10x10的模板图像,查找的过程是这样的:

 (1)从输入图像的左上角(0,0)开始,切割一块(0,0)至(10,10)的临时图像;

 (2)用临时图像和模板图像进行对比,对比结果记为c;

 (3)对比结果c,就是结果图像(0,0)处的像素值;

 (4)切割输入图像从(0,1)至(10,11)的临时图像,对比,并记录到结果图像;

 (5)重复(1)~(4)步直到输入图像的右下角。

模板匹配方法的优缺点:

       优点:简单、直接

       缺点:不具有旋转不变性、不具有尺度不变性

	代码如下:  
	int main()  
	{  
	    Mat img, templ, result;  
	    img = imread("match_dst.jpg");  
	    templ = imread("match_src.jpg");  
	    /*img = imread("1.jpg"); 
	    templ = imread("2.jpg");*/  
	
        int result_cols = img.cols - templ.cols + 1;  
	    int result_rows = img.rows - templ.rows + 1;  
	    result.create(result_cols, result_rows, CV_32FC1);  
	  
	    //进行匹配和标准化  
	    matchTemplate(img, templ, result, CV_TM_SQDIFF_NORMED);  
	    normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat());  
	  
	    double minVal;  //匹配最小值  
	    double maxVal;  //匹配最大值  
	    Point maxLoc;  //匹配最大值的位置坐标  
        Point minLoc;   //匹配最小值的位置坐标  
	    Point matchLoc;    
	  
	    //通过函数minMaxLoc定位最匹配的位置  
	    minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, Mat());  
	  
	    cout << "匹配度:" << minVal << endl;  
	    cout << "匹配度最小值的坐标" << minLoc.x << "," << minLoc.y << endl;  
	  
	    /*Mat H = findHomography(templ,img, CV_RANSAC); 
	 
	    Mat trans1; 
	    perspectiveTransform(img, trans1, H); 
	    imshow("trans1", trans1);*/  
	  
	    matchLoc = minLoc;  
	    rectangle(img, matchLoc, Point(matchLoc.x + templ.cols, matchLoc.y + templ.rows), Scalar(0, 255, 0), 2, 8, 0);  
	  
	    imshow("img", img);  
	    imshow("templ", templ);  
	    //imshow("result", result);  

	    waitKey(0);  	  
	    return 0;  
	}  

效果如下:

       但是,在该方法中,最佳匹配点位于大图匹配位置的左上部分,且只能用矩形框选匹配位置。若小图视角变化,框选无效。

Surf之图像匹配

       利用Surf算法进行图像匹配其一般流程为:检测物体特征点->计算特征点描述子->使用BurteForceMatcher或FLANN进行特征点匹配->匹配到的特征点进行透视变换findHomography()->透视矩阵变换perspectiveTransform()->绘制匹配物体轮廓。

#include<opencv2\core\core.hpp>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\nonfree\features2d.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include<opencv2\calib3d\calib3d.hpp>
#include<iostream>
#include<stdio.h>

using namespace std;
using namespace cv;

int main()
{
	Mat image_object = imread("template.png", IMREAD_GRAYSCALE);
	Mat image_scene = imread("image.png", IMREAD_GRAYSCALE);
	
	//检测特征点
	const int minHessian = 400;
	SurfFeatureDetector detector(minHessian);
	vector<KeyPoint>keypoints_object, keypoints_scene;
	detector.detect(image_object, keypoints_object);
	detector.detect(image_scene, keypoints_scene);

	//计算特征点描述子
	SurfDescriptorExtractor extractor;
	Mat descriptors_object, descriptors_scene;
	extractor.compute(image_object, keypoints_object, descriptors_object);
	extractor.compute(image_scene, keypoints_scene, descriptors_scene);

	//使用FLANN进行特征点匹配
	FlannBasedMatcher matcher;
	vector<DMatch>matches;
	matcher.match(descriptors_object, descriptors_scene, matches);

	//计算匹配点之间的最大和最小距离
	double max_dist = 0;
	double min_dist = 100;
	for (int i = 0; i < descriptors_object.rows; i++)
	{
		double dist = matches[i].distance;
		if (dist < min_dist)
		{
			min_dist = dist;
		}
		else if (dist > max_dist)
		{
			max_dist = dist;
		}
	}
	printf("Max dist: %f \n", max_dist);
	printf("Min dist: %f \n", min_dist);
   //绘制好的匹配点
	vector<DMatch>good_matches;
	for (int i = 0; i < descriptors_object.rows; i++)
	{
		if (matches[i].distance<2 * min_dist)
		{
			good_matches.push_back(matches[i]);
		}
	}
	Mat image_matches;
	drawMatches(image_object, keypoints_object, image_scene, keypoints_scene, good_matches, image_matches,
		Scalar::all(-1), Scalar::all(-1), vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);

	//定位好的匹配点
	vector<Point2f> obj;
	vector<Point2f> scene;
	for (int i = 0; i < good_matches.size(); i++)
	{
		//DMathch类型中queryIdx是指match中第一个数组的索引,keyPoint类型中pt指的是当前点坐标
		obj.push_back(keypoints_object[good_matches[i].queryIdx].pt);
		scene.push_back(keypoints_scene[good_matches[i].trainIdx].pt);
	}

	Mat H = findHomography(obj, scene, CV_RANSAC);

	vector<Point2f> obj_corners(4), scene_corners(4);
	obj_corners[0] = cvPoint(0, 0);
	obj_corners[1] = cvPoint(image_object.cols, 0);
	obj_corners[2] = cvPoint(image_object.cols, image_object.rows);
	obj_corners[3] = cvPoint(0, image_object.rows);

	perspectiveTransform(obj_corners, scene_corners, H);

	//绘制角点之间的直线
	line(image_matches, scene_corners[0] + Point2f(image_object.cols, 0),
		scene_corners[1] + Point2f(image_object.cols, 0), Scalar(0, 0, 255), 2);
	line(image_matches, scene_corners[1] + Point2f(image_object.cols, 0),
		scene_corners[2] + Point2f(image_object.cols, 0), Scalar(0, 0, 255), 2);
	line(image_matches, scene_corners[2] + Point2f(image_object.cols, 0),
		scene_corners[3] + Point2f(image_object.cols, 0), Scalar(0, 0, 255), 2);
	line(image_matches, scene_corners[3] + Point2f(image_object.cols, 0),
		scene_corners[0] + Point2f(image_object.cols, 0), Scalar(0, 0, 255), 2);

	//输出图像
	namedWindow("匹配图像", WINDOW_AUTOSIZE);
	imshow("匹配图像", image_matches);
	waitKey(0);

	return 0;

}

程序说明:

       在定位匹配点中用到了DMatchqueryIdxtrainIdx成员变量和keyPoint的成员变量pt

cv::DMatch::DMatch(int  queryIdx,  //在对描述子匹配时,第一组特征点的索引  
int  trainIdx,  //在对描述子匹配时,第二组特征点的索引  
int  imgIdx,    //多个图像中图像的索引  
float   distance  //两个特征向量间的欧氏距离,越小表明匹配度越高 )  

       对于DrawMatch函数:

void drawMatches( const Mat& img1, const vector<KeyPoint>& keypoints1,  
	              const Mat& img2, const vector<KeyPoint>& keypoints2,  
	              const vector<vector<DMatch> >& matches1to2, Mat& outImg,  
	              const Scalar& matchColor=Scalar::all -1), 
	              const Scalar& singlePointColor=Scalar::all(-1),  
	              const vector<vector<char> >& matchesMask=vector<vector<char> >(), 
                  int flags=DrawMatchesFlags::DEFAULT );  

/其中参数如下:  
img1 – 源图像1  
keypoints1 –源图像1的特征点.  
img2 – 源图像2.  
keypoints2 – 源图像2的特征点  
matches1to2 – 源图像1的特征点匹配源图像2的特征点[matches[i]] .  
outImg – 输出图像具体由flags决定.  
matchColor – 匹配的颜色(特征点和连线),若matchColor==Scalar::all(-1),颜色随机.  
singlePointColor – 单个点的颜色,即未配对的特征点,若matchColor==Scalar::all(-1),颜色随机.  
matchesMask – Mask决定哪些点将被画出,若为空,则画出所有匹配点.  
* flags – Fdefined by DrawMatchesFlags.  

效果图:

 

FAsT-Match

        FAsT-Match是在2D仿射变换下用于近似模板匹配的快速算法,其最小化绝对差分和(SAD)误差测量。 通过图像平滑度的密度对其进行采样。 对于每个可能的变换,使用次线性算法来近似SAD误差, 同时使用分支定界法进一步加速算法。 由于已知图像是分段平滑的,因此结果是具有近似保证的实际仿射模板匹配算法。

算法流程:                                                                                                      

代码地址

结果如下:

 

2015-08-15 10:51:18 llcchh012 阅读数 9802

1. 介绍

块匹配是图像去噪,运动估计中常用的一种方法。通过将查询块与相邻的图像块进行匹配,从这些相邻块中找出距离查询块距离最近的 K 个块。所谓的相邻也并不是绝对的位置上的相邻,也由此可以引出局部搜索(local)和全局搜索(non-local)。

这里写图片描述

如上图所示:
+ Vi 是一个以 i(红色) 为中心的窗(搜索窗),最大的灰色矩阵区域;
+ Ni 是以 i 为中心的矩阵邻域(一个图像块,查询块),紫色矩阵区域;
+ Nj 是以 j 为中心的矩阵邻域(一个图像块,近邻块),紫色矩阵区域。

如果 Vi 表示整幅图像,那么就是全局搜索,否则成为局部搜索。

2. 基于像素的 Non-local Means

在 Non-local Means 图像去噪中,会使用近似块来估计查询块,以实现去噪的目的,如下式所示:

NL(v)(i)=jIω(i,j)v(j)ω(i,j)[0,1];jIω(i,j)=1

其中权重的计算如下所示:

ω(i,j)=1Z(i)ev(Ni)v(Nj)22h2Z(i)=jIω(i,j)

其中 h 为图像的平滑参数,用于控制平滑噪声的强度,如果 h 比较小的话,幂函数的衰减效果比较显著,细节保留程度比较高,因此会保持图像本身的细节信息。

事实上并不是所有的近邻块都与查询块相似,因此可以只考虑几个权重比较大的块来进行估计,也就是对权重进行排序,选择权重大的作为相似块。这样不仅可以提高去噪效果还可以减少计算量。

另外,Mahmoudi 在文章 《Fast Non Local Means Denoising for 3D MR Images》中也提出了一种新的方法(根据v(Ni)v(Nj) 的均值和方差来选择相似块)避免许多无用的计算(欧式距离)。

ω(i,j)=1Z(i)ev(Ni)v(Nj)22h2;ifμ1<v(Ni)¯¯¯¯¯¯¯¯¯v(Nj)¯¯¯¯¯¯¯¯¯<μ2andσ21<var(v(Ni))var(v(Nj))0otherwise<σ22

为了避免重复计算,可以提前计算均值和方差。

3. 基于图像块的 Non-local Means

在第 2 部分中所述的都是基于单个像素(每个块的中心位置)的处理,因此导致计算量很大。但实际上我们计算的是图像块的相似度,因此可以扩展为图像块的处理。如下图所示:

这里写图片描述

则块匹配的计算变为:

NL(u)(Bik)=BjVikω(Bik,Bj)u(Bj)ω(Bik,Bj)=1Zikeu(Bik)u(Bj)222βδ2|Ni|

优化之后的权重估计变为:

ω(Bik,Bj)=1Zikeu(Bik)u(Bj)222βδ2|Ni|;ifμ1<u(Bik)¯¯¯¯¯¯¯¯¯¯u(Bj)¯¯¯¯¯¯¯¯¯<1μ1andσ21<var(u(Bik))var(u(Bj))0otherwise<1σ21

如下是文献《An Optimized Blockwise NL-means Denoising Filter for 3D Magnetic Resonance Images》中的实验结果:

这里写图片描述

其中:
+ NL-means,原始的non local means方法
+ Blockwise NL-means,每次重构一个子块,而不是单个像素
+ Optimized NL-means,针对NL-means,权重的选择进行优化,直选几个最大的权重
+ Optimized Blockwise NL-means,针对Optimized NL-means,权重选择进行了优化

从上可以看出每次对单个像素处理比每次对一个块处理的效果好,但是计算时间长;而对权重参数进行优化后效果明显改善;另外,优化过的方法中选用了多线程的方法。

4. 参考

  1. Fast image and video denoising via non-local means of similar neighborhoods.
  2. Fast Non Local Means Denoising for 3D MR Images
  3. An Optimized Blockwise NL-means Denoising Filter for 3D Magnetic Resonance Images
  4. 3DWavelet SubbandsMixing for Image Denoising
2018-03-05 20:15:54 u013162035 阅读数 211

3.6模板匹配

3.6.1模板匹配概述及原理

模板匹配是一项在一幅图像中寻找与另一幅模板图像最匹配(相似)部分的技术.
那么,模板匹配怎么实现呢?我们需要2幅图像:
原图像 (I): 在这幅图像里,我们希望找到一块和模板匹配的区域;
模板 (T): 将和原图像比照的图像块;
我们的目标是检测最匹配的区域:

这里写图片描述

图1

为了确定匹配区域, 我们不得不滑动模板图像和原图像进行比较 :
这里写图片描述

图2

通过滑动, 我们的意思是图像块一次移动一个像素 (从左往右,从上往下). 在每一个位置, 都进行一次度量计算来表明它是 “好” 或 “坏” 地与那个位置匹配 (或者说块图像和原图像的特定区域有多么相似).
对于 T 覆盖在 I 上的每个位置,你把度量值 保存 到 结果图像矩阵 (R) 中. 在 R 中的每个位置(x,y)都包含匹配度量值:
这里写图片描述

图3

上图就是 TM_CCORR_NORMED 方法处理后的结果图像 R。最白的位置代表最高的匹配. 正如您所见, 红色椭圆框住的位置很可能是结果图像矩阵中的最大数值, 所以这个区域 (以这个点为顶点,长宽和模板图像一样大小的矩阵) 被认为是匹配的。实际上, 我们使用函数 minMaxLoc 来定位在矩阵 R 中的最大值点 (或者最小值, 根据函数输入的匹配参数) .

3.6.2模板匹配相关API及源码

OpenCV通过函数 matchTemplate 实现了模板匹配算法。
matchTemplate( )函数
···
C++: void matchTemplate( InputArray image,
InputArray temp,
OutputArray result,
int method)
···
【参数】
第一个参数,image – Image where the search is running. It must be 8-bit or 32-bit floating-point.
第二个参数,templ – Searched template. It must be not greater than the source image and have the same data type.
第三个参数,result – Map of comparison results. It must be single-channel 32-bit floating-point. If image is W×H and templ is w×h , then result is(Ww+1)×(Hh+1) .
第四个参数,method – Parameter specifying the comparison method (see below).
可用的方法有6个:
【方法一】平方差匹配 method=CV_TM_SQDIFF
这类方法利用平方差来进行匹配,最好匹配为0.匹配越差,匹配值越大.
这里写图片描述
【方法二】标准平方差匹配 method=CV_TM_SQDIFF_NORMED
这里写图片描述
【方法三】相关匹配 method=CV_TM_CCORR
这类方法采用模板和图像间的乘法操作,所以较大的数表示匹配程度较高,0标识最坏的匹配效果。
这里写图片描述
【方法四】标准相关匹配 method=CV_TM_CCORR_NORMED
这里写图片描述
【方法五】相关匹配 method=CV_TM_CCOEFF
这类方法将模版对其均值的相对值与图像对其均值的相关值进行匹配,1表示完美匹配,-1表示糟糕的匹配,0表示没有任何相关性(随机序列)。
这里写图片描述
在这里
这里写图片描述
【方法六】标准相关匹配 method=CV_TM_CCOEFF_NORMED
这里写图片描述
通常,随着从简单的测量(平方差)到更复杂的测量(相关系数),我们可获得越来越准确的匹配(同时也意味着越来越大的计算代价)。最好的办法是对所有这些设置多做一些测试实验,以便为自己的应用选择同时兼顾速度和精度的最佳方案。
matchTemplate( )函数源代码

/*【matchTemplate ( )源代码】*********************************************************
 * @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的对应版本源码完全一样,均在对应的安装目录下)  
 * @源码路径:…\opencv\sources\modules\imgproc\src\templmatch.cpp
 * @起始行数: 900行   
********************************************************************************/
void cv::matchTemplate( InputArray _img, InputArray _templ, OutputArray _result, int method, InputArray _mask )
{
    if (!_mask.empty())
    {
        cv::matchTemplateMask(_img, _templ, _result, method, _mask);
        return;
    }

    int type = _img.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
    CV_Assert( CV_TM_SQDIFF <= method && method <= CV_TM_CCOEFF_NORMED );
    CV_Assert( (depth == CV_8U || depth == CV_32F) && type == _templ.type() && _img.dims() <= 2 );

    bool needswap = _img.size().height < _templ.size().height || _img.size().width < _templ.size().width;
    if (needswap)
    {
        CV_Assert(_img.size().height <= _templ.size().height && _img.size().width <= _templ.size().width);
    }

    CV_OCL_RUN(_img.dims() <= 2 && _result.isUMat(),
               (!needswap ? ocl_matchTemplate(_img, _templ, _result, method) : ocl_matchTemplate(_templ, _img, _result, method)))

    int numType = method == CV_TM_CCORR || method == CV_TM_CCORR_NORMED ? 0 :
                  method == CV_TM_CCOEFF || method == CV_TM_CCOEFF_NORMED ? 1 : 2;
    bool isNormed = method == CV_TM_CCORR_NORMED ||
                    method == CV_TM_SQDIFF_NORMED ||
                    method == CV_TM_CCOEFF_NORMED;

    Mat img = _img.getMat(), templ = _templ.getMat();
    if (needswap)
        std::swap(img, templ);

    Size corrSize(img.cols - templ.cols + 1, img.rows - templ.rows + 1);
    _result.create(corrSize, CV_32F);
    Mat result = _result.getMat();

#ifdef HAVE_TEGRA_OPTIMIZATION
    if (tegra::useTegra() && tegra::matchTemplate(img, templ, result, method))
        return;
#endif

#if defined HAVE_IPP
    bool useIppMT = false;
    CV_IPP_CHECK()
    {
        useIppMT = (templ.rows < img.rows/2 && templ.cols < img.cols/2);

        if (method == CV_TM_SQDIFF && cn == 1 && useIppMT)
        {
            if (ipp_sqrDistance(img, templ, result))
            {
                CV_IMPL_ADD(CV_IMPL_IPP);
                return;
            }
            setIppErrorStatus();
        }
    }
#endif

#if defined HAVE_IPP
    if (cn == 1 && useIppMT)
    {
        if (!ipp_crossCorr(img, templ, result))
        {
            setIppErrorStatus();
            crossCorr( img, templ, result, result.size(), result.type(), Point(0,0), 0, 0);
        }
        else
        {
            CV_IMPL_ADD(CV_IMPL_IPP);
        }
    }
    else
#endif
    crossCorr( img, templ, result, result.size(), result.type(), Point(0,0), 0, 0);

    if( method == CV_TM_CCORR )
        return;

    double invArea = 1./((double)templ.rows * templ.cols);

    Mat sum, sqsum;
    Scalar templMean, templSdv;
    double *q0 = 0, *q1 = 0, *q2 = 0, *q3 = 0;
    double templNorm = 0, templSum2 = 0;

    if( method == CV_TM_CCOEFF )
    {
        integral(img, sum, CV_64F);
        templMean = mean(templ);
    }
    else
    {
        integral(img, sum, sqsum, CV_64F);
        meanStdDev( templ, templMean, templSdv );

        templNorm = templSdv[0]*templSdv[0] + templSdv[1]*templSdv[1] + templSdv[2]*templSdv[2] + templSdv[3]*templSdv[3];

        if( templNorm < DBL_EPSILON && method == CV_TM_CCOEFF_NORMED )
        {
            result = Scalar::all(1);
            return;
        }

        templSum2 = templNorm + templMean[0]*templMean[0] + templMean[1]*templMean[1] + templMean[2]*templMean[2] + templMean[3]*templMean[3];

        if( numType != 1 )
        {
            templMean = Scalar::all(0);
            templNorm = templSum2;
        }

        templSum2 /= invArea;
        templNorm = std::sqrt(templNorm);
        templNorm /= std::sqrt(invArea); // care of accuracy here

        q0 = (double*)sqsum.data;
        q1 = q0 + templ.cols*cn;
        q2 = (double*)(sqsum.data + templ.rows*sqsum.step);
        q3 = q2 + templ.cols*cn;
    }

    double* p0 = (double*)sum.data;
    double* p1 = p0 + templ.cols*cn;
    double* p2 = (double*)(sum.data + templ.rows*sum.step);
    double* p3 = p2 + templ.cols*cn;

    int sumstep = sum.data ? (int)(sum.step / sizeof(double)) : 0;
    int sqstep = sqsum.data ? (int)(sqsum.step / sizeof(double)) : 0;

    int i, j, k;

    for( i = 0; i < result.rows; i++ )
    {
        float* rrow = result.ptr<float>(i);
        int idx = i * sumstep;
        int idx2 = i * sqstep;

        for( j = 0; j < result.cols; j++, idx += cn, idx2 += cn )
        {
            double num = rrow[j], t;
            double wndMean2 = 0, wndSum2 = 0;

            if( numType == 1 )
            {
                for( k = 0; k < cn; k++ )
                {
                    t = p0[idx+k] - p1[idx+k] - p2[idx+k] + p3[idx+k];
                    wndMean2 += t*t;
                    num -= t*templMean[k];
                }

                wndMean2 *= invArea;
            }

            if( isNormed || numType == 2 )
            {
                for( k = 0; k < cn; k++ )
                {
                    t = q0[idx2+k] - q1[idx2+k] - q2[idx2+k] + q3[idx2+k];
                    wndSum2 += t;
                }

                if( numType == 2 )
                {
                    num = wndSum2 - 2*num + templSum2;
                    num = MAX(num, 0.);
                }
            }

            if( isNormed )
            {
                t = std::sqrt(MAX(wndSum2 - wndMean2,0))*templNorm;
                if( fabs(num) < t )
                    num /= t;
                else if( fabs(num) < t*1.125 )
                    num = num > 0 ? 1 : -1;
                else
                    num = method != CV_TM_SQDIFF_NORMED ? 0 : 1;
            }

            rrow[j] = (float)num;
        }
    }
}

3.6.3模板匹配实例

代码参看附件【demo1】。

这里写图片描述

图4平方差匹配

这里写图片描述

图5归一化平方差匹配

这里写图片描述

图6相关匹配法

这里写图片描述

图7归一化相关匹配法

这里写图片描述

图8相关系数匹配法

这里写图片描述

图9归一化相关系数匹配法

参考:
中文
英文

本章参考附件

点击进入

没有更多推荐了,返回首页