【canny边缘检测】canny边缘检测原理及代码详解

这篇具有很好参考价值的文章主要介绍了【canny边缘检测】canny边缘检测原理及代码详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

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


canny边缘检测算法主要流程

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

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

一、高斯模糊

图像去噪是进行边缘检测的第一步,通过去噪可以去除图像中的一些噪点,从而使边缘检测时免受噪点干扰。
一般去噪卷积核中心点的值由周围点像素均值决定,以3×3卷积核的8邻域为例,卷积中心点坐标值由周围坐标与本身的均值决定。
然而这种均值决定中心点的方法本质是周围点均采用相同的权重,但是当卷积核变大时,离中心点最远的点与最近的点占有相同权重显然是不合适的,这就需要我们的高斯核出场啦。
高斯模糊就是将卷积核中不同位置的点按照高斯分布(正态分布)进行权重分配,二维高斯分布公式如下:
【canny边缘检测】canny边缘检测原理及代码详解
这边我们已σ=1为例生成一个3×3的高斯卷积核。如下图所示,首先将普通的卷积核按(x,y)坐标,σ=1,代入上述二维高斯分布公式,得到权重具有高斯分布特性的卷积核。其次,由于卷积核所有点的权重和为1,所以需要将卷积核每个点的权重值再除以所有点的权重合。最后就完成了高斯卷积核,可以对图像进行去噪卷积操作啦。【canny边缘检测】canny边缘检测原理及代码详解
代码如下:

import numpy as np
from scipy import ndimage
from scipy.ndimage.filters import convolve
def gaussian_kernel(self, size, sigma=1):
	size = int(size) // 2
    x, 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))) * normal
    return g

二、图像梯度计算

要进行边缘检测,就需要得到图像梯度信息,根据图像的梯度幅值和梯度方向来确定边缘,一般均采用sobel算子对图像进行梯度幅值与梯度方向计算。
sobel算子分为垂直方向和水平方向两个模板,模板如下:
【canny边缘检测】canny边缘检测原理及代码详解
梯度幅值G和梯度方向θ计算公式如下所示:
【canny边缘检测】canny边缘检测原理及代码详解
【canny边缘检测】canny边缘检测原理及代码详解
【canny边缘检测】canny边缘检测原理及代码详解
代码如下:


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为基础的。
【canny边缘检测】canny边缘检测原理及代码详解
具体代码如下,

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

	return Z

四、双阈值边界跟踪

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

def threshold(self, img):
    highThreshold = img.max() * self.highThreshold
    lowThreshold = highThreshold * self.lowThreshold
    M, N = img.shape
    res = 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] = strong
    res[weak_i, weak_j] = weak
    return (res)

步骤2:

def hysteresis(self, img):

    M, N = img.shape
    weak = self.weak_pixel
    strong = self.strong_pixel

    for 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] = strong
                    else:
                        img[i, j] = 0
                except IndexError as e:
                    pass
    return 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 os


def rgb2gray(rgb):
    r, g, b = rgb[:,:,0], rgb[:,:,1], rgb[:,:,2]
    gray = 0.2989 * r + 0.5870 * g + 0.1140 * b
    return gray

def 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 imgs


def 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+1
        plt.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 = imgs
        self.imgs_final = []
        self.img_smoothed = None
        self.gradientMat = None
        self.thetaMat = None
        self.nonMaxImg = None
        self.thresholdImg = None
        self.weak_pixel = weak_pixel
        self.strong_pixel = strong_pixel
        self.sigma = sigma
        self.kernel_size = kernel_size
        self.lowThreshold = lowthreshold
        self.highThreshold = highthreshold
        return 
    
    def gaussian_kernel(self, size, sigma=1):
        size = int(size) // 2
        x, 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))) * normal
        return 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)
        G = G / G.max() * 255
        theta = np.arctan2(Iy, Ix)
        return (G, theta)
    

    def non_max_suppression(self, img, D):
        M, N = img.shape
        Z = np.zeros((M,N), dtype=np.int32)
        angle = D * 180. / np.pi
        angle[angle < 0] += 180


        for i in range(1,M-1):
            for j in range(1,N-1):
                try:
                    q = 255
                    r = 255

                   #angle 0
                    if (0 <= angle[i,j] < 22.5) or (157.5 <= angle[i,j] <= 180):
                        q = img[i, j+1]
                        r = img[i, j-1]
                    #angle 45
                    elif (22.5 <= angle[i,j] < 67.5):
                        q = img[i+1, j-1]
                        r = img[i-1, j+1]
                    #angle 90
                    elif (67.5 <= angle[i,j] < 112.5):
                        q = img[i+1, j]
                        r = img[i-1, j]
                    #angle 135
                    elif (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] = 0


                except IndexError as e:
                    pass

        return Z

    def threshold(self, img):

        highThreshold = img.max() * self.highThreshold
        lowThreshold = highThreshold * self.lowThreshold

        M, N = img.shape
        res = 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] = strong
        res[weak_i, weak_j] = weak

        return (res)

    def hysteresis(self, img):

        M, N = img.shape
        weak = self.weak_pixel
        strong = self.strong_pixel

        for 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] = strong
                        else:
                            img[i, j] = 0
                    except IndexError as e:
                        pass

        return img
    
    def 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_final

