Otsu 阈值算法的简单实现
1. 什么是 Otsu 阈值算法
Otsu 阈值算法是一种自适应阈值分割算法,用于将图像分割为前景和背景。核心思想是通过最大化类间方差来确定最佳阈值。
Otsu 阈值算法的基本思想是:通过计算图像的灰度直方图,找到一个阈值,使得前景和背景的类间方差最大。
2. 算法步骤
- 计算图像的灰度直方图:
- 图像的像素值被划分为若干个灰度级,计算每个灰度级的像素数。
- 计算每个灰度级的像素数占总像素数的比例。
- 计算前景和背景的类间方差
- 对每个可能的阈值,将图像的像素值划分为前景和背景。
- 计算这两部分的灰度均值、像素比例和类间方差。
- 寻找使类间方差最大的阈值。
3. 数学表达
- 假设总像素数为 \(N\),灰度值的范围为 \([0, L-1]\),灰度级 \(k\) 的像素数为 \(n_k\),灰度级 \(k\) 的像素数占总像素数的比例为 \(p_k\)。
- 对于x给定的阈值\(t\),灰度值被分为两类:
- 前景类\(C_1\),灰度值在\([0, t]\)之间
- 背景类\(C_2\),灰度值在\([t+1, L-1]\)之间
- 前景类的像素数比例:\(w_1 = \sum_{i=0}^{t} p_i\)
- 背景类的像素数比例:\(w_2 = \sum_{i=t+1}^{L-1} p_i\)
- 前景类的灰度均值:\(u_1 = \sum_{i=0}^{t} i p_i / w_1\)
- 背景类的灰度均值:\(u_2 = \sum_{i=t+1}^{L-1} i p_i / w_2\)
- 类间方差:\(\sigma_b^2 = w_1 w_2 (u_1 - u_2)^2\)
4. 代码实现
简洁实现,使用opencv计算灰度直方图
def otsu_threshold(image): # 计算灰度直方图 hist = cv2.calcHist([image], [0], None, [256], [0, 256]) # 计算灰度直方图 hist = hist.ravel() / hist.sum() # 计算灰度直方图的概率 # 计算前景和背景的类间方差 max_var = 0 best_threshold = 0 for threshold in range(256): w1 = np.sum(hist[:threshold]) w2 = np.sum(hist[threshold+1:]) u1 = np.sum(hist[:threshold] * np.arange(256)) / w1 u2 = np.sum(hist[threshold+1:] * np.arange(threshold+1, 256)) / w2 var = w1 * w2 * (u1 - u2) ** 2 if var > max_var: max_var = var best_threshold = threshold return best_threshold
一般数据的分割
def _otsu_threshold(data): hist, bins = np.histogram(data, bins=50) # 可以指定bins的数量t bin_centers = (bins[:-1] + bins[1:]) / 2 # OTSU算法实现 total = hist.sum() sum_total = sum(bin_centers * hist) max_variance = 0 threshold = 0 sum_b = 0 count_b = 0 for i in range(len(hist)): count_b += hist[i] if count_b == 0: continue count_f = total - count_b if count_f == 0: break sum_b += bin_centers[i] * hist[i] mean_b = sum_b / count_b mean_f = (sum_total - sum_b) / count_f variance = count_b * count_f * (mean_b - mean_f) ** 2 if variance > max_variance: max_variance = variance threshold = bin_centers[i] return threshold