Otsu algorithm
大津法是由日本学者大津展之(Nobuyuki Otsu)于1979年提出的,一种基于类间方差最大化的自动阈值选择方法。
一、核心原理
其基本思想是将图像分为前景和背景两部分,通过寻找一个阈值,使得这两部分的类间方差最大。类间方差越大,说明两部分之间的差异越大,分割效果越好。这种方法在图像直方图呈现双峰时效果最好,因为此时前景和背景的像素分布较为明显。
二、计算步骤
Otsu 是一种基于类间方差最大化的自动阈值选择方法,步骤如下:
-
计算直方图:统计图像中每个灰度级的像素数量。
-
遍历阈值:尝试所有可能的阈值 t(0 到 255)。
-
计算类间方差:将图像分为前景(灰度 ≤ t)和背景(灰度 > t),计算两者的类间方差:
σ 2 = ω 0 ω 1 ( μ 0 − μ 1 ) 2 \sigma^2 = \omega_0\omega_1{(\mu_0 - \mu_1)}^2 σ2=ω0ω1(μ0−μ1)2
其中 ω0,ω1 为前景/背景像素数占图像总像素数比例,μ0,μ1 为前景/背景像素平均灰度。-
全局平均灰度
平均灰度是所有像素值的算术平均值。
μ 全局 = 1 N ∑ i = 0 255 i × h ( i ) \mu_{全局} = \frac{1}{N}\sum_{i=0}^{255}i×h(i) μ全局=N1i=0∑255i×h(i)
N:图像总像素数。
h(i):灰度级 i 的像素数。 -
前景平均灰度(像素值 ≤ t)
μ 0 = 1 N 0 ∑ i = 0 t i × h ( i ) \mu_0 = \frac{1}{N_0}\sum_{i=0}^{t}i×h(i) μ0=N01i=0∑ti×h(i)
N 0 = ∑ i = 0 t h ( i ) N_0 = \sum_{i=0}^{t}h(i) N0=∑i=0th(i):前景像素总数。 -
背景平均灰度(像素值 > t)
μ 1 = 1 N 1 ∑ i = t + 1 255 i × h ( i ) \mu_1 = \frac{1}{N_1}\sum_{i=t+1}^{255}i×h(i) μ1=N11i=t+1∑255i×h(i)
N 1 = ∑ i = t + 1 255 h ( i ) N_1 = \sum_{i=t+1}^{255}h(i) N1=∑i=t+1255h(i):背景像素总数。
-
-
选择最佳阈值:使类间方差最大的 t 即为最佳阈值。
三、 适用场景与限制
-
适用场景:图像直方图呈现双峰分布(前景和背景对比明显)。
-
优势:无需手动设定阈值,自适应性强;时间复杂度为 O(L)(L 为灰度级数,通常为256),适用于实时处理。
-
限制:对噪声敏感,图像噪声会破坏直方图的双峰特性,导致阈值偏移;光照不均时效果差,需配合预处理(如 CLAHE)或使用自适应阈值(如 cv2.adaptiveThreshold)提升效果。
四、OpenCV中的函数
retval, dst = cv2.threshold(
src, # 输入图像(必须为灰度图)
thresh, # 手动设定阈值(当使用 Otsu 时此值被忽略)
maxval, # 超过阈值后赋予的像素值
cv2.THRESH_BINARY + cv2.THRESH_OTSU # 阈值化类型(决定如何处理像素值)
)
- 返回值
retval
- 由 Otsu 算法自动计算出的最佳阈值(类型为
float
)
- 由 Otsu 算法自动计算出的最佳阈值(类型为
dst
- 阈值化后的二值图像(与输入同尺寸,像素值为 0 或 255)
-
cv2.THRESH_BINARY + cv2.THRESH_OTSU
cv2.THRESH_BINARY
的作用- 定义二值化的基本规则:像素值= { 255 i f 像素 > 阈值 0 o t h e r w i s e \begin{cases} 255 & if\quad像素>阈值 \\ 0 & otherwise \end{cases} {2550if像素>阈值otherwise
- 单独使用时会要求手动指定阈值(例如
thresh=127
)。
cv2.THRESH_OTSU
的作用- 自动计算图像的最佳阈值(基于最大化类间方差),但本身不定义如何应用阈值。
- 必须与基础阈值类型(如
BINARY
)组合使用,才能完成完整的二值化操作。
- 组合的底层逻辑——OpenCV 的标志位设计
- 阈值类型被编码为位掩码(bitmask),例如:
cv2.THRESH_BINARY
=0
(二进制0000
)cv2.THRESH_OTSU
=8
(二进制1000
)
- 使用
+
或|
组合这些标志,OpenCV 可解析为同时启用两种行为。
- 阈值类型被编码为位掩码(bitmask),例如:
五、示例
-
原图
-
代码
import cv2 import matplotlib.pyplot as plt # 读取彩色图像并转换为灰度图 img = cv2.imread('tree.png') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 应用Otsu阈值分割 threshold, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # 创建画布和子图布局 plt.figure(figsize=(15, 6)) # 绘制灰度直方图 plt.subplot(1, 2, 1) plt.hist(gray.ravel(), bins=256, range=(0, 256), color='gray', alpha=0.7) plt.axvline(x=threshold, color='red', linestyle='--', linewidth=2, label=f'Threshold = {int(threshold)}') plt.title('Grayscale Histogram with Otsu Threshold') plt.xlabel('Pixel Intensity') plt.ylabel('Frequency') plt.legend() # 绘制二值化图像 plt.subplot(1, 2, 2) plt.imshow(binary, cmap='gray') plt.title('Binary Image after Otsu Thresholding') plt.axis('off') # 显示结果 plt.tight_layout() plt.show()
-
效果图
我是终须有梦,如有错误,欢迎指正!