【轉】二值化 大津法---OTSU算法

傳送門:https://www.cnblogs.com/ranjiewen/p/6385564.html

傳送門:https://blog.csdn.net/u012198575/article/details/81128799

簡介:

大津法(OTSU)是一種確定圖像二值化分割閾值的算法,由日本學者大津於1979年提出。從大津法的原理上來講,該方法又稱作最大類間方差法,因爲按照大津法求得的閾值進行圖像二值化分割後,前景與背景圖像的類間方差最大(何爲類間方差?原理中有介紹)。

OTSU算法

OTSU算法也稱最大類間差法,有時也稱之爲大津算法,由大津於1979年提出,被認爲是圖像分割中閾值選取的最佳算法,計算簡單,不受圖像亮度和對比度的影響,因此在數字圖像處理上得到了廣泛的應用。它是按圖像的灰度特性,將圖像分成背景和前景兩部分。因方差是灰度分佈均勻性的一種度量,背景和前景之間的類間方差越大,說明構成圖像的兩部分的差別越大,當部分前景錯分爲背景或部分背景錯分爲前景都會導致兩部分差別變小。因此,使類間方差最大的分割意味着錯分概率最小。

公式:  記 M = 256 單通道灰度分級 Sum = 像素總數

  1. 背景像素佔比 \omega1 = \frac{N1}{Sum}  
  2. 前景像素佔比\omega2 = 1- \omega1 = \frac{N2}{Sum} =1- \frac{N1}{Sum}
  3. 背景的平均灰度值\mu 1 = \sum_{i = 0}^{t} i *Pr(i | C_{0}) = \sum_{i = 0}^{t} i *Pi / \sum_{i = 0}^{t} Pi = \frac{\mu(t))}{\omega_{1}}
  4. 前景的平均灰度值\mu 2 = \sum_{i = t+1}^{M - 1} i *Pr(i | C_{1}) = \sum_{i = t+1}^{M - 1} i *Pi / \sum_{i = t+1}^{M - 1} Pi = \frac{\mu - \mu(t))}{\omega _{2}}
  5. 0~M灰度區間的灰度累計值\mu = \mu1*\omega 1 + \mu2*\omega 2
  6. 類間方差:g = \omega 1 * (\mu - \mu1)^{2} + \omega 2 * (\mu - \mu2)^{2}
  7. 將公式3.4.5帶入公式6 可得最終簡化公式: g = \omega 1 * \omega2 * (\mu1 - \mu2)^{2}

原理:

對於圖像I(x,y),前景(即目標)和背景的分割閾值記作T,屬於前景的像素點數佔整幅圖像的比例記爲ω0,其平均灰度μ0;背景像素點數佔整幅圖像的比例爲ω1,其平均灰度爲μ1。圖像的總平均灰度記爲μ,類間方差記爲g。

假設圖像的背景較暗,並且圖像的大小爲M×N,圖像中像素的灰度值小於閾值T的像素個數記作N0,像素灰度大於閾值T的像素個數記作N1,則有:
      ω0=N0/ M×N (1)
      ω1=N1/ M×N (2)
      N0+N1=M×N (3)
      ω0+ω1=1    (4)
      μ=ω0*μ0+ω1*μ1 (5)
      g=ω0(μ0-μ)^2+ω1(μ1-μ)^2 (6)
將式(5)代入式(6),得到等價公式:
      g=ω0ω1(μ0-μ1)^2    (7) 這就是類間方差
採用遍歷的方法得到使類間方差g最大的閾值T,即爲所求。

matlab函數:

matlab中函數graythresh既是使用大津法求得分割閾值T。用法如下:

      T = graythresh(img);

      BW = im2bw(img,T);

大津法的形象理解:

對於直方圖有兩個峯值的圖像,大津法求得的T近似等於兩個峯值之間的低谷。

      imhist(img);

T = graythresh(img);

如下圖爲圖像的直方圖,使大津法求得的T=0.5294,轉換在[0,255]之間爲134.9970,只好是兩個峯值之間低谷的位置。

                                       

OpenCV的二值化操作中,有一種“大津閾值處理”的方法,使用函數cvThreshold(image,image2,0,255,CV_THRESH_OTSU) 實現,該函數就會使用大律法OTSU得到的全局自適應閾值來進行二值化圖片,而參數中的threshold不再起作用。

以下是一段在OpenCV中實現的C語言程序,即一個使用OTSU算法提取圖像閾值的函數,輸入參數爲一個圖像指針,返回分割該圖像的最佳閾值。

其中的變量說明:當分割的閾值爲t時

w0爲背景像素點佔整幅圖像的比例

u0爲w0平均灰度

w1爲前景像素點佔整幅圖像的比例

u1爲w1平均灰度

u爲整幅圖像的平均灰度

公式:g = w0*pow((u-u0),2) + w1*pow((u-u1),2)

int MyAutoFocusDll::otsuThreshold(IplImage *frame)
{
    const int GrayScale = 256;
    int width = frame->width;
    int height = frame->height;
    int pixelCount[GrayScale];
    float pixelPro[GrayScale];
    int i, j, pixelSum = width * height, threshold = 0;
    uchar* data = (uchar*)frame->imageData;  //指向像素數據的指針
    for (i = 0; i < GrayScale; i++)
    {
        pixelCount[i] = 0;
        pixelPro[i] = 0;
    }

    //統計灰度級中每個像素在整幅圖像中的個數  
    for (i = 0; i < height; i++)
    {
        for (j = 0; j < width; j++)
        {
            pixelCount[(int)data[i * width + j]]++;  //將像素值作爲計數數組的下標
        }
    }

    //計算每個像素在整幅圖像中的比例  
    float maxPro = 0.0;
    int kk = 0;
    for (i = 0; i < GrayScale; i++)
    {
        pixelPro[i] = (float)pixelCount[i] / pixelSum;
        if (pixelPro[i] > maxPro)
        {
            maxPro = pixelPro[i];
            kk = i;
        }
    }

    //遍歷灰度級[0,255]  
    float w0, w1, u0tmp, u1tmp, u0, u1, u, deltaTmp, deltaMax = 0;
    for (i = 0; i < GrayScale; i++)     // i作爲閾值
    {
        w0 = w1 = u0tmp = u1tmp = u0 = u1 = u = deltaTmp = 0;
        for (j = 0; j < GrayScale; j++)
        {
            if (j <= i)   //背景部分  
            {
                w0 += pixelPro[j];
                u0tmp += j * pixelPro[j];
            }
            else   //前景部分  
            {
                w1 += pixelPro[j];
                u1tmp += j * pixelPro[j];
            }
        }
        u0 = u0tmp / w0;
        u1 = u1tmp / w1;
        u = u0tmp + u1tmp;
        deltaTmp = w0 * pow((u0 - u), 2) + w1 * pow((u1 - u), 2);
        if (deltaTmp > deltaMax)
        {
            deltaMax = deltaTmp;
            threshold = i;
        }
    }

    return threshold;
}

效果展示:

                                                                        

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章