matlab從零實現sift---尺度不變特徵變換

前言:首先你還是要了解一下sift的原理,這裏就不多做贅述了!我之前的博客裏有介紹,也有一些大佬的博客,講得特別好!!!大家可以看一下!

話不多說,本人就是菜鳥!所以從最簡單的入門開始!一點點敲代碼!

clc;
clear;

% 讀取兩個要匹配的圖像
% Input1 = imread('DIPbook1.png');
% Input2 = imread('DIPbook2.png');

% 自己的兩個圖片
Input1 = imread('IMG_cup1.jpg');
Input2 = imread('IMG_cup2.jpg');

下面是我要測試的兩張圖片!建議圖像不要太大,不然電腦內存有點受不了!

                              IMG_cup1.jpg                                                                    IMG_cup2.jpg

圖片已經到位,接下來就是提取圖像的SIFT

[Imagedb1,Kpt1,Dsp1] = SIFT(Input1);
fprintf('Found %d SIFT points.\n',size(Kpt1,1));

[Imagedb2,Kpt2,Dsp2] = SIFT(Input2);
fprintf('Found %d SIFT points.\n',size(Kpt2,1));

參數:Imagedb1代表擴大了2倍的灰度圖像,你可以用imshow()展示一下,就是灰度圖像,Kpt1代表圖像的關鍵點,Dsp1表示關鍵點的描述子,對於Input2的情況是一樣的;還有就是對於關鍵點,這裏keypoints數據每行爲所在的1組、2層、3行、4列、以及(5層、6行、7列)的微小偏差、8主方向角度;下圖是Kpt1

如何獲取上面的關鍵點和描述子呢?下面看一下SIFT主函數(SIFT.m)

這裏參考了這位大佬的YangHongbo表示感謝!

function [Imagedb Keypoint Descriptors] = sift(Input1)
% Keypiont 每行爲所在1組、2層、3行、4列、以及(5層、6行、7列)的微小偏差、8主方向角度
% Descriptors每行爲Keypoint中某一行對應的d*d*n爲特徵向量描述符(Lowe給出的是4*4*8=128)

% 判斷是否是3維圖像,如果是轉爲灰度圖
if ndims(Input1)==3
    Imagegray = rgb2gray(Input1);
else
    Imagegray = Input1;
end
% 接下來就是提前聲明一些全局變量,matlab中是global聲明
% global camera_sigma sigma0;
% global enlarge
% global Octave Layers;
% global sigma;

% 1、全局數據管理,並返回灰度圖的浮點數數據Imagedb(0~1之間)
% 後續的參數修改主要就是在這個函數中進行修改
Imagedb = Define(Imagegray);

% 2、創建高斯金字塔
gausspyr = buildgausspyr(Imagedb);

% 3、根據高斯金字塔創建DoG金字塔
Dogpyr = buildDogpyr(gausspyr,Dogpyr);

% 4、根據高斯金字塔和DoG金字塔然後求極值點,不懂得還是看一下論文感覺比較好
Keypoint = findExtrma(gausspyr,Dogpyr);

% 用於保存獲取的描述子
Descriptors = [];
% 遍歷所有的關鍵點的行,如果是2則代表列
% size(x,0/1))代表行和列
for i = 1:size(Keypoint,1)
    kpt = Keypoint(i,:);
    descriptors = calcDescriptors(gausspyr,kpt);
    % a = [a;b] 的意思就是一行一行的接在下面進行保存;這裏要求a和b的維度必須一致
    % a = [a,b] 則是b接在a的尾部,稱爲一個新的a
    Descriptors = [Descriptors;descriptors];
end
end

1、全局參數設置

%  全局變量的定義,matlab中使用global將變量聲明爲全局變量
%  必要的全局變量的定義,所有全局變量將在各個子程序中調用。
%  sigma_num():得到本組內,層之間的尺度關係以及本組內實際尺度
 function Imagedb = Define(Imagegray)
ifelse=@(a,b,c)(a~=0)*b+(a==0)*c;  %生成三目運算符

