在机器视觉开发中,阈值分割(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)>threshdst(x,y)=maxvalsrc(x,y)threshdst(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)>threshdst(x,y)=0src(x,y)threshdst(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)>threshdst(x,y)=threshsrc(x,y)threshdst(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)>threshdst(x,y)=src(x,y)src(x,y)threshdst(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)>threshdst(x,y)=0src(x,y)threshdst(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")

七、总结

尽管阈值分割是图像处理中最基础的技术,但在工业视觉领域仍占据着不可替代的地位。

  • 优势: 算法简单、计算速度快、实时性强。
  • 适用场景: 背景稳定、目标与背景灰度差异明显的场景(如标签检测、字符定位、简单缺陷检测)。
  • 最佳实践: 在实际项目中,阈值分割很少单独使用。它通常作为预处理步骤,与形态学处理(去噪)、轮廓分析(特征提取)以及模板匹配等算法结合,构建鲁棒性更强的视觉检测流水线。
Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