opencv--边缘检测

1 边缘检测原理

边缘检测是图像处理和计算机视觉中的基本问题,边缘检测的目的是标识数字图像中亮度变化明显的点。图像属性中的显著变化通常反映了属性的重要事件和变化。
边缘的表现形式如下所示:
在这里插入图片描述
优势:图像边缘检测大幅度地减少了数据量,并且剔除了可以认定为不相关地信息,还可以保留图像重要地结构属性。
用于边缘检测的方法可以分为两类:基于搜索和基于零穿越。

  • 基于搜索:通过寻找图像一阶导数中的最大值来检测边界,然后利用计算结果估计边缘的局部方向,通常采用梯度的方向,并利用此方向找到局部梯度模的最大值,代表算法有:Sobel算子和Scharr算子。
    在这里插入图片描述
  • 基于零穿越:通过寻找图像二阶导数零穿越来寻找边界,代表算法是Laplacian算子。
    在这里插入图片描述

2 Sobel检测算子

Sobel检测算法较简单,在实际应用中效率比canny检测算法效率更高,但检测精度不如canny准确。Sobel检测算子是高斯平滑和微分操作的结合体,因此其抗噪声能力很强,用途较多。

2.1 Sobel检测方法

对于不连续的函数,一阶导数可写作:
f ′ ( x ) = f ( x ) − f ( x − 1 ) 或 f ′ ( x ) = f ( x + 1 ) − f ( x ) f^{'}(x) = f(x) - f(x-1) \quad 或 \quad f^{'}(x) = f(x+1) - f(x) f(x)=f(x)f(x1)f(x)=f(x+1)f(x)
所以有:
f ′ ( x ) = f ( x + 1 ) − f ( x − 1 ) 2 f^{'}(x) = \frac{f(x+1) - f(x-1)}{2} f(x)=2f(x+1)f(x1)
假设要处理的图像为 I I I,在两个方向(水平和垂直)上求导:

  • 水平变化:将图像 I I I与奇数大小的模板进行卷积,结果为 G x G_x Gx。例如:当模板大小为3时, G x G_x Gx为:
    G x = ∣ − 1 0 + 1 − 2 0 + 2 − 1 0 + 1 ∣ ∗ I G_x = \begin{vmatrix} -1 & 0 & +1 \\ -2 & 0 & +2 \\ -1 & 0 & +1 \end{vmatrix} * I Gx=121000+1+2+1I
  • 垂直变化:将图像 I I I与奇数大小的模板进行卷积,结果为 G y G_y Gy。例如:当模板大小为3时, G y G_y Gy为:
    G y = ∣ − 1 − 2 − 1 0 0 0 + 1 + 2 + 1 ∣ ∗ I G_y = \begin{vmatrix} -1 & -2 & -1 \\ 0 & 0 & 0 \\ +1 & +2 & +1 \end{vmatrix} * I Gy=10+120+210+1I
    在图像的每一点,结合以上两个结果求出:
    G = G x 2 + G y 2 G = \sqrt{G_x^{2} + G_y^{2}} G=Gx2+Gy2
    之后统计极大值所在的位置,就是图像的边缘 ( x l , y l ) (x_l, y_l) (xl,yl)

注意:当内核大小为3时,以上Sobel内核可能产生比较明显的误差, 为解决这一问题,可使用Scharr函数,但该函数仅作用于大小为3的内核。该函数的运算与Sobel函数一样快,但结果却更加精确,其计算方法为:
水平方向:
G x = ∣ − 3 0 + 3 − 10 0 + 10 − 3 0 + 3 ∣ ∗ I G_x = \begin{vmatrix} -3 & 0 & +3 \\ -10 & 0 & +10 \\ -3 & 0 & +3 \end{vmatrix} * I Gx=3103000+3+10+3I
垂直方向:
G y = ∣ − 3 − 10 − 3 0 0 0 + 3 + 10 + 3 ∣ ∗ I G_y = \begin{vmatrix} -3 & -10 & -3 \\ 0 & 0 & 0 \\ +3 & +10 & +3 \end{vmatrix} * I Gy=30+3100+1030+3I

2.2 应用

利用OpenCV进行sobel边缘检测的API:Sobel_x_or_y = cv2.Sobel(src, ddepth, dx, dy, dst, ksize, scale, delta, borderType)
参数:

  • src:传入的图像
  • ddepth: 图像的深度
  • dx和dy: 指求导的阶数,0表示这个方向上没有求导,取值为0、1。
  • ksize: 是Sobel算子的大小,即卷积核的大小,必须为奇数1、3、5、7,默认为3。注意:如果ksize=-1,就演变成为3x3的Scharr算子。
  • scale:缩放导数的比例常数,默认情况为没有伸缩系数
  • borderType:图像边界的模式,默认值为cv2.BORDER_DEFAULT。

Sobel函数求完导数后会有负值,还有会大于255的值。而原图像是uint8,即8位无符号数,所以Sobel建立的图像位数不够,会有截断。因此要使用16位有符号的数据类型,即cv2.CV_16S。处理完图像后,再使用cv2.convertScaleAbs()函数将其转回原来的uint8格式,否则图像无法显示。

Sobel算子是在两个方向计算的,最后还需要用cv2.addWeighted( )函数将其组合起来

Scale_abs = cv2.convertScaleAbs(x)  # 格式转换函数
result = cv2.addWeighted(src1, alpha, src2, beta) # 图像混合
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
# 1 读取图像
img = cv.imread('./image/horse.jpg',0)
# 2 计算Sobel卷积结果
x = cv.Sobel(img, cv.CV_16S, 1, 0)
y = cv.Sobel(img, cv.CV_16S, 0, 1)
# 3 将数据进行转换
Scale_absX = cv.convertScaleAbs(x)  # convert 转换  scale 缩放
Scale_absY = cv.convertScaleAbs(y)
# 4 结果合成
result = cv.addWeighted(Scale_absX, 0.5, Scale_absY, 0.5, 0)
# 5 图像显示
plt.figure(figsize=(10,8),dpi=100)
plt.subplot(121),plt.imshow(img,cmap=plt.cm.gray),plt.title('原图')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(result,cmap = plt.cm.gray),plt.title('Sobel滤波后结果')
plt.xticks([]), plt.yticks([])
plt.show()

在这里插入图片描述
将上述代码中计算sobel算子的部分中将ksize设为-1,就是利用Scharr进行边缘检测。

x = cv.Sobel(img, cv.CV_16S, 1, 0, ksize = -1)
y = cv.Sobel(img, cv.CV_16S, 0, 1, ksize = -1)

在这里插入图片描述

3 Laplacian算子

Laplacian是利用二阶导数来检测边缘。由于图像是二维数据,需要在两个方向上进行求导,如下是所示:
Δ s r c = ∂ 2 ∂ x 2 s r c + ∂ 2 ∂ y 2 s r c \Delta src = \frac{\partial^2}{\partial x^2}{src} + \frac{\partial^2}{\partial y^2}{src} Δsrc=x22src+y22src
不连续函数的二阶导数为:
f ′ ′ ( x ) = f ′ ( x + 1 ) − f ′ ( x ) = f ( x + 1 ) + f ( x − 1 ) f^{''}(x) = f^{'}(x+1) - f^{'}(x) = f(x+1) + f(x-1) f(x)=f(x+1)f(x)=f(x+1)+f(x1)
那么使用的卷积核应为:
k e r n e l = ∣ 0 1 0 1 − 4 1 0 1 0 ∣ kernel = \begin{vmatrix} 0 & 1 & 0 \\ 1 & -4 & 1 \\ 0 & 1 & 0 \end{vmatrix} kernel=010141010
API接口:laplacian = cv2.Laplacian(src, ddepth[, dst[, ksize[, scale[, delta[, borderType]]]]])
参数:

  • Src: 需要处理的图像,
  • Ddepth: 图像的深度,-1表示采用的是原图像相同的深度,目标图像的深度必须大于等于原图像的深度;
  • ksize:算子的大小,即卷积核的大小,必须为1,3,5,7。
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
# 1 读取图像
img = cv.imread('./image/horse.jpg',0)
# 2 laplacian转换
result = cv.Laplacian(img,cv.CV_16S)
Scale_abs = cv.convertScaleAbs(result)
# 3 图像展示
plt.figure(figsize=(10,8),dpi=100)
plt.subplot(121),plt.imshow(img,cmap=plt.cm.gray),plt.title('原图')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(Scale_abs,cmap = plt.cm.gray),plt.title('Laplacian检测后结果')
plt.xticks([]), plt.yticks([])
plt.show()

在这里插入图片描述

4. Canny边缘检测

Canny 边缘检测算法是一种非常流行的边缘检测算法,是 John F. Canny 于 1986年提出的,被认为是最优的边缘检测算法。

4.1 原理

Canny边缘检测算法是由4步构成,分别是:

  1. 噪声去除
    由于边缘检测很容易受到噪声的影响,因此首先使用 5 ∗ 5 5*5 55的高斯滤波器去除噪声。

  2. 计算图像梯度
    对平滑后的图像使用Sobel算子计算水平和垂直方向的一阶导数( G x 和 G y G_x和G_y GxGy)。根据得到的这两幅梯度图找到边界的梯度和方向,公式为:
    E d g e G r a d i e n t ( G ) = G x 2 + G y 2 A n g l e ( θ ) = t a n − 1 ( G y G x ) Edge_Gradient(G)= \sqrt{G_{x}^{2}+G_{y}^{2}} \\ Angle(\theta) = tan^{-1} (\frac{G_y}{G_x}) EdgeGradient(G)=Gx2+Gy2 Angle(θ)=tan1(GxGy)
    如果某个像素点是边缘,则其梯度方向总是垂直与边缘垂直。梯度方向被归为四类:水平、垂直和两个对角线方向。

  3. 非极大值抑制
    在获得梯度的方向和大小后,对整幅图像进行扫描,去除那些非边界上的点。对每一个像素进行检查,看这个点的梯度是不是周围具有相同梯度方向的点中最大的,即:
    在这里插入图片描述
    A点位于图像的边缘,在其梯度变化方向,选择像素点B和C,用来检验A点的梯度是否为极大值,若为极大值,则进行保留,否则A点被抑制,最终的结果是具有“细边”的二进制图像。

  4. 滞后阈值
    现在要确定真正的边界:设置两个阈值: minVal 和 maxVal。 当图像的灰度梯度高于 maxVal 时被认为是真的边界, 低于 minVal 的边界会被抛弃。如果介于两者之间的话,就要看这个点是否与某个被确定为真正的边界点相连,如果是就认为它也是边界点,如果不是就抛弃。如下图:
    在这里插入图片描述
    如上图所示,A 高于阈值 maxVal 所以是真正的边界点,C 虽然低于 maxVal 但高于 minVal 并且与 A 相连,所以也被认为是真正的边界点。而 B 就会被抛弃,因为低于 maxVal 而且不与真正的边界点相连。所以选择合适的 maxVal 和 minVal 对于能否得到好的结果非常重要。

4.2应用

在OpenCV中要实现Canny检测使用的API:canny = cv2.Canny(image, threshold1, threshold2)
参数:

  • image:灰度图
  • threshold1: minval,较小的阈值将间断的边缘连接起来
  • threshold2: maxval,较大的阈值检测图像中明显的边缘
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
# 1 图像读取
img = cv.imread('./image/horse.jpg',0)
# 2 Canny边缘检测
lowThreshold = 0
max_lowThreshold = 100
canny = cv.Canny(img, lowThreshold, max_lowThreshold) 
# 3 图像展示
plt.figure(figsize=(10,8),dpi=100)
plt.subplot(121),plt.imshow(img,cmap=plt.cm.gray),plt.title('原图')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(canny,cmap = plt.cm.gray),plt.title('Canny检测后结果')
plt.xticks([]), plt.yticks([])
plt.show()

在这里插入图片描述

5 算子比较

加粗样式

<think>好的,我现在需要回答用户关于使用OpenCV-Python进行边缘检测的问题。首先,用户想要知道具体的方法、示例和教程。根据用户的引用内容,有三个相关的引用,但看起来这些引用主要涉及安装问题、图像处理中的颜色通道和视频编解码器,可能与边缘检测直接相关的信息不多。不过,我可以结合自己的知识来回答,并在适当的地方引用这些内容。 首先,边缘检测的常用方法在OpenCV中主要是Canny边缘检测。我应该先介绍Canny算法,然后给出具体的步骤和代码示例。代码部分需要确保正确,比如读取图像时可能需要转换为灰度图,处理颜色通道的问题,这可以引用用户提到的引用[3]中的内容,关于BGR顺序的问题。例如,在使用cv2.imread读取图像后,默认是BGR格式,可能需要转换为灰度图,或者确保正确处理颜色通道,避免错误。 接下来,我需要详细说明每一步的操作,比如高斯模糊降噪、计算梯度、非极大值抑制、双阈值检测等。这些步骤是Canny算法的关键,用户可能需要了解这些原理。然后提供一个完整的代码示例,展示如何从读取图像到显示边缘检测结果。 然后,用户可能需要了解如何调整参数,比如阈值的选择,这会影响到边缘检测的效果。可以给出一些调整参数的建议,或者示例代码中的参数设置说明。 关于教程部分,用户提到的引用[1]提到OpenCV官方示例可能在Python2中,不过现在通常使用Python3,所以可能需要建议用户参考最新的文档或教程。同时,安装问题在引用[2]和[3]中涉及,如果在示例代码运行时出现问题,可能需要检查安装是否正确,比如是否安装了opencv-python-headless或其他包。 最后,生成相关问题,考虑用户可能进一步想知道其他边缘检测方法,比如Sobel、Laplacian,或者如何优化边缘检测的结果,以及实际应用场景等。 在编写过程中,需要确保所有的数学公式,比如梯度计算中的方向,如果有的话,使用正确的LaTeX格式。例如,梯度方向可以用$\theta = \arctan(\frac{G_y}{G_x})$来表示,但根据系统指令,如果出现在行内,需要用$...$包裹。 总结步骤: 1. 介绍Canny边缘检测的基本步骤。 2. 提供详细的代码示例,包括图像读取、灰度转换、高斯模糊、Canny边缘检测、显示结果。 3. 解释代码中的关键点,比如颜色通道转换(引用[3])。 4. 提到参数调整对结果的影响。 5. 可能提到其他边缘检测方法作为扩展。 6. 生成相关问题,如其他方法、优化、应用场景等。 需要检查代码是否正确,例如是否在应用Canny之前进行了灰度转换和高斯模糊,以及是否正确使用cv2.imshow和销毁窗口。同时,确保引用适当,比如在提到颜色通道问题时引用[3],在安装问题时引用[2]或[3]。</think>以下是如何使用OpenCV-Python进行边缘检测的详细说明及示例: ### OpenCV-Python边缘检测方法 边缘检测的核心是通过梯度变化识别图像中的物体边界。最常用的方法是**Canny边缘检测算法**[^1],其步骤包括: 1. **高斯模糊**:使用高斯滤波器$G(x,y)=\frac{1}{2\pi\sigma^2}e^{-\frac{x^2+y^2}{2\sigma^2}}$降噪 2. **计算梯度**:通过Sobel算子计算横向($G_x$)和纵向($G_y$)梯度,梯度幅值为$\sqrt{G_x^2 + G_y^2}$ 3. **非极大值抑制**:保留局部梯度最大值,细化边缘 4. **双阈值检测**:设定高低阈值区分强边缘、弱边缘和非边缘 ### 代码示例 ```python import cv2 import numpy as np # 读取图像(注意OpenCV默认BGR格式) image = cv2.imread('input.jpg') gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 转换为灰度图[^3] # 高斯模糊降噪(内核大小5x5,标准差1.4) blurred = cv2.GaussianBlur(gray, (5, 5), 1.4) # Canny边缘检测(阈值建议比例2:1或3:1) edges = cv2.Canny(blurred, threshold1=50, threshold2=150) # 显示结果 cv2.imshow('Original', image) cv2.imshow('Edge Detection', edges) cv2.waitKey(0) cv2.destroyAllWindows() ``` ### 关键说明 1. **颜色通道处理**:OpenCV读取图像为BGR格式,转换为灰度图可简化计算 2. **参数调整**: - 高斯内核大小应为奇数,通常取3-7 - Canny阈值根据图像动态调整,高阈值一般为低阈值的2-3倍 3. **扩展方法**:Sobel算子、Laplacian算子等也可用于边缘检测
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值