global enlarge
enlarge = 1; %是否放大原始圖片,等於0則不放大
% logical()將數值轉換爲邏輯值:True / false   1/0
if ( logical(enlarge) )
    % imresize()調整圖像的大小,是在灰度圖的基礎上,雙線性插值的基礎上
    % 圖像轉換爲雙精度
     Imagedb = im2double(imresize(Imagegray,2,'bilinear'));  %圖片放大2倍
else
    Imagedb = im2double(Imagegray);
end

global camera_sigma sigma0;
camera_sigma = 0.5;  %原始輸入圖片定義尺度爲camera_sigma;
sigma0 = 1.6;        % 高斯最下面一組最下一層的尺度

global Octave Layers;
Layers = 3; % 高斯金字塔爲Layers+3層,Dog爲Layers+2層
Octave = -1; % 定義高斯塔的組數目,其值>0則通過自動獲取,否則按照圖片尺寸確定

% 以下代碼爲組數的計算,是自定義還是根據圖像(有無放大原圖像)尺寸確定組的數量
Octave = ifelse(Octave >0,Octave,ifelse(logical(enlarge), ...
    log(min(size(Imagegray)))/log(2)-1,log(min(size(Imagegray)))/log(2)-2));
Octave = round(Octave);

global sigma;
sigma = zeros(2,Layers+3);
sigma = sigma_num(); %第1行返回本層尺度與上一層尺度的平方差開根號,第2行返回在本組中的實際尺度

global SIFT_Img_Border;
SIFT_Img_Border = 6; %在Dog金字塔中去掉四周的像素數量SIFT_Img_Borde-1

global ExtrThreshold;
% 在極值點精確定位前爲1/2*ExtrThreshold,精確定位閾值爲ExtrThreshold;
ExtrThreshold = 0.03;%lowe 建議值0.03,但是這個值是歸一化0-1中間的

global edgegama; %hessian矩陣中用於判斷是否屬於邊界點的閾值
edgegama = 10;

global SIFT_STEP;
SIFT_STEP = 5;
global IterationMax; %極值點精確查找過程中偏移量過大值,則認爲極值點不穩定
IterationMax = 3;

global SIFT_ORI_HIST_BINS; %定義主方向柱數量
SIFT_ORI_HIST_BINS = 36;

global SIFT_ORI_SIG_FCTR; % 主方向半徑爲SIFT_ORI_RADIUS*SIFT_ORI_SIG_FCTR*所在組內尺度(包含層偏移小數)
SIFT_ORI_SIG_FCTR = 1.5;
global SIFT_ORI_RADIUS;
SIFT_ORI_RADIUS  = 3;

global SIFT_ORI_PEAK_RATIO; %多個主方向,爲最大值主方向的閾值
SIFT_ORI_PEAK_RATIO = 0.8;

global boolParabolicFit;
boolParabolicFit = 0; %主方向柱通過拋物線擬合方法或者中心偏移方法

global SIFT_DESCR_WIDTH;
SIFT_DESCR_WIDTH = 4; %特徵點描述符 分爲SIFT_DESCR_WIDTH *SIFT_DESCR_WIDTH 個正方形
global SIFT_DESCR_HIST_BINS; %特徵點描述符 正方形內方向柱數量:SIFT_DESCR_HIST_BINS
SIFT_DESCR_HIST_BINS = 8;  %128維 = SIFT_DESCR_WIDTH * SIFT_DESCR_WIDTH * SIFT_DESCR_HIST_BINS
global SIFT_DESCR_SCL_FCTR;
SIFT_DESCR_SCL_FCTR = 3; %特徵點周圍SIFT_DESCR_SCL_FCTR*(d+1)範圍。備註:還需要擴大1.414倍
global SIFT_DESCR_MAG_THR;
SIFT_DESCR_MAG_THR = 0.2; % 特徵點描述符中,大於該閾值的點被置爲固定值

2、創建高斯金字塔

