本博客內容來源于網(wǎng)絡以及其他書籍,結合自己學習的心得進行重編輯,因為看了很多文章不便一一標注引用,如圖片文字等侵權,請告知刪除。
傳統(tǒng)2D計算機視覺學習筆記目錄------->傳送門
傳統(tǒng)3D計算機視覺學習筆記目錄------->傳送門
前言
本篇的上一篇文章描寫的是SIFT算法SIFT算法學習筆記(上) ,本篇博主在SIFT算法詳細內容還很清晰的情況下,再描述一下SIFT的優(yōu)化算法-- SURF算法。因為SURF是SIFT的優(yōu)化算法,所以本文只描述與SIFT不同的地方,與SIFT相同的地方就不再重復描述,因而在讀本文之前,希望能夠看完SIFT算法的文章,或則對SIFT足夠理解。那么開始吧?。?!

SURF 簡介
SURF(Speeded-Up Robust Features)加速穩(wěn)健特征,是一種穩(wěn)健的局部特征點檢測和描述算法。最初由Herbert Bay發(fā)表在2006年的歐洲計算機視覺國際會議ECCV上,并在2008年正式發(fā)表在Computer Vision and Image Understanding期刊上。
Surf是可以說是對Sift算法的改進,該算子在保持 SIFT 算子優(yōu)良性能特點的基礎上,同時解決了 SIFT 計算復雜度高、耗時長的缺點,提升了算法的執(zhí)行效率,為算法在實時計算機視覺系統(tǒng)中應用提供了可能,sift在一般的計算機對于日常使用的圖片想做到實時基本上是不可能的。
下面我們來看一下SURF到底對SIFT做了什么改進,是怎么繼續(xù)保證SIFT的優(yōu)秀的效果的以及SURF和SIFT有什么效果上的區(qū)別。
SURF 流程
與Sift算法一樣,Surf算法的基本路程也可以分為四大部分:尺度空間建立、特征點定位、特征點方向確定,特征點描述。
基本的流程都是一樣的,那么SURF是怎么改進其執(zhí)行效率的呢?主要還是在兩個關鍵的優(yōu)化點:
- 積分圖在Hessian(黑塞矩陣)上的使用
- 降維的特征描述子的使用
在梳理SURF的流程之前,我們先了解幾個概念:積分圖,Hessian矩陣,harr小波特征。
-
積分圖
我們知道圖像是由一系列的離散像素點組成, 所以圖像的積分其實就是所有的像素點求和. 圖像積分圖中每個點的值是原圖像中該點左上角的所有像素值之和。



其中I(x,y)為像素點(x,y)的像素值。增量的計算方式,可以保證其計算速度。概念很簡單,理解沒有問題,具體怎么用我們之后在流程中再解釋。
-
Hessian矩陣
這個好像很熟悉?我們的確在之前的幾篇文章中,見過很多次這個矩陣了,包括在SIFT去除邊緣響應?,F(xiàn)在我們詳細解釋一下。
所謂 Hessian矩陣 就是一個多元函數(shù)的二階偏導數(shù)構成的方陣,描述了函數(shù)的局部曲率。此外還有Jacob矩陣也是描述函數(shù)的局部曲率的,是一個多元函數(shù)的一階偏導數(shù)構成的方陣。

