文章目录

  • 前言
  • canny边缘检测算法主要流程
  • 一、高斯模糊
  • 二、图像梯度计算
  • 三、非极大值抑制
  • 四、双阈值边界跟踪

前言

本文通过介绍canny边缘检测原理与代码解析,希望能让大家深入理解canny边缘检测


canny边缘检测算法主要流程

canny边缘检测主要分为4个部分,本文分别从每一个部分进行解析并附代码。

  1. 图像降噪
  2. 梯度计算
  3. 非极大值抑制
  4. 双阈值边界跟踪

一、高斯模糊

图像去噪是进行边缘检测的第一步,通过去噪可以去除图像中的一些噪点,从而使边缘检测时免受噪点干扰。
一般去噪卷积核中心点的值由周围点像素均值决定,以3×3卷积核的8邻域为例,卷积中心点坐标值由周围坐标与本身的均值决定。
然而这种均值决定中心点的方法本质是周围点均采用相同的权重,但是当卷积核变大时,离中心点最远的点与最近的点占有相同权重显然是不合适的,这就需要我们的高斯核出场啦。
高斯模糊就是将卷积核中不同位置的点按照高斯分布(正态分布)进行权重分配,二维高斯分布公式如下:

这边我们已σ=1为例生成一个3×3的高斯卷积核。如下图所示,首先将普通的卷积核按(x,y)坐标,σ=1,代入上述二维高斯分布公式,得到权重具有高斯分布特性的卷积核。其次,由于卷积核所有点的权重和为1,所以需要将卷积核每个点的权重值再除以所有点的权重合。最后就完成了高斯卷积核,可以对图像进行去噪卷积操作啦。
代码如下:

import numpy as np
from scipy import ndimage
from scipy.ndimage.filters import convolve
def gaussian_kernel(self, size, sigma=1):size = int(size) // 2x, y = np.mgrid[-size:size+1, -size:size+1]normal = 1 / (2.0 * np.pi * sigma**2)g = np.exp(-((x**2 + y**2) / (2.0*sigma**2))) * normalreturn g

二、图像梯度计算

要进行边缘检测,就需要得到图像梯度信息,根据图像的梯度幅值和梯度方向来确定边缘,一般均采用sobel算子对图像进行梯度幅值与梯度方向计算。
sobel算子分为垂直方向和水平方向两个模板,模板如下:

梯度幅值G和梯度方向θ计算公式如下所示:



代码如下:


def sobel_filters(self, img):Kx = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]], np.float32)Ky = np.array([[1, 2, 1], [0, 0, 0], [-1, -2, -1]], np.float32)Ix = ndimage.filters.convolve(img, Kx)Iy = ndimage.filters.convolve(img, Ky)G = np.hypot(Ix, Iy)theta = np.arctan2(Iy, Ix)return (G, theta)

三、非极大值抑制

在获取图像的梯度幅值和梯度方向后,需要通过获取的梯度幅值和梯度方向对图像边缘进行非极大值抑制操作,由于梯度方向与边缘方向是垂直的,所以非极大值抑制可以有效的剔除一大部分非边缘点。
如下图所示,在非极大值抑制中,梯度方向是一条无向直线,也就是正负两侧均为梯度方向(即下图红线),并将梯度方向分为四个部分:

  • 垂直梯度方向(0,22.5]∪(-22.5,0]∪(157.5,180]∪(-180,157.5]
  • 45°梯度方向 [22.5,67.5)∪[-157.5,-112.5)
  • 水平梯度方向 [67.5,112.5]∪[-112.5,-67.5]
  • 135°梯度方向 (112.5, 157.5]∪[-67.5, -22.5]

确定了梯度方向后,就需要通过这个梯度方向上交点q’和r’的值来确定是否将中心点抑制,但是实际上我们是得不到交点值的,以下图8邻域我们只能得到周围8个点,这时有两种办法,1)线性插值,通过p、q两点的线性插值求得q’,r’同理可得。2)取相近点作为极大值,即取q点取代q’点。下方代码是以方法2为基础的。

具体代码如下,