% 創建高斯金字塔
% OpenCV中內核的大小計算方法。
%   ksize.height = cvRound(sigma2*(depth == CV_8U ? 3 : 4)*2 + 1)|1;
%    CV_Assert( ksize.width > 0 && ksize.width % 2 == 1 &&
%         ksize.height > 0 && ksize.height % 2 == 1 );//確保核寬和核高爲正奇數
% cell 解決數組問題
% A = cell(numArrays,1);
% for n = 1:numArrays
%     A{n} = magic(n);
% end
function gausspyr = buildgausspyr(Imagedb)
global Octave Layers;
global sigma;
Oct = Octave;S = Layers+3; 

gausspyr = cell(Oct,1) ;
for oct = 1:1:Oct
    clear gausspyr_oct  gauss_at_1;
    for lay = 1:1:S
        ksize = 2*sigma(1,lay)*3+1; % 濾波內核範圍
        ksize = 2*round(ksize/2-0.5)+1; % 濾波內核確保爲奇數
        at = lay;
        if oct == 1 && lay == 1
        w=fspecial('gaussian',[ksize ksize],sigma(1,lay));
        gausspyr_oct(:,:,at) = imfilter(Imagedb,w,'same');
        else if oct >= 1 && lay >= 2
                w=fspecial('gaussian',[ksize ksize],sigma(1,lay));
                gausspyr_oct(:,:,at) = imfilter(gausspyr_oct(:,:,at-1),w,'same');
            else if  oct >= 2 && lay == 1                    
                    gauss_at_1 =  gausspyr{oct-1}(:,:,S-2) ;  % 前一組倒數第三層
                    gauss_at = imresize(gauss_at_1,0.5,'bilinear');
                    gausspyr_oct(:,:,at) = gauss_at;
                end
            end
        end        
    end
    gausspyr{oct} = gausspyr_oct;
end

3、根據高斯金字塔創建DoG金字塔

% 創建Dog金字塔
function Dogpyr = buildDogpyr(gausspyr)
global Octave Layers;
Oct = Octave;D = Layers+2;
Dogpyr = cell(Oct,1) ;
for oct = 1:1:Oct
    for lay = 1:1:D
        % 圖片數據如何歸一化 ???
        % 圖片不歸一化目的是後面的極值點判斷時,歸一化以後原來在0附近的點都被錯誤的線性化了。
        Dogpyr{oct}(:,:,lay) = gausspyr{oct}(:,:,lay+1) - gausspyr{oct}(:,:,lay);
      
        % 將圖片歸一化到(0-1)  如果想顯示並查看Dog金字塔圖像 歸一化顯示否則不歸一化的圖片很可能將顯示的一片黑
% %         laymax = max(max(Dogpyr{oct}(:,:,lay)));
% %         laymin = min(min(Dogpyr{oct}(:,:,lay)));
% %         [oct lay laymax laymin] %用於查看dog塔中各層之間的最大最小值,便於人工通過數據感官其穩定性
%            % 因爲有過考慮畢竟不是Log圖,擔心Dog層與層之間相差一個常數(k-1)sigma*sigma,從而引起極值點都跑到某一個層裏面
%            % 經過實驗對比發現貌似擔心是多餘的,但還沒有嚴格的數學證明
%         Dogpyr{oct}(:,:,lay) = (Dogpyr{oct}(:,:,lay)-laymin)/(laymax-laymin);
%         Dogpyr{oct}(:,:,lay) = 2*(Dogpyr{oct}(:,:,lay)-laymin)/(laymax-laymin)-1;
    end
end

4、根據高斯金字塔和DoG金字塔然後求極值點

