基于阈值的图像分割是图像处理中最基础、最常用的分割方法。其核心思想是:根据像素灰度值与一个或多个阈值的比较,将图像划分为不同的区域(目标与背景)。OpenCV 提供了非常丰富的阈值化函数,能覆盖绝大多数应用场景。
下面我将从全局阈值、自适应阈值、Otsu 最佳阈值以及彩色图像阈值几个方面,配合代码示例,详细讲解如何在 OpenCV 中实现基于阈值的分割。
确保已安装 OpenCV(推荐 opencv-python):
pip install opencv-python
引入必要库:
import cv2
import numpy as np
import matplotlib.pyplot as plt
cv2.threshold()这是最简单的阈值分割函数,对所有像素使用同一个固定阈值。
函数原型:
retval, dst = cv2.threshold(src, thresh, maxval, type)
src:输入图像,必须是单通道灰度图。
thresh:设定的阈值。
maxval:当像素值满足条件时赋予的新值(常为 255)。
type:阈值类型,决定比较和赋值的方式。
常用阈值类型:
| 类型 | 说明 |
|---|---|
cv2.THRESH_BINARY |
大于阈值取 maxval,否则取 0 |
cv2.THRESH_BINARY_INV |
大于阈值取 0,否则取 maxval(黑白反转) |
cv2.THRESH_TRUNC |
大于阈值的像素值设为阈值,其余不变 |
cv2.THRESH_TOZERO |
小于阈值的像素值设为 0,其余不变 |
cv2.THRESH_TOZERO_INV |
大于阈值的像素值设为 0,其余不变 |
cv2.adaptiveThreshold()对于光照不均的图像(如文档扫描、阴影影响),全局阈值效果很差。自适应阈值根据像素邻域的局部特征动态计算阈值,能有效分离前景。
函数原型:
dst = cv2.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C)
adaptiveMethod:局部阈值的计算方法
– cv2.ADAPTIVE_THRESH_MEAN_C:邻域均值减去常量 C
– cv2.ADAPTIVE_THRESH_GAUSSIAN_C:邻域加权均值(高斯窗)减去常量 C
thresholdType:只能是 cv2.THRESH_BINARY 或 cv2.THRESH_BINARY_INV
blockSize:邻域大小,必须为奇数(如 11、21)
C:从均值中减去的常数,用于精细调整
当不清楚具体阈值时,Otsu 算法可以自动寻找一个最佳阈值,使前景与背景的类间方差最大。
使用方式:在 cv2.threshold() 中 type 参数叠加 cv2.THRESH_OTSU。
ret, otsu = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
print("最佳阈值:", ret)
注意:此时 thresh 参数可填任意值(通常写 0),实际阈值由算法算出并返回。
原理:遍历所有可能的阈值,计算前景和背景的像素数、平均灰度,使类间方差最大。对直方图呈双峰分布的图像效果极好。
cv2.inRange()如果目标具有特定的颜色范围(例如提取红色物体),可以在 HSV 色彩空间使用 cv2.inRange() 进行阈值分割。
步骤:
将 BGR 图转为 HSV。
定义目标颜色的 HSV 上下界(数组)。
调用 cv2.inRange() 生成二值掩膜。
预处理:阈值分割前常需要平滑(去噪)或形态学操作(开/闭运算),以减少噪声干扰。
动态阈值:若图像整体亮度缓慢变化,可先做顶帽变换(Top-hat)或使用光照补偿后再阈值化。
多阈值分割:OpenCV 没有直接的多阈值函数,但可以结合 Otsu 手动实现,或使用 cv2.threshold() 多次筛选。
浮点图像:对于深度图或热图,阈值值域不同,注意 maxval 的设置。
# 下面将上述方法综合,做一个简单的图像二值化对比:
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('./images/key.png', 0)
# img = cv2.resize(img, (0, 0), fx=0.5, fy=0.5)
img = cv2.GaussianBlur(img, (5, 5), 0) # 先去噪
# 全局固定阈值
_, th1 = cv2.threshold(img, 140, 255, cv2.THRESH_BINARY)
# Otsu
_, th2 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# 自适应均值
th3 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY, 11, 2)
# 自适应高斯
th4 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 11, 2)
titles = ['Original', 'Global (t=140)', 'Otsu', 'Adaptive Mean', 'Adaptive Gaussian']
images = [img, th1, th2, th3, th4]
for i in range(5):
plt.subplot(2,3,i+1), plt.imshow(images[i], 'gray')
plt.title(titles[i]), plt.xticks([]), plt.yticks([])
plt.show()
效果图展示如下:


全部评论