OpenCV中的边缘检测

news2025/3/13 18:28:56

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

  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 算子:

edges = cv2.Canny(image, threshold1, threshold2, apertureSize=3, L2gradient=False)
  1. image
    • 作用:输入图像,必须是单通道灰度图像
    • 确认方法:如果输入图像是彩色图像,需要先将其转换为灰度图像: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=Gx2+Gy2)。
      • 如果为 False,则使用 L1 范数计算梯度幅值(M=∣Gx∣+∣Gy∣)。
    • 默认值False
    • 确认方法
      • 如果对精度要求较高,可以设置为 True
      • 如果对性能要求较高,可以使用默认值 False
# Canny 边缘检测
edges = cv2.Canny(gray_image, 100, 200)

我们可以找一张图片,先高斯滤波,然后Canny检测,再边缘检测,感受一下各参数的改变的Canny边缘检测的影响:

新建文件:UiTest.py 在其中构建下列类

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)中引用该类

from ImageManipulation import cvTest
from UiTest import EdgeDetectionDemo
def main():

    # cvTest.TestGrayImage()
    # cvTest.TestChannelSplite()
    demo = EdgeDetectionDemo("Cars.jpg")

if(__name__=="__main__"):
    main()
  1. 滤波核大小对弱边缘有影响,滤波核越大,弱边缘越少

 

 

高阈值越高,弱边缘越少

 


极限检测:

上下限和滤波都是0:

 

先逐渐把低阈值拉满:

 

再把高阈值拉满:

 

弱边缘是在持续减少的。

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

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2300379.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

家里WiFi信号穿墙后信号太差怎么处理?

一、首先在调制解调器(俗称:猫)测试网速,网速达不到联系运营商; 二、网线影响不大,5类网线跑500M完全没问题; 三、可以在卧室增加辅助路由器(例如小米AX系列)90~200元区…

【前端学习笔记】Webpack

