上一篇二维图像模板匹配方法(一)主要是利用opencv自带的模板匹配方法做目标的匹配度计算,本文自行定义匹配度计算方法。


工程文件的代码如下:

#include<iostream>
#include<string>
#include<vector>
#include<opencv2\opencv.hpp>
#include"Tracker.h"
 
using namespace cv;
using namespace std;
 
namespace global {
	bool paused = true;  //单击鼠标右键,暂停标志
	Mat displayImg;    //绘制选择目标时鼠标的拖动痕迹
	bool selectObject = false;  //selectObject的初始值为false,
	bool isRoiReady = false;  //ROI区域是否已经选择好
	Point origin;   //ROI区域的左上角起始位置
	Rect selectedRoi; //最终通过鼠标选择的ROI区域
 
	static void onMouse(int event, int x, int y, int, void*) {
		if (selectObject)  //鼠标左键被按下后,该段语句开始执行
		{
			selectedRoi.x = MIN(x, origin.x);
			selectedRoi.y = MIN(y,origin.y);
			selectedRoi.width = std::abs(x - origin.x);
			selectedRoi.height = std::abs(y - origin.y);
			selectedRoi &= Rect(0,0,displayImg.cols,displayImg.rows);  //不能越界
			                                                           
			rectangle(displayImg,selectedRoi,Scalar(0,0,255),1);  //画出鼠标选择框
		}
 
		switch (event)
		{
			//当在第一帧按下鼠标左键后,selectObject被置位1,拖动鼠标,开始选择目标的矩形区域
		case CV_EVENT_LBUTTONDOWN:
			origin = Point(x, y);
			selectedRoi = Rect(x, y, 0, 0);
			selectObject = true;
			isRoiReady = false;
			break;
			//直到鼠标左键抬起,标志着鼠标区域选择完毕。selectObject被置为false
		case CV_EVENT_LBUTTONUP:
			selectObject = false;
			if (selectedRoi.width > 0 && selectedRoi.height > 0) {
				isRoiReady = true;
			}
			cout << "目标区域已经选择完毕" << endl;
			cout << "选中的矩形区域为: " << selectedRoi << endl;
			break;
			//单击右键,暂停/开始
		case CV_EVENT_RBUTTONDOWN:
			paused = !paused;
			break;
		}
	}
}
 
