分割算法-大津算法
- 一、什么是大津算法
- 二、算法原理
- 三、公式推导
- 四、代码
- 五、算法适用性
大津算法介绍以及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、目标和背景比例