本文爲原創作品,未經本人同意,禁止轉載,禁止用於商業用途!本人對博客使用擁有最終解釋權
歡迎關注我的博客:http://blog.csdn.net/hit2015spring和http://www.cnblogs.com/xujianqing/
這個系列的第三篇博客了,比以往時候來的更晚一些,期間經歷了好多事,所以耽擱了分享的進程。Ok回到之前的sift算法的解讀中來,這一篇博客主要是對sift算法的第三個步驟進行解讀和分析,並給出matlab的代碼註釋,如有疏漏之處,歡迎指出交流!
上面一篇的博客我們講到了提取出關鍵點了,這些關鍵點這時候是具有以下的特性的:尺度和位置的不變性,此時還缺少角度的不變性,這個步驟便是尋找這些關鍵點的角度的不變性。而後續的操作基本上都是基於這個角度進行的。
在圖像中一個像素點往往是不具有說服力的,同樣的一個特徵點像素值它也不能代表着具有統計意義的圖像特性。所以它的角度確定同樣需要統計它周圍的像素的一些特徵,從而得到我們所需要的角度。每個特徵點它有自己所在的高斯圖像,也擁有自己的尺度信息,爲了關聯這些的特性,我們統計的是以該特徵點爲圓心,以該特徵點所在的高斯圖像的尺度的1.5倍爲半徑,該圓內所有的像素作爲該特徵點的角度的統計支持者。
像素梯度的幅值和幅角的計算公式爲:
由此可以得到每一個點的幅值和幅角,但是由於與特徵點的距離不同,各個點對特徵點的支持也是不一樣的,所以離得遠的就支持弱一點,離得近的就支持大一點,這裏面的權重就要通過一個規則來管理,而這個規則就是高斯濾波,即以特徵點爲圓心,做一個高斯濾波,給它周圍的點加上一個高斯權重。高斯函數的方差當然還是與這個特徵點所在的圖像層的尺度有關,就是。
計算完各個像素的幅值和幅角,並對幅值進行加權之後,我們要開始計算每一個像素對中心特徵點的支持有多大。首先把一個圓周 分成36個柱體,每個柱體代表 ,表示的是 爲第一個柱體, 爲第二個柱體,依次類推下去,分成36個柱體。分好柱體之後開始統計工作。具體實行如下:
所有相關的像素點計算出來的幅角對應到各自的柱體中,如一個幅角爲 ,則該點對應的柱體爲 所代表的的柱體中,對各個柱體中的像素點的幅度進行加權求和,就可得到該特徵點角度的分佈直方圖。
得到了特徵點角度的分佈直方圖,當然是用得到分數最高的那個柱體所代表的的角度來表示該特徵點的主方向。有時候一個特徵點的角度可能會有好幾個角度與峯值相差是比較少的,這樣我們就可以用多個角度來輔助說明特徵點的角度。我們把這個角度的差值定義爲80%,只要大於最大峯值的80%就定爲該特徵點的輔助方向。
以上便是sift算法第三步的基本過程,這裏面還差最後一部分的內容:爲了對主方向上的角度進行精確地定位,我們需要根據其左右兩個直方圖進行插值處理,可以提高角度的精確程度。對於插值有各種各樣的算法(數值分析這門課裏面介紹了好多,請自行解決),在這裏我用的是拋物線插值的方法對角度進行擬合。
拋物線插值:
給定三點 ,求線性Lagrange插值多項式 使得
於是可以得到下式的擬合
Ok,第三步驟完畢!這個步驟完後就可以得到特徵點的主方向了。
下面是這一部分的matlab代碼:
% 下一步是計算特徵點的主方向.
% 在特徵點的一個區域內計算其梯度直方圖
g = gaussian_filter( 1.5 * absolute_sigma(1,intervals+3) / subsample(1) );
zero_pad = ceil( length(g) / 2 );
% 計算高斯金字塔圖像的梯度方向和幅值
if interactive >= 1
fprintf( 2, 'Computing gradient magnitude and orientation...\n' );
end
tic;
mag_thresh = zeros(size(gauss_pyr));
mag_pyr = cell(size(gauss_pyr));
grad_pyr = cell(size(gauss_pyr));
for octave = 1:octaves
for interval = 2:(intervals+1)
% 計算x,y的差分
diff_x = 0.5*(gauss_pyr{octave,interval}(2:(end-1),3:(end))-gauss_pyr{octave,interval}(2:(end-1),1:(end-2)));
diff_y = 0.5*(gauss_pyr{octave,interval}(3:(end),2:(end-1))-gauss_pyr{octave,interval}(1:(end-2),2:(end-1)));
% 計算梯度幅值
mag = zeros(size(gauss_pyr{octave,interval}));
mag(2:(end-1),2:(end-1)) = sqrt( diff_x .^ 2 + diff_y .^ 2 );
% 存儲高斯金字塔梯度幅值
mag_pyr{octave,interval} = zeros(size(mag)+2*zero_pad);
mag_pyr{octave,interval}((zero_pad+1):(end-zero_pad),(zero_pad+1):(end-zero_pad)) = mag;
% 計算梯度主方向
grad = zeros(size(gauss_pyr{octave,interval}));
grad(2:(end-1),2:(end-1)) = atan2( diff_y, diff_x );
grad(find(grad == pi)) = -pi;
% 存儲高斯金字塔梯度主方向
grad_pyr{octave,interval} = zeros(size(grad)+2*zero_pad);
grad_pyr{octave,interval}((zero_pad+1):(end-zero_pad),(zero_pad+1):(end-zero_pad)) = grad;
end
end
clear mag grad
grad_time = toc;
if interactive >= 1
fprintf( 2, 'Gradient calculation time %.2f seconds.\n', grad_time );
end
% 下一步是確定特徵點的主方向
% 方法:通過尋找每個關鍵點的子區域內梯度直方圖的峯值(注:每個關鍵點的主方向可以有不止一個)
% g = gaussian_filter( 1.5 * absolute_sigma(1,intervals+3) / subsample(1) );
% zero_pad = ceil( length(g) / 2 );
% 將灰度直方圖分爲36等分,每隔10度一份
num_bins = 36;
hist_step = 2*pi/num_bins;
hist_orient = [-pi:hist_step:(pi-hist_step)];
% 初始化關鍵點的位置、方向和尺度信息
pos = [];
orient = [];
scale = [];
% 給關鍵點確定主方向
if interactive >= 1
fprintf( 2, 'Assigining keypoint orientations...\n' );
end
tic;
for octave = 1:octaves
if interactive >= 1
fprintf( 2, '\tProcessing octave %d\n', octave );
end
for interval = 2:(intervals + 1)
if interactive >= 1
fprintf( 2, '\t\tProcessing interval %d ', interval );
end
keypoint_count = 0;
% 構造高斯加權掩模
g = gaussian_filter( 1.5 * absolute_sigma(octave,interval)/subsample(octave) );
hf_sz = floor(length(g)/2);
g = g'*g;
loc_pad = zeros(size(loc{octave,interval})+2*zero_pad);
loc_pad((zero_pad+1):(end-zero_pad),(zero_pad+1):(end-zero_pad)) = loc{octave,interval};
[iy ix]=find(loc_pad==1);
for k = 1:length(iy)
x = ix(k);
y = iy(k);
% % 對其值進行高斯平滑
wght = g.*mag_pyr{octave,interval}((y-hf_sz):(y+hf_sz),(x-hf_sz):(x+hf_sz));
grad_window = grad_pyr{octave,interval}((y-hf_sz):(y+hf_sz),(x-hf_sz):(x+hf_sz));
orient_hist=zeros(length(hist_orient),1);
for bin=1:length(hist_orient)
diff = mod( grad_window - hist_orient(bin) + pi, 2*pi ) - pi;
orient_hist(bin)=orient_hist(bin)+sum(sum(wght.*max(1 - abs(diff)/hist_step,0)));
end
% 運用非極大抑制法查找主方向直方圖的峯值
peaks = orient_hist;
% rot_right = [ peaks(end); peaks(1:end-1) ];
% rot_left = [ peaks(2:end); peaks(1) ];
% peaks( find(peaks < rot_right) ) = 0;
% peaks( find(peaks < rot_left) ) = 0;
% 提取最大峯值的值和其索引位置
[max_peak_val ipeak] = max(peaks);
% 將大於等於最大峯值80% 的直方圖的也確定爲特徵點的主方向
peak_val = max_peak_val;
while( peak_val > 0.8*max_peak_val )
% 最高峯值最近的三個柱值通過拋物線插值精確得到
A = [];
b = [];
for j = -1:1
A = [A; (hist_orient(ipeak)+hist_step*j).^2 (hist_orient(ipeak)+hist_step*j) 1];
bin = mod( ipeak + j + num_bins - 1, num_bins ) + 1;
b = [b; orient_hist(bin)];
end
c = pinv(A)*b;
max_orient = -c(2)/(2*c(1));
while( max_orient < -pi )
max_orient = max_orient + 2*pi;
end
while( max_orient >= pi )
max_orient = max_orient - 2*pi;
end
% 存儲關鍵點的位置、主方向和尺度信息
pos = [pos; [(x-zero_pad) (y-zero_pad)]*subsample(octave) ];
orient = [orient; max_orient];
scale = [scale; octave interval absolute_sigma(octave,interval)];
keypoint_count = keypoint_count + 1;
% % % 峯值賦0,考察的是下一個峯值,即大於主峯值80%的統計圖
peaks(ipeak) = 0;
[peak_val ipeak] = max(peaks);
end
end
if interactive >= 1
fprintf( 2, '(%d keypoints)\n', keypoint_count );
end
end
end
clear loc loc_pad
orient_time = toc;
if interactive >= 1
fprintf( 2, 'Orientation assignment time %.2f seconds.\n', orient_time );
end
% 在交互模式下顯示關鍵點的尺度和主方向信息
if interactive >= 2
fig = figure;
clf;
imshow(im);
hold on;
display_keypoints( pos, scale(:,3), orient, 'y' );
% resizeImageFig( fig, size(im), 2 );
fprintf( 2, 'Final keypoints with scale and orientation (2x scale).\nPress any key to continue.\n' );
pause;
close(fig);
end
function hh = display_keypoints( pos, scale, orient, varargin )
% 功能:在原始圖像上顯示特徵點
% 輸入:
% pos – 特徵點的位置矩陣.
% scale –特徵點的尺度矩陣.
% orient –特徵點的主方向向量.
% 輸出:
% hh-返回向量的線句柄.
hold on;
alpha = 0.33;
beta = 0.33;
autoscale = 1.5;
plotarrows = 1;
sym = '';
filled = 0;
ls = '-';
ms = '';
col = '';
varin = nargin - 3;
while (varin > 0) & isstr(varargin{varin}),
vv = varargin{varin};
if ~isempty(vv) & strcmp(lower(vv(1)),'f')
filled = 1;
nin = nin-1;
else
[l,c,m,msg] = colstyle(vv);
if ~isempty(msg),
error(sprintf('Unknown option "%s".',vv));
end
if ~isempty(l), ls = l; end
if ~isempty(c), col = c; end
if ~isempty(m), ms = m; plotarrows = 0; end
if isequal(m,'.'), ms = ''; end % Don't plot '.'
varin = varin-1;
end
end
if varin > 0
autoscale = varargin{varin};
end
x = pos(:,1);
y = pos(:,2);
u = scale.*cos(orient);
v = scale.*sin(orient);
if prod(size(u))==1, u = u(ones(size(x))); end
if prod(size(v))==1, v = v(ones(size(u))); end
if autoscale,
u = u*autoscale; v = v*autoscale;
end
ax = newplot;
next = lower(get(ax,'NextPlot'));
hold_state = ishold;
x = x(:).'; y = y(:).';
u = u(:).'; v = v(:).';
uu = [x;x+u;repmat(NaN,size(u))];
vv = [y;y+v;repmat(NaN,size(u))];
h1 = plot(uu(:),vv(:),[col ls]);
if plotarrows,
hu = [x+u-alpha*(u+beta*(v+eps));x+u; ...
x+u-alpha*(u-beta*(v+eps));repmat(NaN,size(u))];
hv = [y+v-alpha*(v-beta*(u+eps));y+v; ...
y+v-alpha*(v+beta*(u+eps));repmat(NaN,size(v))];
hold on
h2 = plot(hu(:),hv(:),[col ls]);
else
h2 = [];
end
if ~isempty(ms),
hu = x; hv = y;
hold on
h3 = plot(hu(:),hv(:),[col ms]);
if filled, set(h3,'markerfacecolor',get(h1,'color')); end
else
h3 = [];
end
if ~hold_state, hold off, view(2); set(ax,'NextPlot',next); end
if nargout>0, hh = [h1;h2;h3]; end
得到結果: