经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » 编程经验 » 查看文章
Canny算法检测边缘
来源:cnblogs  作者:KenSporger  时间:2019/10/8 9:15:17  对本文有异议

  Canny算法是边缘检测的一个经典算法,比单纯用一些微分算子来检测的效果要好很多,其优势有以下几点:

  1. 边缘误检与漏检率低。
  2. 边缘定位准确,且边界较细。
  3. 自带一定的滤噪功能,或者说,对噪声的敏感度要比单纯算子低。
  4. 具有多个可调整参数,可影响算法的时间与时效。

  但是Canny相比单纯算子来说计算量偏大,下面简单介绍算法的过程。

 

图像去噪: 

    这一步不是必须的,一般噪声少的图,让Canny自己应付就行。若噪声较多,一般采用高斯滤波。滤波后,噪声灰度下降,对边缘的影响

  小于噪点。

 

获取梯度强度与方向: 

    用一阶微分算子获取梯度强度和方向,如sobel算子,强度与方向公式如下:

    

    要用两个矩阵分别存储强度和方向,其中方向公式需要根据具体算子情况更改正负号,最终使上下左右、45度方向可以区分开来。例如下图所示,

    这里将上下放在(60,90)和(-90,-60)区间,因为它们在非极大抑制时取的都是上下邻域;将左右放在(-30,30)区间,因为它们抑制时取的是

    左右邻域;将处于斜向上对角线的梯度角放在(30,60),它们在抑制时取45度的两个邻域;将斜向下对角线上的梯度角放在(-60,-30),它们在

    抑制时取-45度的两个邻域。

    

 

非极大抑制:

    该步骤目的是删除非边缘像素,主要做法是对每个像素点,与它梯度方向的相邻两像素作灰度比较(比如左右两个像素),如果该像素点

  灰度比它两个相邻像素都大,则保留其灰度值,否则抑制(赋值0)。

 

双阈值处理:

    设定一个高阈值和低阈值,对灰度大于高阈值的像素点,确认为强边缘,赋值255,低于低阈值的点赋值0,介于之间的点保留其灰度值,作为弱边缘。

 

滞后边界追踪:

    这一步主要是处理弱边缘,总的思路是找到每一个弱边缘的连通域,判断该连通域与强边缘有无相邻,如果存在至少一个像素与强边缘相邻,则将该连

  通域都作为强边缘(赋值255),如果没有一个像素与强边缘相邻,则将整个连通域视为噪声(赋值0)。

    所以,关键在于找到连通域,通常有DFS与BFS两种算法可以处理连通域问题,这里采用BFS算法,思路如下:

 

    1.扫描整个图像,判断每个像素是否为弱边缘,如果是,进入步骤2(查找连通域)。

    2.用connect数组保存组成连通域的所有点;用weak数组保存待检查八领域的弱点;用checked数组标记已被扫描过的弱点(也就是连通域里的点),标

       记1表示已扫描;用变量real_edge记录是否有检测到强点。将刚才判定的弱边缘点压入connect、weak,checked相应位置标记为1。进入步骤3。

    3.创建new_weak用来保存新的待检测八领域的弱点。依次对weak中每个待检测点,遍历其八领域,找到所有未被checked标记的新弱点,压入new_weak

       、connect,checked相应位置标记为1,同时检查八领域内是否有强边缘,有则标记real_edge=1。对weak所有待检测点检查完后,如果new_weak不为

      空,则将new_weak赋给weak,重复步骤3;如果new_weak为空,则表示一个连通域寻找完毕,进入步骤4。

    4.如果real_edge=1,则将连通域每个点的灰度赋值255,否则赋值0;弹出connect、weak、checked中所有点。

    5.重复步骤1-4,直道图像扫描完毕。

    

  经过以上这些步骤,Canny算法就已经实现了。我们要做的就是根据图像调整双阈值和滤波强度。下面是Canny的一个处理实例,其中给出了单纯sobel检测和

