opencv python Canny边缘提取实现过程解析

1. Canny边缘检测简介

Canny边缘检测算法是一种非常经典的图像处理算法,常用于计算机视觉任务中边缘检测方面。它可以有效地检测出图像中的边缘,具有较好的鲁棒性和精度。Canny边缘检测算法是由John Canny在1986年提出的,其设计的目的是使得边缘检测能够更加准确地定位真实边缘,同时有效地抑制噪声。

2. Canny边缘检测原理

2.1 介绍

Canny边缘检测算法的原理非常复杂,不过可以简单地概括为以下四个步骤:

使用高斯滤波器对图像进行平滑处理,以降低噪声的干扰

计算图像中每个像素点的梯度大小和方向

对梯度幅值进行非极大值抑制处理,以抑制非边缘响应

使用双阈值算法对梯度幅值进行阈值化处理,从而得到图像中的真实边缘

2.2 具体实现

下面我们来具体解释一下Canny边缘检测算法的实现过程:

高斯滤波器处理

在实现Canny边缘检测算法之前,我们需要用高斯滤波器对图像进行平滑处理。原因是图像中可能存在不可忽略的噪声,而这些噪声会对边缘检测的结果产生影响。高斯滤波器可以在滤波的同时对图像进行模糊化处理,从而去除噪声。实现代码如下:

import cv2

img = cv2.imread('image.jpg')

gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

gaussian_img = cv2.GaussianBlur(gray_img, (3, 3), 0)

在代码中,我们首先读取一张图像,并将其转换为灰度图像。然后使用cv2.GaussianBlur()函数对灰度图像进行高斯滤波处理,其中(3, 3)表示高斯核的大小,0表示高斯核的方差。

计算梯度大小和方向

在进行边缘检测之前,我们需要计算图像中每个像素点的梯度大小和方向。梯度的大小定义为像素值变化最大的方向的导数,而梯度的方向则指示了像素值变化最大的方向。我们可以通过Sobel算子来计算梯度大小和方向。实现代码如下:

import numpy as np

x_gradient = cv2.Sobel(gaussian_img, cv2.CV_64F, 1, 0, ksize=3)

y_gradient = cv2.Sobel(gaussian_img, cv2.CV_64F, 0, 1, ksize=3)

magnitude = np.sqrt(np.square(x_gradient) + np.square(y_gradient)) # 梯度大小

magnitude = cv2.normalize(magnitude, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)

theta = np.arctan2(y_gradient, x_gradient) * 180 / np.pi # 梯度方向

在代码中,我们分别使用Sobel算子计算水平方向和竖直方向的梯度。然后使用np.sqrt()计算梯度大小,使用np.arctan2()计算梯度方向。最后使用cv2.normalize()函数将梯度大小归一化到0-255范围内,并将其转换为uint8类型以便后续处理。

非极大值抑制

在计算梯度大小和方向之后,我们需要对梯度幅值进行非极大值抑制处理。主要的目的是抑制非边缘响应,保留真实的边缘。具体操作是:对每一个像素点,根据其梯度方向,选择其梯度方向上相邻两个像素点的梯度幅值进行比较,若该像素点梯度幅值不是其梯度方向上的最大值,则将该像素点梯度幅值设为0。实现代码如下:

def non_maximum_suppression(magnitude, theta):

rows, cols = magnitude.shape

suppressed = np.zeros((rows, cols))

angle = theta % 180

for i in range(1, rows-1):

for j in range(1, cols-1):

grad = magnitude[i, j]

if grad == 0:

suppressed[i, j] = 0

else:

dx = magnitude[i, j+1] - magnitude[i, j-1]

dy = magnitude[i+1, j] - magnitude[i-1, j]

if abs(dx) > abs(dy):

tangent = np.abs(dy/dx)

if angle[i, j] < 45:

x0, y0 = i, j+1

x1, y1 = i+1, j+1

else:

x0, y0 = i+1, j

x1, y1 = i+1, j-1

else:

tangent = np.abs(dx/dy)

if angle[i, j] < 45:

x0, y0 = i+1, j

x1, y1 = i+1, j-1

else:

x0, y0 = i, j+1

x1, y1 = i+1, j+1

if grad >= magnitude[x0, y0] and grad >= magnitude[x1, y1]:

suppressed[i, j] = grad

else:

suppressed[i, j] = 0

return suppressed

suppressed = non_maximum_suppression(magnitude, theta)

在代码中,我们首先定义了一个non_maximum_suppression()函数,其输入参数为梯度大小和梯度方向的矩阵。然后在函数中,我们遍历每个像素点,在其梯度方向上选择相邻两个像素点的梯度幅值进行比较,从而得到该像素点处的非极大值抑制结果。最后返回抑制结果矩阵。

双阈值算法

在进行完非极大值抑制之后,我们需要使用双阈值算法来确定图像中的真实边缘。双阈值算法将梯度幅值分成两个阈值范围,大于高阈值的被当作强边缘,小于低阈值的被舍弃,介于两个阈值之间的根据连通性被当作弱边缘或噪声,若弱边缘与强边缘连通,则最终认为是真实边缘。实现代码如下:

