版權聲明:本文爲博主原創文章,未經博主允許不得轉載。博客不用於商業活動,博主對博客的使用,擁有最終解釋權本文爲原創作品,未經本人同意,禁止轉載,禁止用於商業用途!本人對博客使用擁有最終解釋權歡迎關注我的網上圖書室:晨鳧追風 和 微信公衆號:青春當追風
這篇博客要做的是對sift的第二個步驟進行介紹和總結。然後對opencv中的源碼進行解釋,因爲對這一部分的理解主要是根據Opencv中的源碼反推的,感謝開源的作者們!
Ok言歸正傳!
Sift的第二個步驟主要是在圖像中找到那些尺度不變性的點,這裏叫做特徵點。這些特徵點是由DOG金字塔中的圖像通過一系列的篩選規則得到的,所以這一步驟就是爲了介紹這些篩選規則,以及用這些篩選規則怎麼選出代表。
首先在前面介紹一些數學的知識吧,這些知識是經過推導得到的一些結論,所以記住它們(微笑臉)。
1.有限差分法求導(這裏沒有推導,也不太嚴謹,不用怕)
這個知識主要是介紹對於圖像中函數的求導法則,介紹的是二元函數的求導。當然在尺度空間中,像素值f是座標(x,y)和尺度σ的三元函數,它的求導法則可以類比。看圖1和公式(1-5)這裏的求導其實可以理解成,每一個像素值對哪一個方向求導,則與其他方向上的無關,就是把其他方向看成一個常數來理解。在同一層的圖像中,與尺度的方向無關,所以把尺度看成一個常數。
圖1
推廣到三元函數,就是增加σ軸向的求導如圖2,這裏把y方向上的看成無關,來求導尺度軸和x方向的關係。
圖2
以此類推y和σ方向上的關係:
圖3
2.三階矩陣求逆的公式
該矩陣存在逆矩陣,則它的行列式不等0,即:
所以
至此所要用到的一部分數學知識就講到這裏。接下來開始講選舉條例!
1.第0輪推舉(閾值檢測)
由於我們不能保證圖片就是完全沒有噪聲的,所以在推選之前我們要排除一些對比度比較低的點,這些點很不穩定,容易受到干擾,本着寧缺毋濫的原則,先把不穩定因子排除。這個閾值一般是自己設定的,在opencv中設定它爲:0.5*T/S,這裏的T lowe 定義它爲0.04,s是該點所在的層。拿到這個入場券的同學,進入下一輪選舉
2.第一輪選舉(極值檢測)
在上一篇博客裏講到了,某某某證明了,高斯差分金字塔裏的極值點比一般的(角點,Hessian,梯度函數)這些方法得到的點性質穩定。所以很自然的就要用到極值點來做候選人。在sift算法裏面極值點的檢測方法是(圖5):
圖5
對圖像中的每一個點進行遍歷,判斷每一個點是否是極值,判斷的標準是,把這個點與相鄰的層,上下層9+9=18個點和同一層中8個點,共18+8=26個點進行比較,看這個點是否是最大值,還是最小值,若是,則保留下來,當做候選人。
3.第二輪選舉(極值點精確定位)
在sift算法中,把尺度看成連續的,而在前面的極值檢測中,只是把它當成離散點來計算,於是極值點的檢測就會出現圖6的情況,這怎麼能符合Lowe同學嚴謹的風格呢?
圖6
所以在這一輪的選舉中,我們的目標就是找到精確的極值點(成仙之前當然要經過考驗)其實這裏的精確點的位置,是一個亞像素級別的概念,比如一個數的個位十位百位都確定了,要更加精細的去找這個數的小數點後的那個數據,就用插值來計算。
這裏又是一系列的數學公式(打公式好麻煩呀!哭!)數學是根本呀!大一高數學不好,本科兵敗如山倒!
言歸正傳!這裏我們需要高數和線性代數的一些知識。高數中提過,函數f(x)在x0處可以進行泰勒展開。於是我們也在這個檢測到的極值點處處,對它進行泰勒級數展開(這裏只展開到二階項,高次項砍掉),以此來擬合這個三維二次函數。於是得到
把上面的式子寫成矢量的形式:
這真是個偉大的公式!,好了我們擬合出來了該點附近的函數了,表示擬合之後連續空間下面的插值點座標,設則表示:相對於插值中心的偏移量,這下公式(13)可以用偏移量表示:
(14)
該咋求極值呢?當然是求導啦!導數爲0的那個點(排除駐點的情況,非要考慮駐點的話,不好意思,我也不會,微笑臉)於是得到下面的求導式子:
(15)
讓導數等於0,就可以得到極值點下的相對於插值中心的偏移量:
(16)
把式(16)代入式(14)可以得到該極值點下的極值爲:
(17)
Ok,其實到這裏,已經把精確的極值點找到了,但是真的是這樣的嗎?答案當然不是。上述的知識只是告訴我們找到偏移量的一個大的方向,真正的細節並沒有說明白!
這裏該注意幾個問題:
-
當求出來的偏移量很大的時候,這時就表明精確的極值點已經完全偏離了離散比較得到的極值點,這時候就得刪除它
-
當求出來的偏移量大於0.5,(只要x,y,和σ任意一個量大於0.5)就表示這個插值點偏離了插值中心,這時候就應該改變插值中心,繼續使用上述的泰勒展開進行擬合,一直到插值偏移量小於0.5,(指的是三個量的偏移都小於0.5)
-
當然在第二個問題中,它的解決辦法是一個迭代的過程,當然它也有自己的迭代停止條件,但是如果要迭代很多次才能找到那個精確點,那說明這個點本身就有自己不穩定的成分,(與問題1類似)所以就要設置一個迭代次數的限制閾值,超過這個閾值,迭代停止!(不是沒給你機會,給你機會,你不珍惜!微笑臉)這個點也得出局!
4.第三輪選舉(低對比度篩選)
在上一步的檢測中我們得到了精確點的位置了,也得到精確點的值了,這時候又有一個問題來了,精確點的值如果很小,那很大程度上是不穩定的點,於是很遺憾,這些精確點應該出局!所以這一輪的標準是:
(18)
論文中T=0.04,s表示處於該組的第幾層。
5.第四輪篩選(消除邊緣效應)
首先恭喜存活下來的選手,來到了最後一輪的篩選了,過了這一關就可以晉級特徵點行列了!
在DOG函數中,它存在比較強的邊緣效應,而當特徵點在邊緣的時候,這些點就會很不穩定,(1是因爲這些點比較難定位,他們具有定位的歧義性。2是因爲這些點容易受到噪聲的干擾而變得不穩定)所以呢,我們應該把這些披着羊皮的狼(邊緣效應很強的點)找出來。啊!又來數學公式!推導!這裏的方法和Harris角點檢測算法的原理相似,即DOG函數欠佳的峯值點附近,在橫跨邊緣的方向上有較大的主曲率,而在垂直於邊緣的方向上具有比較小的主曲率,這個主曲率可以通過Hessian矩陣得到:
(19)
其中 分別表示對DOG圖像的像素在x軸和y軸方向上的二階導數和二階混合偏導數。由於該像素點的主曲率和H的特徵值成正比,該矩陣的特徵值代表着x軸和y軸方向上的梯度。但是爲了避免求解矩陣的特徵值我們只要知道這兩個值的比例就可以得到該點的主曲率。
引入兩個量
(20)
這裏我們先刪除掉那些行列式爲負數的點,即,因爲如果像素的曲率有不同的符號,則該點肯定不會是特徵點。
接着設並且,其中,於是得到:
(21)
上面式子的結果只和兩個特徵值的比例有關,只有在兩個特徵值相等的時候,式(21)才最小,隨着的增大,該式越大,說明兩個特徵值的比值就越大,即在某一方向上的梯度值就越大,而另一方向上的梯度越小,這便是邊緣所符合的情況。所以爲了剔除這些邊緣點,我們讓值小於一定的閾值,因此爲了檢測主曲率是否在某個閾值之下,只需檢測
(22)
對於不滿足式22的點(用擬合的精確點來計算),只有一個原則,刪除!當然這裏需要給出Lowe所建議的值,爲10.
Ok!恭喜!通過上述選舉的同學,滿級!
最後附上OPENCV關於這一部分的源碼解釋
//在DOG金字塔內找到極值點的函數
void SIFT::findScaleSpaceExtrema( const vector<Mat>& gauss_pyr, const vector<Mat>& dog_pyr,
vector<KeyPoint>& keypoints ) const
{
int nOctaves = (int)gauss_pyr.size()/(nOctaveLayers + 3);
int threshold = cvFloor(0.5 * contrastThreshold / nOctaveLayers * 255 * SIFT_FIXPT_SCALE);
const int n = SIFT_ORI_HIST_BINS;
float hist[n];
KeyPoint kpt;
keypoints.clear();
for( int o = 0; o < nOctaves; o++ ) //組的循環遍歷
for( int i = 1; i <= nOctaveLayers; i++ ) //層的循環遍歷
{
int idx = o*(nOctaveLayers+2)+i;
const Mat& img = dog_pyr[idx]; //當前(中間)層尺度圖像
const Mat& prev = dog_pyr[idx-1]; //金字塔下層圖像(底下)
const Mat& next = dog_pyr[idx+1]; //金字塔上層圖像(上方)
int step = (int)img.step1();
int rows = img.rows, cols = img.cols;
for( int r = SIFT_IMG_BORDER; r < rows-SIFT_IMG_BORDER; r++) //行循環遍歷
{
const sift_wt* currptr = img.ptr<sift_wt>(r);
const sift_wt* prevptr = prev.ptr<sift_wt>(r);
const sift_wt* nextptr = next.ptr<sift_wt>(r);
for( int c = SIFT_IMG_BORDER; c < cols-SIFT_IMG_BORDER; c++) //列循環遍歷
{
sift_wt val = currptr[c];
// find local extrema with pixel accuracy
if( std::abs(val) > threshold && //像素大於一定閾值,第一輪選舉
((val > 0 && val >= currptr[c-1] && val >= currptr[c+1] &&
val >= currptr[c-step-1] && val >= currptr[c-step] && val >= currptr[c-step+1] &&
val >= currptr[c+step-1] && val >= currptr[c+step] && val >= currptr[c+step+1] &&
val >= nextptr[c] && val >= nextptr[c-1] && val >= nextptr[c+1] &&
val >= nextptr[c-step-1] && val >= nextptr[c-step] && val >= nextptr[c-step+1] &&
val >= nextptr[c+step-1] && val >= nextptr[c+step] && val >= nextptr[c+step+1] &&
val >= prevptr[c] && val >= prevptr[c-1] && val >= prevptr[c+1] &&
val >= prevptr[c-step-1] && val >= prevptr[c-step] && val >= prevptr[c-step+1] &&
val >= prevptr[c+step-1] && val >= prevptr[c+step] && val >= prevptr[c+step+1]) ||
(val < 0 && val <= currptr[c-1] && val <= currptr[c+1] &&
val <= currptr[c-step-1] && val <= currptr[c-step] && val <= currptr[c-step+1] &&
val <= currptr[c+step-1] && val <= currptr[c+step] && val <= currptr[c+step+1] &&
val <= nextptr[c] && val <= nextptr[c-1] && val <= nextptr[c+1] &&
val <= nextptr[c-step-1] && val <= nextptr[c-step] && val <= nextptr[c-step+1] &&
val <= nextptr[c+step-1] && val <= nextptr[c+step] && val <= nextptr[c+step+1] &&
val <= prevptr[c] && val <= prevptr[c-1] && val <= prevptr[c+1] &&
val <= prevptr[c-step-1] && val <= prevptr[c-step] && val <= prevptr[c-step+1] &&
val <= prevptr[c+step-1] && val <= prevptr[c+step] && val <= prevptr[c+step+1])))
{
int r1 = r, c1 = c, layer = i;
if( !adjustLocalExtrema(dog_pyr, kpt, o, layer, r1, c1,
nOctaveLayers, (float)contrastThreshold,
(float)edgeThreshold, (float)sigma) ) //精確點定位的函數
continue;
float scl_octv = kpt.size*0.5f/(1 << o);
float omax = calcOrientationHist(gauss_pyr[o*(nOctaveLayers+3) + layer],
Point(c1, r1),
cvRound(SIFT_ORI_RADIUS * scl_octv),
SIFT_ORI_SIG_FCTR * scl_octv,
hist, n);
float mag_thr = (float)(omax * SIFT_ORI_PEAK_RATIO);
for( int j = 0; j < n; j++ )
{
int l = j > 0 ? j - 1 : n - 1;
int r2 = j < n-1 ? j + 1 : 0;
if( hist[j] > hist[l] && hist[j] > hist[r2] && hist[j] >= mag_thr )
{
float bin = j + 0.5f * (hist[l]-hist[r2]) / (hist[l] - 2*hist[j] + hist[r2]);
bin = bin < 0 ? n + bin : bin >= n ? bin - n : bin;
kpt.angle = 360.f - (float)((360.f/n) * bin);
if(std::abs(kpt.angle - 360.f) < FLT_EPSILON)
kpt.angle = 0.f;
keypoints.push_back(kpt);
}
}
}
}
}
}
}
//精確點定位函數
//dog_pyr爲DOG金字塔,kpt爲特徵點,octv和layer爲極值點所在的組和層,r和c爲極值點座標
static bool adjustLocalExtrema( const vector<Mat>& dog_pyr, KeyPoint& kpt, int octv,
int& layer, int& r, int& c, int nOctaveLayers,
float contrastThreshold, float edgeThreshold, float sigma )
{
const float img_scale = 1.f/(255*SIFT_FIXPT_SCALE);//先進行歸一化處理,這裏的插值函數都爲[0,1]
const float deriv_scale = img_scale*0.5f; //公式(6)中分母的倒數,這裏h=1
const float second_deriv_scale = img_scale; //公式(7)中分母的倒數,這裏h=1
const float cross_deriv_scale = img_scale*0.25f; //公式(8)中分母的倒數,這裏h=1
float xi=0, xr=0, xc=0, contr=0;
int i = 0;
for( ; i < SIFT_MAX_INTERP_STEPS; i++ ) //SIFT_MAX_INTERP_STEPS表示迭代的次數,爲5次
{
int idx = octv*(nOctaveLayers+2) + layer;
const Mat& img = dog_pyr[idx]; //當前(中間)層尺度圖像
const Mat& prev = dog_pyr[idx-1]; //金字塔下層圖像(底下)
const Mat& next = dog_pyr[idx+1]; //金字塔上層圖像(上方)
//變量dD是對X的一階偏導數公式1,2,6
Vec3f dD((img.at<sift_wt>(r, c+1) - img.at<sift_wt>(r, c-1))*deriv_scale, //對x的一階偏導
(img.at<sift_wt>(r+1, c) - img.at<sift_wt>(r-1, c))*deriv_scale, //對y的一階偏導
(next.at<sift_wt>(r, c) - prev.at<sift_wt>(r, c))*deriv_scale); //對σ的一階偏導
float v2 = (float)img.at<sift_wt>(r, c)*2;
//求二階偏導
float dxx = (img.at<sift_wt>(r, c+1) + img.at<sift_wt>(r, c-1) - v2)*second_deriv_scale;
float dyy = (img.at<sift_wt>(r+1, c) + img.at<sift_wt>(r-1, c) - v2)*second_deriv_scale;
float dss = (next.at<sift_wt>(r, c) + prev.at<sift_wt>(r, c) - v2)*second_deriv_scale;
float dxy = (img.at<sift_wt>(r+1, c+1) - img.at<sift_wt>(r+1, c-1) -
img.at<sift_wt>(r-1, c+1) + img.at<sift_wt>(r-1, c-1))*cross_deriv_scale;
float dxs = (next.at<sift_wt>(r, c+1) - next.at<sift_wt>(r, c-1) -
prev.at<sift_wt>(r, c+1) + prev.at<sift_wt>(r, c-1))*cross_deriv_scale;
float dys = (next.at<sift_wt>(r+1, c) - next.at<sift_wt>(r-1, c) -
prev.at<sift_wt>(r+1, c) + prev.at<sift_wt>(r-1, c))*cross_deriv_scale;
//公式13中的f對向量X求二階導,展開式見公式12
Matx33f H(dxx, dxy, dxs,
dxy, dyy, dys,
dxs, dys, dss);
Vec3f X = H.solve(dD, DECOMP_LU); //公式16
//上面式子和公式16差一個負號,這裏補上
xi = -X[2]; //層偏移量
xr = -X[1]; //縱座標偏移量
xc = -X[0]; //橫座標偏移量
//如果都小於0.5說明找到了極值點,跳出迭代
if( std::abs(xi) < 0.5f && std::abs(xr) < 0.5f && std::abs(xc) < 0.5f )
break;
//如果比一個很大的數還要大,則超過太多,把該點刪除
if( std::abs(xi) > (float)(INT_MAX/3) ||
std::abs(xr) > (float)(INT_MAX/3) ||
std::abs(xc) > (float)(INT_MAX/3) )
return false;
//由偏移量重新定義座標位置,即重新定義插值中心
c += cvRound(xc);
r += cvRound(xr);
layer += cvRound(xi);
//如果超出金字塔的座標範圍,也說明不是極值點,也要刪除
if( layer < 1 || layer > nOctaveLayers ||
c < SIFT_IMG_BORDER || c >= img.cols - SIFT_IMG_BORDER ||
r < SIFT_IMG_BORDER || r >= img.rows - SIFT_IMG_BORDER )
return false;
}
//再次確認沒有超過迭代次數
// ensure convergence of interpolation
if( i >= SIFT_MAX_INTERP_STEPS )
return false;
{
int idx = octv*(nOctaveLayers+2) + layer; //更新精確點的位置
const Mat& img = dog_pyr[idx];
const Mat& prev = dog_pyr[idx-1];
const Mat& next = dog_pyr[idx+1];
//第三輪篩選
Matx31f dD((img.at<sift_wt>(r, c+1) - img.at<sift_wt>(r, c-1))*deriv_scale,
(img.at<sift_wt>(r+1, c) - img.at<sift_wt>(r-1, c))*deriv_scale,
(next.at<sift_wt>(r, c) - prev.at<sift_wt>(r, c))*deriv_scale);
float t = dD.dot(Matx31f(xc, xr, xi));
contr = img.at<sift_wt>(r, c)*img_scale + t * 0.5f; //對比度檢測公式18
if( std::abs( contr ) * nOctaveLayers < contrastThreshold )
return false;
//第4輪篩選
// principal curvatures are computed using the trace and det of Hessian
float v2 = img.at<sift_wt>(r, c)*2.f;
float dxx = (img.at<sift_wt>(r, c+1) + img.at<sift_wt>(r, c-1) - v2)*second_deriv_scale;
float dyy = (img.at<sift_wt>(r+1, c) + img.at<sift_wt>(r-1, c) - v2)*second_deriv_scale;
float dxy = (img.at<sift_wt>(r+1, c+1) - img.at<sift_wt>(r+1, c-1) -
img.at<sift_wt>(r-1, c+1) + img.at<sift_wt>(r-1, c-1)) * cross_deriv_scale;
float tr = dxx + dyy;
float det = dxx * dyy - dxy * dxy;
if( det <= 0 || tr*tr*edgeThreshold >= (edgeThreshold + 1)*(edgeThreshold + 1)*det )
return false;
}
kpt.pt.x = (c + xc) * (1 << octv); //保存特徵點的位置,尺度,這裏的位置是針對擴展的那個最底層金字塔的座標而言的
kpt.pt.y = (r + xr) * (1 << octv);
kpt.octave = octv + (layer << 8) + (cvRound((xi + 0.5)*255) << 16);
kpt.size = sigma*powf(2.f, (layer + xi) / nOctaveLayers)*(1 << octv)*2;
kpt.response = std::abs(contr);
return true;
}
下面是matlab代碼,這部分代碼是承接上一篇博客的下一部分
% 下一步是查找差分高斯金字塔中的局部極值,並通過曲率和照度進行檢驗
curvature_threshold = ((curvature_threshold + 1)^2)/curvature_threshold;
% 二階微分核
xx = [ 1 -2 1 ];
yy = xx';
xy = [ 1 0 -1; 0 0 0; -1 0 1 ]/4;
raw_keypoints = [];%%原始特徵點
contrast_keypoints = [];%%對比度
curve_keypoints = [];%%曲率
% 在高斯金字塔中查找局部極值
if interactive >= 1
fprintf( 2, 'Locating keypoints...\n' );
end
tic;
loc = cell(size(DOG_pyr));
for octave = 1:octaves
if interactive >= 1
fprintf( 2, '\tProcessing octave %d\n', octave );
end
for interval = 2:(intervals+1)
keypoint_count = 0;
contrast_mask = abs(DOG_pyr{octave}(:,:,interval)) >= contrast_threshold;
loc{octave,interval} = zeros(size(DOG_pyr{octave}(:,:,interval)));
if exist('corrsep') == 3
edge = 1;
else
edge = ceil(filter_size(octave,interval)/2);
end
for y=(1+edge):(size(DOG_pyr{octave}(:,:,interval),1)-edge)
y_size = size(DOG_pyr{octave}(:,:,interval),1)-edge;
for x=(1+edge):(size(DOG_pyr{octave}(:,:,interval),2)-edge)
x_size = size(DOG_pyr{octave}(:,:,interval),2)-edge;
if object_mask(round(y*subsample(octave)),round(x*subsample(octave))) == 1
if( (interactive >= 2) | (contrast_mask(y,x) == 1) )
% 通過空間核尺度檢測最大值和最小值
tmp = DOG_pyr{octave}((y-1):(y+1),(x-1):(x+1),(interval-1):(interval+1)); %%取出該位置的上下3層,共27個點
pt_val = tmp(2,2,2); %%該點是中間的待檢測點第二層第二排第二列
if( (pt_val == min(tmp(:))) | (pt_val == max(tmp(:))) )
% % 存儲極值點
% raw_keypoints = [raw_keypoints; x*subsample(octave) y*subsample(octave)]; %%乘以採樣的數,還原到原始圖片的座標
%%迭代插值
for i=1:interactive
Ddx = (DOG_pyr{octave}(y,(x+1),interval)-DOG_pyr{octave}(y,(x-1),interval))*0.5;
Ddy = (DOG_pyr{octave}(y+1,x,interval)-DOG_pyr{octave}(y-1,x,interval))*0.5;
Ddsigama = (DOG_pyr{octave}(y,x,interval+1)-DOG_pyr{octave}(y,x,interval-1))*0.5;
v2 = DOG_pyr{octave}(y,x,interval)*2;
dxx = (DOG_pyr{octave}(y,(x+1),interval)-DOG_pyr{octave}(y,(x-1),interval) - v2);
dyy = (DOG_pyr{octave}(y+1,x,interval)-DOG_pyr{octave}(y-1,x,interval) - v2);
dss = (DOG_pyr{octave}(y,x,interval+1)-DOG_pyr{octave}(y,x,interval-1)-v2);
dxy = (DOG_pyr{octave}(y+1,x+1,interval)-DOG_pyr{octave}(y+1,x-1,interval)-DOG_pyr{octave}(y-1,x+1,interval)+DOG_pyr{octave}(y-1,x-1,interval))*0.25;
dxs = (DOG_pyr{octave}(y,x+1,interval+1)-DOG_pyr{octave}(y,x-1,interval+1)-DOG_pyr{octave}(y,x+1,interval-1)+DOG_pyr{octave}(y,x-1,interval-1))*0.25;
dys = (DOG_pyr{octave}(y+1,x,interval+1)-DOG_pyr{octave}(y-1,x,interval+1)-DOG_pyr{octave}(y+1,x,interval-1)+DOG_pyr{octave}(y-1,x,interval-1))*0.25;
HH = [dxx,dxy,dxs;dxy,dyy,dys;dxs,dys,dss];
D = [Ddx,Ddy,Ddsigama];
X = inv(HH)*D';
xi = -X(3);
xr = -X(2);
xc = -X(1);
if (abs(xi)<0.5&abs(xr)<0.5&abs(xc)<0.5)
break;
end
if (abs(xi)>2147483647|abs(xr)>2147483647|abs(xc)>2147483647)
i = interactive + 1;
break;
end
x = x+ round(xc);
y= y + round(xr);
interval = interval + round(xi);
if(interval<1|interval>(intervals+1)|x<edge|x>x_size|y<edge|y>y_size)
i = interactive + 1;
break;
end
end
if(i<interactive)
Ddx = (DOG_pyr{octave}(y,(x+1),interval)-DOG_pyr{octave}(y,(x-1),interval))*0.5;
Ddy = (DOG_pyr{octave}(y+1,x,interval)-DOG_pyr{octave}(y-1,x,interval))*0.5;
Ddsigama = (DOG_pyr{octave}(y,x,interval+1)-DOG_pyr{octave}(y,x,interval-1))*0.5;
D = [Ddx,Ddy,Ddsigama];
t = (D) * (X) ;
contr = DOG_pyr{octave}(y,x,interval) + 0.5*t;
% 存儲對灰度大於對比度閾值的點的座標
if (abs(contr) >= contrast_threshold)
raw_keypoints = [raw_keypoints; x*subsample(octave) y*subsample(octave)]; %%乘以採樣的數,還原到原始圖片的座標
contrast_keypoints = [contrast_keypoints; raw_keypoints(end,:)];%%存儲的是最後一個的值,即最新檢測到的值
% 計算局部極值的Hessian矩陣
Dxx = sum(DOG_pyr{octave}(y,x-1:x+1,interval) .* xx);
Dyy = sum(DOG_pyr{octave}(y-1:y+1,x,interval) .* yy);
Dxy = sum(sum(DOG_pyr{octave}(y-1:y+1,x-1:x+1,interval) .* xy));
% 計算Hessian矩陣的直跡和行列式.
Tr_H = Dxx + Dyy;
Det_H = Dxx*Dyy - Dxy^2;
% 計算主曲率.
curvature_ratio = (Tr_H^2)/Det_H;
if ((Det_H >= 0) & (curvature_ratio < curvature_threshold))
% 存儲主曲率小於閾值的的極值點的座標(非邊緣點)
curve_keypoints = [curve_keypoints; raw_keypoints(end,:)];
% 將該點的位置的座標設爲1,並計算點的數量.
loc{octave,interval}(y,x) = 1;
keypoint_count = keypoint_count + 1;
end
end
end
end
end
end
end
end
if interactive >= 1
fprintf( 2, '\t\t%d keypoints found on interval %d\n', keypoint_count, interval );
end
end
end
keypoint_time = toc;
if interactive >= 1
fprintf( 2, 'Keypoint location time %.2f seconds.\n', keypoint_time );
end
% 在交互模式下顯示特徵點檢測的結果.
if interactive >= 2
fig = figure;
clf;
imshow(im);
hold on;
plot(raw_keypoints(:,1),raw_keypoints(:,2),'y+');
% resizeImageFig( fig, size(im), 2 );
fprintf( 2, 'DOG extrema (2x scale).\nPress any key to continue.\n' );
% pause;
% close(fig);
fig = figure;
% clf;
imshow(im);
hold on;
plot(contrast_keypoints(:,1),contrast_keypoints(:,2),'y+');
% resizeImageFig( fig, size(im), 2 );
fprintf( 2, 'Keypoints after removing low contrast extrema (2x scale).\nPress any key to continue.\n' );
% pause;
% close(fig);
fig = figure;
% clf;
imshow(im);
hold on;
plot(curve_keypoints(:,1),curve_keypoints(:,2),'y+');
% resizeImageFig( fig, size(im), 2 );
fprintf( 2, 'Keypoints after removing edge points using principal curvature filtering (2x scale).\nPress any key to continue.\n' );
% pause;
% close(fig);
end
clear raw_keypoints contrast_keypoints curve_keypoints
經過前面三輪選舉得到:
經過第四輪選舉得到:
本文爲原創作品,未經本人同意,禁止轉載,禁止用於商業用途!本人對博客使用擁有最終解釋權