//**********下面用于自己定义的模板匹配方法做匹配*******//
float MatchTemplate(const Mat& src,const Mat& temp1,Point2i & match_location,int match_method)
{
	CV_Assert((src.type() == CV_8UC1) && (temp1.type() == CV_8UC1)); //申明只能处理单通道的图像
 
	//原图像和模板的尺寸
	int src_width = src.cols;
	int src_height = src.rows;
	int temp1_cols = temp1.cols;
	int temp1_rows = temp1.rows;
	int y_end = src_height - temp1_rows + 1;
	int x_end = src_width - temp1_cols + 1;
 
	//在匹配过程中,记录最匹配的位置和匹配度
	float match_degree = FLT_MAX;    //给匹配度赋一个最大匹配度初始值
	int y_match = -1, x_match = -1;
 
	//从上到下扫描原图像
	for (int y = 0; y < y_end; y++)
	{
		//从左到右扫描原图像
		for (int x = 0; x < x_end; x++)
		{
			//src(y,x)位置上与模板的匹配度
			float match_yx = 0.0f;
			//将模板左上角temp1(0,0)对齐到src(y,x)位置,在模板内累加每个采样像素点上的差异
			for (int r = 0; r < temp1_rows; r++) {
				for (int c = 0; c < temp1_cols; c++) {
					uchar src_val = src.ptr<uchar>(y + r)[x + c];
					uchar temp1_val = temp1.ptr<uchar>(r)[c];
					if (match_method == 0)  //SQDIFF=0
						match_yx += float(std::abs(src_val - temp1_val)*std::abs(src_val - temp1_val));
					if(match_method==1)  //SADIFF=1
						match_yx+= float(std::abs(src_val - temp1_val));
				}
			}
			//与历史最好的差异度进行比较,找出差异最小的点
			if (match_degree > match_yx) {
				match_degree = match_yx;
				y_match = y;
				x_match = x;
			}
		}
	}
	match_location = Point2i(x_match, y_match);
	return match_degree;
}
//**********以上用自己定义的模板匹配方法做匹配*********//
 
 
int main(int argc, char*argv[]) 
{
//******下面用于自己定义的模板匹配方法做匹配*******//
	const string image_file = "cat.jpg"; 
	Mat srcImg = imread(image_file, IMREAD_GRAYSCALE);
	//用来显示结果
	Mat displayImg;
	srcImg.copyTo(displayImg);
 
	//设置全局变量
	global::isRoiReady = false;
	global::selectObject = false;
	global::displayImg = displayImg;
 
	const string winname = "Result Image";  
 
	namedWindow(winname, WINDOW_AUTOSIZE);
	setMouseCallback(winname, global::onMouse, 0);
 
	//循环显示图像,等待鼠标选择ROI区域
	for (;;)
	{
		imshow(winname, displayImg);
		//一旦选择好ROI区域,就进入处理
		if (global::isRoiReady) {
			//设置为false,处理完此次就接着等待鼠标选择
			global::displayImg = false;
			//提取鼠标选中的图像块
			Rect roiRect = global::selectedRoi;
			Mat roiImg = srcImg(roiRect).clone();
			imshow("ROI Image", roiImg);//显示ROI图像块
 
			//为原始图像增加高斯噪声
			Mat noiseImg(srcImg.size(), srcImg.type());
			cv::randn(noiseImg, Scalar(0), Scalar(30));
			Mat workImg = noiseImg + srcImg;
			//显示噪声污染的图像和选择的ROI区域
			workImg.copyTo(displayImg);
			rectangle(displayImg, global::selectedRoi, Scalar::all(0), 4);
			imshow(winname, displayImg);
			waitKey(15);
 
			//选中的模板在噪声污染的图像上匹配
			Point2i match_location;
			int match_method=0;
			MatchTemplate(workImg, roiImg, match_location, match_method);
			Rect matchedRoi(match_location.x, match_location.y, roiImg.cols, roiImg.rows);
			//显示匹配结果
			rectangle(displayImg, matchedRoi, Scalar::all(255), 2);
			imshow(winname, displayImg);
			waitKey(15);
		}
		waitKey(15);
	}
} 

代码运行结果如下:

其中匹配度用到的公式如下:

如果想要加快匹配的速度,可以试着不用逐个像素进行匹配计算,可以跳跃某几个像素进行计算。参考如下代码改进版,其中的xy_step和xy_stride就是横向和纵向的跳跃步长,可自行更改步长大小。

