sift的java实现解述
代碼已經開源到github上,https://github.com/alibaba/simpleimage項目,其中的?analyze模塊中。
原始圖片為:
主要調用方法:
[java]?view plaincopy同樣可以再讀另一個張圖得到另一個 List<KDFeaturePoint> al1,然后兩個List進行match
[java]?view plaincopy
先從上面的調用入口,詳細講解圖sift的特征點生,至于match,有空再說。其中最難理解的是極值點的查找,主要對這部分講解。
一.構建尺度空間,檢測極值點,獲得尺度不變性
ImagePixelArray就是保存一張圖片的象素點進行灰度化后的數組。在RenderImage的toPixelFloatArray方法中默認灰度處理只是簡單將rgb的值換成r,g,b的平均值并做歸一化處理(除以255);
從調用入口我們進入主類SIFT的用戶接口detectFeatures方法,它實際調用了detectFeaturesDownscaled(ImageMap img, int bothDimHi, double startScale) ,我們看隨了開始的幾行init工作,最后對圖片使用preprocSigma(1.5)的參數進行高斯模數預處理。
[java]?view plaincopy經過上面的預處理效果為:
現在我們還看不出強烈的效果,僅是pre的處理了一下。再接著使用Pyramid的buildOctaves方法構建8度金字塔,我們跟蹤到方法內。
[java]?view plaincopy
先不看????????????
?
整個方法就是以原圖為基礎不斷地構造圖層,這里也是高斯金字塔最微妙的地方。尺度空間概念不是簡單的不同大小尺寸組成的尖塔,也不是相同大小不同模糊因子處理過的直方塔,其實說是金字塔不太準確,更象中國傳統的寶塔。
它首先由原始圖象根據不同的模糊因子進行模糊(說成是平滑更確切,就是把比較相近的顏色值讓他們更相近以便突出反差很強的點),這是在同一尺寸上做的。這些相同尺寸不同高斯模糊因子處理過的圖像集合叫一個8度空間。相當于寶塔中的一層,然后再向下采樣,即以其中一幅進行1/2縮小作為原圖再進行另一個8度空間的高斯模糊處理,直到圖層的width或hight小于minSize(32),這樣不斷模糊并向下采樣的構成的所有8度空間的集合才叫高斯金字塔(高斯寶塔?)。這是為了能檢測到原圖的某一點在不同尺度上都有穩定的特征。
????????????然后我們回頭來看osp.makeGaussianImgs方法。對1個8度空間原始圖象,以不同的 sigma 參數構建多張高斯模糊圖。因為最底層的原圖最大,我選了塔中scale為2的那一個8度間空的smoothedImgs.
可以看出隨著模糊因子變化模糊程度在加大。在塔中的每一個8度空間得到了一個smoothedImgs?數組。這里一共生成6幅圖像,原因在方法內部有注釋,我們最終要在3層的差分尺度中獲取極值點,而每個尺度獲取極值點都要在立體空間(這也是sift最區別于其它特征的革命性突破)上比較,即要比較它上一幅和下一幅的對應點,那么3層的尺度至少要五幅差分圖像,而五幅差分圖像至少要6幅高斯模糊圖象才能生成。
然后對smoothedImgs中的圖象通過osp.makeGaussianDiffImgs();依次求差放入diffImags數組中。
對于使用不同參數進行模糊的兩張圖片,象素相近的連片部分差值極小,只有邊緣,轉角等特征的點表現出較大的差值:
先不要在意連片的黑色,因為求差后的值很小并已經做了歸一化,我把它還原到圖片上時進行絕對值(可能為負)乘10然后模 255,以便清楚地顯示出來。可以看出這些圖片中特征強烈的點都是邊緣,轉角等地方。
二.特征點過濾并進行精確定位
現在回到detectFeaturesDownscaled,經過是面的處理,金字塔中每個8度空間上都有一個OctaveSpace對象保存著一個差分圖象數組。下面的findPeaks其實就是從第2個圖象開始到倒數第二個圖象循環,當前圖象上每一個點和周圍的點比較,如果是最大值或是最小值就視為極值點。(這里的周圍是立體的周圍,不僅和當前圖像上點周圍的8個點比較,還要和他上一幅和下一幅對應的9個點比較)
[java]?view plaincopy回到detectFeaturesDownscaled,下面的filterAndLocalizePeaks方法主要是根據原始論文的page12/13的方法進行過慮和精確定位。
isTooEdgelike是對太象邊緣的點進行過慮,這個意思就是連續的線不是好的極值點,只有角點這樣的孤立的點才是極值點,為什么“太象邊緣”的連續的線不好呢?邊緣點的特點是沿邊緣方向的梯度很小,簡單說一條線的點差別不大,而和它相切的方向梯度很大。由于梯度大它們很容易成為極值點,但因為沿線的兩個點之間梯度又幾乎沒有區別,極值點本身是局部特征,所以邊緣線上兩個點對于某極值進行投射時根本無法區別是點1還是點2.所以要把這些“線性連續點去掉”。
在過慮掉“太象邊緣”的點后,下面的localizeIsWeak就精確化每個尺度空間上的極值點,因為極值點是在連續的尺度空間中計算出來的非常精確化的坐標比如(0.12345678,0.23456789),而原始圖象的點是以整數為坐標的,相對尺度空間的坐標而言是散列的。而極值點最終要映射到原始圖像的整數坐標上,所以要有一個調整過程。
根據極值點的坐標和sigma參數,主要是三元一,二價導數和亞象素計算。這樣精確匹配到原始點后會得到一個原始點的坐標,sigma參數和調整值 local.dValue,下面的過慮條件就簡單了:
??????????? if (Math.abs(peak.local.scaleAdjust) > scaleAdjustThresh) continue;??????????? if (Math.abs(peak.local.dValue) <= dValueLoThresh) continue;
簡單說當匹配到原始點在某一范圍之外的都過慮掉,注意原始論文上建議dValueLoThresh為0.03,這里實際是0.008。
三.為每個關鍵點指定方向參數
查找極值點的工作都完成了,下面就是對這些點和周轉的點比較生成一些向量:
在生成關鍵的方向和梯度時,我們用一個pretreatMagnitudeAndDirectionImgs方法把差分圖上所有點的梯度方向和梯度值先計算出來,因為特征點的方向最終是它周圍的64個點的梯度方向梯值加權計算出來的,這樣每一點可能被多個特征點作為“周圍點”,如果當作為周圍點才計算某點的梯度方向梯值加,很多點會被多次計算,這樣計算的總次數會大于所有點計算一次。所以我們會把每個點先計算出來存在一個數組中。
詳見 makeFeaturePoint的注釋
四.生成關鍵點的描述子
createDescriptors中有詳細注釋。
from:?http://blog.csdn.net/axman/article/details/9243681
總結
以上是生活随笔為你收集整理的sift的java实现解述的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何理解矩阵特征值?
- 下一篇: Office word中mathtype