平台:win10 x64 +VS 2015專業版 +opencv-2.4.11 + gtk_-bundle_2.24.10_win32
主要參考:1.代碼:RobHess的SIFT源碼:SIFT+KD樹+BBF算法+RANSAC算法
2.書:王永明 王貴錦 《圖像局部不變性特征與描述》
研究代碼之前的幾個問題issue:
1.問題描述:RobHess和David.Lowe關於SIFT的關系是什么,David.Lowe初創了SIFT算法,但是網絡上為什么流傳的更多的是RobHess的SIFT源碼?為什么研究RobHess的SIFT源碼?
答:參考期刊:王冬,夏乙等.基於SIFT特征的圖像匹配算法實現.計算機與數字工程.2013(281)
2.問題描述:如何實現RobHess的SIFT源碼?去哪里下載?怎么配置?需要下載哪些軟件?
答案:RobHess的SIFT源碼下載地址:http://robwhess.github.io/opensift/
配置及軟件下載具體參看我的另一篇博客:結合OPENSIFT源碼詳解SIFT算法——https://www.cnblogs.com/Alliswell-WP/p/RobHessSIFTOfEnvironment.html
3.問題描述:RobHess的SIFT源碼解析有哪些比較好的博客或資源可以參考?
答案:(1)圖像特征之SIFT:https://blog.csdn.net/u014568921/article/details/52514181
推薦原因:總述類型的(里邊包括好多鏈接:綜述;imgfeatures.h和imgfeatures.c文件;kdtree.h和kdtree.c文件;sift.h和sift.c文件;xform.h和xform.c文件)
(2)RobHess的SIFT源碼分析:綜述:https://blog.csdn.net/masibuaa/article/details/9191309
推薦原因:給了RobHess的SIFT源碼,介紹了RobHess的SIFT源碼中的幾個文件都是做什么用的,方便剛接觸的同學學習;SIFT算法的原理;k-d樹算法的講解;RANSAC算法。
(3)九之再續:教你一步一步用c語言實現sift算法、上:https://blog.csdn.net/v_JULY_v/article/details/6245939
推薦原因:在他寫的關於sift算法的前倆篇文章里頭,已經對sift算法有了初步的介紹:九、圖像特征提取與匹配之SIFT算法,而后在:九(續)、sift算法的編譯與實現里,也簡單記錄下了如何利用opencv,gsl等庫編譯運行sift程序。
缺點:博客作者代碼已經缺失,但推薦加群:169056165,里邊有相關的群資料
(4)【特征匹配】SIFT原理與C源代碼剖析:https://www.cnblogs.com/liguangsunls/p/6785878.html
推薦原因:構建特征描寫敘述子這塊的公式(搜索半徑,旋轉公式,權值累計)比較詳細。
(5)SIFT算法實現及代碼詳解:https://wenku.baidu.com/view/11a564da5022aaea998f0f1e.html
推薦原因:給出了存儲在文件(feature1.txt)中的SIFT特征分析。
4.問題描述:RobHess的SIFT源碼如何復現?個人電腦環境如何配置?
答案:參看我的博客:RobHess的SIFT環境配置:https://www.cnblogs.com/Alliswell-WP/p/RobHessSIFTOfEnvironment.html
5.問題描述:SIFT開源代碼有哪些?
答案:
1)http://www.vlfeat.org vlfeat開源圖像處理庫,其中http://www.vlfeat.org/api/sift.html關於其代碼中一些細節和SIFT原理的解釋。
2)http://robwhess.github.io/opensift/
RobHess sift
3)http://www.cs.ubc.ca/~lowe/research.html
David Lowe Research Projects中的SIFT
4)http://docs.opencv.org/2.4/modules/nonfree/doc/feature_detection.html
OpenCV2.4以后的版本已實現了SIFT,其源碼和RobHess的很相似。
研究代碼中的問題issue匯總:
一、RobHess的SIFT源碼中的幾個文件說明?
(1) imgfeatures.h和imgfeatures.c文件
imgfeatures.h中有SIFT特征點結構struct feature的定義,這個結構很重要,后面都要用到,除此之外還有一些特征點的導入導出以及特征點繪制函數的聲明。
對應的imgfeatures.c文件中是特征點的導入導出以及特征點繪制函數的實現。
(2) utils.h和utils.c文件
這兩個文件中是一些圖像基本操作的函數,包括:
1、獲取某位置的像素點
2、設置某位置的像素點(8位,32位和64位),
3、計算兩點之間的距離的平方
4、在圖片某一點畫一個“X”
5、將兩張圖片合成為一個(在特征匹配中用到),高是二者之和,寬是二者的較大者。
(3) sift.h和sift.c文件
這兩個是最重要的,里面的內容說白了很簡單,就是兩個特征點檢測函數sift_features()和 _sift_features(),
sift_features()是用默認參數進行特征點檢測, _sift_features()允許用戶輸入各種檢測參數,其實sift_features()中也是再次調用_sift_features()函數。
所以,你只需提供原圖像和存儲特征點的數組以及其他一些檢測參數,然后調用sift_features()或 _sift_features()就可完成SIFT特征點檢測。
但是這兩個文件分析起來也是最復雜的。
sift.h中有默認的各種特征檢測中用到的參數的宏定義,sift.c中是檢測函數的實現,是最核心的一個文件,里面的一些未暴露接口的本地函數就有31個之多。
仔細分析完后發現大神就是大神,程序寫的條理非常清楚,尤其是函數_sift_features()的函數體,從頭到尾的幾部分正好對應SIFT算法的幾個步驟,把一些細節全部放在子函數中,非常清楚明了。
(4) minpq.h和minpq.c文件
這兩個文件中實現了最小優先級隊列(Minimizing Priority Queue),也就是小頂堆,在k-d樹的建立和搜索過程中要用到。
(5) kdtree.h和kdtree.c文件
這兩個文件中實現了k-d樹的建立以及用BBF(Best Bin First)算法搜索匹配點的函數。
如果你需要對兩個圖片中的特征點進行匹配,就要用到這兩個文件。
(6) xform.h和xform.c文件
這兩個文件中實現了RANSAC算法(RANdom SAmple Consensus 隨機抽樣一致)。
RANSAC算法可用來篩選兩個圖像間的SIFT特征匹配並計算變換矩陣。
(7) 其他文件:dspfeat.c,match.c,siftfeat.c
這幾個文件中是一些使用SIFT算法的小例子:
dspfeat.c文件可以從預先保存的特征點文件中讀取特征點並顯示在圖片上。
match.c文件可以檢測兩個圖像中的特征點並進行匹配。
siftfeat.c文件利用SIFT算法檢測特征點,有一些控制台操作提示。
二、RobHess的SIFT源碼中的宏定義說明?
(1) imgfeatures.h和imgfeatures.c文件
/*畫出特征點的顏色 */
#define FEATURE_OXFD_COLOR CV_RGB(255,255,0)
#define FEATURE_LOWE_COLOR CV_RGB(255,0,255)
#define FEATURE_MAX_D 128 //最大特征描述子的長度,定為128
//圓周率
#define M_PI 3.14159265358979323846
(2) utils.h和utils.c文件
#define ABS(x) ( ( (x) < 0 )? -(x) : (x) ) //絕對值函數
(3) sift.h和sift.c文件
//高斯金字塔每組內的層數,S=3,書P83
#define SIFT_INTVLS 3
//第0層的初始尺度,即第0層高斯模糊所使用的參數,σo=1.6,書P83
#define SIFT_SIGMA 1.6
//對比度閾值 |D(x)|,針對歸一化后的圖像,用來去除不穩定特征,|D(x)|<0.03,書P87
#define SIFT_CONTR_THR 0.04 //此處與書上的不太一樣,RobHess此處的閾值使用的是0.04/S
//主曲率比值的閾值,用來去除邊緣特征,r=10,書P88
#define SIFT_CURV_THR 10
//是否將圖像放大為之前的兩倍,書P82,圖像在計算高斯尺度空間前先將尺度擴大一倍(第0組o=-1那層索引)
#define SIFT_IMG_DBL 1
//計算特征描述子過程中,計算方向直方圖時,將特征點附近划分為d*d個區域,每個區域生成一個直方圖,SIFT_DESCR_WIDTH即d的默認值,描述子直方圖矩陣默認尺寸Bp=4,書P133
#define SIFT_DESCR_WIDTH 4
//計算特征描述子過程中,每個方向直方圖的bin個數,8個方向的梯度直方圖,書P133
#define SIFT_DESCR_HIST_BINS 8
//輸入圖像的尺度為0.5,高斯模糊/掩模,書P81
#define SIFT_INIT_SIGMA 0.5
//邊界的像素寬度,檢測過程中將忽略邊界線中的極值點,即只檢測邊界線以內是否存在極值點
#define SIFT_IMG_BORDER 5 //書中未涉及
//通過插值進行極值點精確定位時,最大插值次數,即關鍵點修正次數
#define SIFT_MAX_INTERP_STEPS 5 //書中未涉及最多插值多少次
//特征點方向賦值過程中,梯度方向直方圖中柱子(bin)的個數,0度~360度分36個柱,每10度一個柱,書P130
#define SIFT_ORI_HIST_BINS 36
//特征點方向賦值過程中,搜索鄰域的半徑為:3 * 1.5 * σ,分配方向時高斯權重參數因子,高斯加權(1.5σ),書P132
#define SIFT_ORI_SIG_FCTR 1.5
//特征點方向賦值過程中,搜索鄰域的半徑為:3 * 1.5 * σ,分配方向時計算區域大小,根據3sigma外可忽略,圓半徑3σ(3*1.5σ)圖6-2,書P131
#define SIFT_ORI_RADIUS 3.0 * SIFT_ORI_SIG_FCTR
//特征點方向賦值過程中,梯度方向直方圖的平滑次數,計算出梯度直方圖后還要進行高斯平滑
#define SIFT_ORI_SMOOTH_PASSES 2 //書中未涉及具體多少次平滑處理
//特征點方向賦值過程中,梯度幅值達到最大值的80%則分裂為兩個特征點,相當於主峰值80%能量的峰值,書P132
#define SIFT_ORI_PEAK_RATIO 0.8
//計算特征描述子過程中,特征點周圍的d*d個區域中,每個區域的寬度為m*σ個像素,SIFT_DESCR_SCL_FCTR即m的默認值,σ為特征點的尺度,m=3,書P133
#define SIFT_DESCR_SCL_FCTR 3.0
//計算特征描述子過程中,特征描述子向量中元素的閾值(最大值,並且是針對歸一化后的特征描述子),超過此閾值的元素被強行賦值為此閾值,歸一化處理中,對特征矢量中值大於0.2進行截斷(大於0.2只取0.2),書P135
#define SIFT_DESCR_MAG_THR 0.2
//計算特征描述子過程中,將浮點型的特征描述子變為整型時乘以的系數
#define SIFT_INT_DESCR_FCTR 512.0 //書中未涉及
//定義了一個帶參數的函數宏,用來提取參數f中的feature_data成員並轉換為detection_data格式的指針,返回特征檢測數據
#define feat_detection_data(f) ( (struct detection_data*)(f->feature_data) )
(4) minpq.h和minpq.c文件
//要為其分配空間的優先級隊列元素的初始值
#define MINPQ_INIT_NALLOCD 512
(5) kdtree.h和kdtree.c文件
無
(6) xform.h和xform.c文件
/*RANSAC算法的容錯度
對於匹配點對<pt,mpt>,以及變換矩陣H,
如果pt經H變換后的點和mpt之間的距離的平方小於RANSAC_ERR_TOL,則可將其加入當前一致集
*/
#define RANSAC_ERR_TOL 3
//內點數目占樣本總數目的百分比的最小值
#define RANSAC_INLIER_FRAC_EST 0.25
//一個匹配點對支持錯誤模型的概率(不知道是干什么用的)
#define RANSAC_PROB_BAD_SUPP 0.10
//定義了一個帶參數的函數宏,用來提取參數feat中的feature_data成員並轉換為ransac_data格式的指針
#define feat_ransac_data( feat ) ( (struct ransac_data*) (feat)->feature_data )
(7) match.c文件
// 在k-d樹上進行BBF搜索的最大次數,kdtree_bbf_knn函數用到
#define KDTREE_BBF_MAX_NN_CHKS 200 //書P158超時檢查機制,具體多少,書中未涉及
/* 最近點與次最近點距離之比*/
//目標點與最近鄰和次近鄰的距離的比值的閾值,若大於此閾值,則剔除此匹配點對
//通常此值取0.6,值越小找到的匹配點對越精確,但匹配數目越少
#define NN_SQ_DIST_RATIO_THR 0.49 //與代碼不同,判斷閾值THR=0.49,書P163
三、RobHess的SIFT源碼中使用的結構體及存儲說明?按用到的先后順序排列
(1)imgfeatures.h——結構體feature;feature_type;feature_match_type;
imgfeatures.c——無
/*特征點結構體
此結構體可存儲兩種類型的特征點:
FEATURE_OXFD表示是牛津大學VGG提供的源碼中的特征點格式,
FEATURE_LOWE表示是David.Lowe提供的源碼中的特征點格式。
如果是OXFD類型的特征點,結構體中的a,b,c成員描述了特征點周圍的仿射區域(橢圓的參數),即鄰域。
如果是LOWE類型的特征點,結構體中的scl和ori成員描述了特征點的大小和方向。
fwd_match,bck_match,mdl_match一般同時只有一個起作用,用來指明此特征點對應的匹配點
*/
struct feature
{
double x; /*特征點x的坐標 */
double y; /*特征點y的坐標*/
double a; /* OXFD特征點中橢圓的參數 */
double b; /*OXFD特征點中橢圓的參數*/
double c; /*OXFD特征點中橢圓的參數 */
double scl; /*LOWE特征點的尺度*/
double ori; /*LOWE特征點的方向*/
int d; /*特征描述子的長度,即維數,一般是128*/
double descr[FEATURE_MAX_D]; /*128維的特征描述子,即一個double數組*/
int type; /*特征點類型
OXFD or LOWE */
int category; /*通用功能類別 */
struct feature* fwd_match; /*指明此特征點對應的匹配點*/
struct feature* bck_match; /*指明此特征點對應的匹配點*/
struct feature* mdl_match; /*指明此特征點對應的匹配點fwd_match,bck_match,mdl_match一般同時只有一個起作用,用來指明此特征點對應的匹配點*/
CvPoint2D64f img_pt; /*特征點的坐標,等於(x,y)*/
CvPoint2D64f mdl_pt; /*當匹配類型是mdl_match時用到*/
void* feature_data; /*用戶定義的數據:
//在SIFT極值點檢測中,是detection_data結構的指針
//在k-d樹搜索中,是bbf_data結構的指針
//在RANSAC算法中,是ransac_data結構的指針*/
};
/*特征點的類型:
FEATURE_OXFD表示是牛津大學VGG提供的源碼中的特征點格式,
FEATURE_LOWE表示是David.Lowe提供的源碼中的特征點格式
*/
/** FEATURE_OXFD <BR> FEATURE_LOWE */
enum feature_type
{
FEATURE_OXFD,
FEATURE_LOWE,
};
/*特征點匹配類型:
FEATURE_FWD_MATCH:表明feature結構中的fwd_match域是對應的匹配點
FEATURE_BCK_MATCH:表明feature結構中的bck_match域是對應的匹配點
FEATURE_MDL_MATCH:表明feature結構中的mdl_match域是對應的匹配點
*/
enum feature_match_type
{
FEATURE_FWD_MATCH,
FEATURE_BCK_MATCH,
FEATURE_MDL_MATCH,
};
(2)utils.h——無
utils.c——無
(3)sift.h——結構體detection_data
sift.c——無
struct detection_data //保存與檢測有關的特征數據
{
int r; //特征點所在的行
int c; //特征點所在的列
int octv; //特征點所在的組
int intvl; //特征點所在的組中的層
double subintvl; //特征點層方向(sigma方向,即intval方向)的亞像素偏移量
double scl_octv; //特征點所在的組的尺度
};
(4)minpq.h——結構體min_pq和pq_node
minpq.c——無
struct pq_node //最小化優先級隊列中的元素
{
void* data;
int key;
};
struct min_pq //最小化優先級隊列
{
struct pq_node* pq_array; /*數組包含優先級隊列 */
int nallocd; /* 分配的元素數 */
int n; /*pq中的元素數量*/
};
(5)kdtree.h——結構體kd_node
kdtree.c文件——結構體bbf_data
struct kd_node //K-D樹中的結點結構
{
int ki; /*分割位置(樞軸)的維數索引(哪一維是分割位置),取值為1-128*/
double kv; /*樞軸的值(所有特征向量在樞軸索引維數上的分量的中值)*/
int leaf; /*是否葉子結點的標志 1 if node is a leaf, 0 otherwise */
struct feature* features; /*此結點對應的特征點集合(數組)*/
int n; /*特征點的個數*/
struct kd_node* kd_left; /*左子樹*/
struct kd_node* kd_right; /*右子樹*/
};
struct bbf_data
{
double d;
void* old_data;
};
(6)xform.h——結構體ransac_data
xform.c——無
//RANSAC算法中用到的結構
//在RANSAC算法過程中,此類型數據會被賦值給feature結構的feature_data成員
struct ransac_data
{
void* orig_feat_data; //保存此特征點的feature_data域的以前的值
int sampled; //標識位,值為1標識此特征點是否被選為樣本
};
四、RobHess的SIFT源碼中使用的IplImage結構體說明?
參考:IplImage結構體介紹:https://blog.csdn.net/gflytu/article/details/46563159
IplImage結構體:https://wenku.baidu.com/view/cc937992a417866fb94a8ea0.html
SIFT四步驟和特征匹配及篩選:
步驟一:建立尺度空間,即建立高斯差分(DoG)金字塔dog_pyr
步驟二:在尺度空間中檢測極值點,並進行精確定位和篩選創建默認大小的內存存儲器
步驟三:特征點方向賦值,完成此步驟后,每個特征點有三個信息:位置、尺度、方向
步驟一:建立尺度空間,即建立高斯差分(DoG)金字塔dog_pyr
目錄:
1、建立高斯尺度空間金字塔(GSS - Gauss Scale Space)
2、建立高斯差分金字塔(DOG - Difference of Gauss)
1.建立高斯尺度空間金字塔(GSS - Gauss Scale Space)
(1)問題描述:為什么使用高斯尺度空間去構造尺度不變性?怎么考慮的?
答:Koenderink(1984)和Lindeberg(1994)已經證明,在各種合理的假設下,唯一能產生尺度空間的核為高斯核函數,所以我們將圖像的尺度空間表示成一個函數L(x,y,σ),它是由一個變尺度的高斯函數G(x,y,σ)與圖像I(x,y)卷積產生的。即L(x, y, σ) = G(x, y, σ) ∗ I(x, y)
其中*代表卷積操作,G(x, y, σ) 為二維高斯核函數,表示為:
(2)問題描述:目前的尺度分析的方法有哪些?只有圖像金字塔嗎?
答:參考碩士論文第10頁:基於學習的圖像多尺度表達的研究_陽平(北京工業大學)
可見,目前的多尺度分析方法主要包括圖像金字塔結構方法、基於小波變換的多尺度分析方法以及多尺度幾何分析。
(3)問題描述:如何構建高斯金字塔,代碼如何解讀?
答:代碼描述:
/*將原圖轉換為32位灰度圖並歸一化,然后進行一次高斯平滑,
並根據參數img_dbl決定是否將圖像尺寸放大為原圖的2倍*/
init_img = create_init_img( img, img_dbl, sigma );
//計算高斯金字塔的組數octvs,
octvs = log( MIN( init_img->width, init_img->height ) ) / log(2) - 2;
//為了保證連續性,在每一層的頂層繼續用高斯模糊生成3幅圖像,所以高斯金字塔每組
//有intvls+3層,DOG金字塔每組有intvls+2層
//建立高斯金字塔gauss_pyr,是一個octvs*(intvls+3)的圖像數組
gauss_pyr = build_gauss_pyr( init_img, octvs, intvls, sigma );
分析:3.1)為什么需要圖像需要放大為原圖的2倍?怎么放大?
答案:LOWE建議把初始圖像放大二倍,能夠得到很多其它細節,而且覺得圖像在SIFT_INIT_SIGMA=0.5時最清晰,初始高斯尺度為sigm=1.6。
放大步驟:
//調用函數,將輸入圖像轉換為32位灰度圖,並歸一化
gray = convert_to_gray32( img );
#我理解:將3通道8位彩色圖或8位灰度圖轉換為32位灰度圖進行處理,因為32位顏色分辨率大大高於8位單通道的
//將圖像長寬擴展一倍時,便有了底-1層,該層尺度為:
sig_diff = sqrt( sigma * sigma - SIFT_INIT_SIGMA * SIFT_INIT_SIGMA * 4 );
##sig_diff = sqrt( 1.6 * 1.6 - 0.5 *0.5 * 4 )=1.25
//創建放大圖像,調用的opencv的函數cvCreateImage函數表示創建圖像首地址並分配存儲空間,cvSize函數表示矩形尺寸的結構,IPL_DEPTH_32F為32位單精度浮點數,1為單通道
dbl = cvCreateImage( cvSize( img->width*2, img->height*2 ),IPL_DEPTH_32F, 1 );
//放大原圖的尺寸,gray為輸入圖像,dbl為輸出圖像,CV_INTER_CUBIC為插值方法——立方插值
cvResize( gray, dbl, CV_INTER_CUBIC );
//高斯平滑,調用的opencv的cvSmooth函數,高斯核在x,y方向上的標准差都是sig_diff,dbl為輸入圖像,dbl為輸出圖像,CV_GAUSSIAN為平滑方法——高斯平滑
cvSmooth( dbl, dbl, CV_GAUSSIAN, 0, 0, sig_diff, sig_diff );
3.2)convert_to_gray32( IplImage* img )如何將輸入圖像轉換為32位灰度圖,並進行歸一化?
/*將輸入圖像轉換為32位灰度圖,並進行歸一化
參數:
img:輸入圖像,3通道8位彩色圖(BGR)或8位灰度圖
返回值:32位灰度圖*/
static IplImage* convert_to_gray32( IplImage* img )
{
IplImage* gray8, * gray32;
gray32 = cvCreateImage( cvGetSize(img), IPL_DEPTH_32F, 1 );
//首先將原圖轉換為8位單通道圖像
if( img->nChannels == 1 )//若原圖本身就是單通道,直接克隆原圖,調用的opencv的cvClone函數
gray8 = cvClone( img );
else//若原圖是3通道圖像(彩色圖像)
{
gray8 = cvCreateImage( cvGetSize(img), IPL_DEPTH_8U, 1 );//創建8位單通道圖像
cvCvtColor( img, gray8, CV_BGR2GRAY );//調用的opencv的cvCvtColor函數,將原圖轉換為8位單通道圖像
}
//調用的opencv的cvConvertScale函數,然后將8位單通道圖像gray8轉換為32位單通道圖像,並進行歸一化處理(除以255)
cvConvertScale( gray8, gray32, 1.0 / 255.0, 0 );
//釋放臨時圖像
cvReleaseImage( &gray8 );
//返回32位單通道圖像
return gray32;
}
3.3)怎么計算高斯金字塔的組數octvs?
答:
當輸入850*680分辨率的圖片,經過擴大一倍后為1700*1360,min(1700,1360)=1360,log(1360)/log(2)約等於10.4,10.4-2=8.4,因為octvs為int類型,直接去尾法等於8。
參考:https://blog.csdn.net/lingyunxianhe/article/details/79063547中1.1、高斯金字塔
3.4)問題描述:cvSmooth前兩個參數被設置為0,窗口尺寸是多少?
答:函數原型說明:
void cvSmooth( const CvArr* src, CvArr* dst,
int smoothtype=CV_GAUSSIAN,
int param1=3, int param2=0, double param3=0 );
src
輸入圖像.
dst
輸出圖像.
smoothtype
平滑方法:
CV_BLUR_NO_SCALE (簡單不帶尺度變換的模糊) - 對每個象素領域 param1×param2 求和。如果鄰域大小是變化的,可以事先利用函數 cvIntegral 計算積分圖像。
CV_BLUR (simple blur) - 對每個象素鄰域 param1×param2 求和並做尺度變換 1/(param1•param2).
CV_GAUSSIAN (gaussian blur) - 對圖像進行核大小為 param1×param2 的高斯卷積
CV_MEDIAN (median blur) - 發現鄰域 param1×param1 的中值 (i.e. 鄰域是方的).
CV_BILATERAL (雙濾波) - 應用雙向 3x3 濾波,彩色 sigma=param1,空間 sigma=param2. 關於雙向濾波,可參考http://www.dai.ed.ac.uk/CVonline/LOCAL_COPIES/MANDUCHI1/Bilateral_Filtering.html
param1
平滑操作的第一個參數.代表濾波器窗口的寬度;
param2
平滑操作的第二個參數. param2 為零對應簡單的尺度變換和高斯模糊。代表濾波器窗口的高度;
param3
對應高斯參數的 Gaussian sigma (標准差).代表水平方向的sigma值;
如果為零,這由下面的核尺寸計算:
sigma = (n/2 - 1)*0.3 + 0.8, 其中 n=param1 對應水平核,
n=param2 對應垂直核.
對小的卷積核 (3×3 to 7×7) 使用標准 sigma 速度會快。如果 param3 不為零,而 param1 和 param2 為零,則核大小有 sigma 計算 (以保證足夠精確的操作).
param4
對應高斯參數的 Gaussian sigma (標准差).代表垂直方向的sigma值;
注意:如果第三個,第四個參數已經指定,而前兩個參數為0,那么窗口的尺寸由sigma確定(窗口的尺寸會根據sigma值自動確定),速度較慢但最有效
參看博客:1)https://blog.csdn.net/superjimmy/article/details/6179666
2)https://blog.csdn.net/wyl_steven/article/details/6333650
3.5.1)問題描述:gauss_pyr = build_gauss_pyr( init_img, octvs, intvls, sigma );如何構建高斯金字塔
/*根據輸入參數建立高斯金字塔
參數:
base:輸入圖像,作為高斯金字塔的基圖像
octvs:高斯金字塔的組數
intvls:每組的層數
sigma:初始尺度
返回值:高斯金字塔,是一個octvs*(intvls+3)的圖像數組*/
static IplImage*** build_gauss_pyr( IplImage* base, int octvs,
int intvls, double sigma )
{
IplImage*** gauss_pyr;
//為了保證連續性,在每一層的頂層繼續用高斯模糊生成3幅圖像,所以高斯金字塔每組有
//intvls+3層,DOG金字塔每組有intvls+2層
double* sig = calloc( intvls + 3, sizeof(double));//每層的sigma參數數組
double sig_total, sig_prev, k;
int i, o;
//為高斯金字塔gauss_pyr分配空間,共octvs個元素,每個元素是一組圖像的首指針
//calloc-C庫函數在stdlib.h中,分配所需的內存空間,並返回一個指向它的指針,失敗返回NULL
gauss_pyr = calloc( octvs, sizeof( IplImage** ) );
//為第i組圖像gauss_pyr[i]分配空間,共intvls+3個元素,每個元素是一個圖像指針
for( i = 0; i < octvs; i++ )
gauss_pyr[i] = calloc( intvls + 3, sizeof( IplImage* ) );
//計算每次高斯模糊的sigma參數
sig[0] = sigma;//初始尺度
k = pow( 2.0, 1.0 / intvls );
for( i = 1; i < intvls + 3; i++ )
{
sig_prev = pow( k, i - 1 ) * sigma;//i-1層的尺度
sig_total = sig_prev * k;//i層的尺度
sig[i] = sqrt( sig_total * sig_total - sig_prev * sig_prev );
}
//逐組逐層生成高斯金字塔
for( o = 0; o < octvs; o++ )//遍歷組
for( i = 0; i < intvls + 3; i++ )//遍歷層
{
if( o == 0 && i == 0 )//第0組,第0層,就是原圖像
gauss_pyr[o][i] = cvCloneImage(base);
else if( i == 0 )//新的一組的首層圖像是由上一組最后一層圖像下采樣得到
gauss_pyr[o][i] = downsample( gauss_pyr[o-1][intvls] );
else//對上一層圖像進行高斯平滑得到當前層圖像
{ //創建圖像
gauss_pyr[o][i] = cvCreateImage( cvGetSize(gauss_pyr[o][i-1]),IPL_DEPTH_32F, 1 );
//對上一層圖像gauss_pyr[o][i-1]進行參數為sig[i]的高斯平滑,得到當前層圖像
//gauss_pyr[o][i]
cvSmooth( gauss_pyr[o][i-1], gauss_pyr[o][i], CV_GAUSSIAN, 0, 0, sig[i], sig[i] );
}
}
free( sig );//釋放sigma參數數組
return gauss_pyr;//返回高斯金字塔
}
3.5.2)問題描述:每組尺度如何計算?每層尺度如何計算?我們在源代碼上沒有看到組間的2倍的關系?
1)所有空間尺度為:

