问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

详解OTSU(大津法)原理及C语言代码实现

创作时间:
作者:
@小白创作中心

详解OTSU(大津法)原理及C语言代码实现

引用
CSDN
1.
https://blog.csdn.net/as480133937/article/details/120572867

OTSU(大津法)是一种常用的图像二值化算法,通过计算类间方差来自动选择最佳阈值,实现图像的前景和背景分离。本文将详细介绍OTSU算法的原理,并给出完整的C语言实现代码。

灰度图二值化

在对灰度图像进行处理时,为了便于观察和分析,经常需要将图像中的目标主体和背景分割开来,变成二值化图像(只有黑和白)。灰度图像是由256个灰度级组成的,其中255代表全白,0表示全黑。在进行二值化时,需要设定一个阈值,根据灰度值大于或小于阈值进行黑白显示。背景通常用白色(0)表示,目标物体用黑色(1)表示。阈值的选取对图像二值化的效果影响非常大。

从上图可以看出,阈值的选取对于灰度图二值化有着至关重要的作用。为了找到一个合适的阈值,使得背景和前景能够很好地分开,并且最大程度地减少误判,需要对图像的灰度直方图进行分析。

灰度直方图

灰度直方图显示了图像中不同灰度级的像素点分布情况。通过分析灰度直方图,可以确定图像中目标和背景的灰度范围,从而选择合适的阈值进行二值化。

类间方差

方差是一组数据离散程度的度量,方差越大表示离散程度越大。在图像二值化中,背景和前景的灰度值应该有明显的差异,因此可以通过类间方差来衡量二值化效果。假设图像阈值为T,小于T的像素为目标,数量占总图像比例为w0,平均灰度值为u0;大于T的像素为背景,数量占图像比例为w1,平均灰度值为u1。类间方差g的定义为:

目标是找到一个阈值T,使得类间方差g最大。

大津法(OTSU)

大津法(OTSU)是由日本学者大津(Nobuyuki Otsu)于1979年提出的一种确定图像二值化分割阈值的算法。该算法假设图像像素能够根据全局阈值被分成背景和目标两部分,并计算最佳阈值来区分这两类像素,使得两类像素的区分度最大。

大津法适用于图像灰度分布整体呈现“双峰”的情况。当图像中的目标与背景的面积相差很大时,灰度直方图可能没有明显的双峰,或者两个峰的大小相差很大,此时分割效果可能不佳。

代码实现

下面以C语言为例,详细讲解OTSU算法的实现过程。

函数定义

首先定义一个函数,用于实现OTSU二值化算法:

uint8 otsuThreshold(uint8 *image, uint16 col, uint16 row)
{
    #define GrayScale 256//定义256个灰度级
    uint16 width = col;   //图像宽度
    uint16 height = row;  //图像长度
    int pixelCount[GrayScale];  //每个灰度值所占像素个数
    float pixelPro[GrayScale]; //每个灰度值所占总像素比例
    int i, j;
    int sumPixel = width * height;//总像素点
    uint8 threshold = 0; //最佳阈值
    uint8* data = image;  //指向像素数据的指针

数组初始化

局部变量数组需要进行初始化,可以使用以下三种方法:

  1. for循环赋值
  2. memset
  3. 声明时使用 {0} 初始化

实测表明,for循环浪费的时间最多,{0} 与memset 耗时差不多。

统计灰度级像素个数

统计每个灰度级在整个图像中的个数:

for (i = 0; i < height; i++)
{
    for (j = 0; j < width; j++)
    {
        pixelCount[(int)data[i * width + j]]++;  //将像素值作为计数数组的下标
    }
}

计算像素比例和总平均灰度

计算每个灰度级像素数占图像总像素的比例,以及图像的总平均灰度u:

float w0=0, w1=0, u0Sum=0, u1Sum=0, u0=0, u1=0, u=0, variance=0, maxVariance = 0;
for (i = 0; i < GrayScale; i++)
{
    pixelPro[i] = (float)pixelCount[i] / sumPixel;
    u += i * pixelPro[i];  //总平均灰度
}

计算类间方差

遍历阈值从1到255,计算每次的目标像素数量占总图像比例w0,平均灰度值u0,背景像素数量占图像比例w1,平均灰度值u1,然后比较256次情况下的类间方差g的最大值:

for (i = 0; i < GrayScale; i++)     // i作为阈值 阈值从1-255遍历 
{
    for (j = 0; j < GrayScale; j++)    //求目标前景和背景
    {
        if (j <= i)   //前景部分    
        {
            w0 += pixelPro[j];   
            u0Sum += j * pixelPro[j];
        }
        else   //背景部分  
        {
            w1 += pixelPro[j];
            u1Sum += j * pixelPro[j];
        }
    }
    u0 = u0Sum / w0;
    u1 = u1Sum / w1;
    variance = w0 * pow((u0 - u), 2) + w1 * pow((u1 - u), 2);  //类间方差计算公式
    if (variance > maxVariance)   //判断是否为最大类间方差
    {
        maxVariance = variance;
        threshold = i;
    }
}
return threshold;
}

优化代码

通过利用公式1(w0+w1=1)和公式4,可以将两个for循环叠加,优化代码执行效率:

float maxVariance=0.0;  //最大类间方差
float w0 = 0, avgValue  = 0;  //w0 前景比例 ,avgValue 前景平均灰度
for(int i = 0; i < 256; i++)     //每一次循环都是一次完整类间方差计算 (两个for叠加为1个)
{  
    w0 += pixelPro[i];  //假设当前灰度i为阈值, 0~i 灰度像素所占整幅图像的比例即前景比例
    avgValue  += i * pixelPro[i];        
    float variance = pow((avgValue/w0 - u), 2) * w0 /(1 - w0);    //类间方差   
    if(variance > maxVariance) 
    {  
        maxVariance = variance;  
        threshold = i;  
    }  
} 
return threshold;
}

使用方法

调用OTSU算法计算阈值:

threshold = otsuThreshold(image[0],COL,ROW);  //大津法计算阈值  
threshold = threshold > 50?50:threshold;

总结

OTSU算法通过计算类间方差来自动选择最佳阈值,实现图像的前景和背景分离。虽然该算法对噪声敏感,且在目标与背景面积比例悬殊时效果不佳,但在许多场景下仍能取得很好的二值化效果。通过本文的详细讲解和代码实现,读者可以更好地理解和应用OTSU算法。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号