Canny检测的效果。通过下图可以知道,Canny检测的边缘要薄的多,细节处理更好,噪声也更少。

 

  

   以下是matlab代码实现:

  

  1. %canny前先高斯滤波
  2. function edge=canny(gaussianimg,lthres,hthres)
  3. sobel_operator_x=[-1,0,1;
  4. -2,0,2;
  5. -1,0,1];
  6. sobel_operator_y=[-1,-2,-1;
  7. 0,0,0;
  8. 1,2,1];
  9. %梯度强度
  10. diff_x=filter2(sobel_operator_x,gaussianimg);
  11. diff_y=filter2(sobel_operator_y,gaussianimg);
  12. diff=uint8(sqrt((diff_x.^2+diff_y.^2)/32));
  13. %梯度方向
  14. [sizex,sizey]=size(gaussianimg);
  15. angle=zeros(sizex,sizey);
  16. edge=uint8(zeros(sizex,sizey));
  17. for i=1:sizex
  18. for j=1:sizey
  19. angle(i,j)=atan(-diff_y(i,j)/diff_x(i,j))/pi*180;
  20. end
  21. end
  22. %非极大抑制,排除非边缘像素
  23. for i=2:sizex-1
  24. for j=2:sizey-1
  25. if (angle(i,j)>90||angle(i,j)<-90)
  26. break;
  27. elseif angle(i,j)>=60 || angle(i,j)<=-60
  28. if (diff(i,j)>diff(i-1,j)&&diff(i,j)>=diff(i+1,j))
  29. edge(i,j)=uint8(diff(i,j));
  30. end
  31. elseif (angle(i,j)<=-30)
  32. if (diff(i,j)>diff(i-1,j-1)&&diff(i,j)>=diff(i+1,j+1))
  33. edge(i,j)=uint8(diff(i,j));
  34. end
  35. elseif angle(i,j)>=30
  36. if (diff(i,j)>diff(i-1,j+1)&&diff(i,j)>=diff(i+1,j-1))
  37. edge(i,j)=uint8(diff(i,j));
  38. end
  39. elseif angle(i,j)<30||angle(i,j)>-30
  40. if (diff(i,j)>diff(i,j-1)&&diff(i,j)>=diff(i,j+1))
  41. edge(i,j)=uint8(diff(i,j));
  42. end
  43. end
  44. end
  45. end
  46. %双阈值
  47. for i=1:sizex
  48. for j=1:sizey
  49. if (edge(i,j)>=hthres)
  50. edge(i,j)=255; %一定为边缘
  51. elseif (edge(i,j)<=lthres)
  52. edge(i,j)=0; %一定为非边缘
  53. end
  54. end
  55. end
  56. %候选边缘,与已确定边缘相连才认为是边缘
  57. for i=2:sizex-1
  58. for j=2:sizey-1
  59. if (edge(i,j)>0&&edge(i,j)<255)
  60. real_edge=0;%边缘真假标志
  61. checked=zeros(sizex,sizey);%标记已经扫描过的弱点
  62. weak=zeros(100,2);%存储需要查看八领域的弱点
  63. connect=zeros(100,2);%存储一条联通的所有弱点
  64. weak_length=1;
  65. connect_length=1;
  66. %压入第一个弱边缘点
  67. weak(weak_length,:)=[i,j];
  68. connect(connect_length,:)=[i,j];
  69. checked(i,j)=1;
  70. while(weak_length>0)
  71. new_weak=zeros(100,2);
  72. new_weak_length=0;
  73. for k=1:weak_length
  74. %搜索当前弱点的八领域
  75. x=weak(k,1);y=weak(k,2);
  76. if (x>=2&&x<=sizex-1&&y>=2&&y<=sizey-1)
  77. for m=x-1:x+1
  78. for n=y-1:y+1
  79. if edge(m,n)>0&&edge(m,n)<255&&checked(m,n)==0%领域有弱点且未被扫描过
  80. new_weak_length=new_weak_length+1;
  81. connect_length=connect_length+1;
  82. new_weak(weak_length,:)=[m,n];%压入新弱点集合
  83. connect(connect_length,:)=[m,n]; %压入连通域
  84. checked(m,n)=1; %标记已扫描
  85. elseif edge(m,n)==255
  86. real_edge=1; %边缘为真,等待连通域全部被识别
  87. end
  88. end
  89. end
  90. end
  91. end
  92. weak_length=new_weak_length;%当前深度的弱点集合全部检查过八领域,开始检查新一深度的弱点集合
  93. weak=new_weak;%如果新集合没有弱点,则跳出while
  94. end
  95. %一个连通域已在connect里形成
  96. for z=1:connect_length
  97. edge(connect(z,1),connect(z,2))=uint8((255* real_edge));
  98. end
  99. end
  100. end
  101. end
  102. end

 

  

原文链接:http://www.cnblogs.com/kensporger/p/11628661.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号