def non_max_suppression(self,img, D):M, N = img.shapeZ = np.zeros((M,N), dtype=np.int32)angle = D * 180. / np.piangle[angle < 0] += 180for i in range(1,M-1):for j in range(1,N-1):try:q = 255r = 255#angle 0if (0 <= angle[i,j] < 22.5) or (157.5 <= angle[i,j] <= 180):q = img[i, j+1]r = img[i, j-1]#angle 45elif (22.5 <= angle[i,j] < 67.5):q = img[i+1, j-1]r = img[i-1, j+1]#angle 90elif (67.5 <= angle[i,j] < 112.5):q = img[i+1, j]r = img[i-1, j]#angle 135elif (112.5 <= angle[i,j] < 157.5):q = img[i-1, j-1]r = img[i+1, j+1]if (img[i,j] >= q) and (img[i,j] >= r):Z[i,j] = img[i,j]else:Z[i,j] = 0except IndexError as e:passreturn Z

四、双阈值边界跟踪

双阈值边界跟踪分为两个步骤:1)通过选取强弱阈值,将梯度幅值低与弱阈值的点置为0,大于强阈值的保留并标记为255。2)对于梯度幅值大于弱阈值但又小于高阈值的点,通过判断它的8邻域是否存在大于强阈值的点,若存在,则保留并置为255,若不存在,则舍弃并置为0。
具体代码如下:
步骤1:

def threshold(self, img):highThreshold = img.max() * self.highThresholdlowThreshold = highThreshold * self.lowThresholdM, N = img.shaperes = np.zeros((M,N), dtype=np.int32)weak = np.int32(self.weak_pixel)strong = np.int32(self.strong_pixel)strong_i, strong_j = np.where(img >= highThreshold)zeros_i, zeros_j = np.where(img < lowThreshold)weak_i, weak_j = np.where((img <= highThreshold) & (img >= lowThreshold))res[strong_i, strong_j] = strongres[weak_i, weak_j] = weakreturn (res)

步骤2:

def hysteresis(self, img):M, N = img.shapeweak = self.weak_pixelstrong = self.strong_pixelfor i in range(1, M-1):for j in range(1, N-1):if (img[i,j] == weak):try:if ((img[i+1, j-1] == strong) or (img[i+1, j] == strong) or (img[i+1, j+1] == strong)or (img[i, j-1] == strong) or (img[i, j+1] == strong)or (img[i-1, j-1] == strong) or (img[i-1, j] == strong) or (img[i-1, j+1] == strong)):img[i, j] = strongelse:img[i, j] = 0except IndexError as e:passreturn img

总体代码如下:

