Canny边缘检测算法的落实,图像处理中有多种边缘检测(梯度)算子

图像边缘新闻根本集中在高频段,常常说图像锐化或检测边缘,实质就是高频滤波。大家清楚微分运算是求信号的变化率,具有提升高频分量的法力。在空白运算中的话,对图像的锐化就是统计微分。由于数字图像的离散信号,微分运算就变成计算差分或梯度。图像处理中有多种边缘检测(梯度)算子,常用的概括普通一阶差分,罗伯特(伯特(Bert))算子(交叉差分),Sobel算子等等,是根据寻找梯度强度。拉普拉斯算子(二阶差分)是基于过零点检测。通过总计梯度,设置阀值,获得边缘图像。

图像边缘音信主要集中在高频段,寻常说图像锐化或检测边缘,实质就是高频滤波。大家精通微分运算是求信号的变化率,具有加强高频分量的功效。在空白运算中的话,对图像的锐化就是测算微分。由于数字图像的离散信号,微分运算就成为计算差分或梯度。图像处理中有多种边缘检测(梯度)算子,常用的不外乎一般一阶差分,罗伯特(伯特)(Robert)算子(交叉差分),Sobel算子等等,是基于寻找梯度强度。拉普拉斯算子(二阶差分)是根据过零点检测。通过总结梯度,设置阀值,获得边缘图像。

价值观的Canny边缘检测算法是一种有效而又绝对简单的算法,可以拿走很好的结果(可以参考上一篇Canny边缘检测算法的贯彻)。不过Canny算法本身也有一些毛病,可以有改正的地点。

Canny边缘检测算子是一种多级检测算法。1986年由约翰(John)F. Canny提议,同时提议了边缘检测的三大准则:

Canny边缘检测算子是一种多级检测算法。1986年由约翰(John)F. Canny提议,同时提议了边缘检测的三大准则:

1.
Canny边缘检测第一步用高斯模糊来去掉噪声,可是还要也会平滑边缘,使得边缘音讯减少,有可能使得在末端的步调中漏掉一些急需的边缘,越发是弱边缘和孤立的边缘,可能在双阀值和联通总计中被删去。很自然地得以预感,固然加大高斯模糊的半径,对噪音的平缓力度加大,但也会使得最终得到的边缘图中的边缘分明回落。那里照旧用Lena图为例,保持Canny算法中高阀值100,低阀值50不变,高斯半径分别为2,3,5的Canny边缘二值图像如下。可见高斯模糊把过多灵光的边缘新闻也搅乱掉了,由此如何准确的选用高斯半径就一定重大。

  1. 低错误率的边缘检测:检测算法应该规范地找到图像中的尽可能多的边缘,尽可能的缩减漏检和误检。
  2. 最优定位:检测的边缘点应该规范地稳住于边缘的骨干。
  3. 图像中的任意边缘应该只被标记五回,同时图像噪声不应发生伪边缘。
  1. 低错误率的边缘检测:检测算法应该规范地找到图像中的尽可能多的边缘,尽可能的缩减漏检和误检。
  2. 最优定位:检测的边缘点应该规范地稳定于边缘的着力。
  3. 图像中的任意边缘应该只被标记两遍,同时图像噪声不应发生伪边缘。

710官方网站 1  
710官方网站 2710官方网站,  
710官方网站 3

Canny算法出现之后向来是当做一种标准的边缘检测算法,此后也应运而生了各个基于Canny算法的改革算法。时至明天,Canny算法及其各样变种仍然是一种杰出的边缘检测算法。而且唯有前提条件很符合,你很难找到一种边缘检测算子能肯定地比Canny算子做的更好。

Canny算法现身将来一贯是用作一种标准的边缘检测算法,此后也出现了各个基于Canny算法的创新算法。时至后天,Canny算法及其各类变种依然是一种理想的边缘检测算法。而且唯有前提条件很合乎,你很难找到一种边缘检测算子能分明地比Canny算子做的更好。

                
高斯半径2                                         
高斯半径3                                          高斯半径5

至于各类差分算子,还有Canny算子的简约介绍,那里就不罗嗦了,网上都得以找获得。直接进入Canny算法的完结。Canny算法分为几步。

至于各类差分算子,还有Canny算子的简要介绍,那里就不罗嗦了,网上都可以找得到。直接进入Canny算法的贯彻。Canny算法分为几步。

2.
在最初的Canny算法中是应用的矮小的2×2天地来计量梯度幅值的。那种格局对噪声很乖巧,相比便于检测到伪边缘或漏掉真是边缘。在上一篇算法完成中,实际上选取的是3×3的Sobel梯度算子,是一种相比好的取舍。

  1. 高斯模糊。
  1. 高斯模糊。

3.
价值观Canny算法的双阀值是大局稳定的,由此双阀值大小的挑三拣四对最终的结果影响很大,也有一些经验,比如拔取低阀值是高阀值的0.4或0.5。但是那毕竟是一种经验选取,阀值的确定照旧很难控制一个最优值。而且一个图像的不比部分区域或者需要各区其他阀值来规范地找到真正边缘,由此全局阀值就不太方便了。

这一步很简单,类似于LoG算子(Laplacian of
Gaussian)作高斯模糊一样,主要功用就是去除噪声。因为噪音也集中于高频信号,很简单被识别为伪边缘。应用高斯模糊去除噪声,下跌伪边缘的辨认。不过出于图像边缘音信也是几度信号,高斯模糊的半径接纳很要紧,过大的半径很不难让有些弱边缘检测不到。

这一步很简短,类似于LoG算子(Laplacian of
Gaussian)作高斯模糊一样,首要功用就是删除噪声。因为噪音也集中于高频信号,很简单被识别为伪边缘。应用高斯模糊去除噪声,下落伪边缘的鉴别。可是由于图像边缘信息也是反复信号,高斯模糊的半径选拔很关键,过大的半径很不难让部分弱边缘检测不到。

4.
观念算法依然可能暴发一条宽度大于1的边缘,达不到满足的高精度单点响应。也就是亟需三番五次细化边缘。

710官方网站 4  
710官方网站 5

710官方网站 6  
710官方网站 7

上面就部分可以立异的地点做一些探究。

            
莉娜原图                                      
Lena高斯歪曲,半径2

            
莉娜原图                                      
Lena高斯歪曲,半径2

代表高斯模糊

  1. 算算梯度幅值和样子。
  1. 计算梯度幅值和可行性。

噪音是数十次信号,边缘信号也属于高频信号。既然高斯模糊不加区分的对具有的往往消息进行了模糊,效果当然不如愿。那么自然就想到了蕴藏保留边缘成效的各样接纳性平滑方法,就如在那里比高斯歪曲会越加适合,那大家就来试一试。带有保留边缘成效的坦荡方法的大旨绪维不是让世界范围内的享有像素都参与该种平滑方法的估算,而是设定一个阀值,仅仅让和中坚像素灰度的差值小于那么些阀值的像素加入总括。那样和基本像素相差过大的像素被认为是富含有效的新闻,而不是噪音,不会参加平滑总计,从而保留了那么些使得的再三信号,那么边缘信号自然也在保存的限制。具体的算法可以参考那篇小说http://www.cnblogs.com/Imageshop/p/4694540.html,已经讲得很清楚了。无论是均值平滑,中值平滑,表面模糊,都足以参见这种算法来贯彻选取性模糊。

图像的边缘可以本着分歧方向,由此经典Canny算法用了多少个梯度算子来分别总计水平,垂直和对角线方向的梯度。可是一般都毫不四个梯度算子来分别总括多少个趋势。常用的边缘差分算子(如罗布er,Prewitt,Sobel)总结水平和垂直方向的差分Gx和Gy。那样就足以如下计算梯度模和动向:

图像的边缘可以本着不一致方向,因而经典Canny算法用了三个梯度算子来分别总括水平,垂直和对角线方向的梯度。可是经常都无须七个梯度算子来分别计算八个趋势。常用的边缘差分算子(如罗布(Rob)er,Prewitt,Sobel)计算水平和垂直方向的差分Gx和Gy。那样就足以如下总计梯度模和可行性:

采纳有保存边缘的接纳性模糊来替代高斯模糊后,能够窥见,模糊领域的半径值基本影响不断Canny检测的结果,最终的结果只跟慎选模糊设定的阀值有关。上面以均值模糊为例,Canny检测的高阀值100低阀值50不变,均值模糊阀值30,差异模糊半径的结果。在均值模糊阀值不变的动静下,不一样世界半径下,最后的结果差别不大。