imgs = 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边缘检测原理及代码详解
效果还不错文章来源地址https://www.toymoban.com/news/detail-497198.html


到了这里,关于【canny边缘检测】canny边缘检测原理及代码详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 【C++】【Opencv】cv::Canny()边缘检测函数详解和示例

    【C++】【Opencv】cv::Canny()边缘检测函数详解和示例

    Canny边缘检测是一种流行的边缘检测算法,由John F. Canny在1986年开发。它是一种多阶段过程,包括噪声滤波、计算图像强度的梯度、非最大值抑制以及双阈值检测。本文通过函数原型解读和示例对cv::Canny()函数进行详解,以帮助大家理解和使用。 Canny边缘检测的步骤如下: (

    2024年02月04日
    浏览(7)
  • openCV实战-系列教程5:边缘检测(Canny边缘检测/高斯滤波器/Sobel算子/非极大值抑制/线性插值法/梯度方向/双阈值检测 )、原理解析、源码解读
?????OpenCV实战系列总目录

    openCV实战-系列教程5:边缘检测(Canny边缘检测/高斯滤波器/Sobel算子/非极大值抑制/线性插值法/梯度方向/双阈值检测 )、原理解析、源码解读 ?????OpenCV实战系列总目录

    打印一个图片可以做出一个函数: Canny是一个科学家在1986年写了一篇论文,所以用自己的名字来命名这个检测算法,Canny边缘检测算法这里写了5步流程,会用到之前《openCV实战-系列教程》的内容。  使用高斯滤波器,以平滑图像,滤除噪声。 计算图像中每个像素点的梯度强

    2024年02月11日
    浏览(6)
  • opencv(23) Canny边缘检测流程(Canny算子)

    opencv(23) Canny边缘检测流程(Canny算子)

    Canny 边缘检测算法 是 John F. Canny 于 1986年开发出来的一个多级边缘检测算法,也被很多人认为是边缘检测的 最优算法 , 最优边缘检测的三个主要评价标准是: 低错误率: 标识出尽可能多的实际边缘,同时尽可能的减少噪声产生的误报。 高定位性: 标识出的边缘要与图像中的实

    2024年04月11日
    浏览(16)
  • 使用c++视觉处理----canny 边缘检测、sobel边缘检测、scharr 滤波边缘检测
  • python Canny边缘检测

    python Canny边缘检测

        参考文献 Canny边缘检测算法(python 实现)_Master_miao的博客-CSDN博客_python canny 使用Pytorch从头实现Canny边缘检测 

    2024年02月06日
    浏览(7)
  • Canny边缘检测

    Canny边缘检测

      图象的边缘是指 图象局部区域亮度变化显著的部分 ,该区域的灰度剖面可以看作是一个阶跃,即从一个灰度值在很小的缓冲区域内急剧变化到另一个灰度相差较大的灰度值。 图象的边缘部分集中了图象的大部分信息 ,图象边缘的确定与提取对于整个图象场景的识别与理

    2024年02月06日
    浏览(12)
  • Canny边缘检测算法

    Canny边缘检测算法

    Canny是目前最优秀的边缘检测算法之一,在传统机器学习算法当中,Canny是最优秀的算法,但是有深度学习的方法要比Canny好。Canny算法的目标是找到一个最优的边缘,其最优边缘的定义为: 好的检测:算法能够尽可能的标出图像中的实际边缘 好的定位:标识出的边缘要与实际

    2024年02月09日
    浏览(4)
  • 图像边缘检测(Canny)

    Canny检测的流程 Canny检测主要是用于边缘检测 1)使用高斯滤波器,以平滑图像,滤除噪声。   2)计算图像中每个像素点的梯度强度和方向。 3)应用非极大值(Non-Maximum Suppression)抑制,以消除边缘检测带来的杂散响应 4)应用双阈值(Double-Threshold)检测来确定真实的和潜在的边缘 5)通

    2023年04月22日
    浏览(7)
  • OpenCV——Canny边缘检测算法

    OpenCV——Canny边缘检测算法

    图像分割是将数字图像细分为多个子区域的过程,在计算机视觉/机器视觉领域被广泛应用。它的目的是简化或改变图像的表示形式,以便更容易理解和分析。常见的图像分割方法包括阈值处理、聚类法、边缘检测和区域生长等。解决图像分割问题通常需要结合领域知识,以提

    2024年04月17日
    浏览(13)
  • 【OpenCV】第十章: Canny边缘检测

    【OpenCV】第十章: Canny边缘检测

    第十章: Canny边缘检测 canny边缘检测是一种一阶微分算子检测算法,但为什么还要单独拿出来讲呢,因为它几乎是边缘检测算子中最优秀的边缘检测算子,你很难找到一种边缘检测算子能显著地比Canny算子做的更好。 Canny提出了边缘检测算子优劣评判的三条标准: 1、较高的检

    2024年02月03日
    浏览(8)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包