Canny算子边缘检测(原理)
Canny算子边缘检测是一种经典的图像处理算法,由John F. Canny于1986年提出,用于精确、可靠地检测数字图像中的边缘特征。该算法设计时考虑了三个关键目标:低错误率(即尽可能多地检测真实的边缘,同时避免误报)、边缘定位的准确性(确保检测到的边缘位置与实际边缘位置紧密对应)以及边缘的单响应性(确保图像中的每一个边缘只被检测一次,避免重复或断裂)。Canny算子通过以下五个核心步骤实现这些目标:
-
图像灰度化:
- 如果输入图像为彩色,首先将其转换为灰度图像,因为Canny算子适用于处理单通道灰度图像。
-
高斯滤波(高斯模糊):
- 应用高斯平滑滤波器对灰度图像进行滤波,以消除图像中的噪声。高斯滤波器通过卷积操作将每个像素值替换为周围像素值的加权平均,其中权重由二维高斯函数确定。这种滤波方式既能有效减弱噪声,又能较好地保留边缘细节,避免过度模糊导致边缘定位模糊。
-
计算梯度幅值和方向:
- 对经过高斯滤波的图像计算其梯度。梯度表示图像中像素灰度值的变化率,包含了边缘强度和方向的信息。通常使用一阶偏导数的近似算子(如Sobel算子、Prewitt算子或Roberts算子)来计算水平和垂直方向的梯度分量。然后根据这两个分量计算出梯度幅值(即边缘强度)和梯度方向。梯度方向通常用于后续的非极大值抑制步骤。
-
非极大值抑制:
- 该步骤旨在去除非边缘像素的响应,仅保留真正的边缘点。在梯度方向的直线上,检查每个像素的梯度幅值是否为其邻域内(在其梯度方向上)的最大值。如果不是,说明该像素可能不是真正的边缘点,将其梯度幅值置零或降低。这样可以消除边缘检测过程中的许多虚假响应,确保最终得到的边缘轮廓更为精确。
-
双阈值检测与边缘连接:
- 设置高低两个阈值,通常选择高阈值来确定强边缘,低阈值用于连接可能断开的弱边缘。具体操作如下:
- 首先,将梯度幅值大于高阈值的像素标记为边缘像素,形成初步的边缘集。
- 然后,对于幅值介于高低阈值之间的像素,如果它们与已标记为边缘的像素相邻(即位于已检测边缘的8邻域内),也被认为是边缘像素,以保证边缘的连续性。
- 最终,只有通过上述条件的像素才被认为是有效的边缘点,其余像素则被舍弃。这种方法有助于减少边缘断裂的同时,抑制噪声引起的伪边缘。
- 设置高低两个阈值,通常选择高阈值来确定强边缘,低阈值用于连接可能断开的弱边缘。具体操作如下:
Canny算子通过一系列精心设计的步骤,实现了对图像边缘的稳健、精确检测,即使在存在噪声干扰的情况下也能保持较高的性能。由于其出色的综合性能,Canny算子在计算机视觉、图像分析、机器视觉等领域中被广泛应用,特别是在需要精确边缘信息的应用场景中,如物体轮廓检测、运动目标跟踪、图像分割等。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
边缘检测是基于灰度突变来分割图像的常用方法,其实质是提取图像中不连续部分的特征。目前常见边缘检测算子有差分算子、 Roberts 算子、 Sobel 算子、 Prewitt 算子、 Log 算子以及 Canny 算子等。
其中, Canny 算子是由计算机科学家 John F. Canny 于 1986 年提出的一种边缘检测算子,是目前理论上相对最完善的一种边缘检测算法。
Canny 算子在 MATLAB 、 OpenCV 等常用图像处理工具中已有内置的 API。
在 OpenCV 中, Canny 算子使用的函数是 Canny() ,它的原函数如下:
def Canny(image, threshold1, threshold2, edges=None, apertureSize=None, L2gradient=None)
- image: 表示此操作的源(输入图像)。
- threshold1: 表示迟滞过程的第一个阈值。
- threshold2: 表示迟滞过程的第二个阈值。
接下来,接着操作我们之前的马里奥,对马里奥做一次边缘检测看下效果:
import cv2 as cv
from matplotlib import pyplot as plt
# 图像读入
img = cv.imread('maliao.jpg', 0)
edges = cv.Canny(img, 100, 200)
# 显示结果
titles = ['Original Img', 'Edge Img']
images = [img, edges]
# matplotlib 绘图
for i in range(2):
plt.subplot(1, 2, i+1), plt.imshow(images[i],'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()
图像转化(彩->灰)
图像转化原因:边缘检测最关键的部分是计算梯度,颜色难以提供关键信息,并且颜色本身非常容易受到光照等因素的影响,所以只需要灰度图像中的信息就足够了。并且灰度化后,简化了矩阵,提高了运算速度。
原理:将彩色图像(Color Image)转换为灰度图(Gray Scale Image),即从三通道RGB图像转为单通道图像。
实现:我们实现彩图转化为灰度图需要用到opencv库中的cv.cvtColor函数,需要用到两个参数:src——输入图片,code——颜色转换代码,代码如下:
# 灰度图转换
def grayscale(num_img):
for i in range(num_img):
filename = 'img' + str(i) + '.jpg'
img = cv2.imread(filename)
img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
filename = 'img_gray' + str(i) + '.jpg'
cv2.imwrite(filename, img_gray)
生成Mask掩膜,提取 ROI
代码如下:
# 生成感兴趣区域即Mask掩模
def region_of_interest(image, vertices):
mask = np.zeros_like(image) # 生成图像大小一致的zeros矩
# 填充顶点vertices中间区域
if len(image.shape) > 2:
channel_count = image.shape[2]
ignore_mask_color = (255,) * channel_count
else:
ignore_mask_color = 255
# 填充函数
cv2.fillPoly(mask, vertices, ignore_mask_color)
masked_image = cv2.bitwise_and(image, mask)
return masked_image
Hough变换的路沿检测
Hough变换(原理)
Hough变换是一种使用表决方式的参数估计技术,其原理是利用图像空间和Hough参数空间的线-点对偶性,把图像空间中的检测问题转换到参数空间中进行。
基于霍夫变换的直线检测
用到的是Opencv封装好的函数cv.HoughLinesP函数,使用到的参数如下:
image:输入图像,通常为canny边缘检测处理后的图像
rho:线段以像素为单位的距离精度
theta:像素以弧度为单位的角度精度(np.pi/180较为合适)
threshold:霍夫平面累加的阈值
minLineLength:线段最小长度(像素级)
maxLineGap:最大允许断裂长度
具体代码如下:
def hough_lines(img, rho, theta, threshold, min_line_len, max_line_gap):
# rho:线段以像素为单位的距离精度
# theta : 像素以弧度为单位的角度精度(np.pi/180较为合适)
# threshold : 霍夫平面累加的阈值
# minLineLength : 线段最小长度(像素级)
# maxLineGap : 最大允许断裂长度
lines = cv.HoughLinesP(img, rho, theta, threshold, np.array([]), minLineLength=min_line_len, maxLineGap=max_line_gap)
return lines
高斯滤波
高斯滤波算法是一种去除高频噪声的常用方式,通俗的讲,高斯滤波就是对整幅图像进行加权平均的过程,每一个像素点的值都是由其本身和邻域内的其他像素值经过加权平均后得到的。高斯滤波的原理是根据待滤波的像素点及其邻域点的灰度值按照高斯公式生成的参数规则进行加权平均。
我们这一步需要用到opencv库中的cv.GaussianBlur函数,其中使用到的参数为:src——输入图像,kernel_size——高斯核的大小,sigma——高斯标准差(一般默认为0),具体代码如下:
# 高斯滤波
def gaussian_blur(image, kernel_size):
return cv.GaussianBlur(image, (kernel_size, kernel_size), 0)
绘制高斯滤波后的效果图:
- 绘制车道线
图像融合
参考文章:python --opencv图像处理Canny算子边缘检测(Roberts算子、Prewitt算子、Sobel算子、Laplacian算子、Scharr 算子、 LOG 算子)_分别用roberts算子、sobel算子、prewitt算子、拉普拉斯算子、log算子和canny算-CSDN博客