二值化——全局阈值方法
- 固定阈值方法
- Otsu算法
- 在OpenCV中的实现
- 固定阈值
- Otsu算法
图像二值化(Image Binarization)是指将像素点的灰度值设为0或255,使图像呈现明显的黑白效果。二值化一方面减少了数据维度,另一方面通过排除原图中噪声带来的干扰,可以凸显有效区域的轮廓结构。OCR效果很大程度上取决于该步骤,高质量的二值图像可以显著提升识别的准确率。目前,二值化的方法主要分为全局阈值方法(Global Binarization)、局部阈值方法(Local Binarization)、基于深度学习的方法和其他方法。
固定阈值方法
该方法对输入图像中的所有像素点统一使用同一个固定阈值。其算法如下:
g
(
x
,
y
)
=
{
255
,
若
f
(
x
,
y
)
≥
T
0
,
否则
g(x,y)=\begin{cases} 255, & 若f(x,y)\geq T \\ 0, & 否则 \end{cases}
g(x,y)={255,0,若f(x,y)≥T否则
- T T T为全局阈值
不同的阈值
T
T
T会产生不同的二值化效果。对于不同的输入图像,最佳的阈值
T
T
T也不一样,这也是固定阈值方法的主要缺陷。
于是,解决这一缺陷的相应算法也随之而出现;下面的几种方法均采用了根据输入图像计算最佳阈值的思想。
Otsu算法
Ostu算法又称最大类间方差法,由日本学者Nobuyuki Ostu于1979年提出,是一种在自适应的阈值确定方法。
Ostu算法将输入图像分为
L
L
L个灰度级,
n
i
n_i
ni表示灰度级为
i
i
i的像素个数,则像素总数
N
=
n
1
+
n
2
+
⋯
+
n
L
N=n_1+n_2+ \cdots +n_L
N=n1+n2+⋯+nL。为了简化讨论,这里使用归一化的灰度直方图,并将其视为输入图像的概率分布:
p
i
=
n
i
/
N
,
p
i
>
0
,
∑
i
=
1
L
p
i
=
1
p_i=n_i/N, p_i>0, \sum_{i=1}^{L}p_i=1
pi=ni/N,pi>0,i=1∑Lpi=1
现假设在第
k
k
k个灰度级设置阈值,将图像分为
C
0
C_0
C0和
C
1
C_1
C1(背景和目标物体),
C
0
C_0
C0表示灰度级为
[
1
,
⋯
,
k
]
[1, \cdots, k]
[1,⋯,k]的像素点,
C
1
C_1
C1表示灰度级为
[
k
+
1
,
⋯
,
L
]
[k+1, \cdots, L]
[k+1,⋯,L]的像素点,那么两类出现的概率以及类内灰度级的均值分别为:
ω
0
=
P
r
(
C
0
)
=
∑
i
=
1
k
p
i
=
ω
(
k
)
ω
1
=
P
r
(
C
1
)
=
∑
i
=
k
+
1
L
p
i
=
1
−
ω
(
k
)
μ
0
=
∑
i
=
1
k
i
P
r
(
i
∣
C
0
)
=
∑
i
=
1
k
i
p
i
/
ω
0
=
μ
(
k
)
/
ω
(
k
)
μ
1
=
∑
i
=
k
+
1
L
i
P
r
(
i
∣
C
1
)
=
∑
i
=
k
+
1
k
i
p
i
/
ω
1
=
μ
T
−
μ
(
k
)
1
−
ω
(
k
)
\omega_0=Pr(C_0)=\sum_{i=1}^{k}p_i=\omega(k) \\ \omega_1=Pr(C_1)=\sum_{i=k+1}^{L}p_i=1-\omega(k) \\ \mu_0=\sum_{i=1}^{k}i Pr(i|C_0)=\sum_{i=1}^{k}ip_i/\omega_0=\mu(k)/\omega(k) \\ \mu_1=\sum_{i=k+1}^{L}i Pr(i|C_1)=\sum_{i=k+1}^{k}ip_i/\omega_1=\frac{\mu_T-\mu(k)}{1-\omega(k)}
ω0=Pr(C0)=i=1∑kpi=ω(k)ω1=Pr(C1)=i=k+1∑Lpi=1−ω(k)μ0=i=1∑kiPr(i∣C0)=i=1∑kipi/ω0=μ(k)/ω(k)μ1=i=k+1∑LiPr(i∣C1)=i=k+1∑kipi/ω1=1−ω(k)μT−μ(k)
- ω ( k ) \omega(k) ω(k)和 μ ( k ) \mu(k) μ(k)分别为灰度级从1到 k k k的累计出现概率和平均灰度级;
- μ T \mu_T μT为整张图像的平均灰度级。
容易证得,对于任意
k
k
k值均有:
ω
0
μ
0
+
ω
1
μ
1
=
μ
T
,
ω
0
+
ω
1
=
1
\omega_0\mu_0+\omega_1\mu_1=\mu_T, \omega_0+\omega_1=1
ω0μ0+ω1μ1=μT,ω0+ω1=1
这两类得类内方差也可以算得:
σ
0
2
=
∑
i
=
1
k
(
i
−
μ
0
)
2
P
r
(
i
∣
C
0
)
=
∑
i
=
1
k
(
i
−
μ
0
)
2
p
i
/
ω
0
σ
1
2
=
∑
i
=
k
+
1
L
(
i
−
μ
1
)
2
P
r
(
i
∣
C
0
)
=
∑
i
=
k
+
1
L
(
i
−
μ
1
)
2
p
i
/
ω
1
\sigma_0^2=\sum_{i=1}^{k}(i-\mu_0)^2Pr(i|C_0)=\sum_{i=1}^{k}(i-\mu_0)^2p_i/\omega_0 \\ \sigma_1^2=\sum_{i=k+1}^{L}(i-\mu_1)^2Pr(i|C_0)=\sum_{i=k+1}^{L}(i-\mu_1)^2p_i/\omega_1
σ02=i=1∑k(i−μ0)2Pr(i∣C0)=i=1∑k(i−μ0)2pi/ω0σ12=i=k+1∑L(i−μ1)2Pr(i∣C0)=i=k+1∑L(i−μ1)2pi/ω1
为了评价阈值
k
k
k的好坏,需要引入判别式:
λ
=
σ
B
2
/
σ
W
2
,
κ
=
σ
T
2
/
σ
W
2
,
η
=
σ
B
2
/
σ
T
2
(
1
)
\lambda=\sigma_B^2/\sigma_W^2, \kappa=\sigma_T^2/\sigma_W^2, \eta=\sigma_B^2/\sigma_T^2 \qquad (1)
λ=σB2/σW2,κ=σT2/σW2,η=σB2/σT2(1)
其中
- σ W 2 = ω 0 σ 0 2 + ω 1 σ 1 2 \sigma_W^2=\omega_0\sigma_0^2+\omega_1\sigma_1^2 σW2=ω0σ02+ω1σ12,即类内方差
- σ B 2 = ω 0 ( μ 0 − μ T ) 2 + ω ( μ 1 − μ T ) 2 = ω 0 ω 1 ( μ 1 − μ 0 ) 2 \sigma_B^2=\omega_0(\mu_0-\mu_T)^2+\omega(\mu_1-\mu_T)^2=\omega_0\omega_1(\mu_1-\mu_0)^2 σB2=ω0(μ0−μT)2+ω(μ1−μT)2=ω0ω1(μ1−μ0)2,即类间方差
- σ T 2 = ∑ i = 1 L ( i − μ T ) 2 p i \sigma_T^2=\sum_{i=1}^{L}(i-\mu_T)^2p_i σT2=∑i=1L(i−μT)2pi,即灰度级的总方差
由于
σ
W
2
+
σ
B
2
=
σ
T
2
\sigma_W^2+\sigma_B^2=\sigma_T^2
σW2+σB2=σT2始终成立,而对同一张图片来说
σ
T
2
\sigma_T^2
σT2是确定的,所以
σ
W
2
\sigma_W^2
σW2和
σ
B
2
\sigma_B^2
σB2,一个越大,另一个就会越小。这样的话,(1)式中的三个目标值
λ
,
κ
,
η
\lambda, \kappa, \eta
λ,κ,η就总是同向运动的。
但是从计算简单程度上来说,因为
σ
T
2
\sigma_T^2
σT2与
k
k
k无关,且
σ
B
2
\sigma_B^2
σB2只涉及均值的运算。因此,
η
\eta
η是判别
k
k
k取值好坏的最简单的衡量标准:
η
=
σ
B
2
(
k
)
/
σ
T
2
\eta = \sigma_B^2(k)/\sigma_T^2
η=σB2(k)/σT2
因此,最佳的
k
k
k值选择(
k
∗
k^*
k∗)满足:
σ
B
2
(
k
∗
)
=
max
1
≤
k
≤
L
σ
B
2
(
k
)
\sigma_B^2(k^*)=\max_{1\leq k \leq L}\sigma_B^2(k)
σB2(k∗)=1≤k≤LmaxσB2(k)
在OpenCV中的实现
固定阈值
固定阈值可以在OpenCV中用adptiveThreshold()
函数来实现,其函数原型如下:
void cv::adptiveThreshold( InputArray src,
OutputArray dst,
double maxValue,
int adaptiveMethod,
int thresholdType,
int blockSize,
double C)
将其中的第5个参数thresholdType
指定为THRESH_BINARY
就是固定阈值方法。
Otsu算法
Otsu算法可以在OpenCV中用threshold()
函数来实现,其函数原型如下:
double cv::threshold( InputArray src,
OutputArray dst,
double thresh,
double maxval,
int type)
将其中的第5个参数type
指定为THRESH_OTSU
就是Otsu算法。
这个函数也可以用来通过将该参数指定为THRESH_BINARY
来使用固定阈值的方法。
以下是Otsu算法的一个结果示例(上:原图,中:直方图,下:二值化后的结果):
直方图中的红色竖线为Otsu算法找出的最佳阈值。