SIFT特征(五)
来源:互联网 发布:黄耀明和林夕 知乎 编辑:程序博客网 时间:2024/06/05 19:11
经过前面四个章节的介绍,我们已经知道了如何从一幅图像中提取稳定的SIFT特征。接下来,我们将针对OpenCV中提供的FeatureDetector函数来说明具体如何实现SIFT特征检测,以及如何使用检测到的SIFT特征点进行目标识别。
对于目标检测问题,通常是给定模板图像(如book.jpg)(数据库,可包含一张或多张模板图像提取的SIFT特征点),希望从场景图片(如scene.jpg)(待检测场景)中检测出模板物体的位置及相应变换矩阵。场景中包含的模板物体通常由于相机参数的改变,光照、噪声、遮挡等因素的影响产生改变。而SIFT特征点对于这些改变恰好具有较好的稳定性。
基于SIFT特征点进行物体检测的基本步骤包括,
a. 分别提取模板图像和场景图像的SIFT特征点,及每个特征点生成的128维度的特征向量。
b. 特征点匹配。 对于场景图像提取的每个特征点,在模板图像中寻找最优匹配和次优匹配特征点,如果最优匹配与次优匹配的比值过大,则认为是错误匹配对。
c. 使用RANSAC计算两个图像平面变换的单应矩阵,筛选不符合当前变换的“局外”匹配点(outliers)。
6.1 特征点匹配
关于如何提取场景图像以模板图像的SIFT特征点及特征向量,这里不再赘述。
对于场景图像中检测的特征点,我们希望从模板图像中寻找可能的匹配点,可以通过特征向量的欧式距离计算最近邻的方法获得。但是对于场景中的背景特征点,极有可能是无法在数据库中找到对应点的,或者部分特征点有可能被错误匹配,那么就必须确定一种筛选方法将错误匹配点删除。
最简单的思路是确定一个距离阈值,凡是大于该阈值的匹配认为是错误匹配,但是所有特征点选择统一的阈值度量并不鲁棒。更优的决策是对于每个特征点,计算在数据库中的最优匹配和次优匹配(欧氏距离最小及排名第二小的匹配点)距离。通常情况下,正确匹配点的最优正确匹配应明显地区别于其他错误匹配,而对于错误匹配点,由于描述特征的多维度,在计算距离排序时可能存在前若干个距离相近且较大的错误匹配。所以可以通过计算每个特征点的最优匹配和次优匹配的距离比值,如果该比值小于某个给定的阈值(最优匹配距离要明显大于次优匹配距离),则认为该匹配点是被正确匹配的,否则被认为是错误匹配的。(详见代码part 2部分)
6.2 RANSAC计算单应矩阵筛选特征点
6.2.1 单应矩阵
在计算机视觉中,平面的单应性被定义为从一个二维平面到另一个二维平面的投影映射,包括平移、放射、尺度变换,试图找到一个投影矩阵,该投影矩阵可以将一个平面的所有点,匹配到另一个平面中。例如,照相机标定可以采用棋盘格平面来辅助完成,在这个过程中,从棋盘格平面到摄像机投影平面的映射反应的是平面的单应性;又比如,空间平面在两个摄像机下的投影图像之间具有点一一对应的关系(见下图),这种对应关系也可以通过单应矩阵来进行表达。
具体描述为,设
单应矩阵
注意,这里表示的是齐次坐标,其对应在图像上的坐标分别为
注意,单应矩阵的最后一个元素为常数值1,原因分析是,对于两点
可以看出,单应矩阵具有8个未知量,因此需要8个方程求解。而每一对点提供两个方程,所以至少需要4对点求解。每一对匹配点提供的方程是,
将这个方程重新组织一下,得到等价的矩阵形式表达为,
其中,
取四对不共线匹配点对,则可以将单应矩阵求解出来,且存在唯一解,而如果点对大于四对,可以通过最小二乘或SVD分解求解H。
在opencv中,提供求解两个点集之间的单应变换矩阵函数findhomography,函数原型为(具体参数描述见文档http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html?highlight=findhomography#cv2.findHomography)
Mat findHomography(InputArray srcPoints, InputArray dstPoints, int method=0, double ransacReprojThreshold=3, OutputArray mask=noArray() )
返回单应矩阵H,大小为
1.srcPoints和dstPoints分别表示两个对应点集,大小为
2.method可以选择基于RANSAC方法(method=CV_RANSAC),RANSAC通过多次随机选择四个匹配点来计算单应矩阵,最终返回的单应矩阵所对应误差最小,误差表示为,
3.ransacRojThreshold用在RANSAC方法中,表示被分类为“局内点”的阈值,即满足
4.status表示掩码,长度为输入匹配点对数量。如果为1表示对应点对为“局内点”,否则认为是“局外点”,即错误匹配点。我们可以根据status返回的信息判断匹配点保留与否。
单应矩阵参考链接:http://www.zhihu.com/question/23310855
6.3 C++源代码
#include "opencv2/highgui/highgui.hpp"#include "opencv2/imgproc/imgproc.hpp"#include "opencv2/nonfree/nonfree.hpp"#include "opencv2/nonfree/features2d.hpp"#include <opencv2/calib3d/calib3d.hpp>#include <iostream>using namespace cv;using namespace std;int main(int argc, char* argv[]){ //Part 1. 检测关键点,提取特征向量 initModule_nonfree();//初始化 Ptr<FeatureDetector> detector = FeatureDetector::create("SIFT");//create sift feature dector Ptr<DescriptorExtractor> descriptor_extractor = DescriptorExtractor::create("SIFT");//create feature vector extractor Ptr<DescriptorMatcher> descriptor_matcher = DescriptorMatcher::create("BruteForce");//create feature matcher if (detector.empty() || descriptor_extractor.empty()) return - 1; //读取图片 Mat img1 = imread("scene.jpg"); Mat img2 = imread("book.jpg"); //检测keypoints vector<KeyPoint> keypoint1, keypoint2; detector->detect(img1, keypoint1); detector->detect(img2, keypoint2); //根据keypoints计算128维度描述符 Mat descriptor1, descriptor2; descriptor_extractor->compute(img1, keypoint1, descriptor1); descriptor_extractor->compute(img2, keypoint2, descriptor2); //绘制keypoints Mat img_keypoints1, img_keypoints2; drawKeypoints(img1, keypoint1, img_keypoints1); drawKeypoints(img2, keypoint2, img_keypoints2); imshow("keypoints1", img_keypoints1); imshow("keypoints2", img_keypoints2); //Part 2. 根据sift描述符匹配,并初步筛选 vector<vector<DMatch>> knn_match; descriptor_matcher->knnMatch(descriptor1, descriptor2, knn_match, 2);//返回最优匹配和次优匹配 //正确匹配保证最优匹配和次优匹配之间具有较大差距,如果两者之间差距较小,则认为是错误匹配 const float minRatio = 1.f / 1.5f; vector<DMatch> matches; for (int i = 0; i < knn_match.size(); i++){ const DMatch& bestMatch = knn_match[i][0]; const DMatch& betterMatch = knn_match[i][1]; float r = bestMatch.distance / betterMatch.distance; if (r < minRatio) matches.push_back(bestMatch); } //绘制匹配结果 Mat img_knnMatches; drawMatches(img1, keypoint1, img2, keypoint2, matches, img_knnMatches); imshow("KNN matches", img_knnMatches); // Part 3:RANSAC计算单应变换矩阵排除错误匹配点对 vector<KeyPoint> alignedKps1, alignedKps2; for (int i = 0; i < matches.size(); i++){ alignedKps1.push_back(keypoint1[matches[i].queryIdx]); alignedKps2.push_back(keypoint2[matches[i].trainIdx]); } vector<Point2f> ps1, ps2; for (int i = 0; i < alignedKps1.size(); i++) ps1.push_back(alignedKps1[i].pt); for (int i = 0; i < alignedKps2.size(); i++) ps2.push_back(alignedKps2[i].pt); if (matches.size() < 4){ printf("Matches number is not enough"); return -1; } Mat status = Mat::zeros(matches.size(), 1, CV_8UC1); Mat H = findHomography(ps2, ps1, CV_FM_RANSAC, 3, status); vector<DMatch> RANSAC_matches; uchar *status_p; vector<DMatch>::const_iterator it_match = matches.begin(); for (int i = 0; i < matches.size(); i++){ status_p = status.ptr<uchar>(i); if (*status_p) RANSAC_matches.push_back(it_match[i]); } //绘制RANSAC筛选后的匹配结果 Mat img_RANSAC_matches; drawMatches(img1, keypoint1, img2, keypoint2, RANSAC_matches, img_RANSAC_matches); imshow("RANSAC_matches", img_RANSAC_matches); //检测目标物体在场景图像中的位置 Mat img_detect = img1.clone();; vector<Point2f> model_corners(4);//存储模板图像的四个角点 vector<Point2f> scene_corners(4); //Part 4:确定模板在场景中的位置 model_corners[0] = cvPoint(0, 0);//左上角为原点(0,0);x轴指向→;y轴指向↓ model_corners[1] = cvPoint(img2.cols, 0); model_corners[2] = cvPoint(img2.cols, img2.rows); model_corners[3] = cvPoint(0, img2.rows); perspectiveTransform(model_corners, scene_corners, H); line(img_detect, scene_corners[0], scene_corners[1], Scalar(0, 255, 0), 4); line(img_detect, scene_corners[1], scene_corners[2], Scalar(0, 255, 0), 4); line(img_detect, scene_corners[2], scene_corners[3], Scalar(0, 255, 0), 4); line(img_detect, scene_corners[3], scene_corners[0], Scalar(0, 255, 0), 4); imshow("Object detection", img_detect); cvWaitKey(0); return 0;}
6.3 结果展示
scene.jpg/ book.jpg SIFT特征点
KNN-match 筛选后匹配关系
RANSAC 筛选后匹配关系
模板位置检测,其中绿色框框出的表示被检测物体在场景中的位置
根据knn最优/次优比例筛选后,特征点匹配数目将至41对,RANSAC求解单应矩阵筛选特征点对后剩余38对特征点对。
- SIFT特征(五)
- SIFT特征(一)
- SIFT特征(二)
- SIFT特征(三)
- 局部特征(2)--SIFT特征
- 图像局部特征(五)--斑点检测之SIFT算法原理总结
- sift特征
- SIFT特征
- SIFT特征
- SIFT特征
- SIFT特征
- Sift特征
- sift特征
- SIFT特征
- SIFT特征
- SIFT特征
- SIFT特征
- Sift特征
- 第十五周项目1:阅读程序,领会STL用法(2)
- WEB-INF\classes文件下面没有java项目的java字节码文件
- iOS开发-给TableView或者CollectionView的cell添加简单动画,很方便,直接粘代码即可
- 171ExcelSheetColumnNumber
- 5 款 Photoshop的替代品,可以在Linux上运行
- SIFT特征(五)
- C Tricks(十二)—— 获取字符数组的末尾元素
- 培训讲解——Android studio模拟器创建
- LeetCode-257.Binary Tree Paths
- Android实现图片滚动控件,含页签功能,让你的应用像淘宝一样炫起来
- QT中QT Widgets Application中QWidget、QDialog及QMainWindow的区别
- ListView局部刷新的问题
- 脚本获取场景中游戏体
- Android CardView 控件学习