OpenCV特徵點檢測——ORB特徵

2021-02-26 機器視覺課堂

什麼是ORB

如何解決旋轉不變性

如何解決對噪聲敏感的問題

關於尺度不變性

關於計算速度

關於性能

什麼是ORB

ORB是是ORiented Brief的簡稱。ORB的描述在下面文章中:

Ethan Rublee and Vincent Rabaud and Kurt Konolige and Gary Bradski, ORB: an efcient alternative to SIFT or SURF, ICCV 2011

沒有加上連結是因為作者確實還沒有放出論文,不過OpenCV2.3RC中已經有了實現,WillowGarage有一個talk也提到了這個算法,因此我不揣淺陋,在這裡總結一下。

Brief是Binary Robust Independent Elementary Features的縮寫。這個特徵描述子是由EPFL的Calonder在ECCV2010上提出的。主要思路就是在特徵點附近隨機選取若干點對,將這些點對的灰度值的大小,組合成一個二進位串,並將這個二進位串作為該特徵點的特徵描述子。詳細算法描述參考如下論文:

Calonder M., Lepetit V., Strecha C., Fua P.: BRIEF: Binary Robust Independent Elementary Features. ECCV 2010  http://cvlab.epfl.ch/~calonder/CalonderLSF10.pdf 

注意在BRIEF eccv2010的文章中,BRIEF描述子中的每一位是由隨機選取的兩個像素點做二進位比較得來的。文章同樣提到,在此之前,需要選取合適的gaussian kernel對圖像做平滑處理。(為什麼要強調這一點,因為下述的ORB對此作了改進。)

BRIEF的優點在於速度,缺點也相當明顯:

1:不具備旋轉不變性。

2:對噪聲敏感

3:不具備尺度不變性。

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還是很給力。點擊看大圖。

參考Slides

Related posts

 

最新版的OpenCV中新增加的ORB特徵的使用



看到OpenCV2.3.1裡面ORB特徵提取算法也在裡面了,套用給的SURF特徵例子程序改為ORB特徵一直提示錯誤,類型不匹配神馬的,由於沒有找到示例程序,只能自己找答案。

(ORB特徵論文:ORB: an efficient alternative to SIFT or SURF.點擊閱讀原文下載論文http://www.willowgarage.com/sites/default/files/orb_final.pdf  )

經過查找發現:

描述符數據類型有是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));

 

[cpp]

#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; 

長按下方二維碼識別免費關注