% 尋找極值點
% adjustExtrPoint(Dogpyr,) 調整極值點位置,確定精確點
% oritHist 返回主方向統計圖
function keypoint = findExtrma(gausspyr,Dogpyr)
keypoint = [];
global Octave Layers;
global SIFT_Img_Border;
global ExtrThreshold;
global SIFT_ORI_PEAK_RATIO; %多個主方向,爲最大值主方向的閾值
global SIFT_ORI_HIST_BINS;  %定義主方向柱數量
n = SIFT_ORI_HIST_BINS;
global boolParabolicFit; %主方向柱通過拋物線擬合方法或者中心偏移方法
for oct = 1:1:Octave
    for lay = 2:1:Layers+1
        [rows cols] = size(Dogpyr{oct}(:,:,lay));
        laymax = max(max(Dogpyr{oct}(:,:,lay)));
        laymin = min(min(Dogpyr{oct}(:,:,lay)));
        Extr_lay = 1/2*ExtrThreshold*max( abs(laymax),abs(laymin) );
        % 邊界各去掉SIFT_Img_Border-1個元素
        for r = SIFT_Img_Border:1:rows-SIFT_Img_Border+1
            for c = SIFT_Img_Border:1:cols-SIFT_Img_Border+1
                cub1 = Dogpyr{oct}(r-1:r+1,c-1:c+1,lay-1);
                cub2 = Dogpyr{oct}(r-1:r+1,c-1:c+1,lay);
                cub3 = Dogpyr{oct}(r-1:r+1,c-1:c+1,lay+1);
                cub = [cub1(:) ; cub2(:) ;cub3(:)];  %在r,c附近的立方體共27個元素
                % 先去掉本身不穩定的極值點
                if abs(Dogpyr{oct}(r,c,lay)) > Extr_lay && ...
                        ( Dogpyr{oct}(r,c,lay) == max(cub) || Dogpyr{oct}(r,c,lay) == min(cub) )
                    r1 = r; c1 =c; lay1 = lay;
                    %kpt所在的組、層、行、列、以及(層、行、列)的微小偏差
                    [flagbool kpt] = adjustExtrPoint(Dogpyr,gausspyr,oct,lay1,r1,c1);
                    if flagbool == 1  %極值點位置進一步判斷條件成立以及精確定位
                         [maxval hist] = oritHist(gausspyr,kpt);
                         for j = 1:1:n
                             j_pre = j-1;
                             j_pre(j_pre<1) = n;
                             j_next = j+1;
                             j_next(j_next>n) = 1;
                             if hist(j)>hist(j_pre) && hist(j)>hist(j_next) &&  hist(j)>=SIFT_ORI_PEAK_RATIO*maxval
                                 % 拋物線擬合方程
                                 if boolParabolicFit
                                     bin = j+0.5*(hist(j_pre)-hist(j_next))/(hist(j_pre)+hist(j_next)-2*hist(j));
                                 else
                                     % 我個人認爲直觀的解釋或許下面這個更合適
                                     bin = j+(hist(j_next)-hist(j_pre))/(hist(j_pre)+hist(j)+hist(j_next));
                                 end
                                 angle = (bin-1)*360/n;
                                  %keypoint爲所在的1組、2層、3行、4列、以及(5層、6行、7列)的微小偏差、8主方向角度
                                  keypoint = [keypoint;kpt angle];
                             end
                         end
                    end
                end
            end
        end
    end
end
           
        

代碼中涉及到的一些函數:

(1)計算特徵描述符calcDescriptors.m

% 計算特徵描述符
function descriptors= calcDescriptors(gausspyr,Keypoint)
 %keypoint爲所在的1組、2層、3行、4列、以及(5層、6行、7列)的微小偏差、8主方向角度
 
global SIFT_DESCR_WIDTH; %特徵點描述符 分爲SIFT_DESCR_WIDTH *SIFT_DESCR_WIDTH 個正方形
d = SIFT_DESCR_WIDTH;
global SIFT_DESCR_HIST_BINS; %特徵點描述符 正方形內方向柱數量:SIFT_DESCR_HIST_BINS
n = SIFT_DESCR_HIST_BINS ;  % d*d*n = 128維 
dst = zeros(1,d*d*n);
deg_per_bin = 360/n;
global SIFT_DESCR_SCL_FCTR; %SIFT_DESCR_SCL_FCTR = 3; 
global sigma;
global SIFT_DESCR_MAG_THR;
oct = Keypoint(1);  % 組
lay = Keypoint(2);  % 層
gauss_sigma0 = sigma(2,lay);  % 高斯塔所在層的sigma

