ORB(Oriented FAST and Rotated BRIEF)特征是目前看来非常具有代表性的实时图像特征。它改进了FAST检测子不具有方向性的问题,并采用速度极快的二进制描述子BRIEF(Binary Robust Independent Elementary Feature),使整个图像特征提取的环节大大加速。ORB在保持了特征子具有旋转、尺度不变性的同时,在速度方面提升明显,ORB是质量与性能之间很好地折中,对于实时性要求很高的SLAM而言是一个很好地选择。
ORB算法根据功能不同可分为特征点提取和特征点描述两部分
特征提取模块从预处理的图像中提取角点,随后计算提取出的角点的描述子,其中主要部分为角点提取算法以及角点描述子算法。
早期的角点提取算法有Moravec角点检测算法、Harris角点检测算法、SUSAN角点检测算法、GFTT角点检测算法等等。其中 Moravec是最早的角点检测算法之一,但该算法存在对边缘的响应单一、噪声较多等问题。针对这些问题 Chris Harris等人提出了Harris 角点检测算法,其主要采用窗口滑动法对角点进行检测。
早期角点检测算法对角点的提取效果并不理想,相较而言,后期提出的ORB算法、SURF算法以及SIFT算法的应用性较高,能够满足实际应用需求。
其中以使用ORB算法为基础的ORB-SLAM2系统有着极高的应用性,其采用实时性较高的FAST角点检测算法对角点进行检测,随后采用BRIEF (Binary Robust Independent Elementary Features)特征描述算法对特征点描述子进行计算,虽然ORB算法的旋转不变性和尺度不变性相较于SURF算法和SIFT算法处于劣势,但由于FAST算法和BRIEF算法的计算速度快,因此其具有较高的实时性。在尺度不变性方面,ORB算法采用了高斯金字塔模型因此也具有一定的旋转不变性和尺度不变性,是较为理想的算法。
特征匹配主要分为基于灰度的匹配方法和基于特征的匹配方法。其中灰度匹配相较于特征匹配在匹配速度和精度上均存在不足,因此特征匹配在当下环境中拥有广泛的适应性。
常用的基于特征的匹配方法有暴力匹配(Brute-Force)算法和快速近似最近邻算法(FLANN)两种,其中暴力匹配算法将当前描述符的所有特征都拿来和另一个描述符进行比较并通过对比较两个描述符,产生匹配结果列表。而快速近似最近邻算法在处理大量特征点时有着相较于其他最近邻算法更快的速度。
#include
#include
#include
#include
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;
}