模板匹配是一种用于在源图像S中寻找定位给定目标图像T(即模板图像)的技术。其原理很简单,就是通过一些相似度准则来衡量两个图像块之间的相似度Similarity(S,T)。
模板匹配的工作方式跟直方图的反向投影基本一样,大致过程是这样的:通过在输入图像上滑动图像块对实际的图像块和输入图像进行匹配。假设我们有一张100x100的输入图像,有一张10x10的模板图像,查找的过程是这样的:
(1)从输入图像的左上角(0,0)开始,切割一块(0,0)至(10,10)的临时图像;
(2)用临时图像和模板图像进行对比,对比结果记为c;
(3)对比结果c,就是结果图像(0,0)处的像素值;
(4)切割输入图像从(0,1)至(10,11)的临时图像,对比,并记录到结果图像;
(5)重复(1)~(4)步直到输入图像的右下角。
模板匹配方法的优缺点:
优点:简单、直接
缺点:不具有旋转不变性、不具有尺度不变性
代码如下:
int main()
{
Mat img, templ, result;
img = imread("match_dst.jpg");
templ = imread("match_src.jpg");
/*img = imread("1.jpg");
templ = imread("2.jpg");*/
int result_cols = img.cols - templ.cols + 1;
int result_rows = img.rows - templ.rows + 1;
result.create(result_cols, result_rows, CV_32FC1);
//进行匹配和标准化
matchTemplate(img, templ, result, CV_TM_SQDIFF_NORMED);
normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat());
double minVal; //匹配最小值
double maxVal; //匹配最大值
Point maxLoc; //匹配最大值的位置坐标
Point minLoc; //匹配最小值的位置坐标
Point matchLoc;
//通过函数minMaxLoc定位最匹配的位置
minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, Mat());
cout << "匹配度:" << minVal << endl;
cout << "匹配度最小值的坐标" << minLoc.x << "," << minLoc.y << endl;
/*Mat H = findHomography(templ,img, CV_RANSAC);
Mat trans1;
perspectiveTransform(img, trans1, H);
imshow("trans1", trans1);*/
matchLoc = minLoc;
rectangle(img, matchLoc, Point(matchLoc.x + templ.cols, matchLoc.y + templ.rows), Scalar(0, 255, 0), 2, 8, 0);
imshow("img", img);
imshow("templ", templ);
//imshow("result", result);
waitKey(0);
return 0;
}
效果如下:
但是,在该方法中,最佳匹配点位于大图匹配位置的左上部分,且只能用矩形框选匹配位置。若小图视角变化,框选无效。
利用Surf算法进行图像匹配其一般流程为:检测物体特征点->计算特征点描述子->使用BurteForceMatcher或FLANN进行特征点匹配->匹配到的特征点进行透视变换findHomography()->透视矩阵变换perspectiveTransform()->绘制匹配物体轮廓。
#include
#include
#include
#include
#include
#include
#include
using namespace std;
using namespace cv;
int main()
{
Mat image_object = imread("template.png", IMREAD_GRAYSCALE);
Mat image_scene = imread("image.png", IMREAD_GRAYSCALE);
//检测特征点
const int minHessian = 400;
SurfFeatureDetector detector(minHessian);
vectorkeypoints_object, keypoints_scene;
detector.detect(image_object, keypoints_object);
detector.detect(image_scene, keypoints_scene);
//计算特征点描述子
SurfDescriptorExtractor extractor;
Mat descriptors_object, descriptors_scene;
extractor.compute(image_object, keypoints_object, descriptors_object);
extractor.compute(image_scene, keypoints_scene, descriptors_scene);
//使用FLANN进行特征点匹配
FlannBasedMatcher matcher;
vectormatches;
matcher.match(descriptors_object, descriptors_scene, matches);
//计算匹配点之间的最大和最小距离
double max_dist = 0;
double min_dist = 100;
for (int i = 0; i < descriptors_object.rows; i++)
{
double dist = matches[i].distance;
if (dist < min_dist)
{
min_dist = dist;
}
else if (dist > max_dist)
{
max_dist = dist;
}
}
printf("Max dist: %f \n", max_dist);
printf("Min dist: %f \n", min_dist);
//绘制好的匹配点
vectorgood_matches;
for (int i = 0; i < descriptors_object.rows; i++)
{
if (matches[i].distance<2 * min_dist)
{
good_matches.push_back(matches[i]);
}
}
Mat image_matches;
drawMatches(image_object, keypoints_object, image_scene, keypoints_scene, good_matches, image_matches,
Scalar::all(-1), Scalar::all(-1), vector(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
//定位好的匹配点
vector obj;
vector scene;
for (int i = 0; i < good_matches.size(); i++)
{
//DMathch类型中queryIdx是指match中第一个数组的索引,keyPoint类型中pt指的是当前点坐标
obj.push_back(keypoints_object[good_matches[i].queryIdx].pt);
scene.push_back(keypoints_scene[good_matches[i].trainIdx].pt);
}
Mat H = findHomography(obj, scene, CV_RANSAC);
vector obj_corners(4), scene_corners(4);
obj_corners[0] = cvPoint(0, 0);
obj_corners[1] = cvPoint(image_object.cols, 0);
obj_corners[2] = cvPoint(image_object.cols, image_object.rows);
obj_corners[3] = cvPoint(0, image_object.rows);
perspectiveTransform(obj_corners, scene_corners, H);
//绘制角点之间的直线
line(image_matches, scene_corners[0] + Point2f(image_object.cols, 0),
scene_corners[1] + Point2f(image_object.cols, 0), Scalar(0, 0, 255), 2);
line(image_matches, scene_corners[1] + Point2f(image_object.cols, 0),
scene_corners[2] + Point2f(image_object.cols, 0), Scalar(0, 0, 255), 2);
line(image_matches, scene_corners[2] + Point2f(image_object.cols, 0),
scene_corners[3] + Point2f(image_object.cols, 0), Scalar(0, 0, 255), 2);
line(image_matches, scene_corners[3] + Point2f(image_object.cols, 0),
scene_corners[0] + Point2f(image_object.cols, 0), Scalar(0, 0, 255), 2);
//输出图像
namedWindow("匹配图像", WINDOW_AUTOSIZE);
imshow("匹配图像", image_matches);
waitKey(0);
return 0;
}
程序说明:
在定位匹配点中用到了DMatch的queryIdx、trainIdx成员变量和keyPoint的成员变量pt:
cv::DMatch::DMatch(int queryIdx, //在对描述子匹配时,第一组特征点的索引
int trainIdx, //在对描述子匹配时,第二组特征点的索引
int imgIdx, //多个图像中图像的索引
float distance //两个特征向量间的欧氏距离,越小表明匹配度越高 )
对于DrawMatch函数:
void drawMatches( const Mat& img1, const vector& keypoints1,
const Mat& img2, const vector& keypoints2,
const vector >& matches1to2, Mat& outImg,
const Scalar& matchColor=Scalar::all -1),
const Scalar& singlePointColor=Scalar::all(-1),
const vector >& matchesMask=vector >(),
int flags=DrawMatchesFlags::DEFAULT );
/其中参数如下:
img1 – 源图像1
keypoints1 –源图像1的特征点.
img2 – 源图像2.
keypoints2 – 源图像2的特征点
matches1to2 – 源图像1的特征点匹配源图像2的特征点[matches[i]] .
outImg – 输出图像具体由flags决定.
matchColor – 匹配的颜色(特征点和连线),若matchColor==Scalar::all(-1),颜色随机.
singlePointColor – 单个点的颜色,即未配对的特征点,若matchColor==Scalar::all(-1),颜色随机.
matchesMask – Mask决定哪些点将被画出,若为空,则画出所有匹配点.
* flags – Fdefined by DrawMatchesFlags.
效果图:
FAsT-Match是在2D仿射变换下用于近似模板匹配的快速算法,其最小化绝对差分和(SAD)误差测量。 通过图像平滑度的密度对其进行采样。 对于每个可能的变换,使用次线性算法来近似SAD误差, 同时使用分支定界法进一步加速算法。 由于已知图像是分段平滑的,因此结果是具有近似保证的实际仿射模板匹配算法。
算法流程:
代码地址
结果如下: