边缘检测是图像处理和计算机视觉中的关键技术之一,旨在识别图像中像素强度发生显著变化的区域,这些区域通常对应于物体的边界或轮廓。边缘检测在机器视觉中具有重要的需求背景,主要体现在以下几个方面:
- 图像分割:边缘检测可以帮助将图像分割成不同的区域,便于后续的图像分析和处理。
- 物体识别:通过检测物体的边缘,可以提取物体的特征,为物体识别提供基础。
- 图像增强:边缘检测可以增强图像中的重要特征,提高图像的清晰度和质量。
- 三维重建:在三维重建中,边缘检测可以帮助确定物体的形状和结构。
1.1.2. 应用场景
边缘检测在机器视觉中有着广泛的应用场景,包括但不限于以下几个方面:
- 工业检测:用于检测产品表面的缺陷,如裂纹、划痕、凹陷等。
- 自动驾驶:用于检测道路、交通标志、车辆和行人等物体的轮廓,辅助自动驾驶系统进行决策。
- 医学图像分析:用于识别医学图像中的器官、病变和组织边界,辅助医生进行诊断。
- 安防监控:用于检测视频中的异常行为或物体,提高监控系统的智能化水平。
- 机器人视觉:用于识别环境中的物体和障碍物,帮助机器人进行导航和避障。
1.2. OpenCV中的实现方法
OpenCV提供了多种边缘检测方法,常用的包括:
- Canny 边缘检测
- Sobel 算子
- Laplacian 算子
1.2.1. Canny 边缘检测
原理及内部流程: Canny 边缘检测是一种多阶段的边缘检测算法,主要包括以下步骤:
- 噪声抑制:使用高斯滤波器对图像进行平滑处理,以减少噪声的影响。
- 计算梯度:使用Sobel算子计算图像的梯度幅值和方向。
- 非极大值抑制:沿着梯度方向,保留局部梯度最大的像素点,抑制其他像素点,有效的去除多余的边缘效应,精确图像的边缘。
- 双阈值检测:设置高阈值和低阈值,高于高阈值的像素被认为是强边缘,介于高阈值和低阈值之间的像素被认为是弱边缘。
- 边缘连接:通过强边缘像素和与之相邻的弱边缘像素进行连接,形成完整的边缘线。
参数确定及影响:
- 高斯滤波器的参数:通常根据图像的噪声水平选择合适的高斯滤波器参数,如标准差和核大小。
- 阈值:高阈值和低阈值的选择对边缘检测结果有重要影响。高阈值用于检测强边缘,低阈值用于检测弱边缘。阈值的选择需要根据图像的特点和应用场景进行调整。
1.2.2. Sobel 算子
原理及内部流程: Sobel 算子是一种基于一阶导数的边缘检测算子,通过计算图像在水平和垂直方向的梯度来检测边缘。Sobel 算子使用两个3×3的卷积核,分别计算水平和垂直方向的梯度,然后通过这两个梯度来估计边缘。
参数确定及影响:
- 卷积核大小:通常为3×3,但可以根据需要选择其他大小。
- 阈值:通过设置阈值来确定边缘像素,阈值的选择需要根据图像的特点和应用场景进行调整。
1.2.3. Laplacian 算子
原理及内部流程: Laplacian 算子是一种基于二阶导数的边缘检测算子,通过对图像进行二阶微分操作来检测边缘。Laplacian 算子可以提供边缘的强度和方向信息。
参数确定及影响:
- 卷积核大小:通常为3×3,但可以根据需要选择其他大小。
- 阈值:通过设置阈值来确定边缘像素,阈值的选择需要根据图像的特点和应用场景进行调整。、
1.2.4. 在openCV中如何使用
Sobel 算子与Laplacian 算子:这个在之前的高通滤波中已经说明过。
Canny 算子:
python
edges = cv2.Canny(image, threshold1, threshold2, apertureSize=3, L2gradient=False)
- image
-
- 作用 :输入图像,必须是单通道灰度图像。
- 确认方法 :如果输入图像是彩色图像,需要先将其转换为灰度图像:Python复制
python
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
- threshold1
-
- 作用:低阈值,用于滞后阈值处理。
- 作用机制:用于检测弱边缘。如果梯度幅值低于此阈值,则认为该像素不是边缘。
- 确认方法:
-
-
- 根据经验,可以设置为 50 左右。
- 通过实验调整,观察边缘检测的效果。通常需要结合高阈值一起调整。
- 调的较低会保留更多的弱边缘,对于边缘较弱的,可以适当降低低阈值
-
- threshold2
-
- 作用:高阈值,用于滞后阈值处理。
- 作用机制:用于检测强边缘。如果梯度幅值高于此阈值,则认为该像素是强边缘。
- 确认方法:
-
-
- 根据经验,可以设置为 150 左右。
- 高阈值与低阈值的比值通常在 2:1 到 3:1 之间。例如,低阈值为 50,高阈值为 150 或 100。
- 对于边缘较复杂的图像,可以适当增加高阈值。
-
- apertureSize
-
- 作用:Sobel 算子的卷积核大小,影响梯度计算的精度。
- 默认值:3
- 确认方法:
-
-
- 如果需要更平滑的边缘,可以尝试更大的值(如 5 或 7)。
- 值越大,计算复杂度越高,但边缘检测结果可能更平滑。
-
- L2gradient
-
- 作用:决定梯度幅值的计算方式。
-
-
- 如果为
True
,则使用 L2 范数计算梯度幅值(M =G x 2+G y2)。 - 如果为
False
,则使用 L1 范数计算梯度幅值(M =∣G x ∣+∣G y∣)。
- 如果为
-
-
- 默认值 :
False
- 确认方法:
- 默认值 :
-
-
- 如果对精度 要求较高,可以设置为
True
。 - 如果对性能 要求较高,可以使用默认值
False
。
- 如果对精度 要求较高,可以设置为
-
python
# Canny 边缘检测
edges = cv2.Canny(gray_image, 100, 200)
我们可以找一张图片,先高斯滤波,然后Canny检测,再边缘检测,感受一下各参数的改变的Canny边缘检测的影响:
新建文件:UiTest.py 在其中构建下列类
python
import cv2
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
class EdgeDetectionDemo:
def __init__(self, image_path):
self.image = cv2.imread(image_path)
if self.image is None:
raise ValueError(f"Failed to load image at path: {image_path}")
self.image_gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)
# 初始化默认参数
self.blur_size = 5
self.canny_low = 100
self.canny_high = 200
self.min_w = 100
self.min_h = 100
# 创建图像窗口
self.fig, (self.ax1, self.ax2, self.ax3) = plt.subplots(1, 3, figsize=(18, 6))
self.fig.subplots_adjust(bottom=0.25) # 调整底部空间以放置滑块
# 显示原始图像
self.ax1.imshow(cv2.cvtColor(self.image, cv2.COLOR_BGR2RGB))
self.ax1.set_title("Original Image")
self.ax1.axis('off')
# 显示边缘检测和轮廓检测结果
self.update_processing()
# 创建滑块
self.create_sliders()
plt.show()
def update_processing(self):
# 高斯模糊(确保核大小为奇数)
blur_size = self.blur_size if self.blur_size % 2 == 1 else self.blur_size + 1
blurred = cv2.GaussianBlur(self.image_gray, (blur_size, blur_size), 0)
# Canny边缘检测
edges = cv2.Canny(blurred, self.canny_low, self.canny_high)
# 查找轮廓
contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 绘制边缘检测结果
self.ax2.clear()
self.ax2.imshow(edges, cmap='gray')
self.ax2.set_title(f"Canny Edges (Low={self.canny_low}, High={self.canny_high})")
self.ax2.axis('off')
# 绘制轮廓检测结果
contour_img = self.image.copy()
valid_contours = []
for cnt in contours:
x, y, w, h = cv2.boundingRect(cnt)
if w > self.min_w and h > self.min_h:
cv2.rectangle(contour_img, (x, y), (x+w, y+h), (0, 255, 0), 2)
valid_contours.append(cnt)
self.ax3.clear()
self.ax3.imshow(cv2.cvtColor(contour_img, cv2.COLOR_BGR2RGB))
self.ax3.set_title(f"Contours (Min Size: {self.min_w}x{self.min_h})")
self.ax3.axis('off')
self.fig.canvas.draw_idle() # 重新绘制图像
def create_sliders(self):
# 创建滑块
ax_blur_size = self.fig.add_axes([0.25, 0.15, 0.65, 0.03])
ax_canny_low = self.fig.add_axes([0.25, 0.12, 0.65, 0.03])
ax_canny_high = self.fig.add_axes([0.25, 0.09, 0.65, 0.03])
ax_min_w = self.fig.add_axes([0.25, 0.06, 0.65, 0.03])
ax_min_h = self.fig.add_axes([0.25, 0.03, 0.65, 0.03])
self.slider_blur_size = Slider(ax_blur_size, 'Blur Size', 1, 15, valinit=self.blur_size, valstep=2)
self.slider_canny_low = Slider(ax_canny_low, 'Canny Low', 0, 300, valinit=self.canny_low, valstep=10)
self.slider_canny_high = Slider(ax_canny_high, 'Canny High', 50, 500, valinit=self.canny_high, valstep=10)
self.slider_min_w = Slider(ax_min_w, 'Min Width', 50, 500, valinit=self.min_w, valstep=10)
self.slider_min_h = Slider(ax_min_h, 'Min Height', 50, 500, valinit=self.min_h, valstep=10)
# 绑定滑块事件
self.slider_blur_size.on_changed(self.update_slider)
self.slider_canny_low.on_changed(self.update_slider)
self.slider_canny_high.on_changed(self.update_slider)
self.slider_min_w.on_changed(self.update_slider)
self.slider_min_h.on_changed(self.update_slider)
def update_slider(self, val):
self.blur_size = int(self.slider_blur_size.val)
self.canny_low = int(self.slider_canny_low.val)
self.canny_high = int(self.slider_canny_high.val)
self.min_w = int(self.slider_min_w.val)
self.min_h = int(self.slider_min_h.val)
self.update_processing()
在另外一个文件(learnCV.py)中引用该类
python
from ImageManipulation import cvTest
from UiTest import EdgeDetectionDemo
def main():
# cvTest.TestGrayImage()
# cvTest.TestChannelSplite()
demo = EdgeDetectionDemo("Cars.jpg")
if(__name__=="__main__"):
main()
- 滤波核大小对弱边缘有影响,滤波核越大,弱边缘越少


高阈值越高,弱边缘越少

极限检测:
上下限和滤波都是0:

先逐渐把低阈值拉满:

再把高阈值拉满:

弱边缘是在持续减少的。
大家可以个人自己多尝试尝试