OpenCV中的边缘检测

边缘检测是图像处理和计算机视觉中的关键技术之一,旨在识别图像中像素强度发生显著变化的区域,这些区域通常对应于物体的边界或轮廓。边缘检测在机器视觉中具有重要的需求背景,主要体现在以下几个方面:

  1. 图像分割:边缘检测可以帮助将图像分割成不同的区域,便于后续的图像分析和处理。
  2. 物体识别:通过检测物体的边缘,可以提取物体的特征,为物体识别提供基础。
  3. 图像增强:边缘检测可以增强图像中的重要特征,提高图像的清晰度和质量。
  4. 三维重建:在三维重建中,边缘检测可以帮助确定物体的形状和结构。

1.1.2. 应用场景

边缘检测在机器视觉中有着广泛的应用场景,包括但不限于以下几个方面:

  1. 工业检测:用于检测产品表面的缺陷,如裂纹、划痕、凹陷等。
  2. 自动驾驶:用于检测道路、交通标志、车辆和行人等物体的轮廓,辅助自动驾驶系统进行决策。
  3. 医学图像分析:用于识别医学图像中的器官、病变和组织边界,辅助医生进行诊断。
  4. 安防监控:用于检测视频中的异常行为或物体,提高监控系统的智能化水平。
  5. 机器人视觉:用于识别环境中的物体和障碍物,帮助机器人进行导航和避障。

1.2. OpenCV中的实现方法

OpenCV提供了多种边缘检测方法,常用的包括:

  1. Canny 边缘检测
  2. Sobel 算子
  3. Laplacian 算子

1.2.1. Canny 边缘检测

原理及内部流程: Canny 边缘检测是一种多阶段的边缘检测算法,主要包括以下步骤:

  1. 噪声抑制:使用高斯滤波器对图像进行平滑处理,以减少噪声的影响。
  2. 计算梯度:使用Sobel算子计算图像的梯度幅值和方向。
  3. 非极大值抑制:沿着梯度方向,保留局部梯度最大的像素点,抑制其他像素点,有效的去除多余的边缘效应,精确图像的边缘。
  4. 双阈值检测:设置高阈值和低阈值,高于高阈值的像素被认为是强边缘,介于高阈值和低阈值之间的像素被认为是弱边缘。
  5. 边缘连接:通过强边缘像素和与之相邻的弱边缘像素进行连接,形成完整的边缘线。

参数确定及影响

  • 高斯滤波器的参数:通常根据图像的噪声水平选择合适的高斯滤波器参数,如标准差和核大小。
  • 阈值:高阈值和低阈值的选择对边缘检测结果有重要影响。高阈值用于检测强边缘,低阈值用于检测弱边缘。阈值的选择需要根据图像的特点和应用场景进行调整。

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)
  1. image
    • 作用 :输入图像,必须是单通道灰度图像
    • 确认方法 :如果输入图像是彩色图像,需要先将其转换为灰度图像:Python复制
python 复制代码
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
  1. threshold1
    • 作用:低阈值,用于滞后阈值处理。
    • 作用机制:用于检测弱边缘。如果梯度幅值低于此阈值,则认为该像素不是边缘。
    • 确认方法
      • 根据经验,可以设置为 50 左右。
      • 通过实验调整,观察边缘检测的效果。通常需要结合高阈值一起调整。
      • 调的较低会保留更多的弱边缘,对于边缘较弱的,可以适当降低低阈值
  1. threshold2
    • 作用:高阈值,用于滞后阈值处理。
    • 作用机制:用于检测强边缘。如果梯度幅值高于此阈值,则认为该像素是强边缘。
    • 确认方法
      • 根据经验,可以设置为 150 左右。
      • 高阈值与低阈值的比值通常在 2:1 到 3:1 之间。例如,低阈值为 50,高阈值为 150 或 100。
      • 对于边缘较复杂的图像,可以适当增加高阈值。
  1. apertureSize
    • 作用:Sobel 算子的卷积核大小,影响梯度计算的精度。
    • 默认值:3
    • 确认方法
      • 如果需要更平滑的边缘,可以尝试更大的值(如 5 或 7)。
      • 值越大,计算复杂度越高,但边缘检测结果可能更平滑。
  1. 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()
  1. 滤波核大小对弱边缘有影响,滤波核越大,弱边缘越少

高阈值越高,弱边缘越少

极限检测:

上下限和滤波都是0:

先逐渐把低阈值拉满:

再把高阈值拉满:

弱边缘是在持续减少的。

大家可以个人自己多尝试尝试

相关推荐
ganbingfenxiang几秒前
山西干冰零售
大数据·人工智能·python·零售
薛定猫AI3 分钟前
【深度解析】GLM-5.2 与 Z-Code:AI 编程智能体的原理拆解与 Python 调用实战
开发语言·人工智能·python
weixin_4000056011 分钟前
RL-frenet-trajectory-planning-in-CARLA
人工智能·深度学习·算法·机器学习·自动驾驶
白白白飘15 分钟前
【3】大语言模型的核心原理
人工智能·语言模型·自然语言处理
西西弗Sisyphus16 分钟前
计算机视觉领域 选择 卷积神经网络 还是选择 Vision Transformer
计算机视觉·cnn·transformer
ZeekerLin31 分钟前
AI时代,企业核心业务中台化建设思考
大数据·人工智能·企业ai转型
刘海东刘海东33 分钟前
完成了一二三四章
人工智能
甲维斯34 分钟前
普通人慎入!Fable5原创游戏“死线求生”第一版!
人工智能·ai编程·游戏开发
北鹤M34 分钟前
ai模特画图高效实现换装,批量生图工具实测体验
人工智能·aigc
大C聊AI41 分钟前
通用办公 AI 难以适配招投标专业场景,垂直行业 AI 工具落地实践|智标领航技术选型分析
人工智能·效率提升·ai 工具·招投标·智标领航·ai 辅助办公