模板匹配就是按照一定的相似性规则,在一张图像中寻找与模板最相似的区域。
参考opencv官网:https://docs.opencv.org/3.4.1/de/da9/tutorial_template_matching.html
模板匹配的过程如下:
左上角小狗图像为定义的模板,下面的图片为待匹配图像,匹配的过程就是按照从左到右,从上到下的顺序,依次遍历全图,通过定义的相似度评判准则,计算模板与当前位置图像块的相似度,进而得到最相似位置,实现目标定位。
常见的相似度准则有:
即:平方差、归一化平方差、相关系数、归一化相关系数、互相关五种。
下面就是代码,这里只实现了两种:平方误差及绝对误差
#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;
}
匹配结果:
改进方法:
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;
}
匹配结果如下:
里面绿色点表示采样位置。
参考资料:
opencv 官网