710官方网站 8

710官方网站 9

710官方网站 10  
710官方网站 11  
710官方网站 12

梯度角度θ范围从弧度-π到π,然后把它好像到多个样子,分别表示水平,垂直和三个对角线方向(0°,45°,90°,135°)。可以以±iπ/8(i=1,3,5,7)分割,落在每个区域的梯度角给一个特定值,代表八个趋势之一。

梯度角度θ范围从弧度-π到π,然后把它就像到八个趋势,分别代表水平,垂直和多个对角线方向(0°,45°,90°,135°)。可以以±iπ/8(i=1,3,5,7)分割,落在每个区域的梯度角给一个特定值,代表七个样子之一。

             
均值模糊半径2,阀值30                     均值模糊半径5,阀值30   
                       均值模糊半径15,阀值30 

那里自己选拔Sobel算子计算梯度。Sobel算法很简单,到处都能够找到,就不列出代码来了。相对于其余边缘算子,Sobel算子得出去的边缘粗大明亮。

那边自己选拔Sobel算子计算梯度。Sobel算法很简单,四处都得以找到,就不列出代码来了。相对于任何边缘算子,Sobel算子得出去的边缘粗大明亮。

而保持模糊半径5不变的话,使用分歧模糊门限阀值,阀值越大,也就是有越多的小圈子像素参与模糊计算,最终保留的边缘就越少了。

710官方网站 13

710官方网站 14

710官方网站 15  
710官方网站 16

 

 

          
均值模糊半径5,阀值40                        
均值模糊半径5,阀值50

 

 

争论于高斯模糊,在一如既往半径下,可以寓目应用有保存边缘功用的选取性模糊,明显能保留了越多的边缘细节,使得众多针锋相对较弱的边缘得意保留下来了。我其它还试行了拔取性的中值模糊,表面模糊,他们和接纳性的均值模糊都能达标近似的机能。那里就不一一列举结果了。其中选用性的表面模糊,因为基本像素的权重高,最后Canny检测结果在参数相同的情景下,保留的边缘相对较多一些。总的来说,应用有保留边缘功效的选取性模糊来顶替高斯模糊,参数的选料(领域半径和阀值门限)不像高斯模糊半径参数选拔那么严刻,所以检测结果更平稳一些。可是参数要多了一个。

 

 

梯度算子拔取

下图是对地点半径2的高斯模糊图像L通道(HSL)应用Sobel算子的梯度模图,没有施加任何阀值。

下图是对地点半径2的高斯模糊图像L通道(HSL)应用Sobel算子的梯度模图,没有施加任何阀值。

对此算法中梯度的计量,梯度算子可以有多种选取。我试了须臾间,要是用一阶梯度算子,罗伯特(伯特(Bert))交叉算子,他们都是2×2的算子,来替代Sobel,保持高斯模糊半径2,高阀值100低阀值50不变,结果如下。要专注的是,由于一阶梯度算子和罗伯特(伯特)(Robert)算子都是2×2的算子,他们算出来的梯度在步长上都要自愧不如Sobel算子。即选拔同一的轻重阀值,最终的结果也不具有可比性。由此,参照Sobel算子的拉长率,2×2算子的x,y方向梯度都乘以相应地倍数(4倍),最终举办比较。

710官方网站 17

710官方网站 18

710官方网站 19  
710官方网站 20  
710官方网站 21

           Sobel算子,无阀值

           Sobel算子,无阀值

                一阶差分Canny二值图
                       罗伯特(伯特(Bert))(Robert)交叉梯度Canny二值图                  
Sobel算子Canny二值图

  1. 非最大值抑制。
  1. 非最大值抑制。

可以见到,一阶差分最终的结果在边缘的连通性上是最差的,罗伯特(Bert)(Robert)算子要好有的,Sobel算子最好。在那两种选用中间,就像是Sobel算子是最好的抉择。别的还能动用Prewitt算子和5x5Sobel算子。Prewitt算子也是3×3的,仅仅参数差距,在平坦性能上略微不如Sobel算子。一般的话,比如在Lena图上,Canny边缘结果和用Sobel算子的结果差别不大。5x5Sobel算子在平坦性能上要更强一些。

