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