ORB detector 使用 FAST detector 和 BRIEF descriptor 基本思路。在介绍 ORB 之前,首先对 FAST 与 BRIEF 进行说明。
1 FAST
FAST(Featrues from Accelerated Segment Test),其基本思想是比较当前点与周边点差异,当周边有连续不少于一半的点均比中间点亮或者暗,则认为该点为一个特征点。其中,亮或暗的定义为:
1)当 时,周边点比中间点亮;
2)当 时,周边点比中间点暗;
3)当 时,周边点与中间点相似;
使用以上定义,可以迅速找到图像中候选特征点。
由于需要满足不少于一半的连续周边点亮于或暗于中间点,可以首先检测水平与垂直方向上四个点,当少于两个连续点满足条件,则该点一定不是候选特征点。如此可以提升计算效率。
当完成候选特征点扫描后,会发现存在许多临近特征点,可以使用如下评分进行非极大值抑制:
;
以上即为 FAST 的基本思想,opencv 实现在 cv::FastFeatureDetector 中,参数 threshold 定义了亮或暗,nonmaxSuppression 确定是否排除临近点。
2 BRIEF
BRIEF 对特征点生成描述特征向量。在 SIFT 与 SURF 中均使用了块特征描述方案,使用不同小块的方向梯度直方图构成特征向量。BRIEF 使用点特征描述特征点,基本思想为:
1)在特征点区域内随机生成 N 个点对,这N个点对生成方式有很多种,但一旦生成,对于所有特征点描述均使用相同的点对模式;
2)由于需要对孤立点进行比较,所以首先平滑图像以抑制噪声;
3)构造 N 位向量,第 k 个点对生成第 k 位向量,当点对中前一个点大于后一个点,其值为 1,反之为 0;
opencv 实现在 cv::BriefDescriptorExtractor 中,参数 bytes 确定特征点描述向量长度为 bytes * 8。
结合 FAST 与 BRIEF,可以实现类似 SIFT 与 SURF 的功能,以下给出简单使用代码:
1 cv::FastFeatureDetector detector(20); 2 std::vector<cv::KeyPoint> keypoints1, keypoints2; 3 detector.detect(img1, keypoints1); 4 detector.detect(img2, keypoints2); 5 6 cv::BriefDescriptorExtractor brief; 7 cv::Mat descriptors1, descriptors2; 8 brief.compute(img1, keypoints1, descriptors1); 9 brief.compute(img2, keypoints2, descriptors2); 10 11 // 不同于SIFT与SURF,这里使用汉明距离 12 cv::BFMatcher matcher(cv::NORM_HAMMING); 13 std::vector<DMatch> matches; 14 matcher.match(descriptors1, descriptors2, matches);
其匹配结果如下:
3 ORB
ORB 主要思想如下:
1)使用 FAST 提取候选特征点;
2)为了克服 FAST 可能产生的边缘响应,使用 Harris corner measure 保留角点响应,剔除边缘响应(边缘响应不利于匹配);
3)按以上方法在不同层级图像金字塔上搜索候选特征点;
4)使用归一化图像描述特征点方向 ;
5)使用特征点方向生成 BRIEF 特征点描述向量;
6)使用汉明距离计算特征点之间相似度;
opencv 提供 cv::ORB 实现特征点提取与描述,其构造函数参数如下:
nfeatures 表示需要提取的特征点数量;
scaleFactor,nlevels 为图像金字塔参数;
firstLevel 表示从第几层开始搜索特征点,一般为 0;
patchSize 确定特征点尺寸,edgeThreshold 应不小于 patchSize,该参数忽略边界特征点;
scoreType 确定使用 FAST 评分机制或者 Harris corner 评分机制;
WTA_K 控制比较点个数,当为 2 时,即为 FAST 对点对比较方式;
以下给出简单使用代码:
1 cv::Mat img1 = cv::imread("a.bmp", cv::IMREAD_GRAYSCALE); 2 cv::Mat img2 = cv::imread("b.bmp", cv::IMREAD_GRAYSCALE); 3 4 std::vector<cv::KeyPoint> keypoints1, keypoints2; 5 cv::Mat descriptors1, descriptors2; 6 7 cv::ORB orb(100, 1.5, 4); 8 orb.operator()(img1, cv::noArray(), keypoints1, descriptors1); 9 orb.operator()(img2, cv::noArray(), keypoints2, descriptors2); 10 11 cv::BFMatcher matcher(cv::NORM_HAMMING); 12 std::vector<DMatch> matches; 13 matcher.match(descriptors1, descriptors2, matches); 14 15 cv::Mat img_matches; 16 cv::drawMatches(img1, keypoints1, img2, keypoints2, matches, img_matches); 17 cv::imwrite("c.jpg", img_matches); 18 19 double min_dist = 100; 20 21 for (int i = 0; i < matches.size(); i++) 22 { 23 double dist = matches[i].distance; 24 if (dist < min_dist) min_dist = dist; 25 26 } 27 28 // Draw only "good" matches (i.e. whose distance is less than 2*min_dist, 29 // or a small arbitary value ( 0.02 ) 30 std::vector< DMatch > good_matches; 31 32 for (int i = 0; i < matches.size(); i++) 33 { 34 if (matches[i].distance <= max(2 * min_dist, 0.02)) 35 { 36 good_matches.push_back(matches[i]); 37 } 38 } 39 40 cv::drawMatches(img1, keypoints1, img2, keypoints2, good_matches, img_matches); 41 cv::imwrite("d.jpg", img_matches)
其匹配结果如下:
参考资料 Learning OpenCV 3 Adrian Kaehler & Gary Bradski