大津算法——自动确定图像二值化的阈值

在进行图像二值化的操作中,我们往往要去去定一个阈值,像素值小于阈值的为背景,像素值大于背景的为前景。不同的阈值选择会产生出截然不同的二值化图像。因此,如何选取一个合适的阈值,是一个关键的问题。

大津算法,作为一个自动确定二值化阈值的算法,是从类内方差和类间方差的比值计算得来,各种方差的定义如下:

类内方差: S w 2 = w 0   S 0 2 + w 1   S 1 2 {S_w}^2=w_0\ {S_0}^2+w_1\ {S_1}^2 Sw2=w0 S02+w1 S12
类间方差: S b 2 = w 0   ( M 0 − M t ) 2 + w 1   ( M 1 − M t ) 2 = w 0   w 1   ( M 0 − M 1 ) 2 {S_b}^2 = w_0 \ (M_0 - M_t)^2 + w_1\ (M_1 - M_t)^2 = w_0\ w_1\ (M_0 - M_1) ^2 Sb2=w0 (M0Mt)2+w1 (M1Mt)2=w0 w1 (M0M1)2
图像所有像素的方差: S t 2 = S w 2 + S b 2 = 常数 {S_t}^2 = {S_w}^2 + {S_b}^2 = \text{常数} St2=Sw2+Sb2=常数

在上式中,我们令小于阈值 t t t的类记为 0 0 0,大于阈值 t t t的类记为 1 1 1
w 0 w_0 w0 w 1 w_1 w1是阈值 t t t分开的两个类中的像素数占总像素数的比值(满足 w 0 + w 1 = 1 w_0+w_1=1 w0+w1=1);
S 0 2 {S_0}^2 S02 S 1 2 {S_1}^2 S12是这两个类中像素值的方差;
M 0 M_0 M0 M 1 M_1 M1是这两个类的像素值的平均值;

根据上面的式子,我们定义分离度 X X X:

X = S b 2 S w 2 = S b 2 S t 2 − S b 2 X = \frac{{S_b}^2}{{S_w}^2} = \frac{{S_b}^2}{{S_t}^2 - {S_b}^2} X=Sw2Sb2=St2Sb2Sb2

在上式中,我们希望最大化分离度 X X X,即最大化类间方差 S b 2 {S_b}^2 Sb2,同时最小化类内方差 S w 2 {S_w}^2 Sw2。但由于类间方差与类内方差之和为常数,因此问题转化为最大化类间方差,也就是说:

arg ⁡ max ⁡ t   X = arg ⁡ max ⁡ t   S b 2 \arg\max\limits_{t}\ X=\arg\max\limits_{t}\ {S_b}^2 argtmax X=argtmax Sb2

因此,如果使 S b 2 = w 0   w 1   ( M 0 − M 1 ) 2 {S_b}^2={w_0}\ {w_1}\ (M_0 - M_1)^2 Sb2=w0 w1 (M0M1)2最大,就可以得到最好的二值化阈值 t t t

代码实现
import cv2
import numpy as np

#将BGR图像转化为灰度图(opencv默认颜色通道为BGR)
def RGB2GRAY(img):
    b = img[:, :, 0]
    g = img[:, :, 1]
    r = img[:, :, 2]

    out = 0.2126 * r + 0.7152 * g + 0.0722 * b
    out = out.astype(np.uint8)

    return out

#大津算法得到二值化图像
def otsu(img):
    max_sigma = 0
    max_t = 0

    for t in range(1, 255):
        v0 = img[img < t]
        m0 = np.mean(v0) if len(v0) else 0.
        w0 = len(v0) / (H*W)
        v1 = img[img >= t]
        m1 = np.mean(v1) if len(v1) else 0.
        w1 = len(v1) / (H * W)
        sigma = w0 * w1 * ((m0 - m1) ** 2)
        if sigma > max_sigma:
            max_sigma = sigma
            max_t = t

    print('t = ', max_t)
    img[img < max_t] = 0
    img[img >= max_t] = 255
    return img


img = cv2.imread('../imori.jpg').astype(np.float)
gray = RGB2GRAY(img)
H, W = gray.shape

out = otsu(gray)

cv2.imwrite('4.jpg', out)
cv2.imshow('otsu', out)
cv2.waitKey()
cv2.destroyAllWindows()
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值