Canny算子边缘检测——非极大值抑制Non-Maximum Suppression

Canny算子计算前需要先通过Sobel算子得到图像的梯度值图和方向图。非极大值抑制指的是在一个像素点上A,将它的梯度值和它梯度方向上的前后两个点B和C的进行比较,如果A的梯度值大于B和C的梯度值,则保留其值,否则将A的值置为0。

简单情况

像素点的梯度方向在0°,45°,90°和135°上,此时像素点梯度方向上的前后两点就是它上、下、左、右、左上、左下、右上和右下八个点的其中两个,直接比较大小即可。例如:如果Grad(1)>=Grad(3) && Grad(1)>=Grad(7),那么就保留1的梯度值,否则将其梯度值置为0。

一般情况

梯度方向不是0°,45°,90°或135°,则1点前后的点不能落在像素点上,因此需要添加如图的a、b两点(称为子像素或亚像素),通过计算一定的权重和周边点的梯度值来计算这两个点的梯度值,从而与1进行比较。

具体分类及计算过程如下:

分成4类:


图1

图2

图3

图4

梯度方向向量经过了哪四个点的连线,我们就将用哪四个点来计算,例如图1的向量经过了点3和4、点7和8的连线。

权重weight

weight定义为一点处min(abs(水平梯度),abs(竖直梯度))/max(abs(水平梯度),abs(竖直梯度))。

图1、2的 weight=∣gradx∣∣grady∣weight=\frac{\lvert gradx \rvert}{\lvert grady \rvert}weight=∣grady∣∣gradx∣​

图3、4的 weight=∣grady∣∣gradx∣weight=\frac{\lvert grady \rvert}{\lvert gradx \rvert}weight=∣gradx∣∣grady∣​

梯度方向的判断

0°方向时grady为0
45°方向时梯度值为1
90°方向时gradx为0
135°方向时梯度值为-1

一般情况:
图1 ∣gradx∣<∣grady∣\lvert gradx \rvert \lt \lvert grady \rvert∣gradx∣<∣grady∣ 且两者同号
图2 ∣gradx∣<∣grady∣\lvert gradx \rvert \lt \lvert grady \rvert∣gradx∣<∣grady∣ 且两者异号
图3 ∣gradx∣>∣grady∣\lvert gradx \rvert \gt \lvert grady \rvert∣gradx∣>∣grady∣ 且两者同号
图4 ∣gradx∣>∣grady∣\lvert gradx \rvert \gt \lvert grady \rvert∣gradx∣>∣grady∣ 且两者异号

a、b梯度值的计算

该点水平(或竖直)线上的点的的梯度值乘上1-weight加上与之连线的点的梯度值乘上weight即可。

图1中,
∣gradx∣<∣grady∣且两者同号grada=(1−weight)∗grad4+weight∗grad3gradb=(1−weight)∗grad8+weight∗grad7\lvert gradx \rvert \lt \lvert grady \rvert 且两者同号\\ grad_a=\left(1-weight\right)*grad_4+weight*grad_3 \\ grad_b=\left(1-weight\right)*grad_8+weight*grad_7 ∣gradx∣<∣grady∣且两者同号grada​=(1−weight)∗grad4​+weight∗grad3​gradb​=(1−weight)∗grad8​+weight∗grad7​
图2中,
∣gradx∣<∣grady∣且两者异号grada=(1−weight)∗grad4+weight∗grad5gradb=(1−weight)∗grad8+weight∗grad9\lvert gradx \rvert \lt \lvert grady \rvert 且两者异号\\ grad_a=\left(1-weight\right)*grad_4+weight*grad_5 \\ grad_b=\left(1-weight\right)*grad_8+weight*grad_9 ∣gradx∣<∣grady∣且两者异号grada​=(1−weight)∗grad4​+weight∗grad5​gradb​=(1−weight)∗grad8​+weight∗grad9​
图3中,
∣gradx∣>∣grady∣且两者同号grada=(1−weight)∗grad2+weight∗grad3gradb=(1−weight)∗grad6+weight∗grad7\lvert gradx \rvert \gt \lvert grady \rvert 且两者同号\\ grad_a=\left(1-weight\right)*grad_2+weight*grad_3\\ grad_b=\left(1-weight\right)*grad_6+weight*grad_7 ∣gradx∣>∣grady∣且两者同号grada​=(1−weight)∗grad2​+weight∗grad3​gradb​=(1−weight)∗grad6​+weight∗grad7​
图4中,
∣gradx∣>∣grady∣且两者异号grada=(1−weight)∗grad2+weight∗grad9gradb=(1−weight)∗grad6+weight∗grad5\lvert gradx \rvert \gt \lvert grady \rvert 且两者异号\\ grad_a=\left(1-weight\right)*grad_2+weight*grad_9\\ grad_b=\left(1-weight\right)*grad_6+weight*grad_5 ∣gradx∣>∣grady∣且两者异号grada​=(1−weight)∗grad2​+weight∗grad9​gradb​=(1−weight)∗grad6​+weight∗grad5​

代码

void SobelGradientDirection(const Mat& img, Mat& dire)
{/*
img是灰度图,dire是每一个像素点的梯度方向
sobelEdgeX和sobelEdgY是通过Sobel算子得到X和Y方向上的梯度值(自己写的函数)
*/Mat EdgeX, EdgeY;sobelEdgeX(img, EdgeX);sobelEdgeY(img, EdgeY);int row = EdgeX.size().height;int col = EdgeX.size().width;dire = Mat(row, col, CV_64FC1);for (int i = 0; i < row; i++)for (int j = 0; j < col; j++)//由于atan的值为-pi/2~pi/2,所以给加90使角度均为正值 范围在0~180之间if (int(EdgeX.ptr<uchar>(i)[j] == 0))dire.ptr<double>(i)[j] = 90;elsedire.ptr<double>(i)[j] = int(EdgeY.ptr<double>(i)[j]) / int(EdgeX.ptr<uchar>(i)[j]) * 180 / 3.14159265 + 90;
}/*
Grad是梯度大小图像
GradX是X梯度幅值图像
GradY是Y梯度幅值图像
dire是梯度方向图像
refined是非极大值抑制后的图像
*/
void NonMaxSup(const Mat& Grad, const Mat& GradX, const Mat& GradY, const Mat& dire, Mat& refined)
{int row = Grad.size().height;int col = Grad.size().width;refined = Mat(row, col, CV_8UC1);for (int i = 0; i < row; i++){for (int j = 0; j < col; j++){uchar grad, grad1, grad2, grad3, grad4, grada, gradb;double weight;if (dire.ptr<double>(i)[j] == 0){grad = Grad.ptr<uchar>(i)[j];grad1 = Grad.ptr<uchar>(i)[j - 1];grad2 = Grad.ptr<uchar>(i)[j + 1];refined.ptr<uchar>(i)[j] = max(grad, max(grad1, grad2)) == grad ? grad : 0;}else if (dire.ptr<double>(i)[j] == 45){grad = Grad.ptr<uchar>(i)[j];grad1 = Grad.ptr<uchar>(i - 1)[j + 1];grad2 = Grad.ptr<uchar>(i + 1)[j - 1];refined.ptr<uchar>(i)[j] = max(grad, max(grad1, grad2)) == grad ? grad : 0;}else if (dire.ptr<double>(i)[j] == 90){grad = Grad.ptr<uchar>(i)[j];grad1 = Grad.ptr<uchar>(i - 1)[j];grad2 = Grad.ptr<uchar>(i + 1)[j];refined.ptr<uchar>(i)[j] = max(grad, max(grad1, grad2)) == grad ? grad : 0;}else if (dire.ptr<double>(i)[j] == 135){grad = Grad.ptr<uchar>(i)[j];grad1 = Grad.ptr<uchar>(i - 1)[j - 1];grad2 = Grad.ptr<uchar>(i + 1)[j + 1];refined.ptr<uchar>(i)[j] = max(grad, max(grad1, grad2)) == grad ? grad : 0;}else if (abs(int(GradX.ptr<uchar>(i)[j])) < abs(int(GradY.ptr<double>(i)[j])))if (int(GradX.ptr<uchar>(i)[j]) * int(GradY.ptr<uchar>(i)[j]) < 0){grad = Grad.ptr<uchar>(i)[j];grad1 = Grad.ptr<uchar>(i - 1)[j];grad2 = Grad.ptr<uchar>(i + 1)[j];grad3 = Grad.ptr<uchar>(i - 1)[j + 1];grad4 = Grad.ptr<uchar>(i + 1)[j - 1];weight = double(abs(int(GradX.ptr<uchar>(i)[j]))) / double(abs(int(GradY.ptr<double>(i)[j])));grada = weight * grad3 + (1 - weight) * grad1;gradb = weight * grad4 + (1 - weight) * grad2;refined.ptr<uchar>(i)[j] = max(grad, max(grada, gradb)) == grad ? grad : 0;}else{grad = Grad.ptr<uchar>(i)[j];grad1 = Grad.ptr<uchar>(i - 1)[j];grad2 = Grad.ptr<uchar>(i + 1)[j];grad3 = Grad.ptr<uchar>(i - 1)[j - 1];grad4 = Grad.ptr<uchar>(i + 1)[j + 1];weight = double(abs(int(GradX.ptr<uchar>(i)[j]))) / double(abs(int(GradY.ptr<double>(i)[j])));grada = weight * grad3 + (1 - weight) * grad1;gradb = weight * grad4 + (1 - weight) * grad2;refined.ptr<uchar>(i)[j] = max(grad, max(grada, gradb)) == grad ? grad : 0;}else{if (int(GradX.ptr<uchar>(i)[j]) * int(GradY.ptr<uchar>(i)[j]) < 0){grad = Grad.ptr<uchar>(i)[j];grad1 = Grad.ptr<uchar>(i)[j + 1];grad2 = Grad.ptr<uchar>(i)[j - 1];grad3 = Grad.ptr<uchar>(i - 1)[j + 1];grad4 = Grad.ptr<uchar>(i + 1)[j - 1];weight = double(abs(int(GradY.ptr<uchar>(i)[j]))) / double(abs(int(GradX.ptr<double>(i)[j])));grada = weight * grad3 + (1 - weight) * grad1;gradb = weight * grad4 + (1 - weight) * grad2;refined.ptr<uchar>(i)[j] = max(grad, max(grada, gradb)) == grad ? grad : 0;}else{grad = Grad.ptr<uchar>(i)[j];grad1 = Grad.ptr<uchar>(i)[j - 1];grad2 = Grad.ptr<uchar>(i)[j + 1];grad3 = Grad.ptr<uchar>(i - 1)[j - 1];grad4 = Grad.ptr<uchar>(i + 1)[j + 1];weight = double(abs(int(GradY.ptr<uchar>(i)[j]))) / double(abs(int(GradX.ptr<double>(i)[j])));grada = weight * grad3 + (1 - weight) * grad1;gradb = weight * grad4 + (1 - weight) * grad2;refined.ptr<uchar>(i)[j] = max(grad, max(grada, gradb)) == grad ? grad : 0;}}}}
}

