对于给定的阈值\(T\),可以将图像分为目标和背景。其中背景点数占图像比例为 \(p_0\),平均灰度值为 \(m_0\)。而目标点数占图像比例为 \(p_1\),平均灰度值为 \(m_1\),其中满足

\[p_0 + p_1 = 1 \]

整幅图像的平均灰度值为常数,跟阈值无关,且为

\[\overline m = p_0m_0 + p_1m_1 \]

类间方差为

\[\sigma^2 = p_0(m_0 - \overline m)^2 + p_1(m_1 - \overline m)^2 \]

代入 \(p_0 + p_1 = 1\)\(\overline m\),可化简为

\[\sigma^2 = p_0p_1(m_0 - m_1)^2 \]

遍历灰度值,找出能使 \(\sigma^2\) 最大的值。

大津法步骤

(1) 初始化方差为 \(S = -1\)
(2) 对灰度值为 \(0~255\) 的整数的图像, 遍历每个灰度值 \(T\),每次做第(3)步操作;
(3) 计算以 \(T\) 为阈值时候的类间方差,如果大于 \(S\),更新 \(S\) 并记录 \(T\)
(4) 获得使类间方差最大的 \(S\)

效果

drawing

实现

1. 调用 OpenCV库

img = cv.imread(img_path, 0)       # img_path 为图片路径
th, img_bin = cv.threshold(img, -1, 255, cv.THRESH_OTSU) # img_bin 为二值化结果

2. 自己实现

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

def show(img):
    if img.ndim == 2:
        plt.imshow(img, cmap='gray')
    else:
        img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
        plt.imshow(img)
    plt.show()

# 正式开始
img = cv.imread('pic/eagle500x500.jpg', 0)

Sigma = -1
T = 0

for t in range(0, 256):
    bg  = img[img <= t]
    obj = img[img > t]
    
    p0 = bg.size / img.size
    p1 = obj.size / img.size
    
    m0 = 0 if bg.size == 0 else bg.mean()
    m1 = 0 if obj.size == 0 else obj.mean()
    
    sigma = p0 * p1 * (m0 - m1)**2
    
    if sigma > Sigma:
        Sigma = sigma
        T = t
T = int(T)

# 此时 T 即为最佳阈值
print(f"Best threshold = {T}")

说明:

  1. 未经许可,谢绝转载。
  2. 本教程为《数字图像处理Python OpenCV实战》的配套代码相关内容。
    免费视频教程为0-6章(标题号≤6),可在此处点击观看。
    所有课件及源代码可在此处下载:
    链接:https://pan.baidu.com/s/198PySe_vebO3e06idHSQ6g
    提取码:11o4
    有问题可在QQ群(1079300899)指出,进群答案:数字图像处理。在本文评论指出可能导致回复很晚。