在常见的边缘检测算子或轮廓检测相关算法中都有非极大值抑制这一步,然而对与非极大值抑制在这些边缘检测算子中应用,在理解可能有点似懂非懂。本文将介绍Canny算法中的非极大值抑制,Canny算子中的非极大值抑制是指沿着梯度方向上进行非极大值的抑制。

首先,我们来看看Canny算子中非极大值抑制的一段代码(可以略过):

/*
fucntion: non-maximum suppression
input:
pMag:   pointer to Magnitude,
pGradX: gradient of x-direction
pGradY: gradient of y-direction
sz: size of pMag (width = size.cx, height = size.cy)
output:
pNSRst: result of non-maximum suppression
*/
void NonMaxSuppress(int*pMag,int* pGradX,int*pGradY,SIZE sz,LPBYTE pNSRst)
{LONG x,y;int nPos;// the component of the gradientint gx,gy;// the temp varialbeint g1,g2,g3,g4;double weight;double dTemp,dTemp1,dTemp2;//设置图像边缘为不可能的分界点for(x=0;x<sz.cx;x++){pNSRst[x] = 0;pNSRst[(sz.cy-1)*sz.cx+x] = 0;}for(y=0;y<sz.cy;y++){pNSRst[y*sz.cx] = 0;pNSRst[y*sz.cx + sz.cx-1] = 0;}for (y=1;y<sz.cy-1;y++){for (x=1;x<sz.cx-1;x++){nPos=y*sz.cx+x;// if pMag[nPos]==0, then nPos is not the edge pointif (pMag[nPos]==0){pNSRst[nPos]=0;}else{// the gradient of current pointdTemp=pMag[nPos];// x,y 方向导数gx=pGradX[nPos];gy=pGradY[nPos];//如果方向导数y分量比x分量大,说明导数方向趋向于y分量if (abs(gy)>abs(gx)){// calculate the factor of interplationweight=fabs(gx)/fabs(gy);g2 = pMag[nPos-sz.cx];  // 上一行g4 = pMag[nPos+sz.cx];  // 下一行//如果x,y两个方向导数的符号相同//C 为当前像素,与g1-g4 的位置关系为://g1 g2//   C//   g4 g3if(gx*gy>0){g1 = pMag[nPos-sz.cx-1];g3 = pMag[nPos+sz.cx+1];}                  //如果x,y两个方向的方向导数方向相反//C是当前像素,与g1-g4的关系为://    g2 g1//    C// g3 g4else{g1 = pMag[nPos-sz.cx+1];g3 = pMag[nPos+sz.cx-1];}}else{//插值比例weight = fabs(gy)/fabs(gx);                  g2 = pMag[nPos+1]; //后一列g4 = pMag[nPos-1];   // 前一列              //如果x,y两个方向的方向导数符号相同//当前像素C与 g1-g4的关系为// g3// g4 C g2//       g1if(gx * gy > 0){g1 = pMag[nPos+sz.cx+1];g3 = pMag[nPos-sz.cx-1];}//如果x,y两个方向导数的方向相反// C与g1-g4的关系为// g1// g2 C g4//      g3else{g1 = pMag[nPos-sz.cx+1];g3 = pMag[nPos+sz.cx-1];}}dTemp1 = weight*g1 + (1-weight)*g2;dTemp2 = weight*g3 + (1-weight)*g4;              //当前像素的梯度是局部的最大值//该点可能是边界点if(dTemp>=dTemp1 && dTemp>=dTemp2){pNSRst[nPos] = 128;}else{//不可能是边界点pNSRst[nPos] = 0;}         }}}
}

在上面代码中,有几个IF-ELSE需要注意。在John Canny提出的Canny算子的论文中,非最大值抑制就只是在0、90、45、135四个梯度方向上进行的,每个像素点梯度方向按照相近程度用这四个方向来代替。这种情况下,非最大值抑制所比较的相邻两个像素就是:
1) 0:左边 和 右边

2)45:右上 和 左下

3)90: 上边 和 下边

4)135: 左上 和 右下

这样做的好处是简单, 但是这种简化的方法无法达到最好的效果, 因为,自然图像中的边缘梯度方向不一定是沿着这四个方向的。因此,就有很大的必要进行插值,找出在一个像素点上最能吻合其所在梯度方向的两侧的像素值。

然而,实际数字图像中的像素点是离散的二维矩阵,所以处在真正中心位置C处的梯度方向两侧的点是不一定存在的,或者说是一个亚像素(sub pixel)点,而这个不存在的点, 以及这个点的梯度值就必须通过对其两侧的点进行插值来得到。

