SIFT的全称是Scale Invariant Feature Transform,尺度不变特征变换,由加拿大教授David G.Lowe提出的。SIFT特征对旋转、尺度缩放、亮度变化等保持不变性,是一种非常稳定的局部特征。
有关sift匹配的理论问题可以参考这篇文章:
SIFT特征详解 - Brook_icv - 博客园
https://www.cnblogs.com/wangguchangqing/p/4853263.html
下面我们直接进行基于opencv的算法实现
头文件
#include <opencv2/opencv.hpp>
#include <opencv2/nonfree/features2d.hpp>
#include <iostream>
载入图像并转为灰度图
Mat templateImage , templateImage_gray;
templateImage = imread("map.png");
cvtColor(templateImage, templateImage_gray, CV_BGR2GRAY);
Mat srcImage, srcImage_gray;
srcImage = imread("test1.png");
cvtColor(srcImage, srcImage_gray, CV_BGR2GRAY);
调用特征检测子公用接口和特征描述子提取公用接口
SiftFeatureDetector featureDetector;
SiftDescriptorExtractor featureExtractor;
检测SIFT关键点、提取训练图像描述符
vector<KeyPoint> template_keyPoint;
Mat templateDescription;
featureDetector.detect(templateImage_gray, template_keyPoint);
featureExtractor.compute(templateImage_gray, template_keyPoint, templateDescription);
vector<KeyPoint> src_keyPoint;
Mat srcDescriptor;
featureDetector.detect(srcImage_gray, src_keyPoint);
featureExtractor.compute(srcImage_gray, src_keyPoint, srcDescriptor);
/*
在keypoint定义中包含了pt(0,0), size(0), angle(-1), response(0), octave(0), class_id(-1)
pt:为关键点的坐标
size:为有意义的关键点邻域的直径
response:选择最强关键点的响应。可用于进一步分类或再取样
octave:提取关键点的尺度(金字塔层)
class_id:对象类(如果关键字需要向它们所属的对象聚集)
*/
/*
void FeatureDetector::detect(const Mat& image, vector& keypoints, const Mat& mask) const (C++ function, in 特征检测子公用接口)
image: 为输入图像
keypoints: 为输出的关键特征点
void DescriptorExtractor::compute(const Mat& image, vector& keypoints, Mat& descriptors) const (C++ function, in 特征描述子提取公用接口)
image: 为输入图像
keypoints:为输入的关键特征点
descriptors:为输出的特征描述子
*/
使用特征向量临近匹配
FlannBasedMatcher matcher;
vector<DMatch> matches;
matcher.match(templateDescription, srcDescriptor, matches);
/*
FlannBasedMatcher为特征向量临近匹配,算法更快但是找到的是最近邻近似匹配
BFMatcher为暴力匹配法,总是尝试所有可能的匹配,从而使得它总能够找到最佳匹配
*/
关于匹配方法可以参考这篇文章:
sift算法特征点如何匹配? - 知乎
https://www.zhihu.com/question/23371175
计算向量距离的最大值与最小值
float max_dist = 0.0, min_dist = 100.0;
for (int i = 0; i < templateDescription.rows; i++)
{
if (matches[i].distance > max_dist)
{
max_dist = matches[i].distance;
}
if (matches[i].distance < min_dist)
{
min_dist = matches[i].distance;
}
}
得到距离小于V倍最小距离的匹配
vector<DMatch> goodMatches;
for (int i = 0; i < matches.size(); i++)
{
if (matches[i].distance <= 2*min_dist)
{
goodMatches.push_back(matches[i]);
}
}
下面开始绘制匹配信息
/*绘制匹配点并显示窗口*/
Mat dstImage;
drawMatches(srcImage, src_keyPoint, templateImage, template_keyPoint, goodMatches, dstImage);
/*定义两个局部变量*/
vector<Point2f> obj;
vector<Point2f> scene;
/*从匹配成功的匹配对中获取关键点*/
for (unsigned int i = 0; i < goodMatches.size(); i++)
{
obj.push_back(src_keyPoint[goodMatches[i].queryIdx].pt);
scene.push_back(template_keyPoint[goodMatches[i].trainIdx].pt);
}
/*
queryIdx:是测试图像的特征点描述符(descriptor)的下标,同时也是描述符对应特征点(keypoint)的下标。
trainIdx:是样本图像的特征点描述符的下标,同样也是相应的特征点的下标。
*/
/*计算多个二维点对之间的最优单映射变换矩阵 H(3行x3列)*/
Mat H = findHomography(obj, scene, CV_RANSAC);
/*
Mat findHomography( InputArray srcPoints, InputArray dstPoints,int method=0, double ransacReprojThreshold=3, OutputArray mask=noArray());
srcPoints:源平面中点的坐标矩阵
dstPoints:目标平面中点的坐标矩阵
method:0 - 利用所有点的常规方法 RANSAC-基于RANSAC的鲁棒算法 LMEDS - 最小中值鲁棒算法 RHO - PROSAC-基于PROSAC的鲁棒算法
maxIters RANSAC算法的最大迭代次数,默认值为2000。
confidence 可信度值,取值范围为0到1.
*/
/*从待测图片中获取角点*/
vector<Point2f> obj_corners(4);
obj_corners[0] = cvPoint(0, 0);
obj_corners[1] = cvPoint(srcImage.cols, 0);
obj_corners[2] = cvPoint(srcImage.cols, srcImage.rows);
obj_corners[3] = cvPoint(0, srcImage.rows);
vector<Point2f> scene_corners(4);
/*进行透视变换*/
perspectiveTransform(obj_corners, scene_corners, H);
/*
void perspectiveTransform(InputArray src, OutputArray dst, InputArray m );
src: 输入图像
dst:输出图像
m: 变换矩阵
*/
/*输出匹配点坐标*/
int locx = (scene_corners[0].x + scene_corners[1].x + scene_corners[2].x + scene_corners[3].x) / 4;
int locy = (scene_corners[0].y + scene_corners[1].y + scene_corners[2].y + scene_corners[3].y) / 4;
/*画实心点*/
Point p(locx + srcImage.cols, locy);
circle(dstImage, p, 5, Scalar(0, 0, 255), -1);
/*绘制出角点之间的直线*/
line(dstImage, scene_corners[0] + Point2f(static_cast<float>(srcImage.cols), 0), scene_corners[1] + Point2f(static_cast<float>(srcImage.cols), 0), Scalar(255, 0, 123), 4);
line(dstImage, scene_corners[1] + Point2f(static_cast<float>(srcImage.cols), 0), scene_corners[2] + Point2f(static_cast<float>(srcImage.cols), 0), Scalar(255, 0, 123), 4);
line(dstImage, scene_corners[2] + Point2f(static_cast<float>(srcImage.cols), 0), scene_corners[3] + Point2f(static_cast<float>(srcImage.cols), 0), Scalar(255, 0, 123), 4);
line(dstImage, scene_corners[3] + Point2f(static_cast<float>(srcImage.cols), 0), scene_corners[0] + Point2f(static_cast<float>(srcImage.cols), 0), Scalar(255, 0, 123), 4);
上面的代码我已经打包发在csdn上了
https://download.csdn.net/download/weixin_42521239/11149276
如果没有积分的小伙伴也可以在评论里留下你的邮箱,我发给你。
手机扫一扫
移动阅读更方便
你可能感兴趣的文章