特徵點檢測和匹配
興趣點(interest points),或稱作關鍵點(keypoints)、特徵點(feature points) 被大量用於解決物體識別,影象識別、影象匹配、視覺跟蹤、三維重建等一系列的問題。不再觀察整幅圖,而是選擇某些特殊的點,然後對他們進行區域性有的放矢的分析。如果能檢測到足夠多的這種點,同時他們的區分度很高,並且可以精確定位穩定的特徵,那麼這個方法就有使用價值。
特徵點檢測
- Harris 角點檢測
- FAST 特徵檢測
- SURF 檢測
- SIFT 檢測
- MSER 檢測
Harris 角點檢測
OpenCv 中的 cornerHarris 函式可實現 Harris 角點檢測,輸出結果為浮點數型別的影象,其中每一項為對應位置的角點強度。然後使用閾值函式(threshold)進行閾值化處理,得到一組檢測到的角點。
缺點:
- 需要多個引數,難以調優
- 獲取的角點圖形包含很多角點群,很難精確定位
Harris 運算元提出了角點(特徵點)的正式數學定義,它是基於兩個正交方向上的強度變化率;然而,計算變化率需要計算影象的導數,計算複雜度比較高,效率較低,不適合實時處理。
OpenCV 特徵檢測器介面
OpenCV 2 中的通用特徵檢測器引入了一種新的通用介面用於不同的檢測器。該介面定義了一個 KeyPoint 類以封裝每個特徵點的屬性。對於 Harris 角點,只有位置是有用的。
- FastFeatureDetector:Fast 特徵檢測
- StarFeatureDetector:Star 特徵檢測
- SIFTFeatureDetector:SIFT 特徵檢測(nonfree中)
- SURFFeatureDetector:SURF 特徵檢測(nonfree中)
- ORBFeatureDetector:ORB 特徵檢測
- BRISKFeatureDetector:BRISK 特徵檢測
- MSERFeatureDetector:MSER 特徵檢測
- DenseFeatureDetector:Dense 特徵檢測
其他:
- GoodFeaturesToTrackDetector
- SimpleBlobDetector
- GridAdaptedFeatureDetector
- PyramidAdaptedFeatureDetector
- DynamicAdaptedFeatureDetector
使用示例:
Mat image = imread("...");
vector<KeyPoint> keypoints;
FastFeatureDetector fast(40);
fast.detect(image, keypoints);
另外,OpenCV 同時提供了一個通用的特徵點繪製函式:
drawKeypoints(image, // 原始影象
keypoints, // 關鍵點容器
image, // 結果影象
Scalar(255, 255, 255), // 關鍵點顏色
DrawMatchesFlags::DRAW_OVER_OUTIMG); // 繪製標誌
FAST 特徵點檢測
角點:假定特徵點周圍的影象強度,通過檢測候選畫素周圍一圈畫素來決定是否接手一個特徵點。與中心點差異較大的畫素如果組成連續的圓弧,並且弧長大於圓周長的3/4,那麼認為找到一個特徵點。
優點:該演算法可以獲得非常快速的特徵點檢測,在需要考慮執行速度時可以選用,如在高幀率的視訊序列中進行視覺跟蹤。(如果是相機陣列,需要多個相機同時檢測特徵點,就要考慮速度的問題)
SURF 特徵點檢測
當嘗試在不同影象之間匹配特徵時,通常面臨尺度變化的難題,也就是說,需要分析的影象在拍攝時與目標物體的距離時不同的,導致目標物體在影象中有不同的尺寸。如果嘗試使用固定尺寸的相鄰尺寸來匹配不同影象中的相同特徵,那麼由於尺度的變化,他們的強度模板並不會匹配。
SURF(Speeded Up Robust Features)特徵——加速魯棒特徵。具有尺度不變性,主要思想是:每個檢測到的特徵點都伴隨著對應的尺度因子。
使用示例:
Mat image = imread("...");
vector<KeyPoint> keypoints;
SurfFeatureDetector surf(40);
surf.detect(image, keypoints);
drawKeypoints(image, // 原始影象
keypoints, // 關鍵點容器
image, // 結果影象
Scalar(255, 255, 255), // 關鍵點顏色
DrawMatchesFlags::DRAW_OVER_OUTIMG); // 繪製標誌
SURF 演算法還將方向與每個特徵聯絡使得他們具有旋轉無關性,方向在圓圈中以放射線的形式繪出。
SURF 的實現如下:首先對每個畫素計算 Hessian 矩陣以得到特徵,該矩陣測量一個函式的局部曲率,給出曲率的強度,定義角點為具有較高局部曲率的影象點(即有多個方向具有高曲率),當 Hessian 值同時在空間域和尺度域上達到區域性極大值時,認為找到了尺度不變的特徵。
SIFT 特徵點檢測
SURF 演算法是著名的尺度不變特徵檢測器 SIFT 的高效變種,但 SIFT 使用的是 Laplacian 濾波器響應而不是 Hessian 矩陣。
OpenCV 特徵點描述子提取介面
OpenCV 2 中引入了一個通用類,用於提取不同的特徵點描述子。
- SIFTDescriptorExtractor:對應 SIFT 特徵點
- SURFDescriptorExtractor:對應 SURF 特徵點
- BriefDescriptorExtractor:對應 Brief 特徵點
- BRISKDescriptorExtractor:對應 BRISK 特徵點
- ORBDescriptorExtractor:對應 ORB 特徵點
- FREAKDescriptorExtractor:對應 FREAK 特徵點
使用示例:
Mat image = imread("...");
vector<KeyPoint> keypoints;
SurfFeatureDetector surf(40);
surf.detect(image, keypoints);
// 構造surf描述子提取器
SurfDescriptorExtractor surfDesc;
// 提取surf描述子
Mat descriptors;
surfDesc.compute(image, keypoints, descriptors);
匹配
要想匹配同一場景的兩幅影象:
- 檢測每幅影象的特徵點
- 提取每幅影象的特徵點描述子
- 第一幅圖的每個特徵點描述子向量與第二幅圖的每個特徵點描述子向量比較,得分最高的一對描述子(兩個向量距離最近)視為那個特徵的最佳匹配。
- 重複所有的特徵點
上述過程可以用 BruteForceMatcher 實現。
// 構造匹配器
BruteForceMatcher<L2<float>> matcher;
// 匹配兩幅影象的描述子
vector<DMatch> matches;
matcher.match(descriptors1, descriptors2, matches);
輸出結果是一個 DMatch 向量,Dmatch 是 OpenCV 定義的一個結構體,用於表示一對匹配的描述子。
為了讓匹配操作視覺化,OpenCV 也提供了一個函式以產生由兩幅輸入影象拼接成的影象,匹配的點由直線相連。
Mat imageMatches;
drawMatches(image1, keypoints1, // 第一幅影象及其特徵點
image2, keypoints2, // 第二幅影象及其特徵點
matches, // 匹配結果
imageMatches, // 生成的影象
Scalar(255, 255, 255)); // 直線的顏色
當然,有些時候匹配的特徵點較多,為了清晰的顯示結果,我們往往只顯示最好的一部分匹配點。通過下述方法實現:
// 第n個元素放在第n個位置,之前都是小於第n個元素,之後都大於
nth_element(matches.begin(), // 初始位置
matches.begin() + 24, // 排序元素的位置
matches.end()); // 終止位置
問題:由於場景中物體的對稱性,一些區域性匹配會產生歧義。
參考
參考《OpenCV 2 計算機視覺程式設計手冊》
參考《The OpenCV Reference Manual-2.4.9.0》