from scipy import ndimage
from scipy.ndimage.filters import convolve
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import osdef rgb2gray(rgb):r, g, b = rgb[:,:,0], rgb[:,:,1], rgb[:,:,2]gray = 0.2989 * r + 0.5870 * g + 0.1140 * breturn graydef load_data(dir_name):imgs = []for filename in os.listdir(dir_name):if os.path.isfile(dir_name + '/' + filename):img = mpimg.imread(dir_name + '/' + filename)img = rgb2gray(img)imgs.append(img)return imgsdef visualize(imgs, format=None, gray=False):plt.figure(figsize=(10, 20))for i, img in enumerate(imgs):if img.shape[0] == 3:img = img.transpose(1,2,0)plt_idx = i+1plt.subplot(2, 2, plt_idx)plt.imshow(img, format)plt.show()class cannyEdgeDetector:def __init__(self, imgs, sigma=1, kernel_size=5, weak_pixel=75, strong_pixel=255, lowthreshold=0.05, highthreshold=0.15):self.imgs = imgsself.imgs_final = []self.img_smoothed = Noneself.gradientMat = Noneself.thetaMat = Noneself.nonMaxImg = Noneself.thresholdImg = Noneself.weak_pixel = weak_pixelself.strong_pixel = strong_pixelself.sigma = sigmaself.kernel_size = kernel_sizeself.lowThreshold = lowthresholdself.highThreshold = highthresholdreturn def gaussian_kernel(self, size, sigma=1):size = int(size) // 2x, y = np.mgrid[-size:size+1, -size:size+1]normal = 1 / (2.0 * np.pi * sigma**2)g = np.exp(-((x**2 + y**2) / (2.0*sigma**2))) * normalreturn gdef sobel_filters(self, img):Kx = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]], np.float32)Ky = np.array([[1, 2, 1], [0, 0, 0], [-1, -2, -1]], np.float32)Ix = ndimage.filters.convolve(img, Kx)Iy = ndimage.filters.convolve(img, Ky)G = np.hypot(Ix, Iy)G = G / G.max() * 255theta = np.arctan2(Iy, Ix)return (G, theta)def non_max_suppression(self, img, D):M, N = img.shapeZ = np.zeros((M,N), dtype=np.int32)angle = D * 180. / np.piangle[angle < 0] += 180for i in range(1,M-1):for j in range(1,N-1):try:q = 255r = 255#angle 0if (0 <= angle[i,j] < 22.5) or (157.5 <= angle[i,j] <= 180):q = img[i, j+1]r = img[i, j-1]#angle 45elif (22.5 <= angle[i,j] < 67.5):q = img[i+1, j-1]r = img[i-1, j+1]#angle 90elif (67.5 <= angle[i,j] < 112.5):q = img[i+1, j]r = img[i-1, j]#angle 135elif (112.5 <= angle[i,j] < 157.5):q = img[i-1, j-1]r = img[i+1, j+1]if (img[i,j] >= q) and (img[i,j] >= r):Z[i,j] = img[i,j]else:Z[i,j] = 0except IndexError as e:passreturn Zdef threshold(self, img):highThreshold = img.max() * self.highThresholdlowThreshold = highThreshold * self.lowThresholdM, N = img.shaperes = np.zeros((M,N), dtype=np.int32)weak = np.int32(self.weak_pixel)strong = np.int32(self.strong_pixel)strong_i, strong_j = np.where(img >= highThreshold)zeros_i, zeros_j = np.where(img < lowThreshold)weak_i, weak_j = np.where((img <= highThreshold) & (img >= lowThreshold))res[strong_i, strong_j] = strongres[weak_i, weak_j] = weakreturn (res)def hysteresis(self, img):M, N = img.shapeweak = self.weak_pixelstrong = self.strong_pixelfor i in range(1, M-1):for j in range(1, N-1):if (img[i,j] == weak):try:if ((img[i+1, j-1] == strong) or (img[i+1, j] == strong) or (img[i+1, j+1] == strong)or (img[i, j-1] == strong) or (img[i, j+1] == strong)or (img[i-1, j-1] == strong) or (img[i-1, j] == strong) or (img[i-1, j+1] == strong)):img[i, j] = strongelse:img[i, j] = 0except IndexError as e:passreturn imgdef detect(self):imgs_final = []for i, img in enumerate(self.imgs):self.img_smoothed = convolve(img, self.gaussian_kernel(self.kernel_size, self.sigma))self.gradientMat, self.thetaMat = self.sobel_filters(self.img_smoothed)self.nonMaxImg = self.non_max_suppression(self.gradientMat, self.thetaMat)self.thresholdImg = self.threshold(self.nonMaxImg)img_final = self.hysteresis(self.thresholdImg)self.imgs_final.append(img_final)return self.imgs_finalimgs = load_data(dir_name='faces_imgs')
visualize(imgs, 'gray')
detector = cannyEdgeDetector(imgs, sigma=1, kernel_size=5, lowthreshold=0.09, highthreshold=0.17, weak_pixel=100)
imgs_final = detector.detect()
visualize(imgs_final, 'gray')


效果还不错


