分割算法-大津算法
- 一、什么是大津算法
- 二、算法原理
- 三、公式推导
- 四、代码
- 五、算法适用性
大津算法介绍以及C++函数代码实现。
一、什么是大津算法
大津算法(Otsu)由日本学者大津展之在1979年提出,又称最大类间方差法。此法求得的阈值,使得图像的前景和背景具有最大的类间方差。
二、算法原理
阈值分割属于区域分割,目的是将图像分为前景和背景,提取出需要的前景区域,区域分分割,根据图像的灰度特性。
如下图,假设我们的前景(即目标)为黑色椭圆,背景为白色。此时前景的像素值为0-10左右,为了方便理解,假设值全部为0,背景假设全部为255。假设阈值为k,任意0-255之间的阈值,都可以将图像分为两个部分,但是只有一个阈值,可以将图像分成前景和背景两个区域。
将分成的两个区域设为A(前景)和B(背景),前景所有的像素为0,那么前景内的像素的方差为0,同理,背景的方差也为0。将两个区域的方差加权相加,得到的即是最小类内方差。
区域A和区域B对于整幅图求取方差,因为A和B的像素值具有最大的差别(在阈值的两侧),所以各自对于整幅图的方差也是最大的,加权相加即为最大类间方差。
最大类间方差和最小类内方差相加为一个定值,当一个最大,一个最小时,此时的k即为最佳阈值,将前景和背景完全分开。
差是灰度分布均匀性的一种度量,前景和背景的类间方差越大,说明两个部分的区域灰度差越大,分割出错的概率就越小
三、公式推导
四、代码
int thresh_otsu(Mat input)
{
//定义像素个数统计
int histogram[256] = { 0 };
//定义全图均值
float global_mean = 0;
//计算全图均值
for (int i = 0; i < input.rows; i++)
{
uchar* data = input.ptr<uchar>(i);
for (int j = 0; j < input.cols; j++)
{
//统计该像素的个数
histogram[data[j]]++;
//计算全图像素值总和
global_mean += data[j];
}
}
//计算均值
global_mean /= (input.rows * input.cols);
int sum = 0;
double p1 = 0, p2 = 0, m1 = 0, m2 = 0;
double sg = 0;
double temp_sg = -1;
int k = 0;
//阈值从0到255进行遍历
for (int i = 0; i < 256; i++)
{
//遍历小于阈值的区域
for (int j = 0; j <= i; j++)
{
//小于阈值的总个数
p2 += histogram[j];
//小于阈值的总像素值
m2 += (histogram[j] * j);
}
//求取小于阈值时的均值
m2 /= p2;
//遍历大于阈值的区域
for (int j = i + 1; j < 256; j++)
{
p1 += histogram[j];
m1 += (histogram[j] * j);
}
m1 /= p1;
//计算小于和大于阈值的个数概率
p2 /= (input.rows * input.cols);
p1 = 1 - p2;
//计算最大类间方差
sg = p1 * p2 * (m1 - m2) * (m1 - m2);
//求取最大值,并记录此时的阈值
if (sg > temp_sg)
{
temp_sg = sg;
k = i;
}
//将概率和均值初始化
p1 = 0, p2 = 0, m1 = 0, m2 = 0;
}
// cout<<"k = "<<k<<endl;
return k;
}
五、算法适用性
1、该算法通过寻找区域灰度的差别来进行确定阈值,所以不受图像的亮度和对比度的影响
2、适用于需要全局阈值的场景
3、目标和背景比例