非最大值抑制是一种边缘细化措施。寻常得出去的梯度边缘不止一个像素宽,而是四个像素宽。似乎大家所说Sobel算子得出来的边缘粗大而驾驭,从地点Lena图的Sobel结果可以看得出来。因而那样的梯度图照旧很“模糊”。而轨道3渴求,边缘唯有一个纯粹的点小幅。非最大值抑制能援救保留部分最大梯度而防止有所其余梯度值。那代表只保留了梯度转移中最辛辣的职分。算法如下:

非最大值抑制是一种边缘细化措施。寻常得出去的梯度边缘不止一个像素宽,而是三个像素宽。就如大家所说Sobel算子得出去的边缘粗大而知晓,从下面Lena图的Sobel结果能够看得出来。由此那样的梯度图依然很“模糊”。而轨道3要求,边缘唯有一个标准的点小幅。非最大值抑制能支援保留部分最大梯度而防止有所其余梯度值。那意味只保留了梯度转移中最尖锐的职位。算法如下:

  1. 正如当前点的梯度强度和正负梯度方向点的梯度强度。
  2. 要是当前点的梯度强度和同方向的别的点的梯度强度相相比较是最大,保留其值。否则抑制,即设为0。比如当前点的大势指向正上方90°方向,这它须求和垂直方向,它的正上方和正下方的像素相比较。
  1. 比较当前点的梯度强度和正负梯度方向点的梯度强度。
  2. 万一当前点的梯度强度和同方向的别样点的梯度强度相相比是最大,保留其值。否则抑制,即设为0。比如当前点的倾向指向正上方90°方向,那它须求和垂直方向,它的正上方和正下方的像素相比较。

注意,方向的正负是不起功用的,比如西北方向和西南方向是同样的,都觉得是对角线的一个方向。前面大家把梯度方向近似到水平,垂直和五个对角线多个方向,所以每个像素依照本人方向在那五个样子之一举办相比,决定是或不是保留。这一部分的代码也很简短,列出如下。pModule,pDirection分别记录了上一步梯度模值和梯度方向。

专注,方向的正负是不起功效的,比如东北方向和西北方向是一律的,都认为是对角线的一个势头。后面大家把梯度方向近似到水平,垂直和七个对角线多个趋势,所以每个像素根据本人方向在这多个方向之一举办比较,决定是不是保留。这一片段的代码也很简单,列出如下。pModule,pDirection分别记录了上一步梯度模值和梯度方向。

710官方网站 22710官方网站 23

710官方网站 24710官方网站 25

pmoddrow = pModule + Width + 1; 
pdirdrow = pDirection + Width + 1;
pstrongdrow = pStrong + Width + 1;
for (i = 1; i < Hend - 1; i++)
{
  pstrongd = pstrongdrow;
  pmodd = pmoddrow;
  pdird = pdirdrow;
  for (j = 1; j < Wend - 1; j++)
    {
            switch (*pdird)
            {
            case 0:        // x direction
            case 4:
                if (*pmodd > *(pmodd - 1) && *pmodd > *(pmodd + 1))
                    *pstrongd = 255;
                break;
            case 1:        // northeast-southwest direction. Notice the data order on y direction of bmp data
            case 5:
                if (*pmodd > *(pmodd + Width + 1) && *pmodd > *(pmodd - Width - 1))
                    *pstrongd = 255;
                break;
            case 2:        // y direction
            case 6:
                if (*pmodd > *(pmodd - Width) && *pmodd > *(pmodd + Width))
                    *pstrongd = 255;
                break;
            case 3:        // northwest-southeast direction. Notice the data order on y direction of bmp data
            case 7:
                if (*pmodd > *(pmodd + Width - 1) && *pmodd > *(pmodd - Width + 1))
                    *pstrongd = 255;
                break;
            default:
                ASSERT(0);
                break;
            }
            pstrongd++;
            pmodd++;
            pdird++;
    }
    pstrongdrow += Width;
    pmoddrow += Width;
    pdirdrow += Width;
}
pmoddrow = pModule + Width + 1; 
pdirdrow = pDirection + Width + 1;
pstrongdrow = pStrong + Width + 1;
for (i = 1; i < Hend - 1; i++)
{
  pstrongd = pstrongdrow;
  pmodd = pmoddrow;
  pdird = pdirdrow;
  for (j = 1; j < Wend - 1; j++)
    {
            switch (*pdird)
            {
            case 0:        // x direction
            case 4:
                if (*pmodd > *(pmodd - 1) && *pmodd > *(pmodd + 1))
                    *pstrongd = 255;
                break;
            case 1:        // northeast-southwest direction. Notice the data order on y direction of bmp data
            case 5:
                if (*pmodd > *(pmodd + Width + 1) && *pmodd > *(pmodd - Width - 1))
                    *pstrongd = 255;
                break;
            case 2:        // y direction
            case 6:
                if (*pmodd > *(pmodd - Width) && *pmodd > *(pmodd + Width))
                    *pstrongd = 255;
                break;
            case 3:        // northwest-southeast direction. Notice the data order on y direction of bmp data
            case 7:
                if (*pmodd > *(pmodd + Width - 1) && *pmodd > *(pmodd - Width + 1))
                    *pstrongd = 255;
                break;
            default:
                ASSERT(0);
                break;
            }
            pstrongd++;
            pmodd++;
            pdird++;
    }
    pstrongdrow += Width;
    pmoddrow += Width;
    pdirdrow += Width;
}