Hessian 就是描述的一個點周圍像素梯度大小的變化率,其極值就是生成圖像穩(wěn)定的邊緣點(突變點),其兩個特征值,代表這其在兩個相互垂直方向是的梯度的變化率,當兩個特征值越大時,其圖像中的像素點的像素值波動越大。我們用兩個特征值相加即 Hessian的判別式(即行列式的值)來量化。
-
harr小波特征
這個還挺難解釋的,個人也還是不是理解的那么好,而且越看越迷糊,下次通過一個文章來寫吧。先立個flag
在我們了解了上述內容,我們來看接下來的步驟。
1. 構建尺度空間
同SIFT算法一樣,SURF算法的尺度空間由??組??層組成,不同的是,SIFT算法下一組圖像的長寬均是上一組的一半,同一組不同層圖像之間尺寸一樣,但是所使用的尺度空間因子(高斯模糊系數(shù)??)逐漸增大;而在SURF算法中,不同組間圖像的尺寸都是一致的,不同的是不同組間使用的盒式濾波器的模板尺寸逐漸增大,同一組不同層圖像使用相同尺寸的濾波器,但是濾波器的尺度空間因子(即高斯模糊系數(shù)??)逐漸增大。
左圖表示的是傳統(tǒng)方式建立一個如圖所看到的的金字塔結構。圖像的大小是變化的。而且運 算會重復使用高斯函數(shù)對子層進行平滑處理。右圖說明Surf算法使原始圖像保持不變而僅僅改變?yōu)V波器大小。Surf採用這樣的方法節(jié)省了降採樣過程,其處理速度自然也就提上去了。
-
SURF中盒式濾波器解釋
在構建尺度空間的時候,我們使用剛才解釋的Hessian矩陣,但是在構造Hessian矩陣前需要對圖像進行高斯濾波,經(jīng)過濾波后的Hessian矩陣表述為:
圖中,Lxx(x,??) 是對高斯函數(shù)進行x方向的二次導數(shù),其他的類似。
Hessian矩陣判別式中的L(x,y)是原始圖像的高斯卷積,由于高斯核實服從正態(tài)分布的,從中心點往外,系數(shù)越來越低,為了提高運算速度,Surf使用了盒式濾波器來近似替代高斯濾波器,所以在Dxy上乘了一個加權系數(shù)0.9,目的是為了平衡因使用盒式濾波器近似所帶來的誤差:
其實,當Hessian矩陣的判別式取得局部極大值時,判定當前點是比周圍鄰域內其他點更亮或更暗的點,由此來定位關鍵點的位置。那怎么用用合適濾波器來近似高斯濾波器呢?有為什么能提高速度呢?上邊兩幅圖是9*9高斯濾波器模板分別在圖像上垂直方向上二階導數(shù)Dyy和Dxy對應的值,下邊兩幅圖是使用盒式濾波器對其近似,灰色部分的像素值為0,黑色為-2,白色為1。
盒式濾波器對圖像的濾波轉化成計算圖像上不同區(qū)域間像素和的加減運算問題,這正是積分圖的強項,只需要簡單幾次查找積分圖就可以完成。這樣就大大的提高了構造尺度空間的速度。
支持我們已經(jīng)解釋了surf是怎么構造尺度空間的并能達到和sift類似的效果,為什么要按這種方式構造尺度的,有怎么提高構造尺度空間的速度的。
2. 特征點定位
這一部分和SIFT查找方式一樣,找到尺度空間的局域極大值,然后刪除響應比較弱的關鍵點以及錯誤定位的關鍵點,以及進行亞像素分析。
3. 特征點方向確認
Sift特征點方向分配是采用在特征點鄰域內統(tǒng)計其梯度直方圖,取直方圖最大值的以及超過最大值80%的那些方向作為特征點的主方向。

這樣做的有點就是統(tǒng)計的方向更加精確,但是計算時間會略有上升,但是畢竟計算的只有關鍵點,可忽略不計。
4. 生成特征點描述向量
在SIFT中,是提取特征點周圍44個區(qū)域塊,統(tǒng)計每小塊內8個梯度方向,用著44*8=128維向量作為Sift特征的描述子。
SURF算法中,也是在特征點周圍取一個4*4的矩形區(qū)域塊,但是所取得矩形區(qū)域方向是沿著特征點的主方向。每個子區(qū)域統(tǒng)計25個像素的水平方向和垂直方向的haar小波特征,這里的水平和垂直方向都是相對主方向而言的。該haar小波特征為水平方向值之后、垂直方向值之后、水平方向絕對值之后以及垂直方向絕對值之和4個方向。該過程示意圖如下:這樣SURF提取的就是一個444 的64維的特征向量。
OpenCV SURF特征效果展示[代碼]
#include <opencv2/opencv.hpp>
#include <iostream>
#include <opencv2/xfeatures2d.hpp>
void extracte_surf(cv::Mat input,std::vector<cv::KeyPoint> &keypoint,cv::Mat &descriptor){
cv::Ptr<cv::Feature2D> f2d = cv::xfeatures2d::SURF::create();
f2d->detect(input,keypoint);
cv::Mat image_with_kp;
f2d->compute(input,keypoint,descriptor);
cv::drawKeypoints(input, keypoint, image_with_kp, cv::Scalar::all(-1),cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
cv::imwrite("surf"+std::to_string(keypoint.size())+".png",image_with_kp);
}
void match_two_image(cv::Mat image1,cv::Mat image2, std::vector<cv::KeyPoint> keypoint1,std::vector<cv::KeyPoint> keypoint2,cv::Mat descriptor1,cv::Mat descriptor2){
cv::FlannBasedMatcher matcher;
std::vector<cv::DMatch> matches, goodmatches;
matcher.match(descriptor1,descriptor2, matches);
cv::Mat good_matches_image;
double max_dist = 0; double min_dist = 1000;
for (int i = 0; i < descriptor1.rows; i++) {
if (matches[i].distance > max_dist) {
max_dist = matches[i].distance;
}
if (matches[i].distance < min_dist) {
min_dist = matches[i].distance;
}
}
for (int i = 0; i < descriptor1.rows; i++) {
if (matches[i].distance < 6 * min_dist) {
goodmatches.push_back(matches[i]);
}
}
cv::drawMatches(image1, keypoint1, image2, keypoint2,
goodmatches, good_matches_image, cv::Scalar::all(-1), cv::Scalar::all(-1),
std::vector<char>(), cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
cv::imwrite("good_matches_image.png",good_matches_image);
{
std::vector <cv::KeyPoint> RAN_KP1, RAN_KP2;
std::vector<cv::Point2f> keypoints1, keypoints2;
for (int i = 0; i < goodmatches.size(); i++) {
keypoints1.push_back(keypoint1[goodmatches[i].queryIdx].pt);
keypoints2.push_back(keypoint2[goodmatches[i].trainIdx].pt);
RAN_KP1.push_back(keypoint1[goodmatches[i].queryIdx]);
RAN_KP2.push_back(keypoint2[goodmatches[i].trainIdx]);
}
std::vector<uchar> RansacStatus;
cv::findFundamentalMat(keypoints1, keypoints2, RansacStatus, cv::FM_RANSAC);
std::vector <cv::KeyPoint> ransac_keypoints1, ransac_keypoints2;
std::vector <cv::DMatch> ransac_matches;
int index = 0;
for (size_t i = 0; i < goodmatches.size(); i++)
{
if (RansacStatus[i] != 0)
{
ransac_keypoints1.push_back(RAN_KP1[i]);
ransac_keypoints2.push_back(RAN_KP2[i]);
goodmatches[i].queryIdx = index;
goodmatches[i].trainIdx = index;
ransac_matches.push_back(goodmatches[i]);
index++;
}
}
cv::Mat after_ransac_sift_match;
cv::drawMatches(image1, ransac_keypoints1, image2, ransac_keypoints2,
ransac_matches, after_ransac_sift_match, cv::Scalar::all(-1), cv::Scalar::all(-1),
std::vector<char>(), cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
cv::imwrite("after_ransac_surf_match.png",after_ransac_sift_match);
}
}
int main(int argc, char *argv[])
{
cv::Mat image1 = cv::imread(argv[1]);
cv::Mat image2 = cv::imread(argv[2]);
std::vector<cv::KeyPoint> keypoint1,keypoint2;
cv::Mat descriptor1, descriptor2;
extracte_surf(image1,keypoint1,descriptor1);
extracte_surf(image2,keypoint2,descriptor2);
match_two_image(image1,image2,keypoint1,keypoint2,descriptor1,descriptor2);
return 0;
}
圖片還是SIFT文章中使用的圖片
| 原圖1 | 原圖2 |
|---|---|
![]() |
![]() |
| 原圖1提取SURF | 原圖2提取SURF |
![]() |
![]() |

總結
surf如何保證那些sift類似的那些特點的,按照sift的想法還是很容易分析的,這里就不一一分析。
從實驗結果上看,surf的特征點的提取效果還是不錯的,特征點的數(shù)目也很多,而且時間上有大大的優(yōu)化。論文《A comparison of SIFT, PCA-SIFT and SURF 》對三種方法給出了性能上的比較,結論是:SIFT在尺度和旋轉變換的情況下效果最好,SURF在亮度變化下匹配效果最好,在模糊方面優(yōu)于SIFT,而尺度和旋轉的變化不及SIFT,旋轉不變上比SIFT差很多。速度上看,SURF是SIFT速度的3倍。
重要的事情說三遍:
如果我的文章對您有所幫助,那就點贊加個關注唄 ( * ^ __ ^ * )
如果我的文章對您有所幫助,那就點贊加個關注唄 ( * ^ __ ^ * )
如果我的文章對您有所幫助,那就點贊加個關注唄 ( * ^ __ ^ * )
傳統(tǒng)2D計算機視覺學習筆記目錄------->傳送門
傳統(tǒng)3D計算機視覺學習筆記目錄------->傳送門
任何人或團體、機構全部轉載或者部分轉載、摘錄,請保留本博客鏈接或標注來源。博客地址:開飛機的喬巴
作者簡介:開飛機的喬巴(WeChat:zhangzheng-thu),現(xiàn)主要從事機器人抓取視覺系統(tǒng)以及三維重建等3D視覺相關方面,另外對slam以及深度學習技術也頗感興趣,歡迎加我微信或留言交流相關工作。






