一個MATLAB純m生成的Canny邊緣(三)具體的實施

導言區

%%導言區
%用來測試ui的各項功能分區佈局
%請選擇合適的高斯濾波的sigma值否則用sobel邊緣檢測的時候容易檢測不到邊緣
%倘若找不到邊緣可能是sigma的值不太好或者不適合利用sobel模版進行邊緣檢測
%%
h = figure('menubar','none','NumberTitle','off',...                   %創建出圖形對象
                 'Name','20177740--黃澤盛的課程設計canny邊緣提取',...
                 'Position',[300 120 1000 680],...
                 'tag','figure1');
%%
%%創建uimenu菜單欄(加載原始圖像)
m1 = uimenu('label','加載原始圖像','position',1,...                  %創建第一個菜單欄
                      'tag','menu1');
%%創建uimenu菜單欄(canny算子提取)
m2 = uimenu('label','canny算子邊緣提取','position',2,...                             
                      'Callback',['set(m4,''checked'',''off'');',...
                                       'set(m2,''checked'',''off'');',...
                                       'set(m3,''checked'',''off'');',...
                                       'set(m5,''checked'',''off'');',...
                                       'set(m6,''checked'',''off'');'],...
                        'tag','menu2' ); 
m3 = uimenu('label','高斯濾波平滑圖像',...
                      'parent',m2,'position',1,....
                      'Callback',['set(m2,''checked'',''off'');'...
                                       'set(m4,''checked'',''off'');',...
                                       'set(m3,''checked'',''on'');',...
                                       'set(m5,''checked'',''off'');',...
                                       'set(m6,''checked'',''off'');'],...
                       'tag','menu3');                                                                      
 m4 = uimenu('label','一階偏導計算梯度幅值和方向',...
                       'parent',m2,'position',2,...
                       'Callback',['set(m2,''checked'',''off'');',... 
                                        'set(m3,''checked'',''off'');',...
                                        'set(m4,''checked'',''on'');',...
                                        'set(m5,''checked'',''off'');',...
                                        'set(m6,''checked'',''off'');'],...
                        'tag','menu4');
 m5 = uimenu('label','梯度賦值進行非極大值抑制',...
                       'parent',m2,'position',3,...
                       'Callback',['set(m2,''checked'',''off'');',... 
                                        'set(m3,''checked'',''off'');',...
                                        'set(m4,''checked'',''off'');',...
                                        'set(m5,''checked'',''on'');',...
                                        'set(m6,''checked'',''off'');'],...
                        'tag','menu5');
  m6  = uimenu('label','雙閾值算法檢測和連接邊緣',...
                         'parent',m2,'position',4,...
                         'Callback',['set(m2,''checked'',''off'');',... 
                                        'set(m3,''checked'',''off'');',...
                                        'set(m4,''checked'',''off'');',...
                                        'set(m5,''checked'',''on'');',...
                                        'set(m6,''checked'',''on'');'],...
                          'tag','menu6');                              
%%退出按鈕部分
 m12 = uimenu('label','退出界面',...
                         'position',3,...
                         'tag','menu12');
 %%創建句柄
 handles = guihandles(h);
 %%
 %%設置按鈕的callback屬性爲函數句柄
 set(m1,'callback',@m1_callback);
 set(m3,'callback',@m3_callback);
 set(m4,'callback',@m4_callback);
 set(m5,'callback',@m5_callback);
 set(m6,'callback',@m6_callback);
 set(m12,'callback',@m12_callback);

這裏是佈局區域。運行之後得佈局。

uigetfile交互獲取圖像

 %%
 %%編寫回調函數部分
 function [] = m1_callback(source,evendata)
             handles = guidata(source);
            [filename,path] = uigetfile({'*.';'*.jpg';'*.png';'*.jpeg';'*.bmp';;},'選擇圖片');
             try isa(filename,'numeric');                       
               truename = [ path,filename ];              %拼接真正的路徑名
             im = imread(truename);                        %顯示圖片
             subplot(2,3,1);                         
             mshow(im);
             chicun = size(im);
             switch numel(chicun)
                 case 2
                     im1 = im;
                 case 3
                     im1 = rgb2gray(im);
             end
             im1 = double(im1);                                 %讀入的是uint8類型,要轉double才能計算
             handles.im1 = im1;                                     
             guidata(source,handles);
             title('原始圖像','fontsize',20);   
             catch
             f = errordlg('你取消了選擇,請勾選文件','File Error');
             end
 end

在這裏我加了一個小小的錯誤,是我自己寫代碼的時候遇到的,我並不希望人們直接使用這個ui,它能保證你能運行,不會報錯,但是你會遇到一個小問題。會導致你後面運行不了。

高斯濾波部分

 %%
 function [] = m3_callback(menu3,evendata)     
            handles = guidata(menu3);                    %得到句柄
            im1 = handles.im1;                                  %傳遞出im的參數            
            try isempty(im1)               
                f = inputdlg(['請輸入\sigma的值']);         %對話框的提示
             im2 =  imgaussfilt(im1,str2num(cell2mat(f)));   %高斯濾波 \sigma = 自己輸入的值
             subplot(2,3,2);
             imshow(uint8(im2));
             title(['高斯濾波後的圖像,\sigma = ' cell2mat(f) ],'fontsize',20);   %title
             handles.im2 = im2;                                %存儲原始的影像                                                                          
             handles.f = cell2mat(f);                          %用來傳出\sigma的值
             guidata(menu3,handles);                      %存儲句柄
            catch
                f = warndlg('請先選擇原始圖像','沒有找到原始圖像');
            end
 end

