图像处理(十):模板匹配

模板匹配就是按照一定的相似性规则,在一张图像中寻找与模板最相似的区域。

参考opencv官网:https://docs.opencv.org/3.4.1/de/da9/tutorial_template_matching.html

模板匹配的过程如下:

图像处理(十):模板匹配_第1张图片

    左上角小狗图像为定义的模板,下面的图片为待匹配图像,匹配的过程就是按照从左到右,从上到下的顺序,依次遍历全图,通过定义的相似度评判准则,计算模板与当前位置图像块的相似度,进而得到最相似位置,实现目标定位。

    常见的相似度准则有:

图像处理(十):模板匹配_第2张图片

    即:平方差、归一化平方差、相关系数、归一化相关系数、互相关五种。

    下面就是代码,这里只实现了两种:平方误差及绝对误差

#include 
#include 
#include 

using namespace cv;
using namespace std;

#define SQDIFF 0
#define SADIFF 1

float templateMatch(const Mat & src, const Mat & temp, int & i_match, int & j_match, int Match_methold)
{
	int src_cols = src.cols;
	int src_rows = src.rows;
	int temp_cols = temp.cols;
	int temp_rows = temp.rows;

	int i_end = src_rows - temp.rows + 1;
	int j_end = src.cols - temp.cols + 1;

	float match_degree = FLT_MAX;

	for (int i = 0; i < i_end; i++)
	{
		for (int j = 0; j < j_end; j++)
		{
			float match_ij = 0.0;

			for (int m = 0; m < temp_rows; m++)
			{
				for (int n = 0; n < temp_cols; n++)
				{
					uchar val_s = src.at(i + m, j + n);
					uchar val_t = temp.at(m, n);
					if (Match_methold == SQDIFF)
					{
						match_ij += float((val_t - val_s) * (val_t - val_s));
					}
					else if (Match_methold == SADIFF)
					{
						match_ij += float(abs(val_t - val_s));
					}
				}
			}
			
			//cout << match_ij << endl;
			if (match_ij < match_degree)
			{
				match_degree = match_ij;
				i_match = i;
				j_match = j;
			}
		}
	}
	

	return match_degree;

}

int main()
{
	Mat src = imread("messi5.jpg", CV_LOAD_IMAGE_GRAYSCALE);
	Mat temp = imread("face.png", CV_LOAD_IMAGE_GRAYSCALE);
	
	float match_degree = 0;
	int i_match = -1, j_match = -1;
	match_degree = templateMatch(src, temp, i_match, j_match, SQDIFF);
	
	cout << i_match << j_match << match_degree << endl;

	Mat src_color;
	cvtColor(src, src_color, COLOR_GRAY2BGR);
	rectangle(src_color, Rect(j_match, i_match, temp.cols, temp.rows), Scalar(255, 0, 0));
	imshow("result", src_color);
	waitKey(0);

	return 0;
}

匹配结果:

图像处理(十):模板匹配_第3张图片

改进方法:

1)增加步长

2)随机抽样匹配

    增加步长就不多讨论了,随机抽样,利用的是目标所在矩形区域位置中,目标应该位于矩形区域中央,边界部分大部分为背景,因此考虑对模版进行随机采样,采样方法为:越靠近中心位置,生成越多的采样点,反之,越远离中心位置,生成较少的采样点,这里采用的随机生成函数为Gaussian函数。

#include 
#include 

using namespace cv;
using namespace std;

#define SQDIFF 0
#define SADIFF 1

float templateMatch(const Mat & src, const Mat & temp, int & i_match, int & j_match,
	int Match_methold, vector  real_sample_points)
{
	int src_cols = src.cols;
	int src_rows = src.rows;
	int temp_cols = temp.cols;
	int temp_rows = temp.rows;

	int i_end = src_rows - temp.rows + 1;
	int j_end = src.cols - temp.cols + 1;

	float match_degree = FLT_MAX;

	for (int i = 0; i < i_end; i++)
	{
		for (int j = 0; j < j_end; j++)
		{
			float match_ij = 0.0;

			for (int k = 0; k < real_sample_points.size(); k++)	{
				Point pt = real_sample_points[k];
				uchar val_s = src.at(i + pt.y, j + pt.x);
				uchar val_t = temp.at(pt.y, pt.x);
				if (Match_methold == SQDIFF){
					match_ij += float((val_t - val_s) * (val_t - val_s));
				}
				else if (Match_methold == SADIFF){
					match_ij += float(abs(val_t - val_s));
				}
			}
			//cout << match_ij << endl;
			if (match_ij < match_degree)
			{
				match_degree = match_ij;
				i_match = i;
				j_match = j;
			}
		}
	}


	return match_degree;

}



void GenerateRandomSamplePoints(vector  & sample_points, int num_points = 1000,
	Point2d& sigma = Point2d(0.3, 0.3)){
	sample_points.clear();
	RNG rng = theRNG();
	Rect2d sample_area(0.0, 0.0, 1.0, 1.0);
	for (int k = 0; k < num_points;){
		Point2d pt;
		pt.x = sample_area.width / 2.0 + rng.gaussian(sigma.x);
		pt.y = sample_area.height / 2.0 + rng.gaussian(sigma.y);
		if (sample_area.contains(pt)){
			if (pt.y < 0 || pt.x < 0)
			{
				cout << pt.x << pt.y << endl;
			}
			sample_points.push_back(pt);
			k++;
		}
	}
}

void computeSamplePoints(vector & real_sample_points, const Rect & roi, int num_points = 1000)
{
	vector  generate_sample_points;
	GenerateRandomSamplePoints(generate_sample_points);
	Point2i pt;
	for (int k = 0; k < num_points; k++){
		pt.x = cvRound(generate_sample_points[k].x * roi.width);
		pt.y = cvRound(generate_sample_points[k].y * roi.height);
		if (pt.y < 0 || pt.x < 0)
		{
			cout << roi.width << roi.height << endl;
		}

		real_sample_points.push_back(pt);
	}
}

int main()
{
	Mat src = imread("messi5.jpg", CV_LOAD_IMAGE_GRAYSCALE);
	Mat temp = imread("messi_face.jpg", CV_LOAD_IMAGE_GRAYSCALE);

	float match_degree = 0;
	int i_match = -1, j_match = -1;

	
	vector  real_sample_points;
	computeSamplePoints(real_sample_points, Rect(0, 0, temp.cols - 1, temp.rows - 1));

	match_degree = templateMatch(src, temp, i_match, j_match, SQDIFF, real_sample_points);

	cout << i_match << j_match << match_degree << endl;


	Mat src_color;
	cvtColor(src, src_color, COLOR_GRAY2BGR);
	for (int i = 0; i < 1000; i++){
		circle(src_color, Point(j_match, i_match) + real_sample_points[i], 1, Scalar(0, 255, 0));
	}
	rectangle(src_color, Rect(j_match, i_match, temp.cols, temp.rows), Scalar(255, 0, 0));
	imshow("result", src_color);
	waitKey(0);

	return 0;

}

匹配结果如下:

图像处理(十):模板匹配_第4张图片

里面绿色点表示采样位置。

参考资料:

opencv 官网

你可能感兴趣的:(Image,Process)