canny 算子实现图像边缘检测(详细过程附源码)

canny边缘检测法是高斯函数的一阶微分,它能在噪声抑制边缘检测之间取得较好的平衡.

环境:windows xp+matlab 2010b

时间:2011/12/25

canny算法检测边缘主要步骤:

1)用3x3高斯滤波器进行滤波,消除噪声;

2)针对每一个像素,计算横向与纵向两方向的微分近似,以得到像素的梯度大小和方向;

3)对梯度进行"非极大抑制"(非局部最大值置0);

4)对梯度取两次阈值;

5)对边缘进行连接;

下面详细说明各个步骤:

0)读入图像:

clear;
clc;
i=imread('light.jpg');
k=rgb2y(i);%获取h分量,即亮度分量

 根据边缘的定义,边缘检测的目的是标识数字图像亮度变化明显的点。(参考维基百科http://zh.wikipedia.org/wiki/%E8%BE%B9%E7%BC%98%E6%A3%80%E6%B5%8B);

 

又根据公式Brightness = 0.3 * R + 0.6 * G + 0.1 * B;计算出亮度分量y;

复制代码
function k=rgb2y(z)
%i必须为rgb三维矩阵
[m,n,p]=size(z);
k=zeros(m,n);
z=double(z);
for i=1:m
    for j=1:n
       k(i,j)=0.3*z(i,j,1)+0.6*z(i,j,2)+0.1*z(i,j,3);
    end
end
复制代码

 

1)用3x3高斯滤波器进行滤波,消除噪声;

复制代码
function j=gaosi(i);
%i必须为二维double矩阵
j=i;
[h,w]=size(i);
for m=2;h-1
    for n=2:w-1
        j(m,n)=(i(m,n-1)+2*i(m,n)+i(m,n+1))/4;%横向高斯滤波
    end
end
复制代码

利用上面自定义的gaosi函数对图像进行二维3x3滤波,

k1=gaosi(k);%横向滤波
k1=k1';%对图像进行转置,为下一步纵向滤波作准备(纵向滤波==转置后横向滤波)
k1=gaosi(k1);
k1=k1';%还原

 

2)针对每一个像素,计算横向与纵向两方向的微分近似,以得到像素的梯度大小和方向;

利用上式,易知p,q分别为计算出的横向、纵向的微分近似,由此再计算出梯度的大小和方向。

复制代码
%计算梯度的大小和方向
[h,w]=size(k);
for m=2:h-1
    for n=2:w-1
        zz1=k1(m,n-1)+k1(m+1,n-1);
        zz2=k1(m,n)+k1(m+1,n);
        zz3=k1(m,n-1)+k1(m,n);
        zz4=k1(m+1,n-1)+k1(m+1,n);
        kp(m,n)=0.5*(zz2-zz1);
        kq(m,n)=0.5*(zz3-zz4);
        kfu(m,n)=sqrt((kp(m,n)^2)+(kq(m,n)^2));%梯度大小
        kjiao(m,n)=atan(kq(m,n)/(kp(m,n)+0.001));%梯度方向,0.001防止分母为0
    end
end
复制代码

3)对梯度进行"非极大抑制"(非局部最大值置0);

1.先将梯度方向归类为四个主要方向,左右、上下、左斜、右斜。

复制代码
%非极大值抑制
%首先将梯度方向划分为4个方向0,45,90,135(以及他们的反向延长线)
for m=2:h-1
    for n=2:w-1
        if kjiao(m,n)>=3/8*pi
            kjiao(m,n)=2;
        else if kjiao(m,n)>=1/8*pi
            kjiao(m,n)=1;
        else if  kjiao(m,n)>=-1/8*pi
             kjiao(m,n)=0;
        else if  kjiao(m,n)>=-3/8*pi
             kjiao(m,n)=3;
            else
                kjiao(m,n)=2;
            end
            end
            end
        end
    end
end
复制代码

