Canny边缘检查测量检验算法的兑现

图像边缘音讯首要集中在高频段,日常说图像锐化或检查评定边缘,实质便是高频滤波。大家领略微分运算是求实信号的变化率,具备增加高频分量的效应。在空白运算中的话,对图像的锐化就是计量微分。由于数字图像的离散实信号,微分运算就改成总计差分或梯度。图像处理中有多样边缘检验(梯度)算子,常用的席卷常见一阶差分,罗Bert算子(交叉差分),Sobel算子等等,是依据寻觅梯度强度。拉普Russ算子(二阶差分)是依靠过零点检查测验。通过测算梯度,设置阀值,获得边缘图像。

Canny边缘检查测量检验算子是一种多级检查实验算法。一九八五年由JohnF. Canny提出,同期建议了边缘检验的三大法则:

  1. 低错误率的边缘检查评定:检查实验算法应该标准地找到图像中的尽恐怕多的边缘,尽大概的减弱漏检和误检。
  2. 最优定位:检查测量检验的边缘点应该规范地定位于边缘的中坚。
  3. 图像中的大肆边缘应该只被标志三遍,同一时间图像噪声不应发生伪边缘。

Canny算法现身之后一贯是用作一种标准的边缘检查测验算法,此后也出现了各种基于Canny算法的精雕细琢算法。时至后天,Canny算法及其各个变种依然是一种优异的边缘检查评定算法。何况唯有前提条件很适合,你很难找到一种边缘检验算子能明了地比Canny算子做的越来越好。

关于种种差分算子,还大概有Canny算子的简约介绍,这里就不罗嗦了,互连网都足以找获得。直接步向Canny算法的贯彻。Canny算法分为几步。

  1. 高斯模糊。

这一步很简短,类似于LoG算子(Laplacian of
Gaussian)作高斯模糊同样,主要意义正是删除噪声。因为噪音也聚集于高频实信号,很轻便被辨以为伪边缘。应用高斯模糊去除噪声,减弱伪边缘的识别。但是出于图像边缘音讯也是一再时限信号,高斯模糊的半径选拔很要紧,过大的半径很轻易让某些弱边缘检查评定不到。

图片 1  
图片 2

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

  1. 测算梯度幅值和样子。

图像的边缘能够本着分化方向,因而非凡Canny算法用了五个梯度算子来分别计算水平,垂直和对角线方向的梯度。可是一般都不用八个梯度算子来分别总括多个样子。常用的边缘差分算子(如罗布er,Prewitt,Sobel)总计水平和垂直方向的差分Gx和Gy。那样就足以如下总括梯度模和自由化:

图片 3

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

此处自身选取Sobel算子总计梯度。Sobel算法很简短,四处都足以找到,就不列出代码来了。相对于别的边缘算子,Sobel算子得出来的边缘粗大明亮。

图片 4

 

 

 

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

图片 5

           Sobel算子,无阀值

  1. 非最大值抑制。

非最大值抑制是一种边缘细化措施。平日得出去的梯度边缘不只有七个像素宽,而是四个像素宽。仿佛大家所说Sobel算子得出去的边缘粗大而知晓,从地方Lena图的Sobel结果能够看得出来。因而那样的梯度图依旧很“模糊”。而轨道3渴求,边缘唯有多个正确的点小幅度。非最大值抑制能支持保留部分最大梯度而防止有所其余梯度值。那象征只保留了梯度转移中最辛辣的职位。算法如下:

  1. 正如当前点的梯度强度和正负梯度方向点的梯度强度。
  2. 只要当前点的梯度强度和同方向的任何点的梯度强度相相比是最大,保留其值。不然抑制,即设为0。比方当前点的来头指向正上方90°方向,那它供给和垂直方向,它的正上方和正下方的像素比较。

留神,方向的正负是不起功效的,举例西北方向和西南方向是均等的,都以为是对角线的叁个样子。前面大家把梯度方向近似到水平,垂直和三个对角线多个样子,所以各样像素根据自个儿方向在这多个趋势之一举办相比,决定是还是不是保留。这一有的的代码也很粗大略,列出如下。pModule,pDirection分别记录了上一步梯度模值和梯度方向。

图片 6图片 7

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

下图是非最大值抑制的结果。可知边缘宽度已经大大减缩。然则那个图像中因为从没采纳任何阀值,还隐含多量小梯度模值的点,也正是图中很暗的地方。上面,阀值要出场了。

图片 8

             非最大值抑制结果

  1. 双阀值。

一般的边缘检查测试算法用二个阀值来滤除噪声或颜色变化引起的小的梯度值,而保留大的梯度值。Canny算法应用双阀值,即三个高阀值和叁个低阀值来区分边缘像素。假诺边缘像素点梯度值超越高阀值,则被感觉是强边缘点。假使边缘梯度值紧跟于高阀值,大于低阀值,则标志为弱边缘点。小于低阀值的点则被压制掉。这一步算法很简短。

  1. 失利边界追踪。

迄今,强边缘点能够认为是真的边缘。弱边缘点则或许是确实边缘,也只怕是噪音或颜料变化引起的。为获得正确的结果,前面一个引起的弱边缘点应该去掉。平日以为真正边缘引起的弱边缘点和强边缘点是连接的,而又噪声引起的弱边缘点则不会。所谓的向上边界追踪算法检查一个弱边缘点的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。

图片 9图片 10

// 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

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

图片 11  
图片 12

            
Canny检查评定梯度模图                        Canny检验梯度二值图

作为相比,上边是用一阶差分和Sobel算子对原图总括的结果,阀值100。由于一阶差分的梯度值相对相当小,小编对一阶差分的梯度值放大了必然倍数,使得它和Sobel的梯度值保持一致的程度。

图片 13  
图片 14

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

图片 15  
图片 16

                 Sobel梯度模图            
             Sobel梯度二值图

很领会,Canny边缘检查测试的成效是很醒目标。相比较平时的梯度算法大大抑制了噪声引起的伪边缘,而且是边缘细化,易于后续管理。对于相比较度非常的低的图像,通过调度参数,Canny算法也能有很好的作用。

图片 17  
图片 18  
图片 19

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

图片 20  
图片 21  
图片 22

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

参考

Canny edge
detector

canny
算法

 

相关文章

admin

网站地图xml地图