//**********下面用于自己定义的模板匹配方法做匹配*******//
float MatchTemplate(const Mat& src,const Mat& temp1,Point2i & match_location,int match_method,Vec2i& xy_step,Vec2i& xy_stride)
{
	CV_Assert((src.type() == CV_8UC1) && (temp1.type() == CV_8UC1)); //申明只能处理单通道的图像
 
	//原图像和模板的尺寸
	int src_width = src.cols;
	int src_height = src.rows;
	int temp1_cols = temp1.cols;
	int temp1_rows = temp1.rows;
	int y_end = src_height - temp1_rows + 1;
	int x_end = src_width - temp1_cols + 1;
 
	//在匹配过程中,记录最匹配的位置和匹配度
	float match_degree = FLT_MAX;    //给匹配度赋一个最大匹配度初始值
	int y_match = -1, x_match = -1;
 
	//从上到下扫描原图像
	for (int y = 0; y < y_end; y+=xy_stride[1])
	{
		//从左到右扫描原图像
		for (int x = 0; x < x_end; x+=xy_stride[0])
		{
			//src(y,x)位置上与模板的匹配度
			float match_yx = 0.0f;
			//将模板左上角temp1(0,0)对齐到src(y,x)位置,在模板内累加每个采样像素点上的差异
			for (int r = 0; r < temp1_rows; r+=xy_step[1])    //跳跃匹配可减少匹配计算量
			{
				for (int c = 0; c < temp1_cols; c+=xy_step[0]) 
				{
					uchar src_val = src.ptr<uchar>(y + r)[x + c];
					uchar temp1_val = temp1.ptr<uchar>(r)[c];
					if (match_method == 0)  //SQDIFF=0
						match_yx += float(std::abs(src_val - temp1_val)*std::abs(src_val - temp1_val));
					if(match_method==1)  //SADIFF=1
						match_yx+= float(std::abs(src_val - temp1_val));
				}
			}
			//与历史最好的差异度进行比较,找出差异最小的点
			if (match_degree > match_yx) {
				match_degree = match_yx;
				y_match = y;
				x_match = x;
			}
		}
	}
	match_location = Point2i(x_match, y_match);
	return match_degree;
}
//**********以上用自己定义的模板匹配方法做匹配*********//
 
 
int main(int argc, char*argv[]) 
{
	
	//******下面用于自己定义的模板匹配方法做匹配*******//
	const string image_file = "cat.jpg"; 
	Mat srcImg = imread(image_file, IMREAD_GRAYSCALE);
	//用来显示结果
	Mat displayImg;
	srcImg.copyTo(displayImg);
 
	//设置全局变量
	global::isRoiReady = false;
	global::selectObject = false;
	global::displayImg = displayImg;
 
	const string winname = "Result Image";  
 
	namedWindow(winname, WINDOW_AUTOSIZE);
	setMouseCallback(winname, global::onMouse, 0);
 
	//循环显示图像,等待鼠标选择ROI区域
	for (;;)
	{
		imshow(winname, displayImg);
		//一旦选择好ROI区域,就进入处理
		if (global::isRoiReady) {
			//设置为false,处理完此次就接着等待鼠标选择
			global::displayImg = false;
			//提取鼠标选中的图像块
			Rect roiRect = global::selectedRoi;
			Mat roiImg = srcImg(roiRect).clone();
			imshow("ROI Image", roiImg);//显示ROI图像块
 
			//为原始图像增加高斯噪声
			Mat noiseImg(srcImg.size(), srcImg.type());
			cv::randn(noiseImg, Scalar(0), Scalar(30));
			Mat workImg = noiseImg + srcImg;
			//显示噪声污染的图像和选择的ROI区域
			workImg.copyTo(displayImg);
			rectangle(displayImg, global::selectedRoi, Scalar::all(0), 4);
			imshow(winname, displayImg);
			waitKey(15);
 
			//选中的模板在噪声污染的图像上匹配
			Point2i match_location;
			int match_method=0;
			Vec2i xy_step(2, 2);//定义模板横向和纵向的匹配度跳跃步长各为2
			Vec2i xy_stride(2, 2); //定义外部原图像跳跃
			float matchDegree=MatchTemplate(workImg, roiImg, match_location, match_method,xy_step,xy_stride);
			Rect matchedRoi(match_location.x, match_location.y, roiImg.cols, roiImg.rows);
			cout << "匹配度: " << matchDegree << endl;
			cout << "匹配位置: " << matchedRoi << endl;
			//显示匹配结果
			rectangle(displayImg, matchedRoi, Scalar::all(255), 2);
			imshow(winname, displayImg);
			waitKey(15);
		}
		waitKey(15);
	}
	waitKey(0);
}

并可将匹配度和匹配位置结果打印出来。


本文主要是通过鼠标框选模板图像,用自己定义的匹配度计算公式来寻找输入图像中匹配度满足要求的目标图像。接下来会针对视频流做匹配度计算,本文作为视频目标跟踪系列的导文。

 

转载请注明出处,谢谢!

 ————————————————
版权声明:本文为CSDN博主「ciky奇」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/c20081052/article/details/81100260