65.9K
CodeProject 正在变化。 阅读更多。
Home

C# 中的著名 Otsu 阈值化

2009年7月20日

CPOL

3分钟阅读

viewsIcon

178921

downloadIcon

12322

本文将介绍著名的 Otsu 自动阈值法的基本原理,并提供 C# 实现。

otsu.jpg

引言

阈值处理是图像处理中非常基本的操作。而一个好的算法总是始于一个好的基础!Otsu 阈值法是一种简单而有效的全局自动阈值方法,用于二值化灰度图像,例如前景和背景。

背景

在图像处理中,Otsu 阈值法(1979)根据直方图的形状,用于自动决定二值化水平。该算法假设图像由两个基本类别组成:前景和背景。然后,它计算一个最优阈值,以最小化这两个类别的加权类内方差。数学上已证明,最小化类内方差等同于最大化类间方差。

Otsu 阈值法在从医学成像到低级计算机视觉的许多应用中都有使用。它有很多优点和假设。

优点

  • 速度:由于 Otsu 阈值法处理的是直方图(长度为 256 的整数或浮点数组),因此速度非常快。
  • 编码简便:大约 80 行非常简单的代码。

缺点

  • 假设光照均匀。
  • 直方图应该是双峰的(图像也是如此)。
  • 它不使用任何对象结构或空间相干性。
  • 非局部版本假设统计量均匀。

数学公式

其中 q1 和 q2 表示类概率的估计,定义为

f2.jpg   和   f3.jpg

以及 sigmas 是各个类的方差,定义为

f4.jpg   和   f5.jpg

以及类均值

f6.jpg   和   f7.jpg

这里,P 表示图像直方图。最小化类内方差的问题可以表示为最大化类间方差的问题。它可以写成总方差减去类内方差

f9.jpg   =   f8.jpg

最后,可以安全地最大化此表达式,解为 t,它最大化 f9.jpg

算法

现在,让我们从更算法的角度来看 Otsu 阈值法。以下是算法的步骤

对于每个潜在阈值 T,我们

  1. 根据阈值将像素分成两组。
  2. 找到每组的均值。
  3. 计算均值之差的平方。
  4. 乘以一个组中的像素数乘以另一个组中的像素数。

如果我们在这个简单的分步算法中加入一点数学知识,这种解释就会演变成

  1. 计算每个强度级别的直方图和概率。
  2. 设置初始 qi(0) 和 μi(0)。
  3. 遍历所有可能的阈值,直至最大强度。
  4. 更新 qi 和 μi。
  5. 计算 f9.jpg
  6. 所需的阈值对应于最大值。

另一种视角

由于 Otsu 处理的是直方图,因此分析图像直方图和确定阈值级别非常明智。我认为这张简单的图片足以总结整个故事(阈值由红箭头标记)

f9.jpg

Using the Code

使用代码非常简单。Otsu 阈值法对灰度图像有效。所以首先,我们需要将图像转换为灰度图像。之后,我们可以通过求解最大 t 来获得 Otsu 阈值。最后,我们使用此 t 值对图像进行阈值处理。简而言之,用法如下

Bitmap temp = (Bitmap)org.Clone();
ot.Convert2GrayScaleFast(temp);
int otsuThreshold= ot.getOtsuThreshold((Bitmap)temp);
ot.threshold(temp,otsuThreshold);
textBox1.Text = otsuThreshold.ToString();
pictureBox1.Image = temp;

如果我们查看 getOtsuThreshold 函数,它看起来是这样的

public int getOtsuThreshold(Bitmap bmp)
{
    byte t=0;
    float[] vet=new float[256];
    int[] hist=new int[256];
    vet.Initialize();

    float p1,p2,p12;
    int k;

    BitmapData bmData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),
    ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
    unsafe
    {
        byte* p = (byte*)(void*)bmData.Scan0.ToPointer();

        getHistogram(p,bmp.Width,bmp.Height,bmData.Stride, hist);

        
        for (k = 1; k != 255; k++)
        {
            p1 = Px(0, k, hist);
            p2 = Px(k + 1, 255, hist);
            p12 = p1 * p2;
            if (p12 == 0) 
                p12 = 1;
            float diff=(Mx(0, k, hist) * p2) - (Mx(k + 1, 255, hist) * p1);
            vet[k] = (float)diff * diff / p12;
            
        }
    }
    bmp.UnlockBits(bmData);

    t = (byte)findMax(vet, 256);

    return t;
}

有关如何在 C# 中高效处理图像的更多信息,请参阅我之前的文章,例如“C# 中的图像处理基础”。

关注点

Otsu 阈值法并不声称是最好的自动阈值法,但它在计算机视觉和医学成像中有许多适用的用途。它是实现非参数化、自适应算法的第一步。此外,可能存在更快的实现(例如递归算法)。

此外,Otsu 阈值法可以扩展到多级阈值处理,从而实现图像分割。这方面的研究已在文献中有所体现。这可能是一个后续文章。请查阅

历史

  • 版本 1.0。

参考

  • Otsu, N., "A Threshold Selection Method from Gray-Level Histograms," IEEE Transactions on Systems, Man, and Cybernetics, Vol. 9, No. 1, 1979, pp. 62-66.
© . All rights reserved.