图像二值化算法–大津法OTSU
大津算法是一种图像二值化算法,作用是确定将图像分成黑白两个部分的阈值。
大津法是针对灰度值进行阈值分割二值化,如果是彩色图像的话需要先转化成灰度图再进行计算。
方差越大,相关性越低,黑白越分明。
目的:找出一个灰度值阈值Threshold,对该灰度值以上或以下的像素的分别计算方差,满足Threshold以上计算出来的方差和Threshold以下计算出来的方差的和最大。
# 算法推导:
对于图像 Image[M, N] ,其大小为M x N。有以下信息:
符号 | 含义 |
---|
ω
0
ω_0
ω0? | 前景的像素点数占整幅图像的比例 |
μ
0
μ_0
μ0? | 前景像素平均灰度 |
ω
1
ω_1
ω1? | 背景的像素点数占整幅图像的比例 |
μ
1
μ_1
μ1? | 背景像素平均灰度 |
μ
μ
μ | 总像素平均灰度 |
T
T
T | 前景和背景的分割阈值 |
N
0
N_0
N0? | 灰度值大于阈值的像素个数 |
N
1
N_1
N1? | 灰度值小于阈值的像素个数 |
g
g
g | 类间方差 |
根据变量的含义,可以得出以下关系式:
前后景像素比例:
ω
0
=
N
0
/
M
?
N
ω_0 = N_0 / M * N
ω0?=N0?/M?N
ω
1
=
N
1
/
M
?
N
ω_1 = N_1 / M * N
ω1?=N1?/M?N
数量恒等式:
N
0
+
N
1
=
M
?
N
N_0+N_1 = M * N
N0?+N1?=M?N
ω
0
+
ω
1
=
1
ω_0 + ω_1 = 1
ω0?+ω1?=1
平均灰度值关系:
μ
=
ω
0
?
μ
0
+
ω
1
?
μ
1
μ = ω_0 * μ_0 + ω_1 * μ_1
μ=ω0??μ0?+ω1??μ1?
方差定义式:
g
=
ω
0
(
μ
0
?
μ
)
2
+
ω
1
(
μ
1
?
μ
)
2
g = ω_0 (μ_0 - μ)^2 + ω_1 (μ_1 - μ)^2
g=ω0?(μ0??μ)2+ω1?(μ1??μ)2 将μ代入式g,得到等价公式:
g
=
ω
0
?
ω
1
(
μ
0
?
μ
1
)
2
g = ω_0 * ω_1 (μ_0 - μ_1)^2
g=ω0??ω1?(μ0??μ1?)2
# 算法实现(C语言):
Ostu方法又名最大类间差方法,通过统计整个图像的直方图特性来实现全局阈值T的自动选取,其算法步骤为:
- 先计算图像的直方图,即将图像所有的像素点按照0~255共256个bin,统计落在每个bin的像素点数量
- 归一化直方图,将每个bin中像素点数量除以总的像素点
- i 表示分类的阈值,也即一个灰度级,从0开始迭代 i
- 通过归一化的直方图,统计0-i 灰度级的像素(前景像素) 所占整幅图像的比例w0,平均灰度u0;统计i-255灰度级的像素(背景像素) * 所占整幅图像的比例w1,并统计背景像素的平均灰度u1;
- 计算前景像素和背景像素的方差 $ g = ω_0 * ω_1 * (μ_0 - μ_1) * (μ_0 - μ_1)$
- i++;重复第4、5步,直到 i 为256时结束迭代
- 将最大 g 相应的 i 值作为图像的全局阈值
缺陷:OTSU算法在处理光照不均匀的图像的时候,效果会明显不好,因为利用的是全局像素信息。
short GetOTSU (unsigned char tmImage[LCDH][LCDW])
{
signed short i, j;
unsigned long Amount = 0;
unsigned long PixelBack = 0;
unsigned long PixelshortegralBack = 0;
unsigned long Pixelshortegral = 0;
signed long PixelshortegralFore = 0;
signed long PixelFore = 0;
float OmegaBack, OmegaFore, MicroBack, MicroFore, SigmaB, Sigma;
signed short MinValue, MaxValue;
signed short Threshold = 0;
unsigned char HistoGram[256];
for (j = 0; j < 256; j++)
HistoGram[j] = 0;
for (j = 0; j < LCDH; j++)
{
for (i = 0; i < LCDW; i++)
{
HistoGram[tmImage[j][i]]++;
}
}
for (MinValue = 0; MinValue < 256 && HistoGram[MinValue] == 0; MinValue++);
for (MaxValue = 255; MaxValue > MinValue && HistoGram[MinValue] == 0; MaxValue--);
if (MaxValue == MinValue)
return MaxValue;
if (MinValue + 1 == MaxValue)
return MinValue;
for (j = MinValue; j <= MaxValue; j++)
Amount += HistoGram[j];
Pixelshortegral = 0;
for (j = MinValue; j <= MaxValue; j++)
{
Pixelshortegral += HistoGram[j] * j;
}
SigmaB = -1;
for (j = MinValue; j < MaxValue; j++)
{
PixelBack = PixelBack + HistoGram[j];
PixelFore = Amount - PixelBack;
OmegaBack = (float) PixelBack / Amount;
OmegaFore = (float) PixelFore / Amount;
PixelshortegralBack += HistoGram[j] * j;
PixelshortegralFore = Pixelshortegral - PixelshortegralBack;
MicroBack = (float) PixelshortegralBack / PixelBack;
MicroFore = (float) PixelshortegralFore / PixelFore;
Sigma = OmegaBack * OmegaFore * (MicroBack - MicroFore) * (MicroBack - MicroFore);
if (Sigma > SigmaB)
{
SigmaB = Sigma;
Threshold = j;
}
}
return Threshold;
}
参考: https://blog.csdn.net/Galen_xia/article/details/107911867 代码来自龙邱开源库
|