1. 基本概念
用大津法最终会得到一个阈值,使用该阈值将图像进行二值化。大于这个阈值的,像素值变成1;小于阈值的,像素值变成0。 原本图像的像素范围是从0~255,大津法的思想很简单,就是遍历这256个阈值,对每个阈值,都进行一次二值化。最终看哪个阈值下二值化的结果最合适。接下来就介绍遍历的公式。
2. 公式
这里我将像素值为1的称作前景,像素值为0的称作后景。
大津法的最终公式为很多小的计算拼起来的,如下
2.1 后景像素比w0
ω 0 = 后景像素数量 图像宽 ∗ 图像高 (1) \omega_0= \frac{后景像素数量}{图像宽 *图像高}\tag{1} ω0=图像宽∗图像高后景像素数量(1)
2.2 前景像素比w1
ω 1 = 前景像素数量 图像宽 ∗ 图像高 (2) \omega_1= \frac{前景像素数量}{图像宽 *图像高}\tag{2} ω1=图像宽∗图像高前景像素数量(2)
2.3 后景平均灰度u0
μ 0 = ∑ a l l 后景像素的值 后景像素数量 (3) \mu_0 =\frac{ \sum_{all}后景像素的值}{后景像素数量}\tag{3} μ0=后景像素数量∑all后景像素的值(3)
2.4 前景平均灰度u1
μ 1 = ∑ a l l 前景像素的值 前景像素数量 (4) \mu_1 =\frac{ \sum_{all}前景像素的值}{前景像素数量}\tag{4} μ1=前景像素数量∑all前景像素的值(4)
2.5 公式汇合
根据式1~4,可以得到(5)
μ
=
ω
0
∗
μ
0
+
ω
1
∗
μ
1
、
(5)
\mu = \omega_0*\mu_0 + \omega_1*\mu_1 、\tag{5}
μ=ω0∗μ0+ω1∗μ1、(5)
其中
μ
\mu
μ是图像的平均灰度
而大津法的公式为
g
=
ω
0
∗
(
μ
0
−
μ
)
2
+
ω
1
∗
(
μ
1
−
μ
)
2
(6)
g = \omega_0*(\mu_0-\mu)^2 + \omega_1*(\mu_1-\mu)^2 \tag{6}
g=ω0∗(μ0−μ)2+ω1∗(μ1−μ)2(6)
将(5)带(6),可以得到(7)
g
=
ω
0
ω
1
(
μ
0
−
μ
1
)
2
(7)
g = \omega_0\omega_1(\mu_0-\mu_1)^2 \tag{7}
g=ω0ω1(μ0−μ1)2(7)
3. 代码
代码部分中,上述的公式都会体现出来。已经被注释所标注。
import numpy as np
import math
'''
Summary:
大津阈值分割
Paramaters:
img - 输入的灰度图像 是二维矩阵
'''
def OTSU(img):
# 类间方差g初始最小
g_raw = -1
# 要返回的阈值
T_return = 0
# 获得图像大小
M_N = img.shape[0]*img.shape[1]
# 大津阈值算法
for T in range(256):
# 获取阈值大于T和小于T的两个列表
array0 = img[img<T]
array1 = img[img>T]
# 算出w0和w1
w0 = len(array0)/M_N # 公式1
w1 = len(array1)/M_N # 公式2
# 算出μ0和μ1 这里需要特判除数为0
if len(array0) == 0:
mu0 = 0
else:
mu0 = sum(array0)/len(array0) # 公式3
if len(array1) == 0:
mu1 = 0
else:
mu1 = sum(array1)/len(array1) # 公式4
# 算出g
g=w0*w1*math.pow((mu0-mu1),2) # 公式6
if g > g_raw:
g_raw = g
T_return = T
return T_return
使用该图进行测试,效果如下所示
import cv2
# 读取图片
img = Image.open('2.png')
# 转换成灰度图
img=img.convert('L')
# 转换成array
arr = np.array(img)
# # 获得最佳域值分割
T = OTSU(arr)
print(T)
# 根据阈值进行二值分割
arr[arr>T] = 255
arr[arr<T] = 0
# 展示分割结果
pil_image=Image.fromarray(arr)
pil_image