hist_width = SIFT_DESCR_SCL_FCTR * gauss_sigma0 ;
radius = round(hist_width * sqrt(2)/2*(d+1));
ori = Keypoint(8);  % 主方向角度
cos_ori = cosd(ori);
sin_ori = sind(ori);
[rows cols] = size(gausspyr{oct}(:,:,lay));
exp_scale = -2/(d*d); %高斯權重相當於爲d/2;
cubhist = zeros(d+2,d+2,n+1);
k = 0;
for ri = -radius:1:radius
    for ci = -radius:1:radius
        rcd = [cos_ori -sin_ori;sin_ori cos_ori] *[ri;ci]; %旋轉
        rd_ = rcd(1)/hist_width; %旋轉後的行 d
        cd_ = rcd(2)/hist_width; %旋轉後的列 d
        
        %  移位到左上角
        rbin = rd_ + d/2-0.5;  %旋轉移位後的行 d
        cbin = cd_ + d/2-0.5;  %旋轉移位後的列 d
%         r = gausspyr{oct} (Keypoint(3),Keypoint(4),lay);
        r = Keypoint(3)+ri; %旋轉前的行座標
        c = Keypoint(4)+ci; %旋轉前的列座標
        if rbin>-1 && rbin<d && cbin>-1 && cbin<d && ...
                r>=2 && r<=rows-1 && c>=2 && c<=cols-1
           k=k+1;
           dx = gausspyr{oct}(r,c+1,lay)-gausspyr{oct}(r,c-1,lay);
           dy = gausspyr{oct}(r+1,c,lay)-gausspyr{oct}(r-1,c,lay);
           Dx(k) =dx;Dy(k) =dy;
           Rbin(k) = rbin;Cbin(k) =cbin;
           W(k) = exp((rd_^2+cd_^2)*exp_scale); %權重
        end
    end
end

len = k;
Mag = sqrt(Dx.*Dx + Dy.*Dy);  %梯度幅值;
orik = rad2deg( atan2(Dy,Dx) );  %atan2(a,b)是4象限反正切,它的取值不僅取決於正切值a/b,還取決於點 (b, a) 落入哪個象限:
orik(orik<0) = orik(orik<0)+360; % 將-180~180轉換爲 0~360;該語句在ori>0時不執行;
for k=1:1:len
    rbin =Rbin(k);cbin =Cbin(k); %旋轉移位後的行列 d
    ori1 = orik(k)-ori;
    ori1(ori1<0) = ori1+360; %將-360-360轉換爲 0-360°
    obin = ori1/deg_per_bin;
    mag = Mag(k)*W(k);
    r0 = floor(rbin);c0 = floor(cbin); %取值範圍爲 [-1~d-1]  
    ob0 = floor(obin); %取值範圍爲 [0~8]
    ob0(ob0==8) = 0; %取值範圍爲 [0~7]
    rbin = rbin-r0; % 小數部分
    cbin = cbin-c0;
    obin = obin-ob0;
    % 爲了防止超出邊界
    histc = c0+2;  % c0 取值範圍爲 [-1~d-1]
    histr = r0+2;  % r0 取值範圍爲 [-1~d-1]
    histo = ob0+1; % ob0 取值範圍爲 [0~7]
    %
    cubhist(histc,histr,histo) = (1-cbin)*(1-rbin)*(1-obin)*mag + ...
        cubhist(histc,histr,histo);
    cubhist(histc,histr,histo+1) = (1-cbin)*(1-rbin)*obin*mag + ...
        cubhist(histc,histr,histo+1);
    
    cubhist(histc,histr+1,histo) = (1-cbin)*rbin*(1-obin)*mag + ...
        cubhist(histc,histr+1,histo);
    cubhist(histc,histr+1,histo+1) = (1-cbin)*rbin*obin*mag + ...
        cubhist(histc,histr+1,histo+1);
    
    cubhist(histc+1,histr,histo) = cbin*(1-rbin)*(1-obin)*mag + ...
        cubhist(histc+1,histr,histo);
    cubhist(histc+1,histr,histo+1) = cbin*(1-rbin)*obin*mag + ...
        cubhist(histc+1,histr,histo+1);
    
    cubhist(histc+1,histr+1,histo) = cbin*rbin*(1-obin)*mag + ...
        cubhist(histc+1,histr+1,histo);
    cubhist(histc+1,histr+1,histo+1) = cbin*rbin*obin*mag + ...
        cubhist(histc+1,histr+1,histo+1);
