導言區
%%導言區
%用來測試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周的探索做出來的。
具體實現也可以看第一篇中錄製的視頻。