对于上面的代码,如果|gy|>|gx|,这说明该点的梯度方向更靠近Y轴方向,所以g2和g4则在C的上下,我们可以用下面来说明这两种情况(方向相同和方向不同):

上图中,C表示中心位置点,斜的直线表示梯度方向(非极大值抑制是在梯度方向上的极大值),左边的一副表示gy与gx的方向相同,而右边的这幅这表示gy与gx的方向相反(注意原点在左上角,Y轴向下),而权重则为weight = |gx|/|gy|。

因此则根据此种情况,以左图为例说明。蓝色是梯度方向,g1.g2.g3.g4和dTemp1.dTemp2的位置关系可以表示为:

g2 = pMag[nPos-sz.cx];  // 上一行
g4 = pMag[nPos+sz.cx];  // 下一行
g1 = pMag[nPos+sz.cx+1];
g3 = pMag[nPos-sz.cx-1];

其插值表示则为:

dTemp1 = weight*g1 + (1-weight)*g2;
dTemp2 = weight*g3 + (1-weight)*g4;

这里解释一下插值公式为什么这么写:

weight = fabs(gx)/fabs(gy) = ctan(theta), theta为梯度方向.
上面那个公式变一下就可以变换成:
weight = |dTemp1-g2|/|g1-g2| = |dTemp1-g2|/|C-g2| = ctan(theta);
而从上面的图中的三角形也可以直观的看出。

(只是简单说明一下帮助理解,没有过多考虑正负问题)

同理,我们可以得到|gx|>|gy|的情况,此时说明该点的梯度方向更靠近X轴方向,g2和g4则在水平方向,我们可以用下图来说明该种情况:

上图中,C表示中心位置点,斜的直线表示梯度方向(非极大值抑制是在梯度方向上的极大值),右边的一副表示gy与gx的方向相同,而左边的这幅这表示gy与gx的方向相反(注意原点在左上角),而权重则为weight = |gy|/|gx|,因此则根据此种情况,其插值表示则为:

dTemp1 = weight*g1 + (1-weight)*g2;
dTemp2 = weight*g3 + (1-weight)*g4;

通过上面的分析,我们可以了解Canny算子中的非极大值抑制之前的准备工作,也即进行必要的插值。插值的原因再啰嗦下:

①由于在Canny算子中采用的简化方法来进行边缘方向的确定,自然图像中边缘梯度方向的不一定沿着该四个方向,因此为了找出在一个像素点上最能吻合其所在梯度方向的两侧的像素值,就必须进行必要的插值;

② 也由于实际数字图像中的像素点是离散的二维矩阵,处在真正中心位置C处的梯度方向两侧的点是不一定存在的,或者说是一个亚像素(sub pixel)点,而这个不存在的点, 以及这个点的梯度值就必须通过对其两侧的点进行插值来得到。

接下来的工作就比较简单了,就是比较中心位置C处dTemp的梯度幅值与两个插值点处的梯度幅值dTemp1和dTemp2的比较,确定是否为极值点,代码如下:

if(dTemp>=dTemp1 && dTemp>=dTemp2)
{pNSRst[nPos] = 128;
}
else
{//不可能是边界点pNSRst[nPos] = 0;
}

到这里,canny算子的非极大值抑制部分就介绍完了。

最后,需要说明的一点:Canny算子中的非极大值抑制与我们在角点检测等场景中所说的非极大值抑制有点细微的差别。Canny算子中的非极大值抑制是沿着梯度方向进行的,即是否为梯度方向上的极值点;而在角点检测等场景下说的非极大值抑制,则是检测中心点处的值是否是某一个邻域内的最大值,是,则保留,否则去除,这种情况下的非极大值抑制比较简单,

如果不清楚可以参考:Alexander Neubeck的论文:Efficient Non-Maximum Suppression。

原作者代码注释和图片解释中有两处错误,在此调整过来了。
11.17日更新:在原作者的基础了添加了位置关系和插值表示的原因

原文链接:https://blog.csdn.net/kezunhai/article/details/11620357