end

% cs = [sum(Mag.*W) sum(cubhist(:))]
cubhist(:,:,1) = cubhist(:,:,1) +cubhist(:,:,n+1);  %在0°和360°數據合併
for i=1:1:d % 行r
    for j=1:1:d  %列c 
        for k=1:1:n  %方向柱1~n
            dst(((i-1)*d+j-1)*n+k) = cubhist(i+1,j+1,k);
        end
    end
end

descriptors =dst/sqrt(sumsqr(dst));
% 大於閾值則被置爲閾值值
descriptors(descriptors>SIFT_DESCR_MAG_THR) = SIFT_DESCR_MAG_THR;
descriptors =descriptors/sqrt(sumsqr(descriptors));

(2) 極值點位置處方向統計oritHist.m

% 極值點位置處方向統計
%
function [maxval Hist] = oritHist(gausspyr,kpt) % kpt所在的1組、2層、3行、4列、以及(5層、6行、7列)的微小偏差
global SIFT_ORI_HIST_BINS;  %定義主方向柱數量
n = SIFT_ORI_HIST_BINS; % 
global SIFT_ORI_SIG_FCTR; % 主方向半徑爲SIFT_ORI_RADIUS*SIFT_ORI_SIG_FCTR*所在組內尺度(包含層偏移小數)
global SIFT_ORI_RADIUS;
global sigma0;
global Layers;

oct = kpt(1);lay = kpt(2);
sgm = sigma0*power(2,1/Layers*(lay+kpt(5))); %相對於本組的尺度
radius = round(SIFT_ORI_RADIUS*SIFT_ORI_SIG_FCTR*sgm); %主方向統計鄰域半徑
expf_scale = -1/(2*(sgm*SIFT_ORI_SIG_FCTR)*(sgm*SIFT_ORI_SIG_FCTR)); % 正態權重e指數部分
[rows cols] = size(gausspyr{oct}(:,:,lay));
k = 0;
for i=-radius:1:radius
    r = kpt(3)+i;
    if r<2 || r>rows-1   %行超出範圍
        continue;
    end
    for j=-radius:1:radius
       c = kpt(4)+j;
       if c<2 || c>cols-1 %列超出範圍
           continue;
       end
       % 因爲只需要比值,故差分的分母取消不要
       dx = gausspyr{oct}(r,c+1,lay)-gausspyr{oct}(r,c-1,lay);
       dy = gausspyr{oct}(r+1,c,lay)-gausspyr{oct}(r-1,c,lay);
       k = k+1;
       Dx(k) = dx; Dy(k) = dy;W(k) = (i*i+j*j)*expf_scale;
    end
end
len = k; %實際統計的數量因爲超出範圍有可能並沒有真正在正負radius 的範圍內

clear Mag ori exp_W;
Mag = sqrt(Dx.*Dx + Dy.*Dy);  %梯度幅值;
ori = rad2deg( atan2(Dy,Dx) );  %atan2(a,b)是4象限反正切,它的取值不僅取決於正切值a/b,還取決於點 (b, a) 落入哪個象限:
ori(ori<0) = ori(ori<0)+360; % 將-180~180轉換爲 0~360;該語句在ori>0時不執行;
exp_W = exp(W);  % 權重沒必要歸一化,因爲每個柱子都是相同比例放大或縮小

temphist = zeros(1,n+1);
for k = 1:1:len
    bin = round(ori(k)/360*n); 
    temphist(bin+1) =  temphist(bin+1)+exp_W(k)*Mag(k); 
end
temphist(1) = temphist(1)+temphist(1+n);%第i柱子的角度範圍爲 (i-1)*10-5 ~ (i-1)*10+5
lvbhist = zeros(1,n+4);
lvbhist = [temphist(n-1) temphist(n) temphist(1:n) temphist(1) temphist(2)];
Hist = zeros(1,n);
for i=1:1:n
    ilb=i+2;
    Hist(i) = 6/16*lvbhist(ilb)+4/16*(lvbhist(ilb+1)+lvbhist(ilb-1))+1/16*(lvbhist(ilb+2)+lvbhist(ilb-2)); %第i柱子的角度範圍爲 (i-1)*10-5 ~ (i-1)*10+5
