OSTU算法
简介
OSTU算法(最大类间方差法、大津算法): 用来自动对基于聚类的图像进行二值化,或者说将一个灰度图像退化为二值图像。
算法原理:
通过一个阈值T将图像分为背景和前景, 求出背景的像素均值, 前景的像素均值和整幅图的像素均值, 计算背景和前景的方差. 方差越大意味着背景与前景的区分度越明显, 只要求出使得方差最大的阈值$T_{max}$, 则 $T_{max}$就是最适合的阈值.
公式:
注意: 如果文中数学公式加载错误, 请刷新后重试
- 图像大小: $M \ast N$
- 小于阈值的像素点数量: $N_0$
- 大于阈值的像素点数量: $N_1$
- 方差: $\sigma^2$
$$
\begin{align}
& \omega_0 = \frac {N_0} {M \ast N} \\\
& \omega_1 = \frac {N_1} {M \ast N} \\\
& \omega_0 + \omega_1 = 1 \\\
& \mu = \omega_0 \ast \mu_0 + \omega_1 \ast \mu_1 \\\
& \sigma^2 = \omega_0 \ast (\mu_0 - \mu)^2 + \omega_1 \ast (\mu_1 - \mu)^2
\end{align}
$$
将$\mu$带入$\sigma^2$得到
$$
\sigma^2 = \omega_0 \ast \omega_1 \ast (\mu_0 - \mu_1)^2
$$
算法
- 统计灰度图中各像素的个数, 生成像素直方图.
- 计算各像素的概率.
- 计算阈值
- 遍历每一个像素值(0-255)作为阈值.
- 分别计算前景和背景的像素均值以及概率.
- 计算全局均值.
- 计算方差.
- 更新最大方差和最优阈值.
代码(Opencv)
#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;
}