1.介绍 Webpack 是一个现代 JavaScript 应用程序的静态模块打包工具,它将 JavaScript、CSS、图片、字体等资源文件打包成一个或多个静态文件,以供浏览器使用。当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个 依赖图(depend…

数据结构(陈越,何钦铭)第三讲 树(上)

3.1 树与数的表示 3.1.1 顺序查找 int SequentialSearch(List Tbl,ElementType K){int i;Tbl->Element[0]K;for(iTbl->Length;Tbl->Element[i]!K;i--);return i; } typedef struct LNode *List; struct LNode{ElementType Element[MAXSIZE];int Length; };3.1.2 二分…

【深度解析】图解Deepseek-V3模型架构-混合专家模型(MoE)

一、引言 最近非常火爆的DeepSeek-V3模型,是一个包含6710亿总参数的强大混合专家模型(MoE),其中每个token激活370亿参数。该模型在DeepSeek-V2验证有效的核心架构基础上,采用多头潜在注意力(MLA&#xff0…

c#判断exe文件是不是7z或者rar的自解压文件

亲测可以实现检测7z的自解压,但是对于rar的自解压格式,最新版不支持,尝试修改回发现几乎检测成了exe文件,这显然是不正确的,其他版本未测试。 如下图所示,可以检测出自解压格式的7z文件,黑色显…

富士SC2022,C325,C328打印机扫描到网络详细教程

前言: 在开始教程之前,我先声明目前该教程适用于FujiXerox apeos C325Z和FujiXerox DocuCentre SC2022打印机。这次教程以FujiXerox DocuCentre SC2022为例,该打印机IP地址为10.40.11.240。 前提条件 : 1. 安装打印机所需打印机和扫…

人工智能3d点云之Pointnet++项目实战源码解读(点云分类与分割)

一.项目文件概述 二.数据读取模块配置 实际代码运行时是先定义与加载好模型,然后再去读取数据进来传入到模型网络中去训练。但现在反过来先读取数据开始。 进入ModelNetDataLoader类的_getitem方法, 做标准化的目的是处理异常大的数值 上面返回的cls是类别,相当于…

IP 路由基础 | 路由条目生成 / 路由表内信息获取

注:本文为 “IP 路由” 相关文章合辑。 未整理去重。 IP 路由基础 秦同学学学已于 2022-04-09 18:44:20 修改 一. IP 路由产生背景 我们都知道 IP 地址可以标识网络中的一个节点,并且每个 IP 地址都有自己的网段,各个网段并不相同&#xf…

java后端开发day16--字符串(二)

(以下内容全部来自上述课程) 1.StringBuilder 因为StringBuilder是Java已经写好的类。 java在底层对他进行了一些特殊处理。 打印对象不是地址值而是属性值。 1.概述 StringBuilder可以看成是一个容器,创建之后里面的内容是可变的。 作用…

LabVIEW危化品仓库的安全监测系统

本案例展示了基于LabVIEW平台设计的危化品仓库安全监测系统,结合ZigBee无线通信技术、485串口通讯技术和传感器技术,实现了对危化品仓库的实时无线监测。该系统不仅能提高安全性,还能大幅提升工作效率,确保危化品仓库的安全运营。…

深度学习框架探秘|Keras 应用案例解析以及 Keras vs TensorFlow vs PyTorch

引言 上一篇文章《深度学习框架探秘|Keras:深度学习的魔法钥匙》 我们初步学习了 Keras,包括它是什么、具备哪些优势(简洁易用的 API、强大的兼容性、广泛的应用领域),以及基本使用方法。本文,…

NAT(网络地址转换)技术详解:网络安全渗透测试中的关键应用与防御策略

目录 NAT的作用 NAT类型 NAT工作流程示例 NAT 转换技术的原理 源地址转换(SNAT,Source NAT): 目标地址转换(DNAT,Destination NAT): 端口地址转换(PAT&#xff0c…

容器化部署Kafka的最佳实践:基于KRaft模式的无ZooKeeper方案

一、docker 部署kafka单节点 1.1安装docker 可以参考这篇CentOS 7安装docker并配置镜像加速 1.3 运行kafka(注意修改zookeeper,kafka地址) docker run -d --name kafka -e KAFKA_ADVERTISED_LISTENERSPLAINTEXT://172.16.10.180:9092 -p …

【PHP】php+mysql 活动信息管理系统(源码+论文+数据库+数据库文件)【独一无二】

👉博__主👈:米码收割机 👉技__能👈:C/Python语言 👉专__注👈:专注主流机器人、人工智能等相关领域的开发、测试技术。 【PHP】php 活动信息管理系统(源码论文…

thingboard告警信息格式美化

原始报警json内容: { "severity": "CRITICAL","acknowledged": false,"cleared": false,"assigneeId": null,"startTs": 1739801102349,"endTs": 1739801102349,"ackTs": 0,&quo…

OpenHarmonry 5.0.1源码下载与编译

预置环境:硬盘500G、内存32G、Ubuntu 20.04.6 LTS Ubuntu系统下载路径:ubuntu-releases安装包下载_开源镜像站-阿里云 一、必需环境 sudo apt-get update && sudo apt-get install binutils binutils-dev git git-lfs gnupg flex bison gperf…

STM32 外部中断和NVIC嵌套中断向量控制器

目录 背景 外部中断/事件控制器(EXTI) 主要特性 功能说明 外部中断线 嵌套向量中断控制器 特性 ‌中断线(Interrupt Line) 中断线的定义和作用 STM32中断线的分类和数量 优先级分组 抢占优先级(Preemption Priority) …

string类详解(上)

文章目录 目录1. STL简介1.1 什么是STL1.2 STL的版本1.3 STL的六大组件 2. 为什么学习string类3. 标准库中的string类3.1 string类3.2 string类的常用接口说明 目录 STL简介为什么学习string类标准库中的string类string类的模拟实现现代版写法的String类写时拷贝 1. STL简介 …

【Go并发编程】Goroutine 调度器揭秘:从 GMP 模型到 Work Stealing 算法

每天一篇Go语言干货,从核心到百万并发实战,快来关注魔法小匠,一起探索Go语言的无限可能! 在 Go 语言中,Goroutine 是一种轻量级的并发执行单元,它使得并发编程变得简单高效。而 Goroutine 的高效调度机制是…

【前端】Vue组件库之Element: 一个现代化的 UI 组件库

文章目录 前言一、官网1、官网主页2、设计原则3、导航4、组件 二、核心功能:开箱即用的组件生态1、丰富的组件体系2、特色功能亮点 三、快速上手:三步开启组件化开发1、安装(使用Vue 3)2、全局引入3、按需导入(推荐&am…