灰度图像的⾃动阈值分割(Otsu法)
的⾃动阈值分割(Otsu 法)
什么的湖水领域许多算法都要求先对图像进⾏⼆值化。这种⼆值化操作阈值的选取⾮常重要。阈值选取的不合适,可能得到的结果就毫⽆⽤处。今天就来讲讲⼀种⾃动计算阈值的⽅法。这种⽅法被称之为Otsu法。发明⼈是个⽇本⼈,叫做Nobuyuki Otsu (⼤津展之)。
简单的说,这种算法假设⼀副图像由前景⾊和背景⾊组成,通过的⽅法来选取⼀个阈值,使得这个阈值可以将前景⾊和背景⾊尽可能的分开。或者更准确的说是在某种判据下最优。与数理统计领域的 fisher 线性判别算法其实是等价的。
otsu算法中这个判据就是最⼤类间⽅差 (intra-class variance or the variance within the class)。下⾯就来详细说说什么是 intra-class variance。
我们知道⼀副灰度图像,可以计算它的颜⾊平均值,或者更进⼀步。可以计算出灰度直⽅图。
⽐如下⾯的例⼦图⽚:
这个图⽚拍摄的是⼀个条形码。在这个图中,前景⾊就是⿊⾊的条形码,背景⾊是其余部分的灰⾊。那么我们可以计算出这个图像的灰度直⽅图。
图中那个⼤的峰是背景⾊的部分,⼩的峰是前景⾊。
灰度值的均值是 122. 我们称这个均值为 M。
现在任意选取⼀个灰度值 t,则可以将这个直⽅图分成前后两部分。我们称这两部分分别为 A 和 B。对应的就是前景⾊和背景⾊。这两部分各⾃的平均值成为 MA 和 MB。
A 部分⾥的像素数占总像素数的⽐例记作 PA,B部分⾥的像素数占总像素数的⽐例记作 PB。
Nobuyuki Otsu 给出的类间⽅差定义为:
那么这个最佳的 就是使得 最⼤的那个值。
对于上⾯的测试图像,我们可以遍历 t 的各种取值,计算 ICV。之后可以画出这样的ICV 曲线(绿⾊线条):
可以看出,ICV 取最值的点确实将前景⾊和背景⾊分开了。
下⾯是个例⼦代码,⽤到了 Qt 的QImage。int otsu(const QImage &image){ double hist[256]; normalizedHistogram(image, hist); double omega[256]; double mu[256]; omega[0] = hist[0]; mu[0] = 0; for(int i = 1; i < 256; i++)
1
2
3
4
5
小麦制粉
6
7
8
9
10
11
利⽤这个⽅法计算出的阈值做了⼆值化后得到图像如下: for(int i = 1; i < 256; i++) { omega[i]
= omega[i-1] + hist[i]; //累积分布函数 mu[i] = mu[i-1] + i * hist[i]; } double mean = mu[255];// 灰度平均值 double max = 0; int k_max = 0; for(int k = 1; k < 255; k++) { double PA = omega[k]; // A 类所占的⽐例 double PB = 1 - omega[k]; //B 类所占的⽐例 double value = 0; if( fabs(PA) > 0.001 && fabs(PB) > 0.001) { double MA = mu[k] / PA; //A 类的灰度均值 double MB = (mean - mu[k]) / PB;//B 类灰度均值 value = PA * (MA - mean) * (MA - mean) + PB * (MB - mean) * (MB - mean);//类间⽅差 if (value > max) { max = value; k_max = k; } } //qDebug() <<k << " " << hist[k] << " " << value; } return k_max;}bool normalizedHistogram(const QImage &image, double hist[256]){ for(int i = 0; i < 256; i++) { hist[i] = 0.0; } int height = image.height(); int width = image.width(); int N = height * width; if(image.format() != QImage::Format_Indexed8) { return fal; } for(int i = 0; i < height; i++) { const quint8 *pData = (const quint8 *)stScanLine(i); for(int j = 0; j < width; j++) { ++hist[ pData[j] ]; } } for(int i = 0; i < 256; i++) { hist[i] = hist[i] / N; } return true;}
11
12
13
14
15
16
17
高中单词
18
19
20
21
直流电机维修22
23
24
25
26
27梦见猫说话
28
29
30
31
32
33
34
35
36
37
38
39
一丝不苟意思40
41
42
43
44
45
46
47
48
49
50
51
52
壤字开头的成语53
54
55
瘰疬丸56
57
58
59
60
61
62
63
64
65
66
可以看到效果很好。
Otsu ⽅法也不是万能的。当⽬标与背景的⼤⼩⽐例悬殊时,类间⽅差准则函数可能呈现双峰或多峰,此时效果不好。这时就要考虑其他的办法了。
其实,我们可以仔细观察 ICV 的计算公式。
这⾥⾯ PA 和 PB 相当于是个前景⾊和背景⾊部分做个加权。当前景⾊或背景⾊有⼀个区域很⼩时。⽐如 PA ⾮常的⼩。那么这时计算出来的 t 就会和 B 区域很接近,这时的分割效果就会⽐较差。我们可以对ICV的公式进⾏⼀点⼩⼩的改造。
这⾥的 可以取⼀个 0-1之间的值。⽐如上⾯的例⼦图⽚,如果我们取 计算出的效果会更好⼀些。当然这个 值就要全凭经验来定了。