FPGA图像处理之Canny边缘检测

描述

在边缘检测算法里面Sobel是比较简单的一个算法,但是其检测出来的边缘往往是比较粗的,效果不是很好,因为我们最理想的边缘肯定就是一个宽度为1的细线。

Canny算法在此基础上进行了改进,通过使用边缘的梯度信息进行非最大值抑制(NMS)和利用双阈值,这些措施消除了假性边缘,提高了边缘检测的效果。

Canny边缘检测分为以下几个部分:

高斯滤波

Sobel边缘检测

非最大值抑制

双阈值边缘检测

弱边缘连接

高斯滤波和Sobel边缘检测在之前的文章里面介绍过,在这里不再赘述。

可以参见:FPGA图像处理--Sobel边缘检测  FPGA图像处理--高斯模糊(一) FPGA图像处理--高斯模糊(二) 这三篇文章。

在本篇中为大家介绍Canny边缘检测中的NMS部分。

我们通过Sobel的计算可以获得两个信息,分别是梯度的幅值和方向。

梯度的幅值可以表示为:

因为我们想在FPGA中实现Canny算子,而根号对FPGA来说消耗的资源是比较多的,因此我们选择另一种幅值的表示方式:

梯度的方向可以表示为:

接下来就可以通过梯度的幅值和方向来做NMS了。

我们在做完Sobel之后得到的幅值和方向也就是下面一幅示意图

FPGA

每一个点都有其方向和梯度的值。

假如我们Sobel检测的阈值设置的为90,那么在上面示意图中左上角的99,108,110,97这几个点都会被看做边缘部分,在Canny中会对这个做NMS,首先看到99他的梯度方向向下,然后梯度方向下一个值是110比99大,那么就认为99这个点不是边缘,110比他更靠近边缘部分,这样99就被抑制掉了,边缘也能够被细化。对于108来说,他的梯度方向下一个值是77,比108小,那么108就不会被抑制。通过这种方式就能完成非最大值抑制。

在上图里面展示的梯度方向只有0,45,90这些角度,然而在实际计算中不可能只有这些角度的,所以我们要进行插值计算,接下来就说明一下如何进行插值计算。

FPGA

以任意一点为坐标原点,如上图中的5这个点,他周围有一个3*3的矩阵,然后他的梯度方向是红色箭头的方向。这个方向不在X和Y轴的45°方向(PS:我们规定图像向下为Y轴正方向,向右为X轴正方向,象限为顺时针方向)。

因为其不在45°方向,那么就不能直接将5的梯度和1,9的梯度进行比较来确定是否对5这点进行抑制,那么我们就需要使用插值的方式来生成两个点(亚像素点)进而进行判断。

在上图里面可以看到GX和GY的方向相同并且GX大于GY,所以我们分别选取6 9和1 4这四个点来进行插值以生成两个亚像素点。

在之前的介绍里面我们知道梯度的方向是用arctan来表示的,但是这个计算比较复杂,我们也仅仅是为了插值计算,所以并不需要进行arctan的计算,只需要拿到GX和GY的值即可。

因为GX大于GY,所以让GY/GX这样得到一个小于1的值,记作weight。那么我们就可以得到一个比例关系:

point1 = 9*weight + 6 * (1 - weight)

point2 = 1*weight + 4 * (1 - weight)

这样就能得到两个亚元素点,通过比较5这个点和point1以及point2的大小就可以决定是否对其进行抑制。

通过将GX和GY的关系可以分为以下四种情况:

|GX| > |GY| 并且 GX和GY同号

|GX| > |GY| 并且 GX和GY异号

|GX| < |GY| 并且 GX和GY同号

|GX| < |GY| 并且 GX和GY异号

其他几个方向的分析就不赘述了,可以直接看代码里面的实现:

代码里面矩阵的坐标关系如下图:

FPGA

python实现的参考模型如下:

FPGA

可以根据上面的坐标对应关系来分析剩下的几种情况。

来看一下实现的效果:

 

可以明显看到nms之后的结果相对于sobel之后的结果要细化很多。

Python的NMS参考模型介绍完了,之后就是Verilog的RTL怎么实现它,今天就写到这里,休息。

  审核编辑:汤梓红
 
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分