十三种基于直方图的图像全局二值化算法原理、实现、代码及效果(转)

来源:互联网 发布:水利bim软件 编辑:程序博客网 时间:2024/04/19 07:55

十三种基于直方图的图像全局二值化算法原理、实现、代码及效果

 图像二值化的目的是最大限度的将图象中感兴趣的部分保留下来,在很多情况下,也是进行图像分析、特征提取与模式识别之前的必要的图像预处理过程。这个看似简单的问题,在过去的四十年里受到国内外学者的广泛关注,产生了数以百计的阈值选取方法,但如同其他图像分割算法一样,没有一个现有方法对各种各样的图像都能得到令人满意的结果。

     在这些庞大的分类方法中,基于直方图的全局二值算法占有了绝对的市场份额,这些算法都从不同的科学层次提出了各自的实施方案,并且这类方法都有着一些共同的特点:

  1、简单;

     2、算法容易实现;

     3、执行速度快。

     本文摘取了若干种这类方法进行了介绍。

     一:灰度平局值值法:

  1、描述:即使用整幅图像的灰度平均值作为二值化的阈值,一般该方法可作为其他方法的初始猜想值。

  2、原理:    

             

      3、实现代码:

复制代码
 public static int GetMeanThreshold(int[] HistGram)    {        int Sum = 0, Amount = 0;        for (int Y = 0; Y < 256; Y++)        {            Amount += HistGram[Y];            Sum += Y * HistGram[Y];        }        return Sum / Amount;    }
复制代码

     二、百分比阈值(P-Tile法)

 

     1、描述

    Doyle于1962年提出的P-Tile (即P分位数法)可以说是最古老的一种阈值选取方法。该方法根据先验概率来设定阈值,使得二值化后的目标或背景像素比例等于先验概率,该方法简单高效,但是对于先验概率难于估计的图像却无能为力。

  2、该原理比较简单,直接以代码实现。

复制代码
    /// <summary>    /// 百分比阈值    /// </summary>    /// <param name="HistGram">灰度图像的直方图</param>    /// <param name="Tile">背景在图像中所占的面积百分比</param>    /// <returns></returns>    public static int GetPTileThreshold(int[] HistGram, int Tile = 50)    {        int Y, Amount = 0, Sum = 0;        for (Y = 0; Y < 256; Y++) Amount += HistGram[Y];        //  像素总数         for (Y = 0; Y < 256; Y++)        {            Sum = Sum + HistGram[Y];            if (Sum >= Amount * Tile / 100) return Y;        }        return -1;    }
复制代码

      三、基于谷底最小值的阈值

     1、描述:

  此方法实用于具有明显双峰直方图的图像,其寻找双峰的谷底作为阈值,但是该方法不一定能获得阈值,对于那些具有平坦的直方图或单峰图像,该方法不合适。

  2、实现过程:

  该函数的实现是一个迭代的过程,每次处理前对直方图数据进行判断,看其是否已经是一个双峰的直方图,如果不是,则对直方图数据进行半径为1(窗口大小为3)的平滑,如果迭代了一定的数量比如1000次后仍未获得一个双峰的直方图,则函数执行失败,如成功获得,则最终阈值取两个双峰之间的谷底值作为阈值。

     注意在编码过程中,平滑的处理需要当前像素之前的信息,因此需要对平滑前的数据进行一个备份。另外,首数据类型精度限制,不应用整形的直方图数据,必须转换为浮点类型数据来进行处理,否则得不到正确的结果。

     该算法相关参考论文如下:

   J. M. S. Prewitt and M. L. Mendelsohn, "The analysis of cell images," innnals of the New York Academy of Sciences, vol. 128, pp. 1035-1053, 1966.
     C. A. Glasbey, "An analysis of histogram-based thresholding algorithms," CVGIP: Graphical Models and Image Processing, vol. 55, pp. 532-537, 1993.

     3、实现代码:

 

