ORB算法
ORB是是ORiented Brief的簡稱。ORB的描述在下面文章中:html
Ethan Rublee and Vincent Rabaud and Kurt Konolige and Gary Bradski, ORB: an efcient alternative to SIFT or SURF, ICCV 2011android
沒有加上連接是由於做者確實尚未放出論文,不過OpenCV2.3RC中已經有了實現,WillowGarage有一個talk也提到了這個算法,所以我不揣淺陋,在這裏總結一下。ios
Brief是Binary Robust Independent Elementary Features的縮寫。這個特徵描述子是由EPFL的Calonder在ECCV2010上提出的。主要思路就是在特徵點附近隨機選取若干點對,將這些點對的灰度值的大小,組合成一個二進制串,並將這個二進制串做爲該特徵點的特徵描述子。詳細算法描述參考以下論文:算法
注意在BRIEF eccv2010的文章中,BRIEF描述子中的每一位是由隨機選取的兩個像素點作二進制比較得來的。文章一樣提到,在此以前,須要選取合適的gaussian kernel對圖像作平滑處理。(爲何要強調這一點,由於下述的ORB對此做了改進。)less
BRIEF的優勢在於速度,缺點也至關明顯:ide
1:不具有旋轉不變性。post
2:對噪聲敏感性能
3:不具有尺度不變性。ui
ORB就是試圖解決上述缺點中的1和2.
如何解決旋轉不變性:
在ORB的方案中,是採用了FAST做爲特徵點檢測算子。FAST應用的不少了,是出名的快,以防有人不知道,請看這裏:
在Sift的方案中,特徵點的主方向是由梯度直方圖的最大值和次大值所在的bin對應的方向決定的。略嫌耗時。
在ORB的方案中,特徵點的主方向是經過矩(moment)計算而來,公式以下:
有了主方向以後,就能夠依據該主方向提取BRIEF描述子。可是由此帶來的問題是,因爲主方向會發生變化,隨機點對的相關性會比較大,從而下降描述子的判別性。解決方案也很直接,採起貪婪的,窮舉的方法,暴力找到相關性較低的隨機點對。
如何解決對噪聲敏感的問題:
在前面提到過,在最先的eccv2010的文章中,BRIEF使用的是pixel跟pixel的大小來構造描述子的每個bit。這樣的後果就是對噪聲敏感。所以,在ORB的方案中,作了這樣的改進,再也不使用pixel-pair,而是使用9×9的patch-pair,也就是說,對比patch的像素值之和。(能夠經過積分圖快速計算)。
關於尺度不變性:
ORB沒有試圖解決尺度不變性,(由於FAST自己就不具備尺度不變性。)可是這樣只求速度的特徵描述子,通常都是應用在實時的視頻處理中的,這樣的話就能夠經過跟蹤還有一些啓發式的策略來解決尺度不變性的問題。
關於計算速度:
ORB是sift的100倍,是surf的10倍。
關於性能:
下面是一個性能對比,ORB仍是很給力。點擊看大圖。
Related posts
最新版的OpenCV中新增長的ORB特徵的使用
看到OpenCV2.3.1裏面ORB特徵提取算法也在裏面了,套用給的SURF特徵例子程序改成ORB特徵一直提示錯誤,類型不匹配神馬的,因爲沒有找到示例程序,只能本身找答案。
(ORB特徵論文:ORB: an efficient alternative to SIFT or SURF.點擊下載論文)
通過查找發現:
描述符數據類型有是float的,好比說SIFT,SURF描述符,還有是uchar的,好比說有ORB,BRIEF
對於float 匹配方式有:
FlannBased
BruteForce<L2<float> >
BruteForce<SL2<float> >
BruteForce<L1<float> >
對於uchar有:
BruteForce<Hammin>
BruteForce<HammingLUT>
BruteForceMatcher< L2<float> > matcher;//改動的地方
完整代碼以下:
#include <iostream> #include "opencv2/core/core.hpp" #include "opencv2/features2d/features2d.hpp" #include "opencv2/highgui/highgui.hpp" #include <iostream> #include <vector> using namespace cv; using namespace std; int main() { Mat img_1 = imread("D:\\image\\img1.jpg"); Mat img_2 = imread("D:\\image\\img2.jpg"); if (!img_1.data || !img_2.data) { cout << "error reading images " << endl; return -1; } ORB orb; vector<KeyPoint> keyPoints_1, keyPoints_2; Mat descriptors_1, descriptors_2; orb(img_1, Mat(), keyPoints_1, descriptors_1); orb(img_2, Mat(), keyPoints_2, descriptors_2); BruteForceMatcher<HammingLUT> matcher; vector<DMatch> matches; matcher.match(descriptors_1, descriptors_2, matches); double max_dist = 0; double min_dist = 100; //-- Quick calculation of max and min distances between keypoints for( int i = 0; i < descriptors_1.rows; i++ ) { double dist = matches[i].distance; if( dist < min_dist ) min_dist = dist; if( dist > max_dist ) max_dist = dist; } printf("-- Max dist : %f \n", max_dist ); printf("-- Min dist : %f \n", min_dist ); //-- Draw only "good" matches (i.e. whose distance is less than 0.6*max_dist ) //-- PS.- radiusMatch can also be used here. std::vector< DMatch > good_matches; for( int i = 0; i < descriptors_1.rows; i++ ) { if( matches[i].distance < 0.6*max_dist ) { good_matches.push_back( matches[i]); } } Mat img_matches; drawMatches(img_1, keyPoints_1, img_2, keyPoints_2, good_matches, img_matches, Scalar::all(-1), Scalar::all(-1), vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS); imshow( "Match", img_matches); cvWaitKey(); return 0; }
另外: SURF SIFT
/*
SIFT sift;
sift(img_1, Mat(), keyPoints_1, descriptors_1);
sift(img_2, Mat(), keyPoints_2, descriptors_2);
BruteForceMatcher<L2<float> > matcher;
*/
/*
SURF surf;
surf(img_1, Mat(), keyPoints_1);
surf(img_2, Mat(), keyPoints_2);
SurfDescriptorExtractor extrator;
extrator.compute(img_1, keyPoints_1, descriptors_1);
extrator.compute(img_2, keyPoints_2, descriptors_2);
BruteForceMatcher<L2<float> > matcher;
*/
效果:
另一個是尋找目標匹配
在右邊的場景圖裏面尋找左邊那幅圖的starbucks標誌
效果以下:
須要在以前的那個imshow以前加上以下代碼便可完成一個簡單的功能展現:
// localize the object std::vector<Point2f> obj; std::vector<Point2f> scene; for (size_t i = 0; i < good_matches.size(); ++i) { // get the keypoints from the good matches obj.push_back(keyPoints_1[ good_matches[i].queryIdx ].pt); scene.push_back(keyPoints_2[ good_matches[i].trainIdx ].pt); } Mat H = findHomography( obj, scene, CV_RANSAC ); // get the corners from the image_1 std::vector<Point2f> obj_corners(4); obj_corners[0] = cvPoint(0,0); obj_corners[1] = cvPoint( img_1.cols, 0); obj_corners[2] = cvPoint( img_1.cols, img_1.rows); obj_corners[3] = cvPoint( 0, img_1.rows); std::vector<Point2f> scene_corners(4); perspectiveTransform( obj_corners, scene_corners, H); // draw lines between the corners (the mapped object in the scene - image_2) line( img_matches, scene_corners[0] + Point2f( img_1.cols, 0), scene_corners[1] + Point2f( img_1.cols, 0),Scalar(0,255,0)); line( img_matches, scene_corners[1] + Point2f( img_1.cols, 0), scene_corners[2] + Point2f( img_1.cols, 0),Scalar(0,255,0)); line( img_matches, scene_corners[2] + Point2f( img_1.cols, 0), scene_corners[3] + Point2f( img_1.cols, 0),Scalar(0,255,0)); line( img_matches, scene_corners[3] + Point2f( img_1.cols, 0), scene_corners[0] + Point2f( img_1.cols, 0),Scalar(0,255,0));
代碼片:
#include "opencv2/highgui/highgui.hpp" #include "opencv2/features2d/features2d.hpp" #include <iostream> int main( ) { cv::Ptr<cv::FeatureDetector> detector = cv::FeatureDetector::create( "SIFT" ); cv::Ptr<cv::DescriptorExtractor> extractor = cv::DescriptorExtractor::create("SIFT" ); cv::Mat im = cv::imread("box.png", CV_LOAD_IMAGE_COLOR ); std::vector<cv::KeyPoint> keypoints; cv::Mat descriptors; detector->detect( im, keypoints); extractor->compute( im,keypoints,descriptors); int duplicateNum = 0; for (int i=0;i<keypoints.size();i++) { for (int j=i+1;j<keypoints.size();j++) { float dist = abs((keypoints[i].pt.x-keypoints[j].pt.x))+abs((keypoints[i].pt.y-keypoints[j].pt.y)); if (dist == 0) { cv::Mat descriptorDiff = descriptors.row(i)-descriptors.row(j); double diffNorm = cv::norm(descriptorDiff); std::cout<<"keypoint "<<i<<" equal to keypoint "<<j<<" descriptor distance "<<diffNorm<<std::endl; duplicateNum++; } } } std::cout<<"Total keypoint: "<<keypoints.size()<<", duplicateNum: "<<duplicateNum<<std::endl; return 1; }
本文同步分享在 博客「shiter」(CSDN)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。