Canny算子边缘检测——非极大值抑制Non-Maximum Suppression相关推荐

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

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

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

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

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

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

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

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

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

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

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

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

  7. Non-Maximum Suppression,NMS非极大值抑制

    Non-Maximum Suppression,NMS非极大值抑制 概述 非极大值抑制(Non-Maximum Suppression,NMS),顾名思义就是抑制不是极大值的元素,可以理解为局部最大搜 ...

  8. 下拉多选择框 实现方式_非极大值抑制Non-Maximum Suppression(NMS)一文搞定理论+多平台实现...

    这是独立于薰风读论文的投稿,作为目标检测模型的拓展阅读,目的是帮助读者详细了解一些模型细节的实现. 薰风说 Non-Maximum Suppression的翻译是非"极大值"抑制, ...

  9. 非极大值抑制算法(NMS)

    定义: 非极大值抑制(Non-maximum suppression,NMS)是一种去除非极大值的算法,常用于计算机视觉中的边缘检测.物体识别.人脸检测.目标检测(DPM,YOLO,SSD,Faste ...

  10. 锚框、交并比和非极大值抑制(tf2.0源码解析)

    锚框.交并比和非极大值抑制(tf2.0源码解析) 文章目录 锚框.交并比和非极大值抑制(tf2.0源码解析) 一.锚框生成 1.锚框的宽高 2.锚框的个数 3.注意点(★★★) 4.tf2.0代码 二 ...

最新文章

  1. Python-EEG工具库MNE中文教程(4)-MNE中数据结构Evoked及其对象创建
  2. ISE_软件基本使用流程(win10 的bug工程约束仿真烧写mcs固化)
  3. Sublime Text 2 支持GB2312和GBK
  4. 理论修炼之RabbitMQ,消息队列服务的稳健者
  5. 2019年程序员薪酬报告:平均年薪超70万!40岁后,这类人不“保值”了
  6. php判断数值大小_php 快速判断一个数字属于什么范围的实现方法
  7. 图像处理基础(2):自适应中值滤波器(基于OpenCV实现)
  8. java学习练习预埋件配筋计算
  9. Java工作流系统jflow从表功能介绍一
  10. 51单片机AD模数转换(SPI通信)
  11. java sam接口,Java中的SAM接口是什么?
  12. C# 时间日期的获取与比较
  13. intel英特尔架构
  14. Eviews中实现ARIMA模型并进行预测
  15. 益丰大药房互联网医院,积极推动中国大健康产业发展变革
  16. ZLMediaKit视频推流和播放步骤
  17. 使用Nero 7 刻录DVD镜像文件(Linux,Window的iso文件)
  18. php 数字 字母,怎么使用php实现数字转字母
  19. 深入浅出学Spring Data JPA toPredicate Predicate[] p = new Predicate[list.size()]; query.where(cb.and 201
  20. WinPE 安装 2003

热门文章

  1. windows连接远程桌面必须要有用户名和密码
  2. 支付宝免签在线支付要饭教程
  3. CWMP(TR069)协议标准学习
  4. hexo 炫酷主题配置
  5. linux 小度 驱动_小度WiFi怎么安装 小度WiFi驱动安装
  6. 生成pdf设置中文字体出错 \simsun.ttc' with 'Identity-H' is not recognized或者type of font{0} is not recognized
  7. python高斯求和函数_选择积分方法—高斯积分
  8. COM组件和DLL的区别
  9. 白帽子讲Web安全——世界观安全
  10. Android文字转语音