Canny算子中的非极大值抑制(Non-Maximum Suppression)分析相关推荐

  1. Canny算法中的非极大值抑制

    在canny边缘检测算法中,为了检测边缘,其中会用到非极大值抑制的原理.其基本思想如下: 其目的就是寻找像素点局部最大值,将非极大值点所对应的灰度值置为0,这样可以剔除掉一大部分非边缘的点. 其中蓝色 ...

  2. 非极大值抑制(Non-Maximum Suppression,NMS)(转)

    概述 非极大值抑制(Non-Maximum Suppression,NMS),顾名思义就是抑制不是极大值的元素,可以理解为局部最大搜索.这个局部代表的是一个邻域,邻域有两个参数可变,一是邻域的维数,二 ...

  3. 【YOLOv3 NMS】YOLOv3中的非极大值抑制

    文章目录 1 NMS问题由来 2 NMS操作流程 2.1 进行NMS前要先有什么 2.2 NMS流程 3 NMS代码解读 4 感谢链接 1 NMS问题由来 利用YOLOv3网络结构提取到out0.ou ...

  4. 深度学习之非极大值抑制(Non-maximum suppression,NMS)

    非极大值抑制(Non-maximum suppression,NMS)是一种去除非极大值的算法,常用于计算机视觉中的边缘检测.物体识别等. 算法流程 给出一张图片和上面许多物体检测的候选框(即每个框可 ...

  5. 非极大值抑制(Non-maximum suppression)在物体检测领域的应用

    转载自:http://blog.csdn.net/pb09013037/article/details/45477591 一.Nms主要目的 在物体检测非极大值抑制应用十分广泛,主要目的是为了消除多余 ...

  6. 非极大值抑制(non-maximum suppression)的理解

    最近在学习RCNN时看到了非极大值抑制,一开始有点不明白,在网上学习了之后记录一下. 非极大值抑制就是一个寻找局部最大值的过程. 在进行目标检测时一般会采取窗口滑动的方式,在图像上生成很多的候选框,然 ...

  7. 非极大值抑制(non-maximum suppression)的理解与实现

    非极大抑制(Non-Maximum Suppression) Non-Maximum Suppression for Object Detection in Python RCNN 和微软提出的 SP ...

  8. YOLOv3中的非极大值抑制

    上图中NMS的计算流程:右上角列出了预测框和每个预测框的分数(boxes和scores).就是定义一个列表keep存放每次选出的分数最大且阈值小(阈值小代表两个预测框没重合,也就是框的不是一个对象)的 ...

  9. Canny边缘检测方法中的非极大抑制

    什么是非极大抑制 在目标检测中,通常会使用各种各样的方法来让计算机找到目标的所在位置,然而,计算机的输出往往并不是单一的,也就是说,一个目标可能会输出多个结果(如下图所示),这些结果有好有坏,因此就需 ...

  10. Canny算子中的梯度求取及非最大值抑制(NMS)实现

    @Canny算子中的非最大值抑制(NMS)实现 canny算子中的非极大值抑制是在对图像进行梯度求取之后,在梯度方向进行的运算,也就是说此处的非极大值抑制是在对图像进行梯度求取后,在生成的梯度矩阵上求 ...

最新文章

  1. 【c++】27.事件驱动、IO复用、sellect、poll、epoll三者的区别
  2. Spring注解方式配置切面类
  3. .NET Core 中生成验证码
  4. 田沄(1980-),男,博士,中国工程院—清华大学联合博士后科研工作站博士后,北京师范大学副教授....
  5. Python zipfile – Python ZIP
  6. Ubuntu没有ifconfig
  7. 因为机遇,不会轻易悲伤
  8. 新课程背景下高中化学实验室的硬件建设要求
  9. sis地址发布器_Go 1.15 正式发布:看看都有哪些值得关注的变化
  10. windows11虚拟机安装失败解决办法
  11. Vi IMproved
  12. 帝国cms内容页使用真实下载地址或播放地址
  13. 字体图标的svg导入及寻找
  14. 微软服务器dda,Windows 10 版本2004 微软官方原版镜像
  15. 试试多线程(java)
  16. kaggle | 入门教程
  17. docker服务无法启动 神坑
  18. 变速外挂案例及原理分析
  19. 【编程导航】一文带领小白快速入门RDS
  20. 转载一篇知乎上的文章:抖音是如何毁掉我们的?(深度好文)

热门文章

  1. 利用鱼群算法求解最值问题(一元或多元)MATLAB编程实现
  2. 《大型网站技术架构》笔记(思维导图)
  3. hsqldb mysql_HSQLDB的研究与性能测试(与Mysql对比)
  4. 使用 ActiveReports 报表工具,动态创建报表模板
  5. ad域推送软件_ManageEngine ADManager Plus(AD域管理工具) V7.0.1 官方中文版
  6. c语言12个实验报告,C语言实验报告合集
  7. Windows下安装使用LAMMPS并运行例子
  8. 【LAMMPS系列】LAMMPS安装WIN并行版
  9. JTAG接口简要介绍
  10. VS2013编译最简单的PPAPI插件