OSTU算法(最大类间方差法、大津算法): 用来自动对基于聚类的图像进行二值化,或者说将一个灰度图像退化为二值图像。大津算法wiki

目录

  1. 算法原理:
  2. 公式:
  3. 算法
  4. 代码(Opencv)
  5. 可视化结果

算法原理:

通过一个阈值T将图像分为背景和前景, 求出背景的像素均值, 前景的像素均值和整幅图的像素均值, 计算背景和前景的方差. 方差越大意味着背景与前景的区分度越明显, 只要求出使得方差最大的阈值, 则 就是最适合的阈值.

公式:

注意: 如果文中数学公式加载错误, 请刷新后重试

  • 图像大小:
  • 小于阈值的像素点数量:
  • 大于阈值的像素点数量:
  • 方差:

带入得到

算法

  • 统计灰度图中各像素的个数, 生成像素直方图.
  • 计算各像素的概率.
  • 计算阈值
    • 遍历每一个像素值(0-255)作为阈值.
    • 分别计算前景和背景的像素均值以及概率.
    • 计算全局均值.
    • 计算方差.
    • 更新最大方差和最优阈值.

代码(Opencv)

1
2
3
4
5
6
7
8
9
10
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#include <opencv/highgui.h>
#include <opencv2/opencv.hpp>
#include <cmath>

//OSTU算法
int OSTU(cv::Mat& src)
{
int threshold=0; //阈值
float sigma_temp=0.0f;
float sigma_max = 0.0f;
int pixel_num = src.rows * src.cols; //像素点个数
int pixel_count[256]; //各像素个数
float pixel_pro[256]; //各像素概率

//初始化
for(int i=0; i<256; i++)
{
pixel_count[i] = 0;
}

// 统计像素个数
for(int i=0; i < src.rows; i++)
{
uchar* data = src.ptr<uchar>(i);

for(int j=0; j < src.cols; j++)
{
pixel_count[data[j]]++;
}
}

//计算像素概率
for(int i=0; i<256; i++)
{
pixel_pro[i] = (float)(pixel_count[i]) / (float)(pixel_num);
}

//计算阈值
for(int i=0; i<256; i++)
{
float w0=0, w1=0, u0=0, u1=0, u=0;
//遍历像素
for(int j=0; j<256; j++)
{
//背景像素
if(j < i)
{
w0 += pixel_pro[j];
u0 += pixel_pro[j] * j;
}
else //前景像素
{
u1 += pixel_pro[j] * j;
}
}

//全图像素均值
//u = w0 * u0 + (1-w0) * u1;

//方差
sigma_temp = w0 * (1-w0) * pow((u0 - u1), 2);

//更新阈值
if(sigma_temp > sigma_max)
{
sigma_max = sigma_temp;
threshold = i;
}
}

return threshold;
}

//二值化图像
void BinaryImage(cv::Mat& src, cv::Mat& des, int threshold)
{
for(int i=0; i < src.rows; i++)
{
uchar* src_data = src.ptr<uchar>(i);
uchar* des_data = des.ptr<uchar>(i);

for(int j=0; j < src.cols; j++)
{
if(src_data[j] < threshold)
{
des_data[j] = 0;
}
else
{
des_data[j] = 255;
}
}
}
}

int main()
{
cv::Mat src, gray;

cv::namedWindow("src", cv::WINDOW_NORMAL);
cv::namedWindow("gray", cv::WINDOW_NORMAL);
cv::namedWindow("binary", cv::WINDOW_NORMAL);


src = cv::imread("../wallhaven-698240.jpg", cv::IMREAD_COLOR);
cv::cvtColor(src, gray, cv::COLOR_RGB2GRAY); //转换成灰度图

int threshold = OSTU(gray); //计算阈值

cv::Mat binary = cv::Mat(gray.rows, gray.cols, CV_8U); //初始化二值化图像
BinaryImage(gray, binary, threshold);

printf("threshold: %d\n", threshold);

//显示RGB图像, 灰度图像, 二值化图像
cv::imshow("src", src);
cv::imshow("gray", gray);
cv::imshow("binary", binary);

cv::waitKey(0);

return 0;
}

可视化结果

RGB图像(原图)

灰度图像(经过Opencv转化)

二值化图像(OSTU算法)