end
maxval = max(Hist(:));

(3) 圖像拼接 appendimages.m

% 這個函數來源於LOWE的主頁,別害怕!僅僅是個最簡單的圖片拼接
function im = appendimages(image1, image2)

% Select the image with the fewest rows and fill in enough empty rows
%   to make it the same height as the other image.
rows1 = size(image1,1);
rows2 = size(image2,1);

if (rows1 < rows2)
     image1(rows2,1) = 0;  %雖然只增補最後的一行,實際上中間的區域也自動補零。
else
     image2(rows1,1) = 0;
end

% Now append both images side-by-side.
im = [image1 image2];   

最後添上主函數:matches.mlx

clc;
clear;

% 讀取兩個要匹配的圖像
% Input1 = imread('DIPbook1.png');
% Input2 = imread('DIPbook2.png');

% 自己的兩個圖片
Input1 = imread('IMG_cup1.jpg');
Input2 = imread('IMG_cup2.jpg');

[Imagedb1,Kpt1,Dsp1] = SIFT(Input1);
fprintf('Found %d SIFT points.\n',size(Kpt1,1));

[Imagedb2,Kpt2,Dsp2] = SIFT(Input2);
fprintf('Found %d SIFT points.\n',size(Kpt2,1));

% keypoints數據每行爲所在的1組、2層、3行、4列、以及(5層、6行、7列)的微小偏差、8主方向角度

distRatio = 0.6;
% Kpt1*Kpt2';
k = 0;
for i = 1:size(Dsp1,1)
    % 以度爲單位的反餘弦運算,將值轉換爲角度
    angle = acosd(Dsp1(i,:)*Dsp2');
    [angle,indx] = sort(angle);
    if angle(1)<distRatio*angle(2)
        k = k + 1;
        match(k,1:2) = [i indx(1)];
    end
end
len = k;
fprintf('Found %d matches.\n',len);
twoPic = appendimages(Imagedb1,Imagedb2);
imshow(twoPic);
hold on;
% match()信號匹配的邏輯表達,返回0或者1
% round()四捨五入到最近的小數
cols1 = size(Imagedb1,2);
SIFT_pos1 = [];
SIFT_pos2 = [];
for i = 1:len
    zb1 = [Kpt1(match(i,1),3) + Kpt1(match(i,1),6) Kpt1(match(i,1),4) + Kpt1(match(i,1),7)];
    zb2 = [Kpt2(match(i,2),3) + Kpt2(match(i,2),6) Kpt2(match(i,2),4) + Kpt2(match(i,2),7)];
    zb1_img = round(zb1*2^(Kpt1(match(i,1),1)-1));
    zb2_img = round(zb2*2^(Kpt2(match(i,2),1)-1));
    % line([起點橫座標,終點橫座標],[起點縱座標,終點縱座標]),
    line([zb1_img(2) zb2_img(2) + cols1],...
        [zb1_img(1) zb2_img(1)],'linestyle',':','Color','c');
    
    % text向數據點添加文字說明
    text(zb1_img(2),zb1_img(1),num2str(i),'color','red');  
    text(zb2_img(2)+cols1,zb2_img(1),num2str(i),'color','red');
    SIFT_pos1 = [SIFT_pos1;zb1_img(2) zb1_img(1)];  % 點橫座標x以及縱座標y
    SIFT_pos2 = [SIFT_pos2;zb2_img(2) zb2_img(1)];  % 點橫座標x以及縱座標y 
end

[trsArray,indx,piancha] = affinity(SIFT_pos1,SIFT_pos2);


輸出結果:Found 829 SIFT points

                  Found 715 SIFT points

                  Found 45 matches.

以上的代碼來源於:SIFT程序實現      有需要的可以自己去下載,也可以按照上面的代碼一點點敲;感謝這位大佬的講解!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章