第1組2^0(σ,kσ,k^2 σ,……,k^(n-1) σ)=(σ,kσ,k^2 σ,……,k^(n-1) σ) =(1.6,2^(1/3) *1.6,2^(2/3) *1.6,,2^(3/3) *1.6,2^(4/3) *1.6,2^(5/3) *1.6)
第2組2^1(σ,kσ,k^2 σ,……,k^(n-1) σ)=(2σ,2kσ,2k^2 σ,……,2k^(n-1) σ) =(2*1.6,2*2^(1/3) *1.6,2*2^(2/3) *1.6,2*2^(3/3) *1.6,2*2^(4/3) *1.6,2*2^(5/3) *1.6)
第3組2^2(σ,kσ,k^2 σ,……,k^(n-1) σ)=(4σ,4kσ,4k^2 σ,……,4k^(n-1) σ) =(4*1.6,4*2^(1/3) *1.6,4*2^(2/3) *1.6,4*2^(3/3) *1.6,4*2^(4/3) *1.6,4*2^(5/3) *1.6)
第4組2^3(σ,kσ,k^2 σ,……,k^(n-1) σ)=(8σ,8kσ,8k^2 σ,……,8k^(n-1) σ) =(8*1.6,8*2^(1/3) *1.6,8*2^(2/3) *1.6,8*2^(3/3) *1.6,8*2^(4/3) *1.6,8*2^(5/3) *1.6)
第5組2^4(σ,kσ,k^2 σ,……,k^(n-1) σ)=(16σ,16kσ,16k^2 σ,……,16k^(n-1) σ)=(16*1.6,16*2^(1/3) *1.6,16*2^(2/3) *1.6,16*2^(3/3) *1.6,16*2^(4/3) *1.6,16*2^(5/3) *1.6)
第6組……
第7組……
第8組……
這個尺度因子都是在原圖上進行的。而在算法實現過程中,采用高斯平滑是在上一層圖像上再疊加高斯平滑。即我們在程序中看到的平滑因子為(4-5)
實際代碼中的計算數值如我下邊的演草紙上算的:
2)我們在源代碼上同一時候也沒有看到組間的2倍的關系,實際在對每組的平滑因子都是一樣的,2倍的關系是因為在降採樣的過程中產生的,第二層的第一張圖是由第一層的平滑因子為2σ的圖像上(即倒數第三張)降採樣得到,此時圖像平滑因子為σ,所以繼續採用以上的平滑因子。
但在原圖上看。形成了所有的空間尺度。
3)參考博客——尺度空間的連續性:https://blog.csdn.net/xw20084898/article/details/16832755
3.6)問題描述: 3.5)構建高斯金字塔如何讓下采樣?
答:代碼描述及說明
/*對輸入圖像做下采樣生成其四分之一大小的圖像(每個維度上減半),使用最近鄰插值方法
參數:
img:輸入圖像
返回值:下采樣后的圖像*/
static IplImage* downsample( IplImage* img )
{
//下采樣圖像
IplImage* smaller = cvCreateImage( cvSize(img->width / 2, img->height / 2), img->depth, img->nChannels ); //創建比原圖小四分之一大小的空矩陣(每個維度上減半)
cvResize( img, smaller, CV_INTER_NN );//尺寸變換,調用的opencv的cvResize函數,img待處理圖像, smaller處理后圖像, CV_INTER_NN最近鄰插值
return smaller;
}
用到的CV_INTER_NN最近鄰插值如何實現?
參看博客:
1)https://blog.csdn.net/zxj820/article/details/50594955
2)http://blog.chinaunix.net/uid-26020768-id-3187769.html
2、建立高斯差分金字塔(DOG - Difference of Gauss)
(1)問題描述:SIFT如何使用DoG?
答:SIFT算法建議,在某一尺度上對特征點的檢測,可以通過對兩個相鄰高斯尺度空間的圖像相減,得到一個DoG (Difference of Gaussians)的響應值圖像D(x,y,σ)。然后,仿照LoG方法,通過對響應值圖像D(x,y,σ)進行非最大值抑制(局部極大搜索,正最大和負最大),在位置空間和尺度空間中定位特征點。其中
D(x, y, σ) = (G(x, y, kσ) − G(x, y, σ)) ∗ I(x, y) = L(x, y, kσ) − L(x, y, σ)
式中,k為相鄰尺度空間倍數的常數。
(2)問題描述:為什么用DoG來檢測特征點?原理是什么?
答:參考博客:https://www.cnblogs.com/JiePro/p/sift_1.html
Lindeberg證明用σ2標准化的高斯拉普拉斯(∇2G, LOG, Laplacian of Gauss)有着真正的尺度無關的特性,而Mikolajczyk發現,相比於其他一系列函數(比如梯度,Hessian,Harris角點函數等),用σ2標准化的高斯拉普拉斯(σ2∇2G)有着更穩定的圖像特征,因此σ2∇2G函數是我們理想的尋找特征點的函數。而在此,我們利用DoG來近似σ2∇2G。
(3)問題描述: 為什么用前一個octave中的倒數第三幅圖像生成下一octave中的第一幅圖像?
答:實現了尺度的平滑過渡。詳細參考博客——尺度空間的連續性:https://blog.csdn.net/xw20084898/article/details/16832755
(4)問題描述:dog_pyr = build_dog_pyr( gauss_pyr, octvs, intvls );如何構建高斯差分(DoG)金字塔dog_pyr
答案:代碼注釋如下:
/*通過對高斯金字塔中每相鄰兩層圖像相減來建立高斯差分金字塔
參數:
gauss_pyr:高斯金字塔
octvs:組數
intvls:每組的層數
返回值:高斯差分金字塔,是一個octvs*(intvls+2)的圖像數組*/
static IplImage*** build_dog_pyr( IplImage*** gauss_pyr, int octvs, int intvls )
{
IplImage*** dog_pyr;
int i, o;
//為高斯差分金字塔分配空間,共octvs個元素,每個元素是一組圖像的首指針
dog_pyr = calloc( octvs, sizeof( IplImage** ) );
//為第i組圖像dog_pyr[i]分配空間,共(intvls+2)個元素,每個元素是一個圖像指針
for( i = 0; i < octvs; i++ )
dog_pyr[i] = calloc( intvls + 2, sizeof(IplImage*) );
//逐組逐層計算差分圖像
for( o = 0; o < octvs; o++ )//遍歷組
for( i = 0; i < intvls + 2; i++ )//遍歷層
{ //創建DoG金字塔的第o組第i層的差分圖像
dog_pyr[o][i] = cvCreateImage( cvGetSize(gauss_pyr[o][i]), IPL_DEPTH_32F, 1 );
//將高斯金字塔的第o組第i+1層圖像和第i層圖像相減來得到DoG金字塔的第o組第i層圖像
cvSub( gauss_pyr[o][i+1], gauss_pyr[o][i], dog_pyr[o][i], NULL );//調用opencv的cvSub函數,gauss_pyr[o][i+1]被減矩陣, gauss_pyr[o][i]減矩陣, dog_pyr[o][i]結果矩陣
}
return dog_pyr;//返回高斯差分金字塔
}
(5)問題描述:高斯核性質及其在SIFT中的應用
答:參考:https://www.cnblogs.com/JiePro/p/sift_1.html
對於二維高斯卷積,有如下性質:
1.距離高斯核中心3σ距離外的系數很小,相對於3σ內的系數值可以忽略不計,所以只用(6σ + 1)*(6σ + 1)的面積計算卷積即可。
2.線性可分,二維高斯核卷積(計算次數O(n2*M*N))效果等於水平和豎直方向的兩個一維高斯核(計算次數O(n*M*N) + O(n*M*N)))累積處理的效果。n*n為濾波器大小,M*N圖像大小
3.對一幅圖像進行多次連續高斯模糊的效果與一次更大的高斯模糊可以產生同樣的效果,大的高斯模糊的半徑是所用多個高斯模糊半徑平方和的平方根。例如,使用半徑分別為 6 和 8 的兩次高斯模糊變換得到的效果等同於一次半徑為 10 (勾股定理)的高斯模糊效果。根據這個關系,使用多個連續較小的高斯模糊處理不會比單個高斯較大處理時間要少。
其中,性質3有助我們更快速的生成GSS。雖然兩個小半徑處理時間並不會比單個高斯較大半徑處理的時間少,但相對於原圖像,我們已經有一個小半徑模糊過的圖像,即可用另一小半徑在現成的圖像上進行模糊得到較大半徑的模糊效果。例如尺度為σ0的圖像已經存在,我們要得到尺度為kσ0的圖像,即可只使用尺度為[(kσ0)2-(σ0)2]1/2的核在尺度為σ0的圖像上進行模糊,即可得到尺度為kσ0模糊的圖像,同理可得到高斯尺度空間各層的圖像,使得高斯尺度空間生產得更快。
附:
(1)問題issue:malloc函數的一些問題?memset函數的一些問題?
參看:1)malloc函數百度百科:https://baike.baidu.com/item/malloc%E5%87%BD%E6%95%B0/8582146?fr=aladdin
2)memset函數百度百科:https://baike.baidu.com/item/memset/4747579?fr=aladdin
3)malloc、calloc、realloc的區別:https://www.cnblogs.com/lidabo/p/4611411.html