[图像]Canny检测的Matlab实现(含代码)「建议收藏」

[图像]Canny检测的Matlab实现(含代码)「建议收藏」图象的边缘是指图象局部区域亮度变化显著的部分,该区域的灰度剖面一般可以看作是一个阶跃,既从一个灰度值在很小的缓冲区域内急剧变化到另一个灰度相差较大的灰度值。Canny边缘检测基本特征如下:(1)必须满足两个条件:①能有效地抑制噪声;②必须尽量精确确定边缘的位置。(2)根据对信噪比与定位乘积进行测度,得到最优化逼近算子。这就是Canny

大家好,又见面了,我是你们的朋友全栈君。

原创文章,欢迎转载。转载请注明:转载自 祥的博客

原文链接:http://blog.csdn.net/humanking7/article/details/46606791


图象的边缘是指图象局部区域亮度变化显著的部分,该区域的灰度剖面一般可以看作是一个阶跃,既从一个灰度值在很小的缓冲区域内急剧变化到另一个灰度相差较大的灰度值。

Canny边缘检测基本特征如下:

(1) 必须满足两个条件:①能有效地抑制噪声;②必须尽量精确确定边缘的位置。
(2) 根据对信噪比与定位乘积进行测度,得到最优化逼近算子。这就是Canny边缘检测算子。
(3) 类似与Marr(LoG)边缘检测方法,也属于先平滑后求导数的方法。

Canny边缘检测算法步骤:

步骤1:用高斯滤波器平滑处理原图像;
步骤2:用一阶偏导的有限差分进行计算梯度的幅值和方向;
步骤3:对梯度幅值进行非极大值抑制;
步骤4:用双阈值算法检测和连接边缘。

步骤详解

步骤1:用高斯滤波器平滑处理原图像

1.png
2.png
使用平滑滤波的原因从根本上来说是边缘检测算子中的导数计算。导数计算对噪声十分敏感,如果不提前使用滤波器加以改善,则在导数计算后,噪声将会被放大,使得检测出来的虚假边缘变多,不利于边缘的提取。

平滑滤波和边缘检测是一对矛盾的概念。一方面,平滑滤波能够有效的抑制噪声,而在此过程中会使得图像边缘模糊,增加了边缘定位的不确定性。另一方面,平滑滤波能够除去对边缘检测中导数运算敏感的噪声,有效的抑制了虚假边缘的产生。实际工程经验表明,高斯滤波器可以在抗噪声干扰和边缘检测精确定位之间提供一个较好的折中方案。

步骤2:用一阶偏导的有限差分进行计算梯度的幅值和方向

图像的边缘有方向和幅度两个属性,沿边缘方向像素变化平缓,垂直于边缘方向像素变化剧烈,边缘上的这种变化可以用微分算子检测出来,通常用一阶或二阶导数来检测边缘.

3.png
由上图可以看出,一阶导数可以用于检测图像中的一个点是否是边缘点(也就是判断一个点是否在斜坡上)。同样,二阶导数的符号可以用于判断一个边缘像素是否在亮的一边还是暗的一边。
用一阶偏导的有限差分来计算梯度的幅值和方向。
4.png

下图中,图a经过梯度计算后的得到梯度三维示意图b。图b中x和y代表图像像素位置,竖轴的数值反映了梯度幅值的大小。由公式(3-17)可知,边缘方向与梯度方向是相垂直的,如图b所示。
5.png

步骤3:对梯度幅值进行非极大值抑制

6.png

步骤4:用双阈值算法检测和连接边缘

7.png

代码

主函数代码

主函数代码文件main.m


clear all;
clear
clc;
%读进图像
[filename, pathname] = uigetfile({'*.jpg'; '*.bmp'; '*.gif'}, '选择图片');

%没有图像
if filename == 0
    return;
end

imgsrc = imread([pathname, filename]);
[y, x, dim] = size(imgsrc);

%转换为灰度图
if dim>1
    imgsrc = rgb2gray(imgsrc);
end

sigma = 1;
gausFilter = fspecial('gaussian', [3,3], sigma);
img= imfilter(imgsrc, gausFilter, 'replicate');

zz = double(img);

 %----------------------------------------------------------
 %自己的边缘检测函数
 [m theta sector canny1  canny2 bin] = canny1step(img, 22);
  [msrc thetasrc sectorsrc c1src  c2src binsrc] = canny1step(imgsrc, 22);
 %Matlab自带的边缘检测
 ed = edge(img, 'canny', 0.5); 
 
 
[xx, yy] = meshgrid(1:x, 1:y);

figure(1)
    %mesh(yy, xx, zz);
    surf(yy, xx, zz);
    xlabel('y');
    ylabel('x');
    zlabel('Grayscale');
    axis tight

