學習OpenCV關注微信公衆號【OpenCV學堂】
一:介紹
我們知道SIFT算法通常通過對每個關鍵點生成128個特徵向量作爲描述子、SURF算法通常對關鍵點生成最少64個特徵向量作爲描述子。但是對於圖像來說創建上千或者上萬個這樣的描述子內存開銷比較大,運行速度受到嚴重影響。特別對嵌入式設備與一定設備來說,內存限制尤爲明顯,而且匹配的時候計算也比較耗時。
但是實際上這些特徵數據OpenCV在匹配的時候並沒有完全利用上,而是通過PCA、LDA等方法對它進行壓縮,或者是LSH(局部敏感哈希)方法把這些特徵描述子壓縮從浮點數轉換爲二進制字符串,然後通過漢明距離(HammingDistance)進行比較。這樣就可以通過簡單的異或操作(OXR)與位值計算來加速實現對象特徵匹配,對於SSE指令集的CPU這種方法可以大大加速。但是這種方法仍然需要首先計算描述子,然後使用LSH方法進行壓縮,無法避免過度的內存開銷。
而Brief方法可以直接通過關鍵點生成二進制字符串,跳過了中間描述子生成步驟,這樣就大大減低了內存要求與計算開銷。Brief方法主要思路是對每個關鍵點附件選擇若干個像素點,將這些像素點的像素值組合成二進制字符串,然後使用該字符串作爲該關鍵點的描述子。
此方法是在2010年提出來的。結果實驗測試在選取256個點甚至128個點情況下對沒有旋轉對象識別率非常高而且速度比SURF還快。但是當對象有旋轉時候由於Brief不能很好的支持旋轉不變性識別,特別是當旋轉角度超過30度以上,準確率會快速下降。
二:Brief描述子生成步驟
Brief描述子生成首先需要產生足夠多的隨機點對,然後根據隨機點對座標得到對應像素值,對所有點對進行二進制字符串拼接,拼接完成即生成了描述子。
第一步:選擇關鍵點周圍SxS大小正方形圖像區塊,進行高斯模糊。這樣做的原因是需要降低圖像隨機噪聲,OpenCV在完成Brief的時候考慮到效率問題並沒有採用高斯模糊而是採用了基於積分圖的盒子模糊方法。
第二步:選擇n個像素點對,其中n的取值常見爲256、此外還可以是128、512。每個點對比較像素值輸出如下。
對N個點對完成操作最終得到了二進制字符串,表達如下:
三:方法
高斯模糊比較
通過實驗對比高斯sigma參數在0~3之間準確率比較高,窗口大小取值在9x9取得比較好的模糊去噪效果。論文中實驗結果圖示如下:
隨機點生成方法比較
對於隨機生成點對的方法,論文中給出了五種隨機方法與實驗結果比較,五種方法描述如下:
圖示如下:
對圖像五個幾何採樣完成測試結果如下:
其中第二種方法比其他四種稍微有點優勢,而最不好的方法則是第五種方法。
四:OpenCV中Biref描述子演示
#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>
#include <iostream>
using namespace cv;
using namespace std;
using namespace cv::xfeatures2d;
int main(int argc, char** argv) {
Mat img1 = imread("D:/tree.png", IMREAD_GRAYSCALE);
Mat img2 = imread("D:/tree_in_scene.png", IMREAD_GRAYSCALE);
Mat src = imread("D:/gloomyfish/14.png", IMREAD_GRAYSCALE);
Mat src1 = imread("D:/gloomyfish/14.png");
if (!img1.data || !img2.data) {
printf("could not load images...\n");
return -1;
}
imshow("box", img1);
imshow("scene", img2);
auto detector = FastFeatureDetector::create();
vector<KeyPoint> keypoints_obj;
vector<KeyPoint> keypoints_scene;
detector->detect(img1, keypoints_obj, Mat());
detector->detect(img2, keypoints_scene, Mat());
auto descriptor = BriefDescriptorExtractor::create();
Mat descriptor_obj, descriptor_scene;
descriptor->compute(img1, keypoints_obj, descriptor_obj);
descriptor->compute(img2, keypoints_scene, descriptor_scene);
BFMatcher matcher(NORM_L2);
vector<DMatch> matches;
matcher.match(descriptor_obj, descriptor_scene, matches);
Mat resultImg;
drawMatches(img1, keypoints_obj, img2, keypoints_scene, matches, resultImg);
imshow("Brief Descriptor Match Result", resultImg);
waitKey(0);
return 0;
}
運行結果