def thresholding(img, low_threshold, high_threshold):

strong_edges = (img >= high_threshold)

thresholded = np.zeros_like(img)

thresholded[strong_edges] = 255

weak_edges = (img >= low_threshold) & (img < high_threshold)

rows, cols = img.shape

for i in range(1, rows-1):

for j in range(1, cols-1):

if weak_edges[i, j]:

if np.any(strong_edges[i-1:i+2, j-1:j+2]):

thresholded[i, j] = 255

else:

thresholded[i, j] = 0

return thresholded

low_threshold = 20

high_threshold = 40

thresholded = thresholding(suppressed, low_threshold, high_threshold)

在代码中,我们首先定义了一个threshholding()函数,其输入参数为抑制结果矩阵、低阈值和高阈值。在函数中,我们将大于高阈值的像素点标记为强边缘,小于低阈值的像素点舍弃,介于两个阈值之间但未经过非极大值抑制的像素点标记为噪声,其余标记为弱边缘。然后对于每一个弱边缘像素点,我们检查其8邻域是否存在强边缘像素点,若存在,则将其标记为真实边缘,否则舍弃它。

3. Canny边缘检测实现

上述是Canny边缘检测算法的基本原理及实现过程,以下是使用OpenCV Python库来实现Canny边缘检测的完整代码:

import cv2

import numpy as np

def non_maximum_suppression(magnitude, theta):

rows, cols = magnitude.shape

suppressed = np.zeros((rows, cols))

angle = theta % 180

for i in range(1, rows-1):

for j in range(1, cols-1):

grad = magnitude[i, j]

if grad == 0:

suppressed[i, j] = 0

else:

dx = magnitude[i, j+1] - magnitude[i, j-1]

dy = magnitude[i+1, j] - magnitude[i-1, j]

if abs(dx) > abs(dy):

tangent = np.abs(dy/dx)

if angle[i, j] < 45:

x0, y0 = i, j+1

x1, y1 = i+1, j+1

else:

x0, y0 = i+1, j

x1, y1 = i+1, j-1

else:

tangent = np.abs(dx/dy)

if angle[i, j] < 45:

x0, y0 = i+1, j

x1, y1 = i+1, j-1

else:

x0, y0 = i, j+1

x1, y1 = i+1, j+1

if grad >= magnitude[x0, y0] and grad >= magnitude[x1, y1]:

suppressed[i, j] = grad

else:

suppressed[i, j] = 0

return suppressed

def thresholding(img, low_threshold, high_threshold):

strong_edges = (img >= high_threshold)

thresholded = np.zeros_like(img)

thresholded[strong_edges] = 255

weak_edges = (img >= low_threshold) & (img < high_threshold)

rows, cols = img.shape

for i in range(1, rows-1):

for j in range(1, cols-1):

if weak_edges[i, j]:

if np.any(strong_edges[i-1:i+2, j-1:j+2]):

thresholded[i, j] = 255

else:

thresholded[i, j] = 0

return thresholded

img = cv2.imread('image.jpg')

gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

gaussian_img = cv2.GaussianBlur(gray_img, (3, 3), 0)

x_gradient = cv2.Sobel(gaussian_img, cv2.CV_64F, 1, 0, ksize=3)

y_gradient = cv2.Sobel(gaussian_img, cv2.CV_64F, 0, 1, ksize=3)

magnitude = np.sqrt(np.square(x_gradient) + np.square(y_gradient))

magnitude = cv2.normalize(magnitude, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)

theta = np.arctan2(y_gradient, x_gradient) * 180 / np.pi

suppressed = non_maximum_suppression(magnitude, theta)

low_threshold = 20

high_threshold = 40

thresholded = thresholding(suppressed, low_threshold, high_threshold)

cv2.imshow('Original Image', img)

cv2.imshow('Canny Edge Detection', thresholded)

cv2.waitKey(0)

cv2.destroyAllWindows()

在代码中,我们首先读取了一张图像,并将其转换为灰度图像。然后进行高斯滤波处理,计算梯度大小和方向,进行非极大值抑制,最后进行双阈值算法处理,得到图像中的真实边缘。最后使用cv2.imshow()函数显示原始图像和检测出的边缘图像,并使用cv2.waitKey()函数等待用户的按键输入,最后使用cv2.destroyAllWindows()函数关闭所有窗口。

4. 总结

在本篇文章中,我们详细介绍了Canny边缘检测算法的基本原理及实现过程。Canny边缘检测算法可以有效地检测出图像中的边缘,具有较好的鲁棒性和精度,在计算机视觉任务中得到了广泛的应用。同时,我们也借此机会学习了使用OpenCV Python库实现Canny边缘检测的方法,并实现了一个简单的边缘检测应用。希望本篇文章对大家学习Canny边缘检测算法有所帮助。

后端开发标签