figure(2)    
    subplot(4,2,1);
        imshow(imgsrc);%原图
    subplot(4,2,2);
        imshow(img);%高斯滤波后
    subplot(4,2,3);
        imshow(uint8(m));%导数
    subplot(4,2,4);
        imshow(uint8(canny1));%非极大值抑制
    subplot(4,2,5);
        imshow(uint8(canny2));%双阈值
    subplot(4,2,6);
        imshow(ed);%Matlab自带边缘检测
    subplot(4,2,8);
        imshow(bin);%我自己的bin
    
figure(3)
    edzz = 255*double(ed);
    mesh(yy,xx,edzz);
    xlabel('y');
    ylabel('x');
    zlabel('Grayscale');
    axis tight 
 

 
figure(4)
    mesh(yy,xx,m);%画偏导数
    xlabel('y');
    ylabel('x');
    zlabel('Derivative');
    axis tight 
 
figure(5)
    mesh(yy,xx,theta);
    xlabel('y');
    ylabel('x');
    zlabel('Theta');
    axis tight
 
figure(6)
    mesh(yy,xx,sector);
    xlabel('y');
    ylabel('x');
    zlabel('Sector');
    axis tight
    
figure(7)
    mesh(yy,xx,canny2);
    xlabel('y');
    ylabel('x');
    zlabel('Sector');
    axis tight

Canny边缘检测函数代码

Canny边缘检测函数文件canny1step.m

function [ m, theta, sector, canny1,  canny2, bin] = canny1step( src,  lowTh)
%canny函数第一步,求去x,y方向的偏导,模板如下:
% Gx
% 1  -1
% 1  -1
% Gy
% -1  -1
%  1    1
%------------------------------------
% 输入:
% src:图像,如果不是灰度图转成灰度图
% lowTh:低阈值
% 输出:
% m: 两个偏导的平方差,反映了边缘的强度
% theta:反映了边缘的方向
% sector:将方向分为3个区域,具体如下
% 2 1 0
% 3 X 3
% 0 1 2
% canny1:非极大值
% canny2:双阈值抑制
% bin :     二值化
%--------------------------------------- 


[Ay Ax dim ] = size(src);
%转换为灰度图
if dim>1
    src = rgb2gray(src);
end


src = double(src);
m = zeros(Ay, Ax); 
theta = zeros(Ay, Ax);
sector = zeros(Ay, Ax);
canny1 = zeros(Ay, Ax);%非极大值抑制
canny2 = zeros(Ay, Ax);%双阈值检测和连接
bin = zeros(Ay, Ax);
for y = 1:(Ay-1)
    for x = 1:(Ax-1)
        gx =  src(y, x) + src(y+1, x) - src(y, x+1)  - src(y+1, x+1);
        gy = -src(y, x) + src(y+1, x) - src(y, x+1) + src(y+1, x+1);
        m(y,x) = (gx^2+gy^2)^0.5 ;
        %--------------------------------
        theta(y,x) = atand(gx/gy)  ;
        tem = theta(y,x);
        %--------------------------------
        if (tem<67.5)&&(tem>22.5)
            sector(y,x) =  0;    
        elseif (tem<22.5)&&(tem>-22.5)
            sector(y,x) =  3;    
        elseif (tem<-22.5)&&(tem>-67.5)
            sector(y,x) =   2;    
        else
            sector(y,x) =   1;    
        end
        %--------------------------------        
    end    
end
%-------------------------
%非极大值抑制
%------> x
%   2 1 0
%   3 X 3
%y  0 1 2
for y = 2:(Ay-1)
    for x = 2:(Ax-1)        
        if 0 == sector(y,x) %右上 - 左下
            if ( m(y,x)>m(y-1,x+1) )&&( m(y,x)>m(y+1,x-1)  )
                canny1(y,x) = m(y,x);
            else
                canny1(y,x) = 0;
            end
        elseif 1 == sector(y,x) %竖直方向
            if ( m(y,x)>m(y-1,x) )&&( m(y,x)>m(y+1,x)  )
                canny1(y,x) = m(y,x);
            else
                canny1(y,x) = 0;
            end
        elseif 2 == sector(y,x) %左上 - 右下
            if ( m(y,x)>m(y-1,x-1) )&&( m(y,x)>m(y+1,x+1)  )
                canny1(y,x) = m(y,x);
            else
                canny1(y,x) = 0;
            end
        elseif 3 == sector(y,x) %横方向
            if ( m(y,x)>m(y,x+1) )&&( m(y,x)>m(y,x-1)  )
                canny1(y,x) = m(y,x);
            else
                canny1(y,x) = 0;
            end
        end        
    end%end for x
end%end for y

