canny边缘检测,相对于sobel是较为复杂的一种检测算法,也是迄今为止最优秀的一种边缘检测算法,但是不可否认,它也带来了大量的运算。
canny分四步:
1.对灰度图进行高斯滤波 (很简单)
2.. 求出梯度的幅值图像和角度图像(一般用sobel求,简单)
3.对梯度幅值图形进行非最大值抑制,并进行双阈值处理。(难)
4.连接分析来检测并连接边缘。(很难)
1、高斯滤波。
使用二维高斯公式,产生高斯滤波模版。公式就不写,源码里会有。然后用模版和图像进行卷积。这一步很简单,就不多说了
2、求梯度的赋值和角度
用sobel算子,x方向为[-1,-2,-1; y方向的算子为x方向的转置。然后分别用这两个算子和图像做卷积。然后用公式 A=sqrt(x.^2+y.^2)就是梯度幅值图了。
0, 0, 0
1, 2, 1 ]
然后是求角度,没一点的角度为arctan[y/x],求出梯度角度图;
3、利用求出的梯度角度图,把各个梯度分别划分到水平,垂直,45°,-45°,四个方向上。然后把赋值图按所划分的四个方向,分别同邻近的两个点比较,若为最大值则保留,若不是,则变为0;这样就得到非最大值抑制图,然后设置一个大阈值和一个小阈值,再用小阈值图减去大阈值图。得到阈值图。
4、在大阈值图中定位下一个非0像素P;在小阈值图中用8连通的方式,连接到P,如此递归,连接所有连线。
下面是源码
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%函数功能:canny边缘检测%%%%%%%%%%%%%%%%%%%%%
%%作者:张小胖疯了%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%时间:2013.9.28%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
clear all;close all; clc;
src=imread('D:\Documents\Desktop\lenna.JPG'); %读入图形
[m,n,q]=size(src);
src_gray=my_gray(src); %变为灰度图
src_gaussion=my_gaussian(src_gray,3,9); %高斯滤波
[src_sobel,src_sobel_x,src_sobel_y]=my_sobel(src_gaussion); %%%%%%%sobel梯度检测
figure(1);
imshow(src_sobel);
%%%%%%%%%%%%%%把梯度划分为4个方向%%%%%%%%%%%%%%%%%%%%%%%
new_angel_img=zeros(m,n);
for i=1:m
for j=1:n
Mx=src_sobel_x(i,j);
My=src_sobel_y(i,j);
if My~=0
o=atan(Mx/My); %边缘的法线弧度
elseif My==0 && Mx>0
o=pi/2;
else
o=-pi/2;
end
if( ( o>=( -22.5/180*pi ) && o<( 22.5/180*pi ) ) || ( o>=( 157.5/180*pi ) && o<=pi ) || ( o<=( -157.5/180*pi ) && o>=-pi ) )
d1=0;
elseif ( ( o>= ( 22.5/180*pi ) && o<( 67.5/180*pi ) ) || ( o>=( -157.5/180*pi ) && o<(-112.5/180*pi ) ) )
d1=-45;
elseif ( ( o>=(67.5/180*pi) && o<(112.5/180*pi) ) || ( o>=(-112.5/180*pi) && o<-67.5/180*pi ) )
d1=90;
else
d1=45;
end
new_angel_img(i,j)=d1;
end
end
canny_dst=src_sobel;
%%%%%%%%%%%%%%%%%%非最大值抑制%%%%%%%%%%%%%%%%%%%%%%%5
for i=3:m-2
for j=3:n-2
if( new_angel_img(i,j)==0 )
A=[canny_dst(i-1,j),canny_dst(i+1,j)];
if( abs(canny_dst(i,j))< max( abs(A) ) )
canny_dst(i,j)=0;
end
elseif( new_angel_img(i,j)==-45 )
A=[canny_dst(i-1,j-1),canny_dst(i+1,j+1)];
if( abs(canny_dst(i,j))< max( abs(A) ))
canny_dst(i,j)=0;
end
elseif( new_angel_img(i,j)==90 )
A=[canny_dst(i,j-1),canny_dst(i,j+1)];
if( abs(canny_dst(i,j))< max( abs(A) ) )
canny_dst(i,j)=0;
end
elseif( new_angel_img(i,j)==45 )
A=[canny_dst(i-1,j+1),canny_dst(i+1,j-1)];
if( abs(canny_dst(i,j))< max( abs(A) ) )
canny_dst(i,j)=0;
end
end
end
end
figure(2);
imshow(canny_dst);
gh=zeros(m,n);
gl=zeros(m,n);
gl1=zeros(m,n);
for i=1:m
for j=1:n
if( canny_dst(i,j)>=100 )
gh(i,j)=1;
else
gh(i,j)=0;
end
if( canny_dst(i,j)>=30)
gl(i,j)=1;
else
gl(i,j)=0;
end
end
end
gl1=gl;
gl1=logical(gl1);
gh=logical(gh);
figure(3);
imshow(gl1);
figure(4);
imshow(gh);
%%%%%%%%%%%%%%%%%%%%%%%%递归连接%%%%%%%%%%%%%%%%%%%
up=100; %上阈值
low=33; %下阈值
set(0,'RecursionLimit',10000); %设置最大递归深度
for i=1:m
for j=1:n
if canny_dst(i,j)>up &&canny_dst(i,j)~=255 %判断上阈值
canny_dst(i,j)=255;
canny_dst=connect(canny_dst,i,j,low);
end
end
end
figure(5);
imshow(canny_dst==255);
BW2 = edge(src_gaussion,'canny');
figure(6);
imshow(BW2);
%%%%%%%%高斯滤波函数%%%%%%%%%%%%%%%%%%%%%%
function d=my_gaussian(src,n,k)
n1=(n+1)/2; %%%%%%%%%%%%%计算高斯滤波模版中心
[m,l]=size(src);
b=zeros(n,n); %%%%%%%%%%%%%空模版
I=zeros(m,l);
d=zeros(m,l);
img=zeros(m,l);
%%%%%%%%%%%%%%%%%计算高斯滤波模版的各个系数
for i=1:n
for j=1:n
b(i,j)=(exp(-((i-n1)^2 +(j-n1)^2)/(2*k)))/(2*pi*k);
end
end
b = b/sum(b(:)); %%%%%%%%%%%%%%%%%%%%%%对系数进行归一化 (这个地方必须归一化,不然整体灰度会底)
I=double(src);
img=conv2(I,b,'same'); %%%%%%%%%%%%图像与高斯滤波模版卷积,输出与输入图像大小相同的图像
d=uint8(img); %%%%%%%%%%转为无符号整型,用来显示
end
%%%%%%%%%%%%%%递归连接%%%%%%%%%%%%%%
function nedge=connect(nedge,y,x,low) %种子定位后的连通分析
neighbour=[-1 -1;-1 0;-1 1;0 -1;0 1;1 -1;1 0;1 1]; %八连通搜寻
[m n]=size(nedge);
for k=1:8
yy=y+neighbour(k,1);
xx=x+neighbour(k,2);
if yy>=1 &&yy<=m &&xx>=1 && xx<=n
if nedge(yy,xx)>=low && nedge(yy,xx)~=255 %判断下阈值
nedge(yy,xx)=255;
nedge=connect(nedge,yy,xx,low);
end
end
end
end
原图:
梯度赋值图:
非最大值抑制图:
阈值化后:
连接后:
MATLAB自带函数效果:
很明显自己写的函数跟MATLAB有很大差距,差距主要在非最大值抑制时的细化,和最后的连接。
还有努力啊。