【canny边缘检测】canny边缘检测原理及代码详解相关推荐

  1. DeepLearning tutorial(1)Softmax回归原理简介+代码详解

    FROM: http://blog.csdn.net/u012162613/article/details/43157801 DeepLearning tutorial(1)Softmax回归原理简介 ...

  2. DeepLearning tutorial(3)MLP多层感知机原理简介+代码详解

    FROM:http://blog.csdn.net/u012162613/article/details/43221829 @author:wepon @blog:http://blog.csdn.n ...

  3. DeepLearning tutorial(4)CNN卷积神经网络原理简介+代码详解

    FROM: http://blog.csdn.net/u012162613/article/details/43225445 DeepLearning tutorial(4)CNN卷积神经网络原理简介 ...

  4. Pytorch|YOWO原理及代码详解(二)

    Pytorch|YOWO原理及代码详解(二) 本博客上接,Pytorch|YOWO原理及代码详解(一),阅前可看. 1.正式训练 if opt.evaluate:logging('evaluating ...

  5. batchnorm原理及代码详解

    转载自:http://www.ishenping.com/ArtInfo/156473.html batchnorm原理及代码详解 原博文 原微信推文 见到原作者的这篇微信小文整理得很详尽.故在csd ...

  6. 人脸识别SeetaFace2原理与代码详解

    人脸识别SeetaFace2原理与代码详解 前言 一.人脸识别步骤 二.SeetaFace2基本介绍 三.seetaFace2人脸注册.识别代码详解 3.1 人脸注册 3.1.1 人脸检测 3.1.2 ...

  7. Pytorch | yolov3原理及代码详解(二)

    阅前可看: Pytorch | yolov3原理及代码详解(一) https://blog.csdn.net/qq_24739717/article/details/92399359 分析代码: ht ...

  8. 【OpenCV/C++】KNN算法识别数字的实现原理与代码详解

    KNN算法识别数字 一.KNN原理 1.1 KNN原理介绍 1.2 KNN的关键参数 二.KNN算法识别手写数字 2.1 训练过程代码详解 2.2 预测分类的实现过程 三.KNN算法识别印刷数字 2. ...

  9. Pytorch | yolov3原理及代码详解(一)

    YOLO相关原理 : https://blog.csdn.net/leviopku/article/details/82660381 https://www.jianshu.com/p/d13ae10 ...

  10. 深入浅出吃透多线程、线程池核心原理及代码详解

    一.多线程详解 1.什么是线程 线程是一个操作系统概念.操作系统负责这个线程的创建.挂起.运行.阻塞和终结操作.而操作系统创建线程.切换线程状态.终结线程都要进行CPU调度--这是一个耗费时间和系统资 ...

最新文章

  1. 爬取最好大学网站大学排名
  2. mysql5.7导出数据报错The MySQL server is running with the --secure-file-priv option so it cannot execute
  3. linux 基本指令-线上运维
  4. SHOW VARIABLES LIKE “tx_isolation“查看mysql查看当前数据库的事物隔离级别
  5. 干货|亲测有效的N倍学习效果笔记法
  6. java当中用UDP实现相互交流
  7. VAE【变分自编码器】
  8. 小白的java学习之路 “ 选择结构(一)”
  9. T-SQL的进阶:超越基本级别3:构建相关子查询——701小组
  10. php 5.4 +iis+mysql_IIS+FastCGI+PHP5.4.4搭建PHP环境
  11. DSP2812之定时器
  12. 串口通信协议之SPI通信协议
  13. 黑苹果声卡HDA无声问题
  14. python计算无穷级数求和_[探求无穷级数求和的几种常用方法]无穷级数求和常用公式...
  15. 何谓计算机思维(转载)
  16. 赛博哈希CyberHash顺势而为,获数千万美金的融资
  17. 使用ngrok实现内网穿透,免费在本地发布项目
  18. 国密SSL证书正式上线,知道创宇云防御助力金融和重要领域完成国密升级改造...
  19. 昭和書体 全書体一览
  20. 互联网中的视频/音频服务

热门文章

  1. 提取PDF文件中的文本信息
  2. matlab 函数怎么写,MATLAB如何定义函数
  3. oneinstack安装java_Ubuntu 16.04安装OneinStack以及配置java开发环境
  4. 最隐秘的医疗数据,如何通过区块链流转?
  5. O2OA二次开发办公平台:内容管理数据迁移
  6. 1分2分5分的硬币,组成1角,共有多少种组合
  7. java后台识别二维码的方法
  8. vba clear清除公式、内容、格式的使用
  9. linuxOPS基础_vmware虚拟机安装及介绍
  10. 数据上云,如何解除用户对厂商监守自盗的担忧?