SURF原理詳解:https://wenku.baidu.com/view/2f1e4d8ef705cc1754270945.html
SURF算法工作原理
-
選擇圖像中的POI(Points of interest) Hessian Matrix
-
在不同的尺度空間發現關鍵點,非最大信號壓制
-
發現特征點方法、旋轉不變性要求
-
生成特征向量
SURF構造函數介紹
C++: SURF::SURF(
double hessianThreshold, --閾值檢測器使用Hessian的關鍵點,默認值在
300-500之間
int nOctaves=4, -- 4表示在四個尺度空間
int nOctaveLayers=2, -- 表示每個尺度的層數
bool extended=false,
bool upright=false --表示計算旋轉不變性,不計算的速度更快
)
特征點繪制
特征點繪制是為了把檢測出來的Surf特征點在原圖上繪制出來,這一步是為了把特征點直觀的顯示出來給我們看,跟整個Surf算子的特征提取和匹配流程沒關系。
繪制使用drawKeypoints方法:
void drawKeypoints( const Mat& image, const vector<KeyPoint>& keypoints, CV_OUT Mat& outImage, const Scalar& color=Scalar::all(-1), int flags=DrawMatchesFlags::DEFAULT );
第一個參數image:原始圖像,可以使三通道或單通道圖像;
第二個參數keypoints:特征點向量,向量內每一個元素是一個KeyPoint對象,包含了特征點的各種屬性信息;
第三個參數outImage:特征點繪制的畫布圖像,可以是原圖像;
第四個參數color:繪制的特征點的顏色信息,默認繪制的是隨機彩色;
第五個參數flags:特征點的繪制模式,其實就是設置特征點的那些信息需要繪制,那些不需要繪制,有以下幾種模式可選:
DEFAULT:只繪制特征點的坐標點,顯示在圖像上就是一個個小圓點,每個小圓點的圓心坐標都是特征點的坐標。
DRAW_OVER_OUTIMG:函數不創建輸出的圖像,而是直接在輸出圖像變量空間繪制,要求本身輸出圖像變量就是一個初始化好了的,size與type都是已經初始化好的變量
NOT_DRAW_SINGLE_POINTS:單點的特征點不被繪制
DRAW_RICH_KEYPOINTS:繪制特征點的時候繪制的是一個個帶有方向的圓,這種方法同時顯示圖像的坐標,size,和方向,是最能顯示特征信息的一種繪制方式。
1 #include <opencv2/opencv.hpp> 2 #include <opencv2/xfeatures2d.hpp> 3 #include <iostream> 4 5 using namespace cv; 6 using namespace cv::xfeatures2d; 7 using namespace std; 8 9 int main(int argc, char** argv) { 10 Mat src = imread("test.jpg", IMREAD_GRAYSCALE); 11 if (src.empty()) { 12 printf("could not load image...\n"); 13 return -1; 14 } 15 namedWindow("input image", CV_WINDOW_AUTOSIZE); 16 imshow("input image", src); 17 18 // SURF特征點檢測 19 int minHessian = 100; 20 Ptr<SURF> detector = SURF::create(minHessian);//創建一個surf類對象並初始化 21 vector<KeyPoint> keypoints; 22 detector->detect(src, keypoints, Mat());//找出關鍵點 23 24 // 繪制關鍵點 25 Mat keypoint_img; 26 drawKeypoints(src, keypoints, keypoint_img, Scalar::all(-1), DrawMatchesFlags::DEFAULT); 27 imshow("KeyPoints Image", keypoint_img); 28 29 waitKey(0); 30 return 0; 31 }
繪制匹配點
drawMatches( const Mat& img1, const vector<KeyPoint>& keypoints1, const Mat& img2, const vector<KeyPoint>& keypoints2, const vector<DMatch>& matches1to2, Mat& outImg, const Scalar& matchColor=Scalar::all(-1), const Scalar& singlePointColor=Scalar::all(-1), const vector<char>& matchesMask=vector<char>(), int flags=DrawMatchesFlags::DEFAULT );
其中參數如下:
* img1 – 源圖像1
* keypoints1 –源圖像1的特征點.
* img2 – 源圖像2.
* keypoints2 – 源圖像2的特征點
* matches1to2 – 源圖像1的特征點匹配源圖像2的特征點[matches[i]] .
* outImg – 輸出圖像具體由flags決定.
* matchColor – 匹配的顏色(特征點和連線),若matchColor==Scalar::all(-1),顏色隨機.
* singlePointColor – 單個點的顏色,即未配對的特征點,若matchColor==Scalar::all(-1),顏色隨機.
*matchesMask – Mask決定哪些點將被畫出,若為空,則畫出所有匹配點.
*flags—它跟drawKeypoints方法中flags的含義是一樣的。
當僅使用篩選出的最優匹配點進行匹配的時候,意味着會有很多非最優的特征點不會被匹配,這時候可以設置flags=DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS
BF(暴力)匹配(src目標圖,temp需要查找的背景圖)
1 #include <opencv2/opencv.hpp> 2 #include <opencv2/xfeatures2d.hpp> 3 #include <iostream> 4 5 using namespace cv; 6 using namespace cv::xfeatures2d; 7 using namespace std; 8 9 int main(int argc, char** argv) { 10 Mat src = imread("數字.jpg"); 11 Mat temp = imread("2.png"); 12 if (src.empty() || temp.empty()) { 13 printf("could not load image...\n"); 14 return -1; 15 } 16 namedWindow("input image", CV_WINDOW_AUTOSIZE); 17 imshow("input image", src); 18 19 // SURF特征點檢測 20 int minHessian = 400; 21 Ptr<SURF> detector = SURF::create(minHessian, 4, 3, true, true);//創建一個surf類檢測器對象並初始化 22 vector<KeyPoint> keypoints1, keypoints2; 23 Mat src_vector, temp_vector;//用來存放特征點的描述向量 24 25 //detector->detect(src, keypoints1, Mat());//找出關鍵點 26 //detector->detect(temp, keypoints2, Mat());//找出關鍵點 27 28 //找到特征點並計算特征描述子(向量) 29 detector->detectAndCompute(src, Mat(), keypoints1, src_vector);//輸入圖像,輸入掩碼,輸入特征點,輸出Mat,存放所有特征點的描述向量 30 detector->detectAndCompute(temp, Mat(), keypoints2, temp_vector);//這個Mat行數為特征點的個數,列數為每個特征向量的尺寸,SURF是64(維) 31 32 33 //匹配 34 BFMatcher matcher(NORM_L2); //實例化一個暴力匹配器(括號里可以選擇匹配方法) 35 36 vector<DMatch> matches; //DMatch是用來描述匹配好的一對特征點的類,包含這兩個點之間的匹配信息 37 //比如左圖有個特征m,它和右圖的特征點n最匹配,這個DMatch就記錄它倆最匹配,並且還記錄m和n的 38 //特征向量的距離和其他信息,這個距離在后面用來做篩選 39 40 matcher.match(src_vector, temp_vector, matches); //匹配,數據來源是特征向量,結果存放在DMatch類型里面 41 42 //匹配點篩選 43 //sort函數對數據進行升序排列 44 //篩選匹配點,根據match里面特征對的距離從小到大排序 45 //篩選出最優的30個匹配點(可以不使用,會畫出所有特征點) 46 47 sort(matches.begin(), matches.end()); 48 vector< DMatch > good_matches; 49 int ptsPairs = std::min(30, (int)(matches.size() * 0.15));//匹配點數量不大於50 50 cout << ptsPairs << endl; 51 for (int i = 0; i < ptsPairs; i++) 52 { 53 good_matches.push_back(matches[i]);//距離最小的50個壓入新的DMatch 54 } 55 56 57 Mat MatchesImage; //drawMatches這個函數直接畫出擺在一起的圖 58 drawMatches(src, keypoints1, temp, keypoints2, good_matches, MatchesImage, Scalar::all(-1), Scalar::all(-1), vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS); //繪制匹配點 59 imshow("BFMatcher Image", MatchesImage); 60 61 waitKey(0); 62 return 0; 63 }
FLANN匹配
1 #include <opencv2/opencv.hpp> 2 #include <opencv2/xfeatures2d.hpp> 3 #include <iostream> 4 #include <math.h> 5 6 using namespace cv; 7 using namespace cv::xfeatures2d; 8 using namespace std; 9 10 int main(int argc, char** argv) { 11 Mat src = imread("數字.jpg",0); 12 Mat temp = imread("2.png",0); 13 if (src.empty() || temp.empty()) { 14 printf("could not load image...\n"); 15 return -1; 16 } 17 namedWindow("input image", CV_WINDOW_AUTOSIZE); 18 imshow("input image", src); 19 20 // SURF特征點檢測 21 int minHessian = 400; 22 Ptr<SURF> detector = SURF::create(minHessian, 4, 3, true, true);//創建一個surf類檢測器對象並初始化 23 vector<KeyPoint> keypoints1, keypoints2; 24 Mat src_vector, temp_vector;//用來存放特征點的描述向量 25 26 //detector->detect(src, keypoints1, Mat());//找出關鍵點 27 //detector->detect(temp, keypoints2, Mat());//找出關鍵點 28 29 //找到特征點並計算特征描述子(向量) 30 detector->detectAndCompute(src, Mat(), keypoints1, src_vector);//輸入圖像,輸入掩碼,輸入特征點,輸出Mat,存放所有特征點的描述向量 31 detector->detectAndCompute(temp, Mat(), keypoints2, temp_vector);//這個Mat行數為特征點的個數,列數為每個特征向量的尺寸,SURF是64(維) 32 33 34 //匹配 35 FlannBasedMatcher matcher; //實例化一個FLANN匹配器(括號里可以選擇匹配方法) 36 37 vector<DMatch> matches; //DMatch是用來描述匹配好的一對特征點的類,包含這兩個點之間的匹配信息 38 //比如左圖有個特征m,它和右圖的特征點n最匹配,這個DMatch就記錄它倆最匹配,並且還記錄m和n的 39 //特征向量的距離和其他信息,這個距離在后面用來做篩選 40 41 matcher.match(src_vector, temp_vector, matches); //匹配,數據來源是特征向量,結果存放在DMatch類型里面 42 43 //求最小最大距離 44 double minDistance = 1000;//反向逼近 45 double maxDistance = 0; 46 for (int i=0; i< src_vector.rows; i++) { 47 double distance = matches[i].distance; 48 if (distance > maxDistance) { 49 maxDistance = distance; 50 } 51 if (distance < minDistance) { 52 minDistance = distance; 53 } 54 } 55 printf("max distance : %f\n", maxDistance); 56 printf("min distance : %f\n", minDistance); 57 58 //篩選較好的匹配點 59 vector< DMatch > good_matches; 60 for (int i = 0; i < src_vector.rows; i++) { 61 double distance = matches[i].distance; 62 if (distance < max(minDistance * 2, 0.02)) { 63 good_matches.push_back(matches[i]);//距離小於范圍的壓入新的DMatch 64 } 65 } 66 67 /*//sort函數對數據進行升序排列 68 //篩選匹配點,根據match里面特征對的距離從小到大排序 69 //篩選出最優的50個匹配點(可以不使用,會畫出所有特征點) 70 71 sort(matches.begin(), matches.end()); 72 vector< DMatch > good_matches; 73 int ptsPairs = std::min(50, (int)(matches.size() * 0.15));//匹配點數量不大於50 74 cout << ptsPairs << endl; 75 for (int i = 0; i < ptsPairs; i++) 76 { 77 good_matches.push_back(matches[i]);//距離最小的50個壓入新的DMatch 78 } 79 */ 80 81 Mat MatchesImage; //drawMatches這個函數直接畫出擺在一起的圖 82 drawMatches(src, keypoints1, temp, keypoints2, good_matches, MatchesImage, Scalar::all(-1), Scalar::all(-1), vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS); //繪制匹配點 83 imshow("FLANN Image", MatchesImage); 84 85 waitKey(0); 86 return 0; 87 }
對象查找
1 #include <opencv2/opencv.hpp> 2 #include <opencv2/xfeatures2d.hpp> 3 #include <iostream> 4 #include <math.h> 5 6 using namespace cv; 7 using namespace cv::xfeatures2d; 8 using namespace std; 9 10 int main(int argc, char** argv) { 11 Mat src = imread("fire_5.jpg"); 12 Mat temp = imread("數字.jpg"); 13 if (src.empty() || temp.empty()) { 14 printf("could not load image...\n"); 15 return -1; 16 } 17 namedWindow("input image", CV_WINDOW_AUTOSIZE); 18 imshow("input image", src); 19 20 // SURF特征點檢測 21 int minHessian = 400; 22 Ptr<SURF> detector = SURF::create(minHessian, 4, 3, true, true);//創建一個surf類檢測器對象並初始化 23 vector<KeyPoint> keypoints1, keypoints2; 24 Mat src_vector, temp_vector;//用來存放特征點的描述向量 25 26 //detector->detect(src, keypoints1, Mat());//找出關鍵點 27 //detector->detect(temp, keypoints2, Mat());//找出關鍵點 28 29 //找到特征點並計算特征描述子(向量) 30 detector->detectAndCompute(src, Mat(), keypoints1, src_vector);//輸入圖像,輸入掩碼,輸入特征點,輸出Mat,存放所有特征點的描述向量 31 detector->detectAndCompute(temp, Mat(), keypoints2, temp_vector);//這個Mat行數為特征點的個數,列數為每個特征向量的尺寸,SURF是64(維) 32 33 34 //匹配 35 FlannBasedMatcher matcher; //實例化一個FLANN匹配器(括號里可以選擇匹配方法) 36 37 vector<DMatch> matches; //DMatch是用來描述匹配好的一對特征點的類,包含這兩個點之間的匹配信息 38 //比如左圖有個特征m,它和右圖的特征點n最匹配,這個DMatch就記錄它倆最匹配,並且還記錄m和n的 39 //特征向量的距離和其他信息,這個距離在后面用來做篩選 40 41 matcher.match(src_vector, temp_vector, matches); //匹配,數據來源是特征向量,結果存放在DMatch類型里面 42 43 //求最小最大距離 44 double minDistance = 1000;//反向逼近 45 double maxDistance = 0; 46 for (int i = 0; i < src_vector.rows; i++) { 47 double distance = matches[i].distance; 48 if (distance > maxDistance) { 49 maxDistance = distance; 50 } 51 if (distance < minDistance) { 52 minDistance = distance; 53 } 54 } 55 printf("max distance : %f\n", maxDistance); 56 printf("min distance : %f\n", minDistance); 57 58 //篩選較好的匹配點 59 vector< DMatch > good_matches; 60 for (int i = 0; i < src_vector.rows; i++) { 61 double distance = matches[i].distance; 62 if (distance < max(minDistance * 3, 0.02)) { 63 good_matches.push_back(matches[i]);//距離小於范圍的壓入新的DMatch 64 } 65 } 66 67 68 Mat MatchesImage; //drawMatches這個函數直接畫出擺在一起的圖 69 drawMatches(src, keypoints1, temp, keypoints2, good_matches, MatchesImage, Scalar::all(-1), Scalar::all(-1), vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS); //繪制匹配點 70 71 72 vector<Point2f> obj; 73 vector<Point2f> objInScene; 74 for (size_t t = 0; t < good_matches.size(); t++) { 75 obj.push_back(keypoints1[good_matches[t].queryIdx].pt);//返回對象在模板圖特征點坐標 76 objInScene.push_back(keypoints2[good_matches[t].trainIdx].pt);//返回對象在背景查找圖的坐標 77 } 78 79 Mat H = findHomography(obj,objInScene,RANSAC);//計算透視變換矩陣 80 81 vector<Point2f> obj_corner(4); 82 vector<Point2f> scene_corner(4); 83 obj_corner[0] = Point(0, 0); 84 obj_corner[1] = Point(src.cols, 0); 85 obj_corner[2] = Point(src.cols,src.rows); 86 obj_corner[3] = Point(0, src.rows); 87 88 perspectiveTransform(obj_corner, scene_corner,H);//透視變換 89 90 //畫出邊框線 91 line(MatchesImage, scene_corner[0] + Point2f(src.cols, 0), scene_corner[1] + Point2f(src.cols, 0), Scalar(0, 0, 255), 2, 8, 0); 92 line(MatchesImage, scene_corner[1] + Point2f(src.cols, 0), scene_corner[2] + Point2f(src.cols, 0), Scalar(0, 0, 255), 2, 8, 0); 93 line(MatchesImage, scene_corner[2] + Point2f(src.cols, 0), scene_corner[3] + Point2f(src.cols, 0), Scalar(0, 0, 255), 2, 8, 0); 94 line(MatchesImage, scene_corner[3] + Point2f(src.cols, 0), scene_corner[0] + Point2f(src.cols, 0), Scalar(0, 0, 255), 2, 8, 0); 95 imshow("FLANN Image", MatchesImage); 96 97 line(temp, scene_corner[0], scene_corner[1], Scalar(0, 0, 255), 2, 8, 0); 98 line(temp, scene_corner[1], scene_corner[2], Scalar(0, 0, 255), 2, 8, 0); 99 line(temp, scene_corner[2], scene_corner[3], Scalar(0, 0, 255), 2, 8, 0); 100 line(temp, scene_corner[3], scene_corner[0], Scalar(0, 0, 255), 2, 8, 0); 101 imshow("temp Image", temp); 102 103 waitKey(0); 104 return 0; 105 }