opencv自带的模板匹配常规做法:
1.转换灰度图
cvtColor(img_template, img_template, CV_RGB2GRAY);
cvtColor(org, org, CV_RGB2GRAY);
2.matchTemplate模版匹配
matchTemplate(const CvArr* image, //待搜索图像
constCvArr* templ, //模板图像
CvArr* result, //匹配结果
int method );//计算匹配程度的方法
关于参数 method:
CV_TM_SQDIFF平方差匹配法:该方法采用平方差来进行匹配;最好的匹配值为0;匹配越差,匹配值越大。
CV_TM_CCORR相关匹配法:该方法采用乘法操作;数值越大表明匹配程度越好。
CV_TM_CCOEFF相关系数匹配法:1表示完美的匹配;-1表示最差的匹配。
CV_TM_SQDIFF_NORMED归一化平方差匹配法
CV_TM_CCORR_NORMED归一化相关匹配法
CV_TM_CCOEFF_NORMED归一化相关系数匹配法
Mat result;
result.create(org.dims,org.size,org.type());
matchTemplate(org, img_template, result, 0);
3.minMaxLoc寻找矩阵(一维数组当作向量,用Mat定义) 中最小值和最大值的位置.
void minMaxLoc( const Mat& src, double* minVal, double* maxVal=0, Point* minLoc=0, Point* maxLoc=0, const Mat& mask=Mat() );
函数的形参有的是指针类型,这样是为了传递最大值和最小值
Point minPoint;
Point maxPoint;
double *minVal ;
double *maxVal ;
minMaxLoc(result, minVal,maxVal,&minPoint,&maxPoint);
- 显示匹配后图像:
Rect rect(minPoint.x,minPoint.y,template_cols,template_rows);
Mat image = org.clone();
rectangle(image,rect,Scalar(0,0,255));
imshow("window",image);
waitKey(0);
matchTemplate模版匹配,检测图像中标注的匹配区域与模版的尺寸大小一致;若是目标在检测图像中的大小比例不定(如拍摄距离的远近),若是想要得到较好的匹配效果,则必须准备出各个尺寸大小的模版,每次选择出比较吻合的模版,也显然是非常不合适的;
项目中,目标物体的拍摄距离不固定,最近距离拍摄与最远距离拍摄,目标在不同距离拍摄图像中的尺寸可能相差多达一倍;
因此有一个自适应尺寸模版匹配的想法:
1.载入模版图像,令它循环每次缩放一定比例,再进行模版匹配;
2.得到了在模版不同缩放比例下的多个匹配后的roi图像,对所有得到的roi图像与初始的模版图像一一进行相似度比较,从中选出相似度最高的匹配图像,还可以得到最佳匹配度的缩放因子(这是我这个算法的主要目的,得到缩放因子,从而可以判断目标物体与摄像头的距离,作为重要参数供项目使用)
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
//相似度匹配算法之灰度值方差匹配法:
double get_variance(Mat&a ,Mat&b)
{
if(a.rows!=b.rows || a.cols != b.cols ||a.channels()!=b.channels())
{
printf("not the same size!\n");
return 0;
}
//处理图像相似度
//1.求出每一行到灰度值均值,加入容器,作为特征值;
//2.求出灰度值总平均值与每行平均值的方差;
//3.行行比较与模版方差的接近程度
vector<double> variance_a;
vector<double> variance_b;
double var_a =0;
double var_b =0;
double sum_a =0;
double sum_b =0;
double mean_a;
double mean_b;
double sum_variance=0.0;
//将每行灰度值均值存入容器
for(int i=0; i<a.rows; i++){
mean_a =0;
mean_b =0;
for(int j=0; j< a.cols; j++){
mean_a += a.at<uchar>(i,j);
mean_b += b.at<uchar>(i,j);
}
mean_a /=(double)(a.rows*a.cols);
mean_b /=(double)(a.rows*a.cols);
sum_a += mean_a;
sum_b += mean_b;
variance_a.push_back(mean_a);
variance_b.push_back(mean_b);
}
//全图灰度值均值
mean_a = sum_a / (double)variance_a.size();
mean_b = sum_b / (double)variance_b.size();
//灰度值方差之差累加
for(int i =0; i<variance_a.size(); i++){
var_a = (variance_a[i] - mean_a)*(variance_a[i] - mean_a);
var_b = (variance_b[i] - mean_b)*(variance_b[i] - mean_b);
sum_variance += abs(var_a - var_b);
}
return sum_variance;
}
int main(int argc, const char * argv[])
{
//加载图像
Mat org = imread("------");
Mat my_template = imread("----------");
cvtColor(my_template, my_template, CV_RGB2GRAY);
cvtColor(org, org, CV_RGB2GRAY);
int best_index ; //存储最佳匹配的序号
double min_diff; //存储最小的方差值之差
Rect best_rect; //存储最佳的匹配框
//循环缩放,当前模版为最大尺寸,每次循环缩小5%,循环10次
for(int index =0; index <10; index ++)
{
//获得缩放后的模版
Mat temp_template = my_template.clone();
int new_rows =my_template.rows - index * 0.05*my_template.rows;
int new_cols =my_template.cols - index * 0.05 *my_template.cols;
resize(temp_template, temp_template, Size(new_cols,new_rows));
//模版匹配
Mat result;
result.create(org.dims,org.size,org.type());
matchTemplate(org, temp_template, result, 0);
//获取模版匹配得到的rect
Point minPoint;
Point maxPoint;
double *minVal ;
double *maxVal ;
minMaxLoc(result, minVal,maxVal,&minPoint,&maxPoint);
Rect rect(minPoint.x,minPoint.y,new_cols,new_rows);
//显示
Mat image_show = org.clone();
rectangle(image_show,rect,Scalar(0,0,255));
imshow("window",image_show);
waitkey(0);
//获取匹配部分的roi图像
Mat result_img = org.clone();
Mat result_img_roi =result_img(rect);
//相似度比较部分:
//比较相似度的算法很多,各有所长,这里用的是一个灰度值方差的相似度比较
//variance_diff表示灰度值方差,方差越小,相似度越高;
double variance_diff = get_variance(result_img_roi,temp_template) ;
//默认值为index=0时获取的值;方便与之后的值最比较
if(index == 0){
min_diff = variance_diff ;
best_index = index;
best_rect = rect;
}
//当前值与目前的最小方差做比较
if(variance_diff < min_diff ){
min_diff = variance_diff ;
best_index = index;
best_rect = rect;
}
}//for
Mat image_show = org.clone();
rectangle(org, best_rect, Scalar(0,255,0),3);
imshow("result",org);
waitKey(0);
}
上效果图:
大模版匹配小目标:
相似度比较结果:
大模版匹配大目标(模版图片没有更换):
后记:
这是我刚接触图像算法不久的时候写的,现在感觉有点幼稚,只是在正常模板匹配中加入了一个循环缩放。实际上有非常多更好的方式来达到相同的效果,比如深度学习中的目标检测。
后面推荐一些:
1.opencv-cascade 级联分类器
参考博客:关于opencv-CascadeClassifier(级联分类器)的初步认识
2.yolo目标检测
参考博客:
【darknet-yolo系列】在window10下安装GPU版的darknet
【darknet-yolo系列】yolov3 训练模型操作流程(包含所有资源下载)