(纯新手教学)计算机视觉(opencv)实战七——边缘检测Sobel 算子(cv2.Sobel())详解

更多学习:四种边缘检测

(纯新手教学)计算机视觉(opencv)实战八------四种边缘检测详解:Sobel、Scharr、Laplacian、Canny-CSDN博客https://blog.csdn.net/2302_78022640/article/details/150592942?sharetype=blogdetail&sharerId=150592942&sharerefer=PC&sharesource=2302_78022640&spm=1011.2480.3001.8118


边缘检测

1. 边缘检测的概念

在图像处理中,**边缘(Edge)**是像素灰度值急剧变化的区域,通常对应图像中的物体边界、纹理或者结构特征。检测边缘有助于我们提取图像的几何结构,广泛应用于物体识别、分割和计算机视觉任务。

最常见的边缘检测方法之一就是 Sobel 算子


2. Sobel 算子的基本原理

Sobel 算子通过计算图像在 水平方向(x)垂直方向(y) 的梯度,来检测图像的边缘。

  • 梯度(Gradient):反映灰度值变化的快慢,变化越大说明该区域越可能是边缘。

  • Sobel 算子在计算时,会结合平滑微分操作,减少噪声干扰。

其常见的卷积核为:

  • 水平方向(x方向)核
  • 垂直方向(y方向)核

通过与图像进行卷积,分别得到 x方向梯度y方向梯度,再进行组合就能得到最终的边缘。


3. cv2.Sobel() 函数

在 OpenCV 中,Sobel 算子的函数为:

复制代码
cv2.Sobel(src, ddepth, dx, dy[, ksize[, scale[, delta[, borderType]]]])

参数说明:

参数 说明
src 输入图像(一般用灰度图像)
ddepth 输出图像的深度。常用 cv2.CV_64F,避免数据溢出
dx x方向的差分阶数(1 表示一阶导数)
dy y方向的差分阶数(1 表示一阶导数)
ksize Sobel 核大小,必须是 1, 3, 5, 7(常用 3)
scale 可选参数,缩放导数值
delta 可选参数,结果加上某个值
borderType 边界填充方式,默认 cv2.BORDER_DEFAULT

4. 代码解析与效果

可直接使用下图,也可自备图片:但是要记得灰度处理(根据需要也可以尝试彩色)

所有处理后的结果:

(1)读取与显示图像

复制代码
import cv2
img = cv2.imread('violet.jpg',cv2.IMREAD_GRAYSCALE)
img = cv2.resize(img,(800,500))
cv2.imshow('img', img)
cv2.waitKey(0)
  • 使用灰度图像,便于边缘提取。

  • 图像缩放到合适大小,便于展示。


(2)x方向的边缘

复制代码
# x方向上的边缘
img_x = cv2.Sobel(img, -1, dx=1, dy=0)
cv2.imshow('img_x', img_x)
cv2.waitKey(0)
  • 这里 dx=1, dy=0,表示只计算水平方向的梯度。

  • 能突出 竖直边缘(例如物体左右边界)。

  • 使用 -1 表示输出图像深度与原图相同。


(3)保留负值信息

复制代码
# x方向上的边缘,包含负值信息(直接显示不出来,因为范围是0~255)
img_x_64 = cv2.Sobel(img, cv2.CV_64F, dx=1, dy=0)  # 默认uint8改为float64, 可保存负数
cv2.imshow('img_x_64', img_x_64)
cv2.waitKey(0)
  • cv2.CV_64F 可保存浮点数,包括正负值。

  • 因为梯度可能是正数(亮到暗的变化)或负数(暗到亮的变化)。

  • 但直接显示会出现"黑屏",因为显示范围是 0~255


(4)转换为可视化图像

复制代码
# x方向上的边缘,将负数的数值信息转成可视化的数值,右端的负值信息就能显示出来了
img_x_full = cv2.convertScaleAbs(img_x_64)  # 转换为绝对值,负数转换为正数
cv2.imshow('img_x_full', img_x_full)
cv2.waitKey(0)
  • 将梯度的负值转换为绝对值,保证显示效果。

  • 这样左右边界都能显示出来。


(5)y方向的边缘

复制代码
# y方向的边缘
img_y = cv2.Sobel(img, -1, dx=0, dy=1)
cv2.imshow('img_y', img_y)
cv2.waitKey(0)

# y方向上的边缘,包括负数信息(下端),但是示不出来,因为范围是(0~255)
img_y_64= cv2.Sobel(img,cv2.CV_64F,dx=0,dy=1)#默认int8改为float64,可保存负数
img_y_full = cv2.convertScaleAbs(img_y_64)#转换为绝对值,负数转换为正数
cv2.imshow( 'img_y_full',img_y_full)
cv2.waitKey(0)
  • dx=0, dy=1,表示只计算垂直方向的梯度。

  • 能突出 水平边缘(例如物体上下边界)。

同样地,可以通过 cv2.CV_64F + cv2.convertScaleAbs 来显示完整信息。


(6)直接同时计算 x 和 y

复制代码
# 同时使用x与y方向的边缘(不建议直接使用!!)
img_xy = cv2.Sobel(img, -1, dx=1, dy=1)
cv2.imshow('img_xy', img_xy)
cv2.waitKey(0)
  • 这种方式会将 x 和 y 梯度直接结合,但通常效果不佳,不推荐。

(7)分别计算x和y后,加权融合

复制代码
# 使用图像加权运算组合x和y方向的2个边缘
img_xy_full = cv2.addWeighted(img_x_full,1,img_y_full,1,0)
cv2.imshow('img_xy_full', img_xy_full)
cv2.waitKey(0)
cv2.destroyAllWindows()
  • 更合理的方式是把 x方向y方向 的梯度结果加权融合。

  • addWeighted 会线性叠加两张图像。

  • 得到的结果类似于常见的 梯度幅值图像,即完整的边缘检测效果。


5. 总结

  1. Sobel 算子利用梯度变化检测边缘,能提取水平与垂直方向上的特征。

  2. 使用 cv2.CV_64F 保留负值信息,再通过 cv2.convertScaleAbs 转换为可视化结果。

  3. x方向突出竖直边缘,y方向突出水平边缘。

  4. 最佳实践是分别提取 x、y 梯度,然后再融合,得到更清晰的边缘图像。


📌 扩展 :除了 Sobel 算子,常用的边缘检测方法还有 Prewitt 算子、Laplacian 算子、Canny 算子,各有优劣,Canny 是实际应用最广的。