View
Code

View
Code

下图是非最大值抑制的结果。可知边缘宽度已经大大减小。然而那么些图像中因为从没动用任何阀值,还含有大批量小梯度模值的点,也就是图中很暗的地点。上边,阀值要出台了。

下图是非最大值抑制的结果。可知边缘宽度已经大大缩减。但是那个图像中因为从没动用任何阀值,还蕴涵多量小梯度模值的点,也就是图中很暗的地点。上边,阀值要出场了。

710官方网站 26

710官方网站 27

             非最大值抑制结果

             非最大值抑制结果

  1. 双阀值。
  1. 双阀值。

诚如的边缘检测算法用一个阀值来滤除噪声或颜料变化引起的小的梯度值,而保留大的梯度值。Canny算法应用双阀值,即一个高阀值和一个低阀值来区分边缘像素。即便边缘像素点梯度值超出高阀值,则被认为是强边缘点。假使边缘梯度值紧跟于高阀值,大于低阀值,则标记为弱边缘点。小于低阀值的点则被压制掉。这一步算法很简短。

相似的边缘检测算法用一个阀值来滤除噪声或颜料变化引起的小的梯度值,而保留大的梯度值。Canny算法应用双阀值,即一个高阀值和一个低阀值来分别边缘像素。即使边缘像素点梯度值超越高阀值,则被认为是强边缘点。如若边缘梯度值紧跟于高阀值,大于低阀值,则标记为弱边缘点。小于低阀值的点则被抑制掉。这一步算法很不难。

  1. 落前面界跟踪。
  1. 落前面界跟踪。

迄今截至,强边缘点可以认为是真的边缘。弱边缘点则可能是真的边缘,也可能是噪音或颜料变化引起的。为获取确切的结果,后者引起的弱边缘点应该去掉。寻常认为实际边缘引起的弱边缘点和强边缘点是对接的,而又噪声引起的弱边缘点则不会。所谓的落后面界跟踪算法检查一个弱边缘点的8连通领域像素,只要有强边缘点存在,那么那几个弱边缘点被认为是真是边缘保留下来。

从那之后,强边缘点可以认为是的确边缘。弱边缘点则可能是当真边缘,也可能是噪音或颜料变化引起的。为获取确切的结果,后者引起的弱边缘点应该去掉。日常认为实际边缘引起的弱边缘点和强边缘点是过渡的,而又噪声引起的弱边缘点则不会。所谓的落前边界跟踪算法检查一个弱边缘点的8连通领域像素,只要有强边缘点存在,那么这几个弱边缘点被认为是真是边缘保留下来。

那么些算法搜索所有连接的弱边缘,假使一条连接的弱边缘的任何一个点和强边缘点连通,则保留那条弱边缘,否则抑制那条弱边缘。搜索时得以用广度优先或者深度优先算法,我在那边已毕了应当是最不难的吃水优先算法。一次连通一条边缘的深浅优先算法如下:

