【數字圖像處理】灰度圖像二值化


 

灰度圖像

每副圖像的每個像素對應二維空間中一個特定的位置,並且有一個或者多個與那個點相關的采樣值組成數值。

灰度圖像,也稱為灰階圖像,圖像中每個像素可以由0(黑)到255(白)的亮度值(Intensity)表示。0-255之間表示不同的灰度級。

 

灰度圖像二值化

二值化:以一個值(閾值)為基准,大於(等於)這個值的數全部變為是1(或者0),小於等於這個數的就全部將他們變為0(或1)。

二值化算法處理飛思卡爾賽道思路:設定一個閾值valve,對於圖像矩陣中的每一行,從左至右比較各像素值和閾值的大小,若像素值大於或等於閾值,則判定該像素對應的是白色賽道;反之,則判定對應的是黑色的目標引導線。

記下第一次和最后一次出現像素值小於閾值時的像素點的列號,算出兩者的平均值,以此作為該行上目標引導線的位置。

攝像頭的二值化的代碼:

 

Void image_binaryzation()

{

for(int i=0;i

{

    for(int j=0;j

    {

if(Image[i][j] >= Threshold)

       Image_new[i][j]=1;

else

    Image_new[i][j]=0;

    }

}

}

Row是對應采集到的行數,Col是列數,Image[i][j]是攝像頭采集未二值化的數據存放的數組,Img[i][j]是新建的存放二值化后的數組。

 

合適的閾值

在閾值二值化中,最主要的是選取合適的閾值,這也是二值化的難點所在。常用的二值化閾值選取方法有雙峰法、p參數法、大律法(Otsu法)、最大熵閾值法、迭代法等。

大律法(Otsu法)

Otsu方法又名最大類間差方法,通過統計整個圖像的直方圖特性來實現全局閾值T的自動選取,其算法步驟為:
1) 先計算圖像的直方圖,即將圖像所有的像素點按照0~255共256個bin,統計落在每個bin的像素點數量
2) 歸一化直方圖,也即將每個bin中像素點數量除以總的像素點
3) i表示分類的閾值,也即一個灰度級,從0開始迭代
4) 通過歸一化的直方圖,統計0~i 灰度級的像素(假設像素值在此范圍的像素叫做前景像素) 所占整幅圖像的比例w0,並統計前景像素的平均灰度u0;統計i~255灰度級的像素(假設像素值在此范圍的像素叫做背景像素) 所占整幅圖像的比例w1,並統計背景像素的平均灰度u1;
5) 計算前景像素和背景像素的方差 g = w0*w1*(u0-u1) (u0-u1)
6) i++;轉到4),直到i為256時結束迭代
7)將最大g相應的i值作為圖像的全局閾值
缺陷:OSTU算法在處理光照不均勻的圖像的時候,效果會明顯不好,因為利用的是全局像素信息。
解決光照不均勻:https://blog.csdn.net/kk55guang2/article/details/78475414
              https://blog.csdn.net/kk55guang2/article/details/78490069
              https://wenku.baidu.com/view/84e5eb271a37f111f0855b2d.html