复制代码
    public static int GetMinimumThreshold(int[] HistGram)    {        int Y, Iter = 0;        double[] HistGramC = new double[256];           // 基于精度问题,一定要用浮点数来处理,否则得不到正确的结果        double[] HistGramCC = new double[256];          // 求均值的过程会破坏前面的数据,因此需要两份数据        for (Y = 0; Y < 256; Y++)        {            HistGramC[Y] = HistGram[Y];            HistGramCC[Y] = HistGram[Y];        }        // 通过三点求均值来平滑直方图        while (IsDimodal(HistGramCC) == false)                                        // 判断是否已经是双峰的图像了              {            HistGramCC[0] = (HistGramC[0] + HistGramC[0] + HistGramC[1]) / 3;                 // 第一点            for (Y = 1; Y < 255; Y++)                HistGramCC[Y] = (HistGramC[Y - 1] + HistGramC[Y] + HistGramC[Y + 1]) / 3;     // 中间的点            HistGramCC[255] = (HistGramC[254] + HistGramC[255] + HistGramC[255]) / 3;         // 最后一点            System.Buffer.BlockCopy(HistGramCC, 0, HistGramC, 0, 256 * sizeof(double));            Iter++;            if (Iter >= 1000) return -1;                                                   // 直方图无法平滑为双峰的,返回错误代码        }       // 阈值极为两峰之间的最小值         bool Peakfound = false;        for (Y = 1; Y < 255; Y++)        {            if (HistGramCC[Y - 1] < HistGramCC[Y] && HistGramCC[Y + 1] < HistGramCC[Y]) Peakfound = true;            if (Peakfound == true && HistGramCC[Y - 1] >= HistGramCC[Y] && HistGramCC[Y + 1] >= HistGramCC[Y])                return Y - 1;        }        return -1;    }
复制代码

 

  其中IsDimodal函数为判断直方图是否是双峰的函数,代码如下:

 

复制代码
    private static bool IsDimodal(double[] HistGram)       // 检测直方图是否为双峰的    {        // 对直方图的峰进行计数,只有峰数位2才为双峰         int Count = 0;        for (int Y = 1; Y < 255; Y++)        {            if (HistGram[Y - 1] < HistGram[Y] && HistGram[Y + 1] < HistGram[Y])            {                Count++;                if (Count > 2) return false;            }        }        if (Count == 2)            return true;        else            return false;    }
复制代码

 

  4、效果:

   

        原图                                                 二值图    

  

               原始直方图                          平滑后的直方图

    

       原图                                                 二值图    

  

                          原始直方图      平滑后的直方图

  对于这种有较明显的双峰的图像,该算法还是能取得不错的效果的。

     四、基于双峰平均值的阈值

       1、描述:

   该算法和基于谷底最小值的阈值方法类似,只是最后一步不是取得双峰之间的谷底值,而是取双峰的平均值作为阈值。

       2、参考代码:

 

复制代码
 public static int GetIntermodesThreshold(int[] HistGram)    {        int Y, Iter = 0, Index;        double[] HistGramC = new double[256];           // 基于精度问题,一定要用浮点数来处理,否则得不到正确的结果        double[] HistGramCC = new double[256];          // 求均值的过程会破坏前面的数据,因此需要两份数据        for (Y = 0; Y < 256; Y++)        {            HistGramC[Y] = HistGram[Y];            HistGramCC[Y] = HistGram[Y];        }        // 通过三点求均值来平滑直方图        while (IsDimodal(HistGramCC) == false)                                                  // 判断是否已经是双峰的图像了              {            HistGramCC[0] = (HistGramC[0] + HistGramC[0] + HistGramC[1]) / 3;                   // 第一点            for (Y = 1; Y < 255; Y++)                HistGramCC[Y] = (HistGramC[Y - 1] + HistGramC[Y] + HistGramC[Y + 1]) / 3;       // 中间的点            HistGramCC[255] = (HistGramC[254] + HistGramC[255] + HistGramC[255]) / 3;           // 最后一点            System.Buffer.BlockCopy(HistGramCC, 0, HistGramC, 0, 256 * sizeof(double));         // 备份数据,为下一次迭代做准备            Iter++;            if (Iter >= 10000) return -1;                                                       // 似乎直方图无法平滑为双峰的,返回错误代码        }// 阈值为两峰值的平均值        int[] Peak = new int[2];        for (Y = 1, Index = 0; Y < 255; Y++)            if (HistGramCC[Y - 1] < HistGramCC[Y] && HistGramCC[Y + 1] < HistGramCC[Y]) Peak[Index++] = Y - 1;        return ((Peak[0] + Peak[1]) / 2);    }
复制代码

 

      3、效果:

 

     

        原图                                                 二值图                

 

                           原始直方图                平滑后的直方图

    五、迭代最佳阈值

  1、描述:

    该算法先假定一个阈值,然后计算在该阈值下的前景和背景的中心值,当前景和背景中心值得平均值和假定的阈值相同时,则迭代中止,并以此值为阈值进行二值化。

    2、实现过程:

  (1)求出图象的最大灰度值和最小灰度值,分别记为gl和gu,令初始阈值为:

                        

      (2) 根据阈值T0将图象分割为前景和背景,分别求出两者的平均灰度值Ab和Af:

              

      (3) 令

                          

    如果Tk=Tk+1,则取Tk为所求得的阈值,否则,转2继续迭代

     3、参考代码:

 

复制代码
    public static int GetIterativeBestThreshold(int[] HistGram)    {        int X, Iter = 0;        int MeanValueOne, MeanValueTwo, SumOne, SumTwo, SumIntegralOne, SumIntegralTwo;        int MinValue, MaxValue;        int Threshold, NewThreshold;        for (MinValue = 0; MinValue < 256 && HistGram[MinValue] == 0; MinValue++) ;        for (MaxValue = 255; MaxValue > MinValue && HistGram[MinValue] == 0; MaxValue--) ;        if (MaxValue == MinValue) return MaxValue;          // 图像中只有一个颜色                     if (MinValue + 1 == MaxValue) return MinValue;      // 图像中只有二个颜色        Threshold = MinValue;        NewThreshold = (MaxValue + MinValue) >> 1;        while (Threshold != NewThreshold)    // 当前后两次迭代的获得阈值相同时,结束迭代            {            SumOne = 0; SumIntegralOne = 0;            SumTwo = 0; SumIntegralTwo = 0;            Threshold = NewThreshold;            for (X = MinValue; X <= Threshold; X++)         //根据阈值将图像分割成目标和背景两部分,求出两部分的平均灰度值                  {                SumIntegralOne += HistGram[X] * X;                SumOne += HistGram[X];            }            MeanValueOne = SumIntegralOne / SumOne;            for (X = Threshold + 1; X <= MaxValue; X++)            {                SumIntegralTwo += HistGram[X] * X;                SumTwo += HistGram[X];            }            MeanValueTwo = SumIntegralTwo / SumTwo;            NewThreshold = (MeanValueOne + MeanValueTwo) >> 1;       //求出新的阈值            Iter++;            if (Iter >= 1000) return -1;        }        return Threshold;    }
复制代码

 

  4、效果:

                   

                   

 

                   原图                                          二值图                                         直方图 

   六、OSTU大律法

  1、描述:

       该算法是1979年由日本大津提出的,主要是思想是取某个阈值,使得前景和背景两类的类间方差最大,matlab中的graythresh即是以该算法为原理执行的。

       2、原理:

       关于该算法的原理,网络上有很多,这里为了篇幅有限,不加以赘述。

       3、参考代码:

 

复制代码
    public static int GetOSTUThreshold(int[] HistGram)    {        int X, Y, Amount = 0;        int PixelBack = 0, PixelFore = 0, PixelIntegralBack = 0, PixelIntegralFore = 0, PixelIntegral = 0;        double OmegaBack, OmegaFore, MicroBack, MicroFore, SigmaB, Sigma;              // 类间方差;        int MinValue, MaxValue;        int Threshold = 0;        for (MinValue = 0; MinValue < 256 && HistGram[MinValue] == 0; MinValue++) ;        for (MaxValue = 255; MaxValue > MinValue && HistGram[MinValue] == 0; MaxValue--) ;        if (MaxValue == MinValue) return MaxValue;          // 图像中只有一个颜色                     if (MinValue + 1 == MaxValue) return MinValue;      // 图像中只有二个颜色        for (Y = MinValue; Y <= MaxValue; Y++) Amount += HistGram[Y];        //  像素总数        PixelIntegral = 0;        for (Y = MinValue; Y <= MaxValue; Y++) PixelIntegral += HistGram[Y] * Y;        SigmaB = -1;        for (Y = MinValue; Y < MaxValue; Y++)        {            PixelBack = PixelBack + HistGram[Y];            PixelFore = Amount - PixelBack;            OmegaBack = (double)PixelBack / Amount;            OmegaFore = (double)PixelFore / Amount;            PixelIntegralBack += HistGram[Y] * Y;            PixelIntegralFore = PixelIntegral - PixelIntegralBack;            MicroBack = (double)PixelIntegralBack / PixelBack;            MicroFore = (double)PixelIntegralFore / PixelFore;            Sigma = OmegaBack * OmegaFore * (MicroBack - MicroFore) * (MicroBack - MicroFore);            if (Sigma > SigmaB)            {                SigmaB = Sigma;                Threshold = Y;            }        }        return Threshold;    }
复制代码

 

  4、效果:

              

  该算法对于那些具有平坦的直方图的图像具有一定的适应能力。

      七、一维最大熵

      1、描述:

       该算法把信息论中熵的概念引入到图像中,通过计算阈值分割后两部分熵的和来判断阈值是否为最佳阈值。

      2、算法原理

       这方面的文章也比较多,留给读者自行去查找相关资料。

       3、参考代码: 

复制代码
public static int Get1DMaxEntropyThreshold(int[] HistGram)    {        int  X, Y,Amount=0;        double[] HistGramD = new double[256];        double SumIntegral, EntropyBack, EntropyFore, MaxEntropy;        int MinValue = 255, MaxValue = 0;        int Threshold = 0;        for (MinValue = 0; MinValue < 256 && HistGram[MinValue] == 0; MinValue++) ;        for (MaxValue = 255; MaxValue > MinValue && HistGram[MinValue] == 0; MaxValue--) ;        if (MaxValue == MinValue) return MaxValue;          // 图像中只有一个颜色                     if (MinValue + 1 == MaxValue) return MinValue;      // 图像中只有二个颜色        for (Y = MinValue; Y <= MaxValue; Y++) Amount += HistGram[Y];        //  像素总数        for (Y = MinValue; Y <= MaxValue; Y++)   HistGramD[Y] = (double)HistGram[Y] / Amount+1e-17;        MaxEntropy = double.MinValue; ;        for (Y = MinValue + 1; Y < MaxValue; Y++)        {            SumIntegral = 0;            for (X = MinValue; X <= Y; X++) SumIntegral += HistGramD[X];            EntropyBack = 0;            for (X = MinValue; X <= Y; X++) EntropyBack += (-HistGramD[X] / SumIntegral * Math.Log(HistGramD[X] / SumIntegral));            EntropyFore = 0;            for (X = Y + 1; X <= MaxValue; X++) EntropyFore += (-HistGramD[X] / (1 - SumIntegral) * Math.Log(HistGramD[X] / (1 - SumIntegral)));            if (MaxEntropy < EntropyBack + EntropyFore)            {                Threshold = Y;                MaxEntropy = EntropyBack + EntropyFore;            }        }        return Threshold;    }
复制代码

 

     八、力矩保持法 
     1、描述:

  该算法通过选择恰当的阈值从而使得二值后的图像和原始的灰度图像具有三个相同的初始力矩值。

  2、原理:

    参考论文:W. Tsai, “Moment-preserving thresholding: a new approach,” Comput.Vision Graphics Image Process., vol. 29, pp. 377-393, 1985.

     由于无法下载到该论文(收费的),仅仅给出从其他一些资料中找到的公式共享一下。

             

    其中的A\B\C的函数可见代码部分。

   3、参考代码:

 

复制代码
public static byte GetMomentPreservingThreshold(int[] HistGram)    {        int X, Y, Index = 0, Amount=0;        double[] Avec = new double[256];        double X2, X1, X0, Min;        for (Y = 0; Y <= 255; Y++) Amount += HistGram[Y];        //  像素总数        for (Y = 0; Y < 256; Y++) Avec[Y] = (double)A(HistGram, Y) / Amount;       // The threshold is chosen such that A(y,t)/A(y,n) is closest to x0.        // The following finds x0.        X2 = (double)(B(HistGram, 255) * C(HistGram, 255) - A(HistGram, 255) * D(HistGram, 255)) / (double)(A(HistGram, 255) * C(HistGram, 255) - B(HistGram, 255) * B(HistGram, 255));        X1 = (double)(B(HistGram, 255) * D(HistGram, 255) - C(HistGram, 255) * C(HistGram, 255)) / (double)(A(HistGram, 255) * C(HistGram, 255) - B(HistGram, 255) * B(HistGram, 255));        X0 = 0.5 - (B(HistGram, 255) / A(HistGram, 255) + X2 / 2) / Math.Sqrt(X2 * X2 - 4 * X1);        for (Y = 0, Min = double.MaxValue; Y < 256; Y++)        {            if (Math.Abs(Avec[Y] - X0) < Min)            {                Min = Math.Abs(Avec[Y] - X0);                Index = Y;            }        }        return (byte)Index;    }    private static double A(int[] HistGram, int Index)    {        double Sum = 0;        for (int Y = 0; Y <= Index; Y++)            Sum += HistGram[Y];        return Sum;    }    private static double B(int[] HistGram, int Index)    {        double Sum = 0;        for (int Y = 0; Y <= Index; Y++)            Sum += (double)Y * HistGram[Y];        return Sum;    }    private static double C(int[] HistGram, int Index)    {        double Sum = 0;        for (int Y = 0; Y <= Index; Y++)            Sum += (double)Y * Y * HistGram[Y];        return Sum;    }    private static double D(int[] HistGram, int Index)    {        double Sum = 0;        for (int Y = 0; Y <= Index; Y++)            Sum += (double)Y * Y * Y * HistGram[Y];        return Sum;    }
复制代码

 

   对于很多图像,该算法页能取得比较满意的结果。

   九、基于模糊集理论的阈值

    该算法的具体分析可见:基于模糊集理论的一种图像二值化算法的原理、实现效果及代码

    此法也借用香农熵的概念,该算法一般都能获得较为理想的分割效果,不管是对双峰的还是单峰的图像。

  这是篇很古老的论文中的算法,发表与1994年,是清华大学黄良凯(Liang-kai Huang) 所写,因此国外一些论文里和代码里称之为Huang's fuzzy thresholding method。虽然古老也很简单,但是其算法的原理还是值得学习的。

     该论文的原文可从此处下载: Image thresholding by minimizing the measure of fuzziness。

     该论文结合了当时处于研究热潮的模糊集理论,提出了一种具有较好效果的图像二值化算法,本文主要是对其进行简单的翻译和注释,并提供了测试代码。

     一、模糊集及其隶属度函数

     首先,我们假定X代表一副大小为M×N的具有L个色阶的灰度图像,而xmn代表图像X中点(m,n)处的像素灰度值,定义μx(xmn)表示该点具有某种属性的隶属度值,也就是说我们定义了一个从图像X映射到[0,1]区间的模糊子集,用专业的模糊集表达,即有:

                                       

       其中0≤μx(xmn)≤1,m=0,1,...M-1,n=0,1,...N-1。对于二值化来说,每个像素对于其所属的类别(前景或背景)都应该有很相近的关系,因此,我们可以这种关系来表示μx(xmn)的值。

       定义h(g)表示图像中具有灰度级g的像素的个数,对于一个给定的阈值t,背景和前景各自色阶值的平均值μ0和μ1可用下式表示:

                                

                                         

      上述μ0和μ1,可以看成是指定阈值t所对应的前景和背景的目标值,而图像X中某一点和其所述的区域之间的关系,在直觉上应该和该点的色阶值与所属区域的目标值之间的差异相关。因此,对于点(m,n),我们提出如下的隶属度定义函数:

         

  其中C是一个常数,该常数使得0.5≤μx(xmn)≤1。因此,对于一个给定的阈值t,图像中任何一个像素要么属于背景,要么属于前景,因此,每个像素的隶属度不应小于0.5。

     C值在实际的编程中,可以用图像的最大灰度值减去最小灰度值来表达,即 C=gmax-gmin;

  二、模糊度的度量及取阈值的原则

     模糊度表示了一个模糊集的模糊程度,有好几种度量方式已经被提及了,本文仅仅使用了香农熵函数来度量模糊度。

     基于香农熵函数,一个模糊集A的熵定义为:

       

      其中香农函数:

      

      扩展到2维的图像,图像X的熵可以表达为:

     

  因为灰度图像至多只有L个色阶,因此使用直方图式(7)可进一步写成:

         

    可以证明式(6)在区间[0,0.5]之间是单调递增而在[0.5,1]之间是单调递减的,并且E(X)具有以下属性:

     (1)0≤E(X)≤1 ;

      (2)如果μx(xmn)=0或者μx(xmn)=1时,E(X)具有最小值0,在本文中μx(xmn)只可能为1,此时分类具有最好的明确性。

     (3)当μx(xmn)=0.5,E(X)获得最大值1,此时的分类具有最大的不明确性。

     那么对于图像X,我们确定最好的阈值t的原则就是:对于所有的可能的阈值t,取香农熵值最小时的那个t为最终的分割阈值。

     三、编程中的技巧

     有了上述原理,其实编程也是件很容易的事情了,你可以按照你的想法去做,不过作者论文中的阐述会让代码写起来更清晰、更有效。

     首先,为了表达方便,我们定义如下一些表达式:

        

     根据上述表达式,可以知道S(L-1)及W(L-1)对于一副图像来说是个常量,其中S(L-1)明显就是像素的总个数。

  我们的算法步骤如下:

     (1)、计算S(L-1)、W(L-1),设置初始阈值t=gmin,令S(t-1)=0、W(t-1)=0;

      (2)、 计算下面算式:

                

      稍微有点数学基础的人都应该能看懂上述算式的推导原理。

        根据式(2)和式(3),可以知道背景和前景的区域的平均灰度值为:

          

   上式中int表示取整操作。

       (3)根据式(4)及式(11)计算图像的模糊度;

       (4)令t=t+1,然后重新执行步骤2,直到t=gmax-1;

        (5)找到整个过程中的最小模糊度值对应的阈值t,并作为最佳的分割阈值。

     为了稍微加快点速度,上述式4中的计算可以在步骤1中用一查找表实现。


public static int GetHuangFuzzyThreshold(int[] HistGram)
{
    int X, Y;
    int First, Last;
    int Threshold = -1;
    double BestEntropy = Double.MaxValue, Entropy;
    //   找到第一个和最后一个非0的色阶值
    for (First = 0; First < HistGram.Length && HistGram[First] == 0; First++) ;
    for (Last = HistGram.Length - 1; Last > First && HistGram[Last] == 0; Last--) ;
    if (First == Last) return First;                // 图像中只有一个颜色
    if (First + 1 == Last) return First;            // 图像中只有二个颜色


    // 计算累计直方图以及对应的带权重的累计直方图
    int[] S = new int[Last + 1];
    int[] W = new int[Last + 1];            // 对于特大图,此数组的保存数据可能会超出int的表示范围,可以考虑用long类型来代替
    S[0] = HistGram[0];
    for (Y = First > 1 ? First : 1; Y <= Last; Y++)
    {
        S[Y] = S[Y - 1] + HistGram[Y];
        W[Y] = W[Y - 1] + Y * HistGram[Y];
    }


    // 建立公式(4)及(6)所用的查找表
    double[] Smu = new double[Last + 1 - First];
    for (Y = 1; Y < Smu.Length; Y++)
    {
        double mu = 1 / (1 + (double)Y / (Last - First));               // 公式(4)
        Smu[Y] = -mu * Math.Log(mu) - (1 - mu) * Math.Log(1 - mu);      // 公式(6)
    }


    // 迭代计算最佳阈值
    for (Y = First; Y <= Last; Y++)
    {
        Entropy = 0;
        int mu = (int)Math.Round((double)W[Y] / S[Y]);             // 公式17
        for (X = First; X <= Y; X++)
            Entropy += Smu[Math.Abs(X - mu)] * HistGram[X];
        mu = (int)Math.Round((double)(W[Last] - W[Y]) / (S[Last] - S[Y]));  // 公式18
        for (X = Y + 1; X <= Last; X++)
            Entropy += Smu[Math.Abs(X - mu)] * HistGram[X];       // 公式8
        if (BestEntropy > Entropy)
        {
            BestEntropy = Entropy;      // 取最小熵处为最佳阈值
            Threshold = Y;
        }
    }
    return Threshold;
}

  十、Kittler最小错误分类法

    由于精力有限,以下几种算法仅仅给出算法的论文及相关的代码。

    该算法具体的分析见:

  • Kittler, J & Illingworth, J (1986), "Minimum error thresholding", Pattern Recognition 19: 41-47

  参考代码:

复制代码
 public static int GetKittlerMinError(int[] HistGram)    {        int X, Y;        int MinValue, MaxValue;        int Threshold ;        int PixelBack, PixelFore;        double OmegaBack, OmegaFore, MinSigma, Sigma, SigmaBack, SigmaFore;        for (MinValue = 0; MinValue < 256 && HistGram[MinValue] == 0; MinValue++) ;        for (MaxValue = 255; MaxValue > MinValue && HistGram[MinValue] == 0; MaxValue--) ;        if (MaxValue == MinValue) return MaxValue;          // 图像中只有一个颜色                     if (MinValue + 1 == MaxValue) return MinValue;      // 图像中只有二个颜色        Threshold = -1;        MinSigma = 1E+20;        for (Y = MinValue; Y < MaxValue; Y++)        {            PixelBack = 0; PixelFore = 0;            OmegaBack = 0; OmegaFore = 0;            for (X = MinValue; X <= Y; X++)            {                PixelBack += HistGram[X];                OmegaBack = OmegaBack + X * HistGram[X];            }            for (X = Y + 1; X <= MaxValue; X++)            {                PixelFore += HistGram[X];                OmegaFore = OmegaFore + X * HistGram[X];            }            OmegaBack = OmegaBack / PixelBack;            OmegaFore = OmegaFore / PixelFore;            SigmaBack = 0; SigmaFore = 0;            for (X = MinValue; X <= Y; X++) SigmaBack = SigmaBack + (X - OmegaBack) * (X - OmegaBack) * HistGram[X];            for (X = Y + 1; X <= MaxValue; X++) SigmaFore = SigmaFore + (X - OmegaFore) * (X - OmegaFore) * HistGram[X];            if (SigmaBack == 0 || SigmaFore == 0)            {                if (Threshold == -1)                    Threshold = Y;            }            else            {                SigmaBack = Math.Sqrt(SigmaBack / PixelBack);                SigmaFore = Math.Sqrt(SigmaFore / PixelFore);                Sigma = 1 + 2 * (PixelBack * Math.Log(SigmaBack / PixelBack) + PixelFore * Math.Log(SigmaFore / PixelFore));                if (Sigma < MinSigma)                {                    MinSigma = Sigma;                    Threshold = Y;                }            }        }        return Threshold;    }
复制代码

  从实际的运行效果看,该算法并不很好。

  十一:ISODATA(也叫做intermeans法)

  参考论文:

  • Ridler, TW & Calvard, S (1978), "Picture thresholding using an iterative selection method", IEEE Transactions on Systems, Man and Cybernetics 8: 630-632, <http://ieeexplore.ieee.org/xpls/abs_all.jsp?arnumber=4310039>

     参考代码(未做整理):

 

复制代码
 // Also called intermeans    // Iterative procedure based on the isodata algorithm [T.W. Ridler, S. Calvard, Picture     // thresholding using an iterative selection method, IEEE Trans. System, Man and     // Cybernetics, SMC-8 (1978) 630-632.]     // The procedure divides the image into objects and background by taking an initial threshold,    // then the averages of the pixels at or below the threshold and pixels above are computed.     // The averages of those two values are computed, the threshold is incremented and the     // process is repeated until the threshold is larger than the composite average. That is,    //  threshold = (average background + average objects)/2    // The code in ImageJ that implements this function is the getAutoThreshold() method in the ImageProcessor class.     //    // From: Tim Morris (dtm@ap.co.umist.ac.uk)    // Subject: Re: Thresholding method?    // posted to sci.image.processing on 1996/06/24    // The algorithm implemented in NIH Image sets the threshold as that grey    // value, G, for which the average of the averages of the grey values    // below and above G is equal to G. It does this by initialising G to the    // lowest sensible value and iterating:    // L = the average grey value of pixels with intensities < G    // H = the average grey value of pixels with intensities > G    // is G = (L + H)/2?    // yes => exit    // no => increment G and repeat    //    // There is a discrepancy with IJ because they are slightly different methods    public static int GetIsoDataThreshold(int[] HistGram)    {        int i, l, toth, totl, h, g = 0;        for (i = 1; i < HistGram.Length; i++)        {            if (HistGram[i] > 0)            {                g = i + 1;                break;            }        }        while (true)        {            l = 0;            totl = 0;            for (i = 0; i < g; i++)            {                totl = totl + HistGram[i];                l = l + (HistGram[i] * i);            }            h = 0;            toth = 0;            for (i = g + 1; i < HistGram.Length; i++)            {                toth += HistGram[i];                h += (HistGram[i] * i);            }            if (totl > 0 && toth > 0)            {                l /= totl;                h /= toth;                if (g == (int)Math.Round((l + h) / 2.0))                    break;            }            g++;            if (g > HistGram.Length - 2)            {                return 0;            }        }        return g;    }
复制代码

       十二、Shanbhag 法

        参考论文:

 

      Shanbhag, Abhijit G. (1994), "Utilization of information measure as a means of image thresholding", Graph. Models Image Process. (Academic Press, Inc.) 56 (5): 414--419, ISSN1049-9652, DOI 10.1006/cgip.1994.1037

     参考代码(未整理):

 

复制代码
public static int GetShanbhagThreshold(int[] HistGram)    {        int threshold;        int ih, it;        int first_bin;        int last_bin;        double term;        double tot_ent;  /* total entropy */        double min_ent;  /* max entropy */        double ent_back; /* entropy of the background pixels at a given threshold */        double ent_obj;  /* entropy of the object pixels at a given threshold */        double[] norm_histo = new double[HistGram.Length]; /* normalized histogram */        double[] P1 = new double[HistGram.Length]; /* cumulative normalized histogram */        double[] P2 = new double[HistGram.Length];        int total = 0;        for (ih = 0; ih < HistGram.Length; ih++)            total += HistGram[ih];        for (ih = 0; ih < HistGram.Length; ih++)            norm_histo[ih] = (double)HistGram[ih] / total;        P1[0] = norm_histo[0];        P2[0] = 1.0 - P1[0];        for (ih = 1; ih < HistGram.Length; ih++)        {            P1[ih] = P1[ih - 1] + norm_histo[ih];            P2[ih] = 1.0 - P1[ih];        }        /* Determine the first non-zero bin */        first_bin = 0;        for (ih = 0; ih < HistGram.Length; ih++)        {            if (!(Math.Abs(P1[ih]) < 2.220446049250313E-16))            {                first_bin = ih;                break;            }        }        /* Determine the last non-zero bin */        last_bin = HistGram.Length - 1;        for (ih = HistGram.Length - 1; ih >= first_bin; ih--)        {            if (!(Math.Abs(P2[ih]) < 2.220446049250313E-16))            {                last_bin = ih;                break;            }        }        // Calculate the total entropy each gray-level        // and find the threshold that maximizes it         threshold = -1;        min_ent = Double.MaxValue;        for (it = first_bin; it <= last_bin; it++)        {            /* Entropy of the background pixels */            ent_back = 0.0;            term = 0.5 / P1[it];            for (ih = 1; ih <= it; ih++)            { //0+1?                ent_back -= norm_histo[ih] * Math.Log(1.0 - term * P1[ih - 1]);            }            ent_back *= term;            /* Entropy of the object pixels */            ent_obj = 0.0;            term = 0.5 / P2[it];            for (ih = it + 1; ih < HistGram.Length; ih++)            {                ent_obj -= norm_histo[ih] * Math.Log(1.0 - term * P2[ih]);            }            ent_obj *= term;            /* Total entropy */            tot_ent = Math.Abs(ent_back - ent_obj);            if (tot_ent < min_ent)            {                min_ent = tot_ent;                threshold = it;            }        }        return threshold;    }
复制代码

     十三、Yen法

  参考论文:

     1) Yen J.C., Chang F.J., and Chang S. (1995) "A New Criterion  for Automatic Multilevel Thresholding" IEEE Trans. on Image  Processing, 4(3): 370-378
     2) Sezgin M. and Sankur B. (2004) "Survey over Image Thresholding Techniques and Quantitative Performance Evaluation" Journal of  Electronic Imaging, 13(1): 146-165

       参考代码(未整理):

复制代码
 // M. Emre Celebi    // 06.15.2007    // Ported to ImageJ plugin by G.Landini from E Celebi's fourier_0.8 routines    public static int GetYenThreshold(int[] HistGram)    {        int threshold;        int ih, it;        double crit;        double max_crit;        double[] norm_histo = new double[HistGram.Length]; /* normalized histogram */        double[] P1 = new double[HistGram.Length]; /* cumulative normalized histogram */        double[] P1_sq = new double[HistGram.Length];        double[] P2_sq = new double[HistGram.Length];        int total = 0;        for (ih = 0; ih < HistGram.Length; ih++)            total += HistGram[ih];        for (ih = 0; ih < HistGram.Length; ih++)            norm_histo[ih] = (double)HistGram[ih] / total;        P1[0] = norm_histo[0];        for (ih = 1; ih < HistGram.Length; ih++)            P1[ih] = P1[ih - 1] + norm_histo[ih];        P1_sq[0] = norm_histo[0] * norm_histo[0];        for (ih = 1; ih < HistGram.Length; ih++)            P1_sq[ih] = P1_sq[ih - 1] + norm_histo[ih] * norm_histo[ih];        P2_sq[HistGram.Length - 1] = 0.0;        for (ih = HistGram.Length - 2; ih >= 0; ih--)            P2_sq[ih] = P2_sq[ih + 1] + norm_histo[ih + 1] * norm_histo[ih + 1];        /* Find the threshold that maximizes the criterion */        threshold = -1;        max_crit = Double.MinValue;        for (it = 0; it < HistGram.Length; it++)        {            crit = -1.0 * ((P1_sq[it] * P2_sq[it]) > 0.0 ? Math.Log(P1_sq[it] * P2_sq[it]) : 0.0) + 2 * ((P1[it] * (1.0 - P1[it])) > 0.0 ? Math.Log(P1[it] * (1.0 - P1[it])) : 0.0);            if (crit > max_crit)            {                max_crit = crit;                threshold = it;            }        }        return threshold;    }
复制代码

      一行很多代码是摘自开源软件ImageJ的资料,读者也可以参考:http://fiji.sc/wiki/index.php/Auto_Threshold  这里获得更多的信息。

      最后,我对这些算法的做了简单的UI界面,供有兴趣的读者参考。

      工程代码下载:http://files.cnblogs.com/Imageshop/HistgramBinaryzation.rar

 

http://www.cnblogs.com/carekee/articles/3643394.html

0 0