这些算法搜索所有连接的弱边缘,要是一条连接的弱边缘的任何一个点和强边缘点连通,则保留那条弱边缘,否则抑制这条弱边缘。搜索时得以用广度优先或者深度优先算法,我在此地已毕了应当是最简单的吃水优先算法。一次连通一条边缘的深浅优先算法如下:

  1. 未雨绸缪一个栈s,一个种类q,设联通提醒变量connected为假。从图像的首先个点开始,进入2。
  2. 若是那么些点是弱边界点并且没有被标记,把它标志,并把它看成首个要素放入栈s中,同时把它放入记录连通曲线的种类q,进入3。如若那么些点不是弱边界或者已经被标记过,到图像的下一个点,重复2。
  3. 从栈s中取出一个元素,查找它的8像素领域。如若一个领域像素是弱边界并且没有被标记过,把这么些世界像素标记,并投入栈s中,同时插手队列q。同时招来领域对应的强边界图,即使有一个像素是强边界,表示那条弱边界曲线和强边界联通,设置connected为真。重复3直到栈中没有元素了。借使connected为假,则相继从队列q中取出每个元素,清空标记。如若connected为真,保留标记。
  4. 清空队列q,设置connected为假,移动到图像的下一个点,到2。
  1. 未雨绸缪一个栈s,一个行列q,设联通提示变量connected为假。从图像的率先个点起来,进入2。
  2. 假如这些点是弱边界点并且没有被标记,把它标志,并把它当作第四个要素放入栈s中,同时把它放入记录连通曲线的系列q,进入3。若是那个点不是弱边界或者已经被标记过,到图像的下一个点,重复2。
  3. 从栈s中取出一个元素,查找它的8像素领域。如果一个领域像素是弱边界并且没有被标记过,把那一个小圈子像素标记,并参与栈s中,同时到场队列q。同时摸索领域对应的强边界图,假诺有一个像素是强边界,表示那条弱边界曲线和强边界联通,设置connected为真。重复3直到栈中没有元素了。假若connected为假,则相继从队列q中取出每个元素,清空标记。如若connected为真,保留标记。
  4. 清空队列q,设置connected为假,移动到图像的下一个点,到2。

710官方网站 28710官方网站 29

710官方网站 30710官方网站 31

// 5. Edge tracking by hysteresis
    stack<CPoint> s;
    queue<CPoint> q;
    BOOL connected = FALSE;
    long row_idx = Width;
    for (i = 1; i < Height - 1; i++, row_idx += Width)
    {
        for (j = 1; j < Width - 1; j++)
        {
            pweakd = pWeak + row_idx + j;
            if (*pweakd == 255)
            {
                s.push(CPoint(j, i));
                q.push(CPoint(j, i));
                *pweakd = 1;        // Label it

                while (!s.empty())
                {
                    CPoint p = s.top();
                    s.pop();
                    // Search weak edge 8-point neighborhood
                    pweakd = pWeak + p.y*Width + p.x;
                    if (*(pweakd - Width - 1) == 255)
                    {
                        CPoint np = CPoint(p.x - 1, p.y - 1);
                        s.push(np);
                        q.push(np);
                        *(pweakd - Width - 1) = 1;        // Label it
                    }
                    if (*(pweakd - Width) == 255)
                    {
                        CPoint np = CPoint(p.x, p.y - 1);
                        s.push(np);
                        q.push(np);
                        *(pweakd - Width) = 1;        // Label it
                    }
                    if (*(pweakd - Width + 1) == 255)
                    {
                        CPoint np = CPoint(p.x + 1, p.y - 1);
                        s.push(np);
                        q.push(np);
                        *(pweakd - Width + 1) = 1;        // Label it
                    }
                    if (*(pweakd - 1) == 255)
                    {
                        CPoint np = CPoint(p.x - 1, p.y);
                        s.push(np);
                        q.push(np);
                        *(pweakd - 1) = 1;        // Label it
                    }
                    if (*(pweakd + 1) == 255)
                    {
                        CPoint np = CPoint(p.x + 1, p.y);
                        s.push(np);
                        q.push(np);
                        *(pweakd + 1) = 1;        // Label it
                    }
                    if (*(pweakd + Width - 1) == 255)
                    {
                        CPoint np = CPoint(p.x - 1, p.y + 1);
                        s.push(np);
                        q.push(np);
                        *(pweakd + Width - 1) = 1;        // Label it
                    }
                    if (*(pweakd + Width) == 255)
                    {
                        CPoint np = CPoint(p.x, p.y + 1);
                        s.push(np);
                        q.push(np);
                        *(pweakd + Width) = 1;        // Label it
                    }
                    if (*(pweakd + Width + 1) == 255)
                    {
                        CPoint np = CPoint(p.x + 1, p.y + 1);
                        s.push(np);
                        q.push(np);
                        *(pweakd + Width + 1) = 1;        // Label it
                    }
                    // Search strong edge 8-point neighborhood
                    if (connected == FALSE)
                    {
                        pstrongd = pStrong + p.y*Width + p.x;
                        for (int m = -1; m <= 1; m++)
                        {
                            for (int n = -1; n <= 1; n++)
                            {
                                if (*(pstrongd + m*Width + n) == 255)
                                {
                                    connected = TRUE;
                                    goto next;
                                }
                            }
                        }
                    }
                next:
                    continue;
                }
                // No more element in the stack
                if (connected == FALSE)
                {
                    // The weak edge is not connected to any strong edge. Suppress it.
                    while (!q.empty())
                    {
                        CPoint p = q.front();
                        q.pop();
                        pWeak[p.y*Width + p.x] = 0;
                    }
                }
                else
                {
                    // Clean the queue
                    while (!q.empty()) q.pop();
                    connected = FALSE;
                }
            }
        }
    }
    // Add the connected weak edges (labeled) into strong edge image.
    // All strong edge pixels are labeled 255, otherwise 0.
    for (i = 0; i < len; i++)
    {
        if (pWeak[i] == 1) pStrong[i] = 255;
    }
