Python OpenCV 图像处理:阈值分割详解与实战
在机器视觉开发中,阈值分割(Thresholding)是最基础且应用最广泛的图像处理方法之一。它能够快速将目标区域与背景分离,是字符识别、缺陷检测、尺寸测量等场景中的核心预处理步骤。
本文将深入解析阈值分割的原理,介绍 OpenCV 中常见的几种阈值类型,并提供完整的 Python 代码示例。
一、什么是阈值分割?
阈值分割的核心原理非常直观:对于图像中的每一个像素,根据其灰度值与预设阈值的关系,将其映射为新的灰度值。
基本规则:
- 若像素灰度值 大于 阈值 → 设为白色(255);
- 否则 → 设为黑色(0)。
原理示例
假设原始灰度图像如下:
20 60 120
90 180 250
30 160 210
设定阈值 thresh = 100,处理后得到的二值图像为:
0 0 255
0 255 255
0 255 255
通过这种方式,我们可以得到一幅黑白分明的二值图像,从而简化后续的特征提取工作。
二、固定阈值分割
OpenCV 提供了 cv2.threshold() 函数用于执行固定阈值分割。
函数定义
retval, dst = cv2.threshold(src, thresh, maxval, type)
参数说明
| 参数名 | 描述 |
|---|---|
src | 输入图像,必须为单通道灰度图。 |
thresh | 阈值大小。 |
maxval | 满足条件时赋予像素的最大值(通常设为 255)。 |
type | 阈值类型,决定具体的映射规则。 |
基础代码示例
import cv2
# 读取图片并转为灰度图
img = cv2.imread("test.png", 0)
# 执行固定阈值分割
# 灰度 > 120 的像素变为 255,其余变为 0
_, binary = cv2.threshold(img, 120, 255, cv2.THRESH_BINARY)
cv2.imshow("Binary Image", binary)
cv2.waitKey(0)
cv2.destroyAllWindows()
三、常见阈值类型详解
OpenCV 主要支持五种阈值类型,适用于不同的应用场景:
1. cv2.THRESH_BINARY(二值化)
规则:
src(x,y)>thresh→dst(x,y)=maxvalsrc(x,y)≤thresh→dst(x,y)=0 src(x,y) > thresh \rightarrow dst(x,y) = maxval \\ src(x,y) \leq thresh \rightarrow dst(x,y) = 0 src(x,y)>thresh→dst(x,y)=maxvalsrc(x,y)≤thresh→dst(x,y)=0
适用场景: 提取亮色目标。
2. cv2.THRESH_BINARY_INV(反二值化)
规则:
src(x,y)>thresh→dst(x,y)=0src(x,y)≤thresh→dst(x,y)=maxval src(x,y) > thresh \rightarrow dst(x,y) = 0 \\ src(x,y) \leq thresh \rightarrow dst(x,y) = maxval src(x,y)>thresh→dst(x,y)=0src(x,y)≤thresh→dst(x,y)=maxval
适用场景: 提取暗色目标(如黑字白底)。
3. cv2.THRESH_TRUNC(截断/截尾)
规则:
src(x,y)>thresh→dst(x,y)=threshsrc(x,y)≤thresh→dst(x,y)=src(x,y) src(x,y) > thresh \rightarrow dst(x,y) = thresh \\ src(x,y) \leq thresh \rightarrow dst(x,y) = src(x,y) src(x,y)>thresh→dst(x,y)=threshsrc(x,y)≤thresh→dst(x,y)=src(x,y)
效果: 将所有高于阈值的像素“截断”至阈值水平,低于阈值的保持不变。
示例:
- 原图:
[50, 100, 180, 220](阈值=120) - 结果:
[50, 100, 120, 120]
4. cv2.THRESH_TOZERO(零阈值)
规则:
src(x,y)>thresh→dst(x,y)=src(x,y)src(x,y)≤thresh→dst(x,y)=0 src(x,y) > thresh \rightarrow dst(x,y) = src(x,y) \\ src(x,y) \leq thresh \rightarrow dst(x,y) = 0 src(x,y)>thresh→dst(x,y)=src(x,y)src(x,y)≤thresh→dst(x,y)=0
效果: 保留高亮部分,将低亮度部分置黑。
5. cv2.THRESH_TOZERO_INV(反零阈值)
规则:
src(x,y)>thresh→dst(x,y)=0src(x,y)≤thresh→dst(x,y)=src(x,y) src(x,y) > thresh \rightarrow dst(x,y) = 0 \\ src(x,y) \leq thresh \rightarrow dst(x,y) = src(x,y) src(x,y)>thresh→dst(x,y)=0src(x,y)≤thresh→dst(x,y)=src(x,y)
效果: 保留低亮度部分,将高亮度部分置黑。
四、实际案例:提取白色目标
在工业视觉中,除了使用 cv2.threshold(),我们更常使用 cv2.inRange() 进行范围筛选。这种方法在处理颜色或特定灰度区间时更加灵活。
代码示例
import cv2
# 读取彩色图像
img = cv2.imread("label.png")
# 转换为灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 使用 inRange 提取灰度值在 [120, 255] 之间的像素
# 这本质上也是一种基于范围的阈值分割
mask = cv2.inRange(gray, 120, 255)
cv2.imshow("Mask", mask)
cv2.waitKey(0)
cv2.destroyAllWindows()
注意:
cv2.inRange返回的是掩膜(Mask),其中符合条件的像素为 255,不符合的为 0。它在处理非均匀光照或颜色分割时往往比简单的全局阈值更有效。
五、寻找目标轮廓
阈值分割完成后,通常需要进行轮廓提取以定位目标位置。
核心函数
contours, hierarchy = cv2.findContours(
image, # 二值图像或掩膜
mode, # 轮廓检索模式
method # 轮廓近似方法
)
绘制轮廓
# 复制原图用于绘制
draw = img.copy()
# 查找外部轮廓
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 绘制所有轮廓(绿色,线宽2)
cv2.drawContours(draw, contours, -1, (0, 255, 0), 2)
cv2.imshow("Result", draw)
cv2.waitKey(0)
cv2.destroyAllWindows()
后续分析指标
获得轮廓后,可以进一步计算以下几何特征,广泛应用于质量检测:
- 面积 (Area): 判断目标大小。
- 长宽比 (Aspect Ratio): 筛选形状异常的目标。
- 外接矩形 (Bounding Rect): 获取目标边界框。
- 圆度 (Circularity): 判断是否为圆形缺陷。
- 中心点 & 旋转角度: 用于定位和对齐。
六、完整实战示例
以下代码整合了从读取、预处理、阈值分割到轮廓检测及可视化的完整流程,并增加了面积过滤以去除噪声。
import cv2
def process_image(image_path):
# 1. 读取图片
img = cv2.imread(image_path)
if img is None:
print("Error: Could not read image.")
return
# 2. 转换为灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 3. 阈值分割 (提取高亮区域)
# 这里使用 inRange 提取灰度值 >= 120 的部分
mask = cv2.inRange(gray, 120, 255)
# 4. 查找轮廓
# RETR_EXTERNAL: 只检测最外层轮廓
# CHAIN_APPROX_SIMPLE: 压缩水平和垂直分量,只保留端点
contours, _ = cv2.findContours(
mask,
cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE
)
# 5. 处理并绘制有效轮廓
result = img.copy()
for cnt in contours:
# 计算轮廓面积
area = cv2.contourArea(cnt)
# 过滤掉面积过小的噪声点
if area < 500:
continue
# 获取外接矩形
x, y, w, h = cv2.boundingRect(cnt)
# 在原图上绘制矩形框
cv2.rectangle(result, (x, y), (x + w, y + h), (0, 255, 0), 2)
# 6. 显示结果
cv2.imshow("Original Mask", mask)
cv2.imshow("Detection Result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 运行示例
if __name__ == "__main__":
process_image("test.png")
七、总结
尽管阈值分割是图像处理中最基础的技术,但在工业视觉领域仍占据着不可替代的地位。
- 优势: 算法简单、计算速度快、实时性强。
- 适用场景: 背景稳定、目标与背景灰度差异明显的场景(如标签检测、字符定位、简单缺陷检测)。
- 最佳实践: 在实际项目中,阈值分割很少单独使用。它通常作为预处理步骤,与形态学处理(去噪)、轮廓分析(特征提取)以及模板匹配等算法结合,构建鲁棒性更强的视觉检测流水线。
更多推荐



所有评论(0)