type
status
date
slug
summary
tags
category
icon
password
一、图像梯度:理解边缘的基础
图像的边缘通常是像素值发生剧烈变化的地方。在数学中,我们用导数来描述函数的变化率;在图像处理中,这个概念就成了“梯度”。通过计算图像梯度,我们可以量化像素值的变化程度,从而定位边缘。
1.1 Sobel 算子:定向边缘检测
Sobel 算子是一种常用的一阶微分算子,它通过两个特定的卷积核分别计算图像在水平(X)和垂直(Y)方向上的梯度。
- 水平梯度:通过卷积核计算,用于检测垂直边缘。
- 垂直梯度:通过卷积核(即的转置) 计算,用于检测水平边缘。
最终的梯度幅值可以综合两个方向的结果得到:
OpenCV 提供了
cv2.Sobel()
函数来方便地实现这一过程。- 函数原型:
cv2.Sobel(src, ddepth, dx, dy, ksize)
src
: 输入图像。ddepth
: 输出图像的深度,通常设为1
表示与输入图像相同。dx
,dy
: 分别表示在 x 和 y 方向上的求导阶数。例如,dx=1, dy=0
表示计算水平梯度(检测垂直边缘)。ksize
: Sobel 核的大小,如 3, 5, 7。
1.2 Laplacian 算子:二阶导数边缘检测
与一阶导数在极值点取得最大值不同,二阶导数在极值点(即边缘)处的值为零。Laplacian 算子就是基于二阶导数来检测边缘的。它对噪声更敏感,但能检测到更精细的边缘。
其卷积核通常为:
- 函数原型:
cv2.Laplacian(src, ddepth)
- 参数相对简单,只需提供输入图像和输出深度。
1.3 自定义卷积核:cv2.filter2D
cv2.filter2D
是一个通用的二维卷积函数,我们可以用它来实现包括 Sobel 和 Laplacian 在内的任何卷积操作,只需定义自己的卷积核即可。这为我们提供了极大的灵活性。示例:使用 Numpy 数组模拟卷积
为了直观理解卷积过程,我们先用一个简单的 Numpy 数组模拟图像,并应用一个垂直边缘检测核。
示例:在真实图像上应用自定义核
下面是在真实图像上应用垂直和水平边缘检测核的例子。
二、Canny 边缘检测:更优的边缘提取方案
Canny 边缘检测不是一个单一的算子,而是一套完整的多阶段算法,被公认为目前效果最优的边缘检测算法之一。它能有效抑制噪声,并生成细化的、单像素宽的边缘。
其主要步骤如下:
- 高斯滤波:使用高斯核平滑图像,去除噪声。
- 计算梯度:使用 Sobel 算子计算图像的梯度幅值和方向。
- 非极大值抑制:沿着梯度方向,检查像素邻域,只保留梯度值最大的像素,从而将模糊的边缘“瘦身”成单像素的细线。
- 双阈值筛选:
- 设置一个高阈值(
threshold2
)和一个低阈值(threshold1
)。 - 梯度值高于高阈值的像素被确定为“强边缘”。
- 梯度值低于低阈值的像素被舍弃。
- 梯度值介于两者之间的像素,只有当它与强边缘像素相连时才被保留,否则舍弃。
5.
cv2.Canny(image, threshold1, threshold2)
:canny边缘检测image
:输入的灰度/二值化图像数据。
threshold1
:低阈值,用于决定可能的边缘点。
threshold2
:高阈值,用于决定强边缘点。
三、轮廓发现与绘制
边缘是像素层面的变化,而轮廓则是更高层次的抽象,它是一系列相连的点构成的曲线,代表了物体的完整外形。寻找轮廓通常基于二值化图像,算法会在白色物体和黑色背景的边界上寻找连续的像素点。
3.1 查找与绘制轮廓
OpenCV 提供了两个核心函数来处理轮廓:
cv2.findContours(image, mode, method)
:查找轮廓。image
: 输入的二值图像。mode
: 轮廓检索模式。cv2.RETR_EXTERNAL
只查找最外层轮廓,非常常用。method
: 轮廓近似方法。cv2.CHAIN_APPROX_SIMPLE
会压缩轮廓,例如只存储矩形的四个顶点,节省内存。- 它返回轮廓点列表 (
contours
) 和层级信息 (hierarchy
)。
cv2.drawContours(image, contours, contourIdx, color, thickness)
:绘制轮廓。image
: 要在其上绘制轮廓的原图。contours
:findContours
返回的轮廓列表。contourIdx
: 要绘制的轮廓索引,1
表示绘制所有轮廓。color
: 轮廓颜色 (BGR格式)。thickness
: 线条粗细。
四、轮廓特征分析
找到轮廓后,我们可以进一步分析它们的几何特征,这对于物体识别和形状分析至关重要。
4.1 凸包(Convex Hull)
凸包可以想象成用一根橡皮筋包裹住物体所有轮廓点所形成的最小凸多边形。它描述了物体的整体外边界。
cv2.convexHull(points)
: 计算轮廓的凸包点。
cv2.polylines(image, pts, isClosed, color, thickness)
: 绘制多边形,可以用来绘制凸包。image
:要绘制线条的目标图像,它应该是一个OpenCV格式的二维图像数组(如numpy数组)。pts
:一个二维 numpy 数组,每个元素是一维数组,代表一个多边形的一系列顶点坐标。isClosed
:布尔值,表示是否闭合多边形,如果为 True,会在最后一个顶点和第一个顶点间自动添加一条线段,形成封闭的多边形。color
:线条颜色,可以是一个三元组或四元组,分别对应BGR或BGRA通道的颜色值,或者是灰度图像的一个整数值。thickness
(可选):线条宽度,默认值为1。
4.2 外接矩形
外接矩形是描述轮廓范围的常用特征。
4.2.1 标准外接矩形
这是最简单的外接矩形,它的边与坐标轴平行,能够完全包围轮廓。
cv2.boundingRect(contour)
: 计算并返回矩形的左上角坐标(x, y)
和宽高(w, h)
。
cv.rectangle(img, pt1, pt2, color, thickness)
:绘制矩形img
:要绘制矩形的目标图像,通常是一个 OpenCV 格式的二维或三维 numpy 数组。pt1
:矩形的左上角顶点坐标,格式为 (x, y)。pt2
:矩形的右下角顶点坐标,格式为 (x, y)。color
:矩形边框的颜色,BGR 格式的三元组(如 (0, 255, 0) 表示绿色)。thickness
(可选):矩形边框的线宽,默认为 1。若为负值,则填充矩形。
4.2.2 最小外接矩形
与标准外接矩形不同,最小外接矩形会考虑旋转,以最小的面积包裹住轮廓。这对于分析倾斜物体的方向和尺寸非常有用。
cv2.minAreaRect(contour)
: 计算最小外接矩形,返回中心点坐标、宽高和旋转角度。
cv2.boxPoints(rect)
: 根据minAreaRect
的返回值计算出矩形的四个顶点坐标。
4.2.3 最小外接圆
简单的最小外接圆,包含图像的所有点。
cv2.minEnclosingCircle(points)
:计算最小外接圆,返回圆心坐标、半径
cv2.circle(img, center, radius, color, thickness)
:绘制圆img
:输入图像,通常是一个numpy数组,代表要绘制圆形的图像。center
:一个二元组(x, y)
,表示圆心的坐标位置。radius
:整型或浮点型数值,表示圆的半径长度。color
:颜色标识,可以是BGR格式的三元组(B, G, R)
,例如(255, 0, 0)
表示红色。thickness
:整数,表示圆边框的宽度。如果设置为-1
,则会填充整个圆。
五、绘制直方图
就是以像素值为横坐标,像素值的个数为纵坐标绘制一个统计图。
hist=cv2.calcHist(images, channels, mask, histSize, ranges)
:是一个长度为255的数组,数组中的每个值表示图像中对应灰度等级的像素计数。images
:输入图像列表,可以是一幅或多幅图像(通常是灰度图像或者彩色图像的各个通道),例如:[image]
channels
:一个包含整数的列表,指示在每个图像上计算直方图的通道编号。如果输入图像是灰度图,它的值就是 [0];如果是彩色图像的话,传入的参数可以是 [0],[1],[2] 它们分别对应着通道 B,G,R。mask
(可选):一个与输入图像尺寸相同的二值掩模图像,其中非零元素标记了参与直方图计算的区域,None为全部计算。histSize
:一个整数列表,也就是直方图的区间个数(BIN 的数目)。用中括号括起来,例如:[256]
。ranges
:每维数据的取值范围,它是一个二维列表,每一维对应一个通道的最小值和最大值,例如对灰度图像可能是[0, 256]
。
minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(hist)
:获取直方图的最小值、最大值及其对应最小值的位置索引、最大值的位置索引
cv.normalize(src, dst, alpha, beta, norm_type, dtype)
:归一化到[alpha, beta]src
:输入数组(如直方图数据),通常为一个 numpy 数组。dst
:输出数组。如果为 None,则函数会自动创建一个与输入同类型和大小的数组。alpha
:归一化后数组的下界(最小值)。beta
:归一化后数组的上界(最大值)。norm_type
:归一化类型,常用如 cv.NORM_MINMAX,表示将数据线性缩放到[alpha, beta]
区间。dtype
(可选):输出数组的数据类型,默认与输入相同。
cv2.line(img, pt1, pt2, color, thickness)
:绘制直方图img
:原始图像,即要在上面画线的numpy数组(一般为uint8类型)。pt1
和pt2
:分别为线段的起点和终点坐标,它们都是元组类型,例如(x1, y1)
和(x2, y2)
分别代表线段两端的横纵坐标。color
:线段的颜色,通常是一个包含三个元素的元组(B, G, R)
表示BGR色彩空间的像素值,也可以是灰度图像的一个整数值。thickness
:线段的宽度,默认值是1,如果设置为负数,则线宽会被填充。
六、总结与扩展阅读
本次学习,我们从像素级的梯度出发,理解了边缘的本质;接着掌握了强大Canny 边缘检测算法;然后,我们从点和线跃升到对“形状”的认知,学会了如何查找和绘制轮廓;并在此基础上,提取了轮廓的多种几何特征(凸包、外接矩形、外接圆);最后,我们用直方图从统计学的角度审视了整幅图像。
这个流程体现了计算机视觉中一个经典的处理范式:从低级特征(像素、边缘)到中级特征(轮廓、形状),为最终的高级理解(物体识别)打下坚实基础。
推荐扩展阅读:
- OpenCV 官方教程:。
- 作者:sisui
- 链接:https://www.sisui.me//article/py-opencv-image-analysis-from-edge-to-histogram
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
相关文章