// 5. Edge tracking by hysteresis
    stack<CPoint> s;
    queue<CPoint> q;
    BOOL connected = FALSE;
    long row_idx = Width;
    for (i = 1; i < Height - 1; i++, row_idx += Width)
    {
        for (j = 1; j < Width - 1; j++)
        {
            pweakd = pWeak + row_idx + j;
            if (*pweakd == 255)
            {
                s.push(CPoint(j, i));
                q.push(CPoint(j, i));
                *pweakd = 1;        // Label it

                while (!s.empty())
                {
                    CPoint p = s.top();
                    s.pop();
                    // Search weak edge 8-point neighborhood
                    pweakd = pWeak + p.y*Width + p.x;
                    if (*(pweakd - Width - 1) == 255)
                    {
                        CPoint np = CPoint(p.x - 1, p.y - 1);
                        s.push(np);
                        q.push(np);
                        *(pweakd - Width - 1) = 1;        // Label it
                    }
                    if (*(pweakd - Width) == 255)
                    {
                        CPoint np = CPoint(p.x, p.y - 1);
                        s.push(np);
                        q.push(np);
                        *(pweakd - Width) = 1;        // Label it
                    }
                    if (*(pweakd - Width + 1) == 255)
                    {
                        CPoint np = CPoint(p.x + 1, p.y - 1);
                        s.push(np);
                        q.push(np);
                        *(pweakd - Width + 1) = 1;        // Label it
                    }
                    if (*(pweakd - 1) == 255)
                    {
                        CPoint np = CPoint(p.x - 1, p.y);
                        s.push(np);
                        q.push(np);
                        *(pweakd - 1) = 1;        // Label it
                    }
                    if (*(pweakd + 1) == 255)
                    {
                        CPoint np = CPoint(p.x + 1, p.y);
                        s.push(np);
                        q.push(np);
                        *(pweakd + 1) = 1;        // Label it
                    }
                    if (*(pweakd + Width - 1) == 255)
                    {
                        CPoint np = CPoint(p.x - 1, p.y + 1);
                        s.push(np);
                        q.push(np);
                        *(pweakd + Width - 1) = 1;        // Label it
                    }
                    if (*(pweakd + Width) == 255)
                    {
                        CPoint np = CPoint(p.x, p.y + 1);
                        s.push(np);
                        q.push(np);
                        *(pweakd + Width) = 1;        // Label it
                    }
                    if (*(pweakd + Width + 1) == 255)
                    {
                        CPoint np = CPoint(p.x + 1, p.y + 1);
                        s.push(np);
                        q.push(np);
                        *(pweakd + Width + 1) = 1;        // Label it
                    }
                    // Search strong edge 8-point neighborhood
                    if (connected == FALSE)
                    {
                        pstrongd = pStrong + p.y*Width + p.x;
                        for (int m = -1; m <= 1; m++)
                        {
                            for (int n = -1; n <= 1; n++)
                            {
                                if (*(pstrongd + m*Width + n) == 255)
                                {
                                    connected = TRUE;
                                    goto next;
                                }
                            }
                        }
                    }
                next:
                    continue;
                }
                // No more element in the stack
                if (connected == FALSE)
                {
                    // The weak edge is not connected to any strong edge. Suppress it.
                    while (!q.empty())
                    {
                        CPoint p = q.front();
                        q.pop();
                        pWeak[p.y*Width + p.x] = 0;
                    }
                }
                else
                {
                    // Clean the queue
                    while (!q.empty()) q.pop();
                    connected = FALSE;
                }
            }
        }
    }
    // Add the connected weak edges (labeled) into strong edge image.
    // All strong edge pixels are labeled 255, otherwise 0.
    for (i = 0; i < len; i++)
    {
        if (pWeak[i] == 1) pStrong[i] = 255;
    }

