最近老师布置了一个边缘检测的作业,我借此机会详细学习了一下canny算子,在此进行总结,并分别给出OpenCV代码和MATLAB代码,自己水平有限,若有错误或者更好的编程方法,请广大网友留言,一定虚心学习。好了废话少说,直接开始吧。

基本原理

  • 须满足条件:抑制噪声;精确定位边缘。
  • 从数学上表达了三个准则[信噪比准则(低错误率)、定位精度准则、单边缘响应准则],并寻找表达式的最佳解。
  • 属于先平滑后求导的方法。

算法步骤

  1. 高斯平滑滤波(这个比较简单,后续介绍会省略)
  2. 计算图像梯度的幅值和方向
  3. 对幅值图像进行非极大值抑制
  4. 用双阈值算法检测和连接边缘

详细过程

1、高斯平滑滤波(略)

2、计算图像梯度的幅值和方向

可选用的模板:soble算子、Prewitt算子、一阶差分卷积模板等等;

在此选用Prewitt算子为例:

由此可算得x方向梯度幅值:

      y方向梯度幅值:

进一步可以得到图像梯度的幅值和方向:

如下图表示了中心点的梯度向量、方位角以及边缘方向。(任一点的边缘与梯度向量正交)

3、对幅值图像进行非极大值抑制

首先将角度划分成四个方向范围 :水平(0°)、−45°、垂直(90°)、+45°。如下图: 

注:在opencv中由于反三角函数 cv::fastAtan2()得到的角度是0~360°,故划分为:水平(0°)、45°、垂直(90°)、135°,原理同上。 

下面看上述角度划分对应于3*3邻域的4种可能组合,如下图:

扇形区标号d1~d4,对应3*3领域的4种可能的组合,1-x-5 , 7-x-3 , 2-x-6 , 8-x-4。

在每一点上,领域中心 x 与沿着其对应的梯度方向的两个像素相比,若中心像素为最大值,则保留,否则中心置0,这样可以抑制非极大值,保留局部梯度最大的点,以得到细化的边缘。
 
4、用双阈值算法检测和连接边缘
 

选取系数TH和TL,比率为2:1或3:1。(一般取TH=0.3或0.2,TL=0.1);
取出非极大值抑制后的图像中的最大梯度幅值,定义高低阈值。即:TH×Max,TL×Max (当然可以自己给定) ;
将小于低阈值的点抛弃,赋0;将大于高阈值的点立即标记(这些点就是边缘点),赋1;
将小于高阈值,大于低阈值的点使用8连通区域确定(即:只有与TH像素连接时才会被接受,成为边缘点,赋  1)。
 
算法实现
matlab代码

clc
clear all
img_in=imread('lenna.jpg');
img_in=rgb2gray(img_in);
figure,imshow(img_in);
title('原图');

[rows,cols]=size(img_in);
thresh=graythresh(img_in);
img_bw=im2bw(img_in,thresh);%二值图像

%%%%step1:高斯滤波
template=fspecial('gaussian',3,0.8);%生成一个3*3的高斯模板,标准差选0.8
img_filt=imfilter(img_bw,template);

%%%%step2:计算梯度(幅度和方向)
%Prewitt梯度模板
%也可选择一阶差分卷积模板:
%dx=[-1,-1;1,1] dy=[1,-1;1,-1]
%*********************
dx = [-1 -1 -1;0 0 0;1 1 1];%x方向的梯度模板
dy = [-1 0 1; -1 0 1;-1 0 1];%y方向的梯度模板
img_filt=double(img_filt);

grad_x=conv2(img_filt,dx,'same');%获取x方向的梯度图像.使用梯度模板进行二维卷积,结果与原图像大小相同
grad_y=conv2(img_filt,dy,'same');%获取y方向的梯度图像.使用梯度模板进行二维卷积,结果与原图像大小相同
grad=sqrt((grad_x.^2)+(grad_y.^2));%梯度幅值图像

figure,imshow(grad);
title('梯度幅值图');

grad_dir=atan2(grad_y,grad_x);%获取梯度方向弧度
grad_dir=grad_dir*180/pi;
%%%%step3:对梯度幅值进行非极大值抑制
%首先将角度划分成四个方向范围:水平(0°)、-45°、垂直(90°)、+45°
for i = 1:rows
    for j = 1:cols
        if((grad_dir(i,j)>=-22.5 && grad_dir(i,j)<=22.5) || (grad_dir(i,j)>=157.5 && grad_dir(i,j)<=180)...
                                       ||(grad_dir(i,j)<=-157.5 && grad_dir(i,j)>=-180) )
            grad_dir(i,j) = 0;
        elseif((grad_dir(i,j) >= 22.5) && (grad_dir(i,j) < 67.5) || (grad_dir(i,j) <= -112.5) && (grad_dir(i,j) > -157.5))
            grad_dir(i,j) = -45;
        elseif((grad_dir(i,j) >= 67.5) && (grad_dir(i,j) < 112.5) || (grad_dir(i,j) <= -67.5) && (grad_dir(i,j) >- 112.5))
            grad_dir(i,j) = 90;
        elseif((grad_dir(i,j) >= 112.5) && (grad_dir(i,j) < 157.5) || (grad_dir(i,j) <= -22.5) && (grad_dir(i,j) > -67.5))
            grad_dir(i,j) = 45;  
        end
    end
end

%讨论对3x3区域的四个基本边缘方向进行非极大值抑制.获取非极大值抑制图像
Nms = zeros(rows,cols);%定义一个非极大值抑制图像
for i = 2:rows-1
    for j= 2:cols-1
        if (grad_dir(i,j) == 90 && grad(i,j) == max([grad(i,j), grad(i,j+1), grad(i,j-1)]))
            Nms(i,j) = grad(i,j);
        elseif (grad_dir(i,j) == -45 && grad(i,j) == max([grad(i,j), grad(i+1,j-1), grad(i-1,j+1)]))
            Nms(i,j) = grad(i,j);
        elseif (grad_dir(i,j) == 0 && grad(i,j) == max([grad(i,j), grad(i+1,j), grad(i-1,j)]))
            Nms(i,j) = grad(i,j);
        elseif (grad_dir(i,j) == 45 && grad(i,j) == max([grad(i,j), grad(i+1,j+1), grad(i-1,j-1)]))
            Nms(i,j) = grad(i,j);
        end;
    end;
end;

figure,imshow(Nms);
title('非极大值抑制图');

%%%%step4:双阈值检测和连接边缘
img_out=zeros(rows,cols);%定义一个双阈值图像
YH_L=0.1*max(max(Nms));%低阈值
YH_H=0.3*max(max(Nms));%高阈值
for i = 1:rows
    for j = 1:cols
        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
bw=edge(img_bw,'canny');

figure,imshow(img_out);
title('本实验结果图');

figure,imshow(bw);
title('工具箱Canny算子效果图');

效果图