模板匹配的工作方式
模板匹配的工作方式跟直方图的反向投影基本一样,大致过程是这样的:通过在输入图像上滑动图像块对实际的图像块和输入图像进行匹配。
假设我们有一张100x100的输入图像,有一张10x10的模板图像,查找的过程是这样的:
(1)从输入图像的左上角(0,0)开始,切割一块(0,0)至(10,10)的临时图像;
(2)用临时图像和模板图像进行对比,对比结果记为c;
(3)对比结果c,就是结果图像(0,0)处的像素值;
(4)切割输入图像从(0,1)至(10,11)的临时图像,对比,并记录到结果图像;
(5)重复(1)~(4)步直到输入图像的右下角。
大家可以看到,直方图反向投影对比的是直方图,而模板匹配对比的是图像的像素值;模板匹配比直方图反向投影速度要快一些,但是我个人认为直方图反向投影的鲁棒性会更好。
参考的是:
http://www.cnblogs.com/xrwang/archive/2010/02/05/MatchTemplate.html
cvMatchTemplate()函数
http://www.lxway.com/914114412.htm
定义:
void cvMatchTemplate(
const CvArr* image,
const CvArr* templ,
CvArr* result, int method
);
参数:
image:欲搜索的图像。它应该是单通道、8-比特或32-比特 浮点数图像
templ :搜索模板,不能大于输入图像,且与输入图像具有一样的数据类型
result :比较结果的映射图像。单通道、32-比特浮点数.。如果图像是 W×H 而 templ 是 w×h ,则 result 一定是 (W-w+1)×(H-h+1)。
所以在下面的程序中,才会有
int iwidth = src->width - temp1->width + 1;
int iheight = src->height - temp1->height + 1;
method 指定匹配方法:
在OpenCv和EmguCv中支持以下6种对比方式:
CV_TM_SQDIFF 平方差匹配法:该方法采用平方差来进行匹配;最好的匹配值为0;匹配越差,匹配值越大。
CV_TM_CCORR 相关匹配法:该方法采用乘法操作;数值越大表明匹配程度越好。
CV_TM_CCOEFF 相关系数匹配法:1表示完美的匹配;-1表示最差的匹配。
CV_TM_SQDIFF_NORMED 归一化平方差匹配法
CV_TM_CCORR_NORMED 归一化相关匹配法
CV_TM_CCOEFF_NORMED 归一化相关系数匹配法
根据我的测试结果来看,上述几种匹配方式需要的计算时间比较接近(跟《学习OpenCv》书上说的不同),我们可以选择一个能适应场景的匹配方式。
cvMinMaxLoc()函数
通过使用 cvMinMaxLoc函数,我们确定结果矩阵 R 的最大值和最小值的位置。cvMinMaxLoc()必须对单通道做处理。
函数中的参数有:
double minVal; double maxVal; Point minLoc; Point maxLoc;
Point matchLoc;
格式:
cvMinMaxLoc( result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );
result: 匹配结果矩阵
&minVal 和 &maxVal: 在矩阵 result 中存储的最小值和最大值。该值是矩阵中的最大值和最小值。
&minLoc 和 &maxLoc: 在结果矩阵中最小值和最大值的坐标.
Mat(): 可选的掩模
程序实例:
代码来自:
http://www.lxway.com/914114412.htm
#include <iostream>
#include <cv.h>
#include <highgui.h>
#include <cxcore.h>
using namespace std;
void GetHSV(const IplImage *image,IplImage **h,IplImage **s,IplImage **v);
int main()
{
IplImage *src = cvLoadImage("t1.jpg");
IplImage *h_src = NULL ,*s_src = NULL;
GetHSV(src,&h_src,&s_src,NULL);
IplImage *images[] = {h_src,s_src};
CvHistogram *hist_src;
{//计算二维直方图
int dims = 2;
int size[] = {30,32}; // 这个地方不要取的太大!
float range_h[] = {0,180}
//再用cvCvtColor转换时h已经归一化到180了
,range_s[] = {0,256};
float *ranges[] = {range_h,range_s};
hist_src = cvCreateHist(dims,size,CV_HIST_ARRAY,ranges);
cvCalcHist(images,hist_src);
cvNormalizeHist(hist_src,1);
}
IplImage *dst = cvLoadImage("t2.jpg");
IplImage *h_dst = NULL,*s_dst = NULL;
GetHSV(dst,&h_dst,&s_dst,NULL);
images[0] = h_dst ,images[1] = s_dst;
CvSize patch_size = cvSize(src->width,src->height);
IplImage *result = cvCreateImage(cvSize(h_dst->width - patch_size.width +1,h_dst->height - patch_size.height +1)
,IPL_DEPTH_32F,1);//块搜索时处理边缘是直接舍去,故result的大小比dst小path_size大小
//32F类型,取值为0~1最亮为1,可直接显示
//CV_COMP_CORREL相关度,1时最匹配,0时最不匹配
cvCalcBackProjectPatch(images,result,patch_size,hist_src,CV_COMP_CORREL,1);
cvShowImage("result",result);
//找出最大值位置,可得到此位置即为杯子所在位置
CvPoint max_location;
cvMinMaxLoc(result,NULL,NULL,NULL,&max_location,NULL);
//加上边缘,得到在原始图像中的实际位置
max_location.x += cvRound(patch_size.width/2);
max_location.y += cvRound(patch_size.height/2);
//在dst图像中用红色小圆点标出位置——小圆点就表示杯子所在区域
cvCircle(dst,max_location,3,CV_RGB(255,0,0),-1);
cvShowImage("dst",dst);
cvWaitKey();
cvReleaseImage(&src);
cvReleaseImage(&dst);
cvReleaseImage(&h_src);
cvReleaseImage(&h_dst);
cvReleaseImage(&s_dst);
cvReleaseImage(&s_src);
cvReleaseHist(&hist_src);
cvReleaseImage(&result);
cvDestroyAllWindows();
}
void GetHSV(const IplImage *image , IplImage **h,IplImage **s,IplImage **v)
{
IplImage *hsv = cvCreateImage(cvGetSize(image),8,3);
cvCvtColor(image,hsv,CV_BGR2HSV);
if((h != NULL) && (*h == NULL))
*h = cvCreateImage(cvGetSize(image),8,1);
if((s != NULL) && (*s == NULL))
*s = cvCreateImage(cvGetSize(image),8,1);
if((v != NULL) && (*v == NULL))
*v = cvCreateImage(cvGetSize(image),8,1);
cvSplit(hsv,*h,(s == NULL)?NULL:*s,(v==NULL)?NULL:*v,NULL);
cvReleaseImage(&hsv);
}
运行结果
用到的图片和上一个实验一样,就不传了。最终结果是用红色的小圆点标记出杯子的位置。