View
Code

View
Code

上边是对莉娜图总括Canny边缘检测的梯度模图和二值化图,高斯半径2,高阀值100,低阀值50。

下边是对莉娜(Lena)图计算Canny边缘检测的梯度模图和二值化图,高斯半径2,高阀值100,低阀值50。

710官方网站 32  
710官方网站 33

710官方网站 34  
710官方网站 35

            
Canny检测梯度模图                        Canny检测梯度二值图

            
Canny检测梯度模图                        Canny检测梯度二值图

作为对照,下面是用一阶差分和Sobel算子对原图总结的结果,阀值100。由于一阶差分的梯度值相对较小,我对一阶差分的梯度值放大了迟早倍数,使得它和Sobel的梯度值保持一如既往的水平。

用作对照,下边是用一阶差分和Sobel算子对原图统计的结果,阀值100。由于一阶差分的梯度值相对较小,我对一阶差分的梯度值放大了肯定倍数,使得它和Sobel的梯度值保持同等的档次。

710官方网站 36  
710官方网站 37

710官方网站 38  
710官方网站 39

               一阶差分梯度模图         
              一阶差分梯度二值图

               一阶差分梯度模图         
              一阶差分梯度二值图

710官方网站 40  
710官方网站 41

710官方网站 42  
710官方网站 43

                 Sobel梯度模图            
             Sobel梯度二值图

                 Sobel梯度模图            
             Sobel梯度二值图

很肯定,Canny边缘检测的效能是很强烈的。比较普通的梯度算法大大抑制了噪声引起的伪边缘,而且是边缘细化,易于后续处理。对于比较度较低的图像,通过调试参数,Canny算法也能有很好的成效。

很醒目,Canny边缘检测的功效是很显眼的。比较平日的梯度算法大大抑制了噪声引起的伪边缘,而且是边缘细化,易于后续处理。对于相比较度较低的图像,通过调节参数,Canny算法也能有很好的法力。

710官方网站 44  
710官方网站 45  
710官方网站 46

710官方网站 47  
710官方网站 48  
710官方网站 49

                       原图               
            Canny梯度模,高斯半径2,低阀值30,高阀值100      
Canny梯度二值化图,高斯半径2,低阀值30,高阀值100

                       原图               
            Canny梯度模,高斯半径2,低阀值30,高阀值100      
Canny梯度二值化图,高斯半径2,低阀值30,高阀值100

710官方网站 50  
710官方网站 51  
710官方网站 52

710官方网站 53  
710官方网站 54  
710官方网站 55

                      原图               
         Canny梯度模,高斯半径1,低阀值40,高阀值80 
Canny梯度二值化图,高斯半径1,低阀值40,高阀值80

                      原图               
         Canny梯度模,高斯半径1,低阀值40,高阀值80 
Canny梯度二值化图,高斯半径1,低阀值40,高阀值80

参考

参考

Canny edge
detector

Canny edge
detector

canny
算法

canny
算法

 

 

相关文章