%---------------------------------
%双阈值检测
ratio = 2;
for y = 2:(Ay-1)
    for x = 2:(Ax-1)        
        if canny1(y,x)<lowTh %低阈值处理
            canny2(y,x) = 0;
            bin(y,x) = 0;
            continue;
        elseif canny1(y,x)>ratio*lowTh %高阈值处理
            canny2(y,x) = canny1(y,x);
            bin(y,x) = 1;
            continue;
        else %介于之间的看其8领域有没有高于高阈值的,有则可以为边缘
            tem =[canny1(y-1,x-1), canny1(y-1,x), canny1(y-1,x+1);
                       canny1(y,x-1),    canny1(y,x),   canny1(y,x+1);
                       canny1(y+1,x-1), canny1(y+1,x), canny1(y+1,x+1)];
            temMax = max(tem);
            if temMax(1) > ratio*lowTh
                canny2(y,x) = temMax(1);
                bin(y,x) = 1;
                continue;
            else
                canny2(y,x) = 0;
                bin(y,x) = 0;
                continue;
            end
        end
    end%end for x
end%end for y




end%end of function

结果对比

对比图像

8.png

分析

图2所示的四张图片展示了整个边缘检测的过程。

  • **图(a)**显示的为图像的梯度值,其三维图像为图3左所示。
  • **图(b)**显示了经过极大值抑制之后的图像,可以看出经过非极大值抑制之后,排除非边缘的像素,仅仅保留了一些细的线条,即就是候选的边缘。
  • **图©**为经过双阈值算法处理过后的二值化图像(选用的低阈值为22,高低阈值之比为2:1),对比图(b)发现此过程有效的抑制了“虚假边缘”的产生,而且也相应的连接了一些断裂的边缘。
  • **图(d)**是利用Matlab自带edge函数的Canny方法,可以看出其效果优于我实现的传统Canny程序,说明Matlab在阈值选取原则和梯度算子两方面有自己相应的改进。

图3展示了梯度幅值在非极大值抑制前后的三维效果,对应于图2中的图(a)和图(b)。


注: 本文所用的图像和代码源自于本人的毕设论文。


donate

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/139040.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • java ftl_.ftl文件 是什么文件 freemaker「建议收藏」

    java ftl_.ftl文件 是什么文件 freemaker「建议收藏」freemarker的文件一般以后缀ftl,ftl文件的头上要写这样才能在ftl中使用webwork的tag。freemarker确实是不错的模版语言引擎,尤其是处理对象图很方便,处理xml也很方便,还支持xpath什么是FreeMarker?FreeMarker是一个模版引擎,一个基于文本的模板输出工具(生成任意的HTML表单代码)。它是一个Javapackage,面向Java程序员的cla…

    2022年6月26日
    42
  • 微信小程序发送消息推送_小程序推送消息设置

    微信小程序发送消息推送_小程序推送消息设置在小程序开发中,如果想实现:用户发给小程序的消息以及开发者需要的事件推送,在小程序项目中,我们想要实现这样一个功能,比如我们小程序中的客服功能,我不想要使用小程序后台的在线客服功能,但我又想实现客服功能,这个时候微信提供了消息推送功能,在小程序后台的设置,开发设置中,消息推送功能:点击开启消息推送功能,认证成功进入到消息推送配置界面:这里配置几个参数注意一下:URL(服务器………

    2022年10月7日
    4
  • yuv422转rgb(422sp转420p)

    YUV420转RGB888yuv420的数据存储方式是planar,就是在一帧中先存y分量,存完y存u,接着v分量。而在yuv420中有y分量widthheightbyte,uv分量各是widthheight1/4,一帧中总的数据是widthheight3/2(widthheight12/8).所以当时认为,yuv分量代入转换公式的话,uv分量是不是少了。其实不然,因为是每四个y分量共用一个u分量一个v分量,但也不是[YiYi+1Yi+2Yi+3]共用[Ui],[Vi],因为是一个22的窗口内的Y分量

    2022年4月10日
    55
  • Unity 安装个人免费版步骤详解「建议收藏」

    Unity 安装个人免费版步骤详解「建议收藏」1、进入Unity官网,滑动到最底部,点击“所有版本”:

    2022年6月27日
    719
  • mysql倒序截取字符串_MySQL数据库之mysql截取字符串与reverse函数

    mysql倒序截取字符串_MySQL数据库之mysql截取字符串与reverse函数本文主要向大家介绍了MySQL数据库之mysql截取字符串与reverse函数,通过具体的内容向大家展现,希望对大家学习MySQL数据库有所帮助。这个网页上很多知识点,可以学习下,关于mysql的函数,也可以作为API查询:这里只说下mysql的截取函数和reverse函数:MySQL字符串截取函数:left(),right(),substring(),substring_index()…

    2025年6月16日
    2
  • 硬件知识–LM393的应用总结「建议收藏」

    硬件知识–LM393的应用总结「建议收藏」总结:正向输入的电压高于负向输入电压,比较器将输出高电平;反之则低。 最近要做一个光电传感器模块,去年的电赛上因为光电传感器的失败,心里总是有些不甘心。要做光电传感器模块首先,就参考某宝上的模块的设计。在那些成品模块上,用到了LM393;并且LM393是常用的比较器的芯片,所以有必要仔细研究一下它的电路应用,为以后参考。 某宝上有大量的这样的成品模块。一般是用于光电传感器,主要的芯片就是

    2022年7月22日
    9

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号