直接使用surf提取,匹配的效果还是相当糟糕的,如果我们拿着这样子的匹配结果去实现图像拼接或者物体追踪,效果肯定是极差的。所以我们需要进一步筛选匹配点,来获取优秀的匹配点,这就是所谓的“去粗取精”。这里我们采用了Lowe’s算法来进一步获取优秀匹配点。
为了排除因为图像遮挡和背景混乱而产生的无匹配关系的关键点,SIFT的作者Lowe提出了比较最近邻距离与次近邻距离的SIFT匹配方式:取一幅图像中的一个SIFT关键点,并找出其与另一幅图像中欧式距离最近的前两个关键点,在这两个关键点中,如果最近的距离除以次近的距离得到的比率ratio少于某个阈值T,则接受这一对匹配点。因为对于错误匹配,由于特征空间的高维性,相似的距离可能有大量其他的错误匹配,从而它的ratio值比较高。显然降低这个比例阈值T,SIFT匹配点数目会减少,但更加稳定,反之亦然。
Lowe推荐ratio的阈值为0.8,但作者对大量任意存在尺度、旋转和亮度变化的两幅图片进行匹配,结果表明ratio取值在0. 4~0. 6 之间最佳,小于0. 4的很少有匹配点,大于0. 6的则存在大量错误匹配点,所以建议ratio的取值原则如下:
ratio=0. 4:对于准确度要求高的匹配;
ratio=0. 6:对于匹配点数目要求比较多的匹配;
ratio=0. 5:一般情况下。
#include "highgui/highgui.hpp"
#include "opencv2/nonfree/nonfree.hpp"
#include "opencv2/legacy/legacy.hpp"
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
Mat image01 = imread("C:\\Users\\Administrator\\Desktop\\1_A.jpg", 1);
Mat image02 = imread("C:\\Users\\Administrator\\Desktop\\1_B.jpg", 1);
imshow("p1", image01);
imshow("p2", image02);
//灰度图转换
Mat image1, image2;//声明表示图像的向量
cvtColor(image01, image1, CV_RGB2GRAY);//将彩色图像灰度化,输出为image1
cvtColor(image02, image2, CV_RGB2GRAY);
//提取特征点
SurfFeatureDetector surfDetector(2000); //构造SURF特征检测器,Hessian矩阵阈值,在这里调整精度,值越大点越少,越精准
vector<KeyPoint> keyPoint1, keyPoint2;//特征点的向量
surfDetector.detect(image1, keyPoint1);//检测SURF特征
surfDetector.detect(image2, keyPoint2);
//特征点描述,为下边的特征点匹配做准备
SurfDescriptorExtractor SurfDescriptor;//构造SURF描述子提取器
Mat imageDesc1, imageDesc2;//声明表示图像的向量
SurfDescriptor.compute(image1, keyPoint1, imageDesc1);//提取SURF描述子
SurfDescriptor.compute(image2, keyPoint2, imageDesc2);
//获得匹配特征点
FlannBasedMatcher matcher;//构造匹配器
vector<vector<DMatch> > matchePoints;//匹配点的向量
vector<DMatch> GoodMatchePoints;//更好匹配点的向量
vector<Mat> train_desc(1, imageDesc1);
matcher.add(train_desc);
matcher.train();
matcher.knnMatch(imageDesc2, matchePoints, 2);//featuresA和featuresB是两幅图片的特征向量,该函数的返回值是一个DMatch,DMatch是一个匹配之后的集合。
cout << "total match points: " << matchePoints.size() << endl;//一共的匹配点数
// Lowe's algorithm,获取优秀匹配点
for (int i = 0; i < matchePoints.size(); i++)
{
if (matchePoints[i][0].distance < 0.6 * matchePoints[i][1].distance)//如果最近的距离除以次近的距离得到的比率ratio少于某个阈值T,则接受这一对匹配点
{
GoodMatchePoints.push_back(matchePoints[i][0]);
}
}
Mat first_match;
drawMatches(//使用函数 drawMatches来绘制检测到的匹配点
image02, keyPoint2, //第一幅图像及其特征点
image01, keyPoint1, //第二幅图像及其特征点
GoodMatchePoints, //匹配结果
first_match);//生成的图像
imshow("first_match ", first_match);
imwrite("C:\\Users\\Administrator\\Desktop\\first_match.jpg ", first_match);
waitKey();
return 0;
}
输入图像:
输出图像:
匹配点明显减少:由特征点匹配的效果来看,现在的特征点匹配应该是非常精准了,因为我们已经把不合格的匹配点统统移除出去了。