根据划分后的4个方向,判断该点是否是8邻域的局部最大值(梯度方向),比如,梯度方向为左右方向的点,判断其是否比左右两点的值来的大,如果不是,使该点的值为0.

复制代码
%按照各个方向分别判断
k2=k1;
for m=2:h-1
    for n=2:w-1
       if kjiao(m,n)==0
           if k1(m,n)>k1(m,n-1)&&k1(m,n)>k1(m,n+1);
           else k2(m,n)=0;
           end
       end
       if kjiao(m,n)==1
           if k1(m,n)>k1(m+1,n-1)&&k1(m,n)>k1(m-1,n+1);
           else k2(m,n)=0;
           end
       end
       if kjiao(m,n)==2
           if k1(m,n)>k1(m-1,n)&&k1(m,n)>k1(m+1,n);
           else k2(m,n)=0;
           end
       end
       if kjiao(m,n)==3
           if k1(m,n)>k1(m-1,n-1)&&k1(m,n)>k1(m+1,n+1);
           else k2(m,n)=0;
           end
       end
    end
end
复制代码

4)对梯度取两次阈值;

用两个阈值t1和t2(t2>t1,一般取t2=2*t1),我们把梯度值小于t1的像素的灰度设为0,得到图像1,然后我们把梯度值小于t2的像素的灰度设为0,得到图像2。由于图像2的阈值较高,噪音较少(但同时也损失了有用的边缘信息,而图像1的阈值较低,保留了较多信息,因此我们可以以图像2为基础,以图像1为补充来连接图像的边缘。

复制代码
%两次阈值分割
k3=k2;%以t1为阈值分割后的矩阵
k4=k2;%以t2为阈值分割后的矩阵
t1=50;
t2=2*t1;
for m=2:h-1
    for n=2:w-1
       if kfu(m,n)<t1
           k3(m,n)=0;
       end
       if kfu(m,n)<t2
           k4(m,n)=0;
       end
    end
end
复制代码



5)对边缘进行连接;

a.扫描图像2,当我们遇到一个非零值的像素p时(跟踪以p为开始点的轮廓线直到该轮廓线的终点q;
b.在图像1中,考察与图像2中p点位置对应的点p'的8邻域,如果在p'点的8邻域中有非零像素q'存在,将其包括到图像2中,作为点r,从r开始(重复第a步,直到我们在图像1和图像2中都无法继续为止;
c.我们已经结束了对包含p的轮廓线的连接,将这条轮廓标记为已访问过,回到第a步,寻找下一条轮廓线,重复第(a)(b)(c)步直到图像2中再也找不到新轮廓线为止.

findline.m:

复制代码
function [ff,flag1]=findline(k3,k4,flag,m,n)

flag1=flag;
  m1=m+1;n1=n+1;
  while(m~=m1||n~=n1)%若m和n都不发生变化,表明line已到终点
  flagg=0;
  for i=1:3
      if(flagg==1) break;          
      end
      for j=1:3
          if k3(m-2+i,n-2+j)~=0
              k4(m-2+i,n-2+j)=255;
              m1=m-2+i;n1=n-2+j;%新的[m,n]点
              flag1(m,n)=1;%标记已检测过
              flagg=1;break;
      end
      end
  end
    m=m1;n=n1;
  end
  ff=k4;
复制代码

主函数里写上:

复制代码
figure;
subplot(221);imshow(i);title('原图像');
subplot(222);imshow(k3,[]);title('阈值为50的分割图像');
subplot(223);imshow(k4,[]);title('阈值为100的分割图像');
flag=zeros(h,w);%标记该点是否以检测过,1表示检测过
for m=2:h-1
    for n=2:w-1
        if k4(m,n)~=0&&flag(m,n)==0
            [k4,flag]=findline(k3,k4,flag,m,n);
        end
    end
end   
subplot(224);imshow(k4,[]);title('修正后的分割图像');
复制代码

至此,程序完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值