高斯濾波,能讓你交互的輸入高斯的sigma值。。同時也會讓你報警。

梯度賦值和計算方向導數

 function [] = m4_callback(menu4,evendata)
            handles = guidata(menu4);
            im2 =  handles.im2;                               %拿到高斯濾波後的圖像
            f  = handles.f;
           % axes(handles.axes1);
            %imshow(uint8(im2));                                          %顯示高斯濾波後的圖像
            %title(['高斯濾波後的圖像,\sigma = ',f],'fontsize',20);
            w = fspecial('sobel');                               %用sobel算子進行邊緣檢測
            im3h = imfilter(im2,w,'replicate');              %橫邊緣
            w =  [-1,0,1;-2,0,2;-1,0,1];
            im3v = imfilter(im2,w,'replicate');             %縱邊緣
            im3 = sqrt(im3h.^2 + im3v.^2);               %平方再求和
            subplot(2,3,3);                         
            imshow(uint8(im3));                                          %顯示sobel邊緣提取後的圖像
            title('一階偏導計算幅值和方向導數','fontsize',20);
            arah = atan2(im3v,im3h);
            arah = arah*180/pi;
            handles.im3 = im3;                                 %存儲一階方向導數計算的圖像
            handles.arah = arah;
            guidata(menu4,handles);
 end

計算角度和NMS

 function m5_callback(menu5,evendata)           %梯度幅值的計算按鈕
            handles = guidata(menu5);
            arah = handles.arah;
            im3 = handles.im3;
            [m,n] = size(im3);
            for i = 1:m
                for j = 1:n
                     if((arah(i,j)>=-22.5 && arah(i,j)<=22.5) || (arah(i,j)>=157.5 && arah(i,j)<=180)...
                                       ||(arah(i,j)<=-157.5 && arah(i,j)>=-180) )
                      arah(i,j) = 0;
                     elseif((arah(i,j) >= 22.5) && (arah(i,j) < 67.5) || (arah(i,j) <= -112.5) && (arah(i,j) > -157.5))
                       arah(i,j) = -45;
                      elseif((arah(i,j) >= 67.5) && (arah(i,j) < 112.5) || (arah(i,j) <= -67.5) && (arah(i,j) >- 112.5))
                       arah(i,j) = 90;
                       elseif((arah(i,j) >= 112.5) && (arah(i,j) < 157.5) || (arah(i,j) <= -22.5) && (arah(i,j) > -67.5))
                       arah(i,j) = 45;  
                     end
            end
end
Nms = zeros(m,n);
            for i = 2:m-1
                for j= 2:n-1
                    if (arah(i,j) == 90 && im3(i,j) == max([im3(i,j), im3(i,j+1), im3(i,j-1)]))
                         Nms(i,j) = im3(i,j);
                     elseif (arah(i,j) == 45 && im3(i,j) == max([im3(i,j), im3(i+1,j-1), im3(i-1,j+1)]))
                        Nms(i,j) = im3(i,j);
                     elseif (arah(i,j) == 0 && im3(i,j) == max([im3(i,j), im3(i+1,j), im3(i-1,j)]))
                        Nms(i,j) = im3(i,j);
                      elseif (arah(i,j) == -45 && im3(i,j) == max([im3(i,j), im3(i+1,j+1), im3(i-1,j-1)]))
                        Nms(i,j) = im3(i,j);
                    end
             end
            subplot(2,3,4);
            imshow(Nms);
            title('非極大值抑制後的圖像','fontsize',20);
end
        handles.Nms = Nms;
        handles.m = m;
        handles.n =  n;
        guidata(menu5,handles);
 end

我參考了canny實現
具體可以慢慢看。

閾值選取和8連接

 function m6_callback(menu6,~)
       handles = guidata(menu6);
       im1 = handles.im1;
       im3 = handles.im3;
       Nms = handles.Nms;
       [m,n] = size(im3);
       img_out=zeros(m,n);%定義一個雙閾值圖像
       YH_L=0.08*max(max(Nms));%低閾值
       YH_H=0.24*max(max(Nms));%高閾值
        for i = 1:m
             for j = 1:n
                if(Nms(i,j)<YH_L)
                    img_out(i,j)=0;
                 elseif(Nms(i,j)>YH_H)
                    img_out(i,j)=1;
        %對TL < Nms(i, j) < TH 使用8連通區域確定
                  elseif ( Nms(i+1,j) < YH_H || Nms(i-1,j) < YH_H || Nms(i,j+1) < YH_H || Nms(i,j-1) < YH_H ||...
                    Nms(i-1,j-1) < YH_H || Nms(i-1, j+1) < YH_H || Nms(i+1, j+1) < YH_H || Nms(i+1, j-1) < YH_H)
                    img_out(i,j) = 1;   
                end  
             end
        end
    subplot(2,3,5);
    imshow(img_out);
    title('自做canny邊緣','fontsize',20);
    cannyedge = edge(im1,'canny');
    subplot(2,3,6);
    imshow(cannyedge);
    title('庫函數的canny邊緣','fontsize',20);
    pause(3);
 end

退出


 function   m12_callback(~,~,handles)
               clf;clear all;close all;
 end 

      

最後我上傳一個完整的代碼。需要積分,但是我希望人們能夠去學習而不是去抄抄就當課程設計或者商用了。。這個是我當時課程設計做了快8周的探索做出來的。
具體實現也可以看第一篇中錄製的視頻。

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