***************************************************************/ 
int GetOSTU(uint8_t tmImage[Use_ROWS][Use_Line]) 
{ 
/**
  * @brief    未優化過的大津法
  *
  * @param    運算時間比較長
  *
  * @return   實測120*160的圖像
  *
  * @note     K66 220MHz需要9ms
  *
  * @example  
  *
  * @date     2019/4/16 星期二
  */
//    int width = Use_ROWS;
//    int height = Use_Line;
//    int x = 0, y = 0;
//    int pixelCount[256];
//    float pixelPro[256];
//    int i, j, pixelSum = width * height, threshold = 0;
//    
//    
//    //初始化
//    for (i = 0; i < 256; i++)
//    {
//        pixelCount[i] = 0;
//        pixelPro[i] = 0;
//    }
//    
//    //統計灰度級中每個像素在整幅圖像中的個數
//    for (i = y; i < height; i++)
//    {
//        for (j = x; j <width; j++)
//        {
//            pixelCount[tmImage[i][j]]++;
//        }
//    }
//    
//    //計算每個像素在整幅圖像中的比例
//    for (i = 0; i < 256; i++)
//    {
//        pixelPro[i] = (float)(pixelCount[i]) / (float)(pixelSum);
//    }
//    
//    //經典ostu算法,得到前景和背景的分割
//    //遍歷灰度級[0,255],計算出方差最大的灰度值,為最佳閾值
//    float w0, w1, u0tmp, u1tmp, u0, u1, u, deltaTmp, deltaMax = 0;
//    for (i = 0; i < 256; i++)
//    {
//        w0 = w1 = u0tmp = u1tmp = u0 = u1 = u = deltaTmp = 0;
//        
//        for (j = 0; j < 256; j++)
//        {
//            if (j <= i) //背景部分
//            {
//                //以i為閾值分類,第一類總的概率
//                w0 += pixelPro[j];
//                u0tmp += j * pixelPro[j];
//            }
//            else       //前景部分
//            {
//                //以i為閾值分類,第二類總的概率
//                w1 += pixelPro[j];
//                u1tmp += j * pixelPro[j];
//            }
//        }
//        
//        u0 = u0tmp / w0;        //第一類的平均灰度
//        u1 = u1tmp / w1;        //第二類的平均灰度
//        u = u0tmp + u1tmp;        //整幅圖像的平均灰度
//        //計算類間方差
//        deltaTmp = w0 * (u0 - u)*(u0 - u) + w1 * (u1 - u)*(u1 - u);
//        //找出最大類間方差以及對應的閾值
//        if (deltaTmp > deltaMax)
//        {
//            deltaMax = deltaTmp;
//            threshold = i;
//        }
//    }
//    //返回最佳閾值;
//    return threshold;
    
/**
  * @brief    優化過的大津法
  *
  * @param    大大減少運算時間
  *
  * @return   實測K66 220MHz 120*160的圖像
  *
  * @note     只需要1.5ms
  *
  * @example  未優化的大津法需要9ms
  *
  * @date     2019/4/16 星期二
  */ 
    int16_t i,j; 
    uint32_t Amount = 0; 
    uint32_t PixelBack = 0; 
    uint32_t PixelIntegralBack = 0; 
    uint32_t PixelIntegral = 0; 
    int32_t PixelIntegralFore = 0; 
    int32_t PixelFore = 0; 
    float OmegaBack, OmegaFore, MicroBack, MicroFore, SigmaB, Sigma; // 類間方差; 
    int16_t MinValue, MaxValue; 
    uint8_t Threshold = 0;
    uint8_t HistoGram[256];              //  
    
    for (j = 0; j < 256; j++)  HistoGram[j] = 0; //初始化灰度直方圖 
    
    for (j = 0; j < Use_ROWS; j++) 
    { 
        for (i = 0; i < Use_Line; i++) 
        { 
            HistoGram[tmImage[j][i]]++; //統計灰度級中每個像素在整幅圖像中的個數
        } 
    } 
    
    for (MinValue = 0; MinValue < 256 && HistoGram[MinValue] == 0; MinValue++) ;        //獲取最小灰度的值
    for (MaxValue = 255; MaxValue > MinValue && HistoGram[MinValue] == 0; MaxValue--) ; //獲取最大灰度的值
    
    if (MaxValue == MinValue)      return MaxValue;         // 圖像中只有一個顏色    
    if (MinValue + 1 == MaxValue)  return MinValue;         // 圖像中只有二個顏色
    
    for (j = MinValue; j <= MaxValue; j++)    Amount += HistoGram[j];        //  像素總數
    
    PixelIntegral = 0;
    for (j = MinValue; j <= MaxValue; j++)
    {
        PixelIntegral += HistoGram[j] * j;//灰度值總數
    }
    SigmaB = -1;
    for (j = MinValue; j < MaxValue; j++)
    {
        PixelBack = PixelBack + HistoGram[j];   //前景像素點數
        PixelFore = Amount - PixelBack;         //背景像素點數
        OmegaBack = (float)PixelBack / Amount;//前景像素百分比
        OmegaFore = (float)PixelFore / Amount;//背景像素百分比
        PixelIntegralBack += HistoGram[j] * j;  //前景灰度值
        PixelIntegralFore = PixelIntegral - PixelIntegralBack;//背景灰度值
        MicroBack = (float)PixelIntegralBack / PixelBack;   //前景灰度百分比
        MicroFore = (float)PixelIntegralFore / PixelFore;   //背景灰度百分比
        Sigma = OmegaBack * OmegaFore * (MicroBack - MicroFore) * (MicroBack - MicroFore);//計算類間方差
        if (Sigma > SigmaB)                    //遍歷最大的類間方差g //找出最大類間方差以及對應的閾值
        {
            SigmaB = Sigma;
            Threshold = j;
        }
    }
    return Threshold;                        //返回最佳閾值;
} 

可以參考文獻

https://wenku.baidu.com/view/acc24dcf680203d8ce2f2469.html

https://wenku.baidu.com/view/bb6e38f7c8d376eeaeaa3163.html

 

二值化圖像去噪

對於二值化圖像而言,去除噪聲是很重要的一步。

思路:對任意像素點判斷是否為0,取得該像素點周圍8個或者四個像素點相加,總和等於255 * 8或者 255 *4,則說明該像素點為噪聲,置為255。

注意:領域的計算方法是沒有邊界的,所以通常不計算圖像四邊。

            int bai;
                    for(int i = 1; i < Use_ROWS-1; i++)   
                    {
                        for(int j =1; j < Use_Line-1; j++)
                        { if(Image_Use[i-1][j] == 255) continue;     
                          bai = Image_Use[i-1][j] + Image_Use[i-1][j-1] + Image_Use[i-1][j+1] +Image_Use[i][j+1] +Image_Use[i][j-1] +Image_Use[i+1][j] +Image_Use[i+1][j-1] +Image_Use[i+1][j+1] ;
                          if(bai == 2040)
                          Image_Use[i][j] = 255;
                          
                        }
                    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM