本博客内容来源于网络以及其他书籍,结合自己学习的心得进行重编辑,因为看了很多文章不便一一标注引用,如图片文字等侵权,请告知删除。
前言
休息了一天,雨天去了一次颐和园,不幸,我感冒了。所以,如果本文有任何常识性问题,请不要怀疑作者的知识水平,而是脑子有点烧糊涂了。前面我们描述完了sift以及surf算法,本片我们描述一下orb特征检测算法。
今天一看,竟然已经写了15篇文章了,也为自己的坚持点个赞 ~ (≧▽≦)/ ~ 。的的确确这段时间,对一些算法的理解也有了更深的层次,再接再厉,今晚奖励给我家猫奖励一罐小鱼干。
ORB简介
ORB 全称:Oriented FAST and Rotated BRIEF,是一种快速特征点提取和描述的算法,发布于“ORB:An Efficient Alternative to SIFT or SURF” 论文中。从名字中,我们可以看出是由两部分构成,Oriented FAST 和 Rotated BRIEF,这也以最简单的语言描述了ORB算法。ORB算法分为两部分,分别是特征点提取和特征点描述。特征提取是由FAST算法(前面已经学过)发展来的,特征点描述是根据BRIEF特征描述算法改进的。
ORB算法最大的特点就是计算速度快,计算时间大概只有SIFT的1%,SURF的10%,这主要是因为使用了FAST来加速了特征点的提取。再次是使用BRIEF算法计算描述子,该描述子特有的2进制串的表现形式不仅节约了存储空间,而且大大缩短了匹配的时间。
当然ORB算法也有一些缺点,比如尺度变换的应对能力比较低。接下来我们来看ORB的流程,来分析一下ORB特征提取的优缺点。
ORB 算法流程
1. 关键点提取
有关FAST算法,可以跳转去看另一篇来详细描述FAST特征点的笔记:FAST角点学习笔记中查看。本节主要描述ORB算法中对FAST算法的改进。
ORB对FAST的改进或者拓展,主要是为其增加了其尺度不变性以及旋转不变性。接下里来看一看怎么实现的。
1. 提取FAST特征点
通过FAST算法提取出FAST特征点,过程不在详细描述,这样我们就找到了一张图片的基本的关键点。
2. 建立金字塔
ORB实现尺度不变性,也是通过图像金字塔来实现的。ORB论文本身并没有解决尺度不变性,而是在opencv实现中添加了这部分,具体做法为设置一个比例因子scale(opencv默认取1.2)和金字塔的层数n(通常取8)。将原图像按比例因子缩小成n幅图像。缩放后的图像为:I’= I / scale^k (k=1,2,…, n)。n幅不同比例的图像提取特征点总和作为这幅图像的FAST特征点。
当然可以通过采用不同的高斯核进行高斯模糊来建立金字塔,但是时间成本就很高了。
3. 定义特征点方向
ORB实现旋转不变性,也是通过确定一个特征点的方向来实现的,我们来看看ORB是怎么来确定特征点的方向的?
ORB的论文中提出了一种利用灰度质心法来解决这个问题,通过计算一个矩来计算特征点以r为半径范围内的质心,特征点坐标到质心形成一个向量作为该特征点的方向。我们来看看具体怎么实现灰度质心法。
一个图像块(比如5x5的图像块中),对应的2x2的矩的元素表达为:x,y分别为坐标值,I(x,y)为像素值
而该图像窗口的质心就是:其实灰度的质心就是对所有的位置进行加权,权重就是像素值在整个图像中像素值之和的比例。
那么特征点与质心的夹角定义为FAST特征点的方向:也就有了特征点的方向,继而实现旋转不变性。
通过上述步骤我们找到了所有的特征点,并计算出了特征点的方向,下面就看一下怎么描述这些特征点。
2. 关键点描述
ORB选择了BRIEF作为特征描述方法,并对其进行改进使其加上旋转不变性并增加其可区分性,首先我们先看看BRIEF描述子。
-
BRIEF描述子
BRIEF算法计算出来的是一个二进制串的特征描述符。它是在每一个特征点的邻域内,选择n对像素点pi、qi(i=1,2,…,n)。然后比较每个点对的灰度值的大小。如果I(pi)> I(qi),则生成二进制串中的1,否则为0。所有的点对都进行比较,则生成长度为n的二进制串。一般n取128、256或512,通常取256。
为了增强抗噪性,一般会先对图像进行高斯平滑。ORB算子采用5x5的子窗口进行平滑。
那么这n个点对如何选取呢?
在点周围选取点对(p,q)的方法有以下5种:
- 在图像块内平均采样;
- p和q都符合(0,S2/25)的高斯分布;
- p符合(0,S2/25)的高斯分布,而q符合(0,S2/100)的高斯分布;
- 在空间量化极坐标下的离散位置随机采样;
- 把p固定为(0,0),q在周围平均采样。
BRIEF作者采用的是第二种。而ORB作者没有选择以上任意方式,而是一种新的方式,我们在下面再说。
BRIEF流程简单实时性较好,论文中生成512个描述子用时8.18ms,并且其描述子是二进制码,其匹配也比较快。但是,当BRIEF对于旋转过大时,比如超过30度时,匹配正确率迅速下降直到45度时为0。所以需要增加其描述子的旋转不变性。
-
BRIEF算法改进
在描述基础BRIEF算法时,我们提出了两个问题,一个是增加其旋转不变性,一个是选点方式。
-
steered BRIEF 增加其旋转不变性
steered BRIEF加入了旋转不变性,但同时特征描述量的可区分行就下降了,所以就有了ORB作者提出的rBRIEF。
-
rBRIEF 增加其可区分性
我们在上边说过ORB没有使用BRIEF 5种选取点对方法中的任意一种,那么ORB用的是什么方式?
ORB使用统计学习的方法来重新选择点对集合,目的是增大其特征描述量的可区分行。这里我们先不解释其具体的实验流程(主要是我还没有完全理解),但是通过其实验,选出来的点对对特征点描述的区分度变高了。
至此ORB算法计算完毕。
OpenCV ORB特征效果展示[代码]
#include <opencv2/opencv.hpp>
#include <iostream>
#include <opencv2/xfeatures2d.hpp>
#include <opencv2/features2d/features2d.hpp>
void extracte_orb(cv::Mat input,std::vector<cv::KeyPoint> &keypoint,cv::Mat &descriptor){
cv::Ptr<cv::ORB> f2d = cv::ORB::create(500);
f2d->detect(input,keypoint);
cv::Mat image_with_kp;
f2d->compute(input,keypoint,descriptor);
cv::drawKeypoints(input, keypoint, image_with_kp, cv::Scalar::all(-1),cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
cv::imwrite("orb"+std::to_string(random())+".png",image_with_kp);
}
void match_two_image(cv::Mat image1,cv::Mat image2, std::vector<cv::KeyPoint> keypoint1,std::vector<cv::KeyPoint> keypoint2,cv::Mat descriptor1,cv::Mat descriptor2){
cv::BFMatcher matcher(cv::NORM_HAMMING);
std::vector<cv::DMatch> matches;
matcher.match(descriptor1,descriptor2, matches);
cv::Mat good_matches_image;
cv::drawMatches(image1, keypoint1, image2, keypoint2,
matches, good_matches_image, cv::Scalar::all(-1), cv::Scalar::all(-1),
std::vector<char>(), cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
cv::imwrite("good_matches_image.png",good_matches_image);
{
std::vector <cv::KeyPoint> RAN_KP1, RAN_KP2;
std::vector<cv::Point2f> keypoints1, keypoints2;
for (int i = 0; i < matches.size(); i++) {
keypoints1.push_back(keypoint1[matches[i].queryIdx].pt);
keypoints2.push_back(keypoint2[matches[i].trainIdx].pt);
RAN_KP1.push_back(keypoint1[matches[i].queryIdx]);
RAN_KP2.push_back(keypoint2[matches[i].trainIdx]);
}
std::vector<uchar> RansacStatus;
cv::findFundamentalMat(keypoints1, keypoints2, RansacStatus, cv::FM_RANSAC);
std::vector <cv::KeyPoint> ransac_keypoints1, ransac_keypoints2;
std::vector <cv::DMatch> ransac_matches;
int index = 0;
for (size_t i = 0; i < matches.size(); i++)
{
if (RansacStatus[i] != 0)
{
ransac_keypoints1.push_back(RAN_KP1[i]);
ransac_keypoints2.push_back(RAN_KP2[i]);
matches[i].queryIdx = index;
matches[i].trainIdx = index;
ransac_matches.push_back(matches[i]);
index++;
}
}
cv::Mat after_ransac_sift_match;
cv::drawMatches(image1, ransac_keypoints1, image2, ransac_keypoints2,
ransac_matches, after_ransac_sift_match, cv::Scalar::all(-1), cv::Scalar::all(-1),
std::vector<char>(), cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
cv::imwrite("after_ransac_orb_match.png",after_ransac_sift_match);
}
}
int main(int argc, char *argv[])
{
cv::Mat image1 = cv::imread(argv[1]);
cv::Mat image2 = cv::imread(argv[2]);
std::vector<cv::KeyPoint> keypoint1,keypoint2;
cv::Mat descriptor1, descriptor2;
extracte_orb(image1,keypoint1,descriptor1);
extracte_orb(image2,keypoint2,descriptor2);
match_two_image(image1,image2,keypoint1,keypoint2,descriptor1,descriptor2);
return 0;
}
因为orb难以应对我们类似SIFT或surf那么大的仿射变换,所以不得不换了仿射变换小的照片
原图1 orb识别效果 | 原图2 orb识别效果 |
---|---|
初步匹配效果 | ransac后匹配效果 |
总结
总体来说ORB在上述实验实例中的效果还是很好的。我们已经讲完SIFT,SURF还有ORB,我们来对比一下这三个算法。
计算速度: ORB>>SURF>>SIFT(各差一个量级)
旋转鲁棒性: SURF>ORB~SIFT(~表示差不多)
模糊鲁棒性: SURF>ORB~SIFT
尺度变换鲁棒性: SURF>SIFT>ORB(ORB尺度变换性很弱)
在日常应用中,有SURF基本就不用考虑SIFT,SURF基本就是SIFT的全面升级版,当然也有其他SIFT的改进版比如Affine SIFT的效果就要比SUFR要好更多,但是计算时间也有延长,而ORB的强点在于计算时间。ORB主要还是在VSLAM中应用较多,场景变化不明显,但是需要高速的计算时间,这正好符合ORB。
重要的事情说三遍:
如果我的文章对您有所帮助,那就点赞加个关注呗 ( * ^ __ ^ * )
如果我的文章对您有所帮助,那就点赞加个关注呗 ( * ^ __ ^ * )
如果我的文章对您有所帮助,那就点赞加个关注呗 ( * ^ __ ^ * )
任何人或团体、机构全部转载或者部分转载、摘录,请保留本博客链接或标注来源。博客地址:开飞机的乔巴
作者简介:开飞机的乔巴(WeChat:zhangzheng-thu),现主要从事机器人抓取视觉系统以及三维重建等3D视觉相关方面,另外对slam以及深度学习技术也颇感兴趣,欢迎加我微信或留言交流相关工作。