相關焦點

  • OpenCV-Python 特徵匹配|四十四
    它使用第一組中一個特徵的描述符,並使用一些距離計算將其與第二組中的所有其他特徵匹配。並返回最接近的一個。對於BF匹配器,首先我們必須使用cv.BFMatcher()創建BFMatcher對象。它需要兩個可選參數。第一個是normType,它指定要使用的距離測量。默認情況下為cv.NORM_L2。
  • OpenCV-Python SIFT尺度不變特徵變換|三十九
    目標在這一章當中,我們將學習SIFT算法的概念我們將學習找到SIFT關鍵點和描述算符。理論在前兩章中,我們看到了一些像Harris這樣的拐角檢測器。它們是旋轉不變的,這意味著即使圖像旋轉了,我們也可以找到相同的角。
  • 霍夫變換——形狀特徵提取算法:車道線檢測
    注意:建正交直角坐標系過程中, 要選取好以圖片的某個方位左起始點,以像素作為標度。下圖所示:圖像是一個 2D 矩陣,超過一些 x 和 y 坐標系,一條線可以描述為y = mx + b類似地,如果給的點坐標越多,形成的霍夫空間為:
  • 81個特徵點覆蓋全臉,面部特徵點檢測更精準(附代碼)
    新智元原創  來源:Reddit、GitHub  編輯: 金磊  【新智元導讀】人臉特徵點檢測是人臉檢測過程中的一個重要環節。以往我們採用的方法是OpenCV或者Dlib,雖然Dlib優於OpenCV,但是檢測出的68個點並沒有覆蓋額頭區域。
  • 解密:面部特徵點檢測的關鍵技術
    面部特徵點定位任務即根據輸入的人臉圖像,自動定位出面部關鍵特徵點,如眼睛、鼻尖、嘴角點、眉毛以及人臉各部件輪廓點等,如下圖所示。由於不同的姿態、表情、光照以及遮擋等因素的影響,準確地定位出各個關鍵特徵點看似很困難。
  • 世界上最好的語言PHP:OpenCV與計算機視覺已在我掌控之下
    特徵點不會干擾人臉的定位。人臉識別對於人臉識別,OpenCV 擁有「LBPHFaceRecognizer」類和「train / predict」方法。人臉標記/特徵點當我開始熟悉 OpenCV 時,我經常看到一些人的照片,這些照片上的點標記著眼睛、鼻子、嘴唇等。我想自己重複這個實驗,但在 OpenCV 的 Python 版本中並沒有實現。我花了一個晚上為 PHP 添加了 FacematkLBF 支持並返回一個對象。
  • OpenCV中的快速直線檢測
    cv::ximgproc::FastLineDetectors是opencv-contrib中用於檢測直線的模塊,該方法能在較短時間內獲得精度較高的直線檢測結果
  • 乾貨|一文讀懂圖像局部特徵點檢測算法
    研究圖像特徵檢測已經有一段時間了,圖像特徵檢測的方法很多,又加上各種算法的變形,所以難以在短時間內全面的了解,只是對主流的特徵檢測算法的原理進行了學習研究。總體來說,圖像特徵可以包括顏色特徵、紋理特徵、形狀特徵以及局部特徵點等。其中局部特點具有很好的穩定性,不容易受外界環境的幹擾,本篇文章也是對這方面知識的一個總結。
  • 圖像配準的前世今生:從人工設計特徵到深度學習
    傳統的基於特徵的方法自本世紀初以來,圖像配準主要使用傳統的基於特徵的方法。這些方法基於三個步驟:關鍵點檢測和特徵描述,特徵匹配,圖像變形。簡而言之,我們在兩幅圖像中選擇興趣點,將參考圖像中的每個興趣點和它在待配準圖像中的對應點關聯起來,然後對待批准圖像進行變換,這樣兩幅圖像就得以對齊。
  • OpenCV(四)邊緣檢測
    同時Sobel也可以只檢測垂直方向或者只檢測水平方向。因此當我們不需要注意細紋理或者只需要單方向檢測的時候不妨使用一下Sobel。我們上一篇文章末尾的例子(opencv自帶的例子)就是使用的Canny算子進行的邊緣檢測的展示。其中也包含了這兩個核心的設置,模糊和閾值。
  • 基於OpenCv 和 Python 的手指識別及追蹤
    翻譯 | 餘杭 Lamaric 校對 | 吳曉曼 審核 | 餘杭詳細代碼參考:https://github.com/amarlearning/opencv手指追蹤是許多計算機視覺應用的重要特徵。在該應用中,使用基於直方圖的方法將手與背景幀分離。
  • OpenCV-Python 輪廓特徵|二十二
    目標在本文中,我們將學習如何找到輪廓的不同特徵,例如面積,周長,質心,邊界框等。您將看到大量與輪廓有關的功能。1. 特徵矩特徵矩可以幫助您計算一些特徵,例如物體的質心,物體的面積等。請查看特徵矩上的維基百科頁面。函數cv.moments()提供了所有計算出的矩值的字典。
  • 曠視科技新增「狗臉識別」專利 可檢測狗鼻紋特徵點
    天眼查數據顯示,近日,北京曠視科技有限公司新增一條「狗臉識別」專利,一種狗鼻紋特徵點的檢測方法、裝置、系統及存儲介質。本專利申請於2018年12月,公布日在2020年7月。 天眼查數據顯示,近日,北京曠視科技有限公司新增一條「狗臉識別」專利,一種狗鼻紋特徵點的檢測方法、裝置、系統及存儲介質。
  • OpenCV-Python 特徵匹配 + 單應性查找對象|四十五
    目標在本章節中,我們將把calib3d模塊中的特徵匹配和findHomography混合在一起,以在複雜圖像中找到已知對象。基礎那麼我們在上一環節上做了什麼?我們使用了queryImage,找到了其中的一些特徵點,我們使用了另一個trainImage,也找到了該圖像中的特徵,並且找到了其中的最佳匹配。簡而言之,我們在另一個混亂的圖像中找到了對象某些部分的位置。此信息足以在trainImage上準確找到對象。
  • 一種基於點雲的Voxel(三維體素)特徵的深度學習方法
    本文介紹一種基於點雲的Voxel(三維體素)特徵的深度學習方法,實現對點雲中目標的準確檢測,並提供一個簡單的ROS實現,供大家參考。 VoxelNet結構 VoxelNet是一個端到端的點雲目標檢測網絡,和圖像視覺中的深度學習方法一樣,其不需要人為設計的目標特徵
  • 【OpenCV+Python】輪廓特徵中階
    本次我們將討論OpenCV中圖像輪廓的另一些特徵,它們將非常有用。有兩類邊界矩形。
  • 圖像特徵點|SIFT特徵點之圖像金字塔
    ,但SIFT除了計算比較耗時以外,其他方面的優點讓其成為特徵點提取算法中的一顆璀璨的明珠。尺度不變特徵轉換即SIFT (Scale-invariant feature transform)是一種計算機視覺的算法。它用來偵測與描述影像中的局部性特徵,它在空間尺度中尋找極值點,並提取出其位置、尺度、旋轉不變量,此算法由 David Lowe在1999年所發表,2004年完善總結。
  • OpenCV 之 霍夫變換
    若極坐標中有N條曲線相交於一點(如下左圖), 則說明在平面坐標中, 該點對應的直線包含N條曲線對應的N個點, 也即這N個點組成了一條直線。         因此, 霍夫變換的目的是將平面坐標內的直線檢測, 轉換為極坐標內的交點檢測, 顯然尋找一個交點要比尋找直線(實際上是尋找許多點)容易得多了。 2  OpenCV中的函數1)  HoughLines
  • 一種用於SLAM/SFM的深度學習特徵點 SuperPoint
    介紹諸多應用(諸如SLAM/SfM/相機標定/立體匹配)的首要一步就是特徵點提取,這裡的特徵點指的是能夠在不同光照&不同視角下都能夠穩定且可重複檢測的2D圖像點位置。基於CNN的算法幾乎在以圖像作為輸入的所有領域表現出相比於人類特徵工程更加優秀的表達能力。
  • 圖像特徵點、投影變換與圖像拼接
    比如說上圖,其中我們選擇的點要求:在兩個視角中都很顯著在兩個視角中都容易檢測不會和其他的點混淆我們稱這種點為」特徵點「。有一種著名的角點檢測算法是"Harris Corner」檢測算法,其過程也非常容易理解: