OpenCV基于阈值的图像分割

18人浏览 / 0人评论 / 添加收藏

基于阈值的图像分割是图像处理中最基础、最常用的分割方法。其核心思想是:根据像素灰度值与一个或多个阈值的比较,将图像划分为不同的区域(目标与背景)。OpenCV 提供了非常丰富的阈值化函数,能覆盖绝大多数应用场景。

下面我将从全局阈值自适应阈值Otsu 最佳阈值以及彩色图像阈值几个方面,配合代码示例,详细讲解如何在 OpenCV 中实现基于阈值的分割。

1. 环境准备

确保已安装 OpenCV(推荐 opencv-python):

pip install opencv-python

引入必要库:

import cv2
import numpy as np
import matplotlib.pyplot as plt

2. 全局固定阈值: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,其余不变

3. 自适应阈值: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_BINARYcv2.THRESH_BINARY_INV

blockSize:邻域大小,必须为奇数(如 11、21)

C:从均值中减去的常数,用于精细调整

4. Otsu 最佳阈值(大津法)

当不清楚具体阈值时,Otsu 算法可以自动寻找一个最佳阈值,使前景与背景的类间方差最大。

使用方式:在 cv2.threshold()type 参数叠加 cv2.THRESH_OTSU

ret, otsu = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
print("最佳阈值:", ret)

注意:此时 thresh 参数可填任意值(通常写 0),实际阈值由算法算出并返回。

原理:遍历所有可能的阈值,计算前景和背景的像素数、平均灰度,使类间方差最大。对直方图呈双峰分布的图像效果极好。

5. 彩色图像的分割:cv2.inRange()

如果目标具有特定的颜色范围(例如提取红色物体),可以在 HSV 色彩空间使用 cv2.inRange() 进行阈值分割。

步骤

将 BGR 图转为 HSV。

定义目标颜色的 HSV 上下界(数组)。

调用 cv2.inRange() 生成二值掩膜。

6. 常用技巧与注意事项

预处理:阈值分割前常需要平滑(去噪)或形态学操作(开/闭运算),以减少噪声干扰。

动态阈值:若图像整体亮度缓慢变化,可先做顶帽变换(Top-hat)或使用光照补偿后再阈值化。

多阈值分割:OpenCV 没有直接的多阈值函数,但可以结合 Otsu 手动实现,或使用 cv2.threshold() 多次筛选。

浮点图像:对于深度图或热图,阈值值域不同,注意 maxval 的设置。

7. 完整实例:

# 下面将上述方法综合,做一个简单的图像二值化对比:
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()

效果图展示如下:

全部评论