1. 程式人生 > >自適應閾值演算法(大津閾值法

自適應閾值演算法(大津閾值法

                最大類間方差法是由日本學者大津於1979年提出的,是一種自適應的閾值確定的方法,又叫大津法,簡稱OTSU。它是按影象的灰度特性,將影象分成背景和目標2部分。背景和目標之間的類間方差越大,說明構成影象的2部分的差別越大,當部分目標錯分為背景或部分背景錯分為目標都會導致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)

採用遍歷的方法得到使類間方差最大的閾值T,即為所求。

由於,當影象在254或255灰度值上沒有畫素點時,求平均灰度時會出現0/0的情況,為避免丟擲異常,可在當出現前景畫素數為零時,跳出迴圈。

以下是c++程式碼(用到OpenCV):

  1. int otsuThreshold(IplImage* img)
  2. {
  3.  int T = 0;//閾值
  4.  int height = img->height;
  5.  int width  = img->width;
  6.  int step      = img->widthStep;
  7.  int channels  = img->nChannels;
  8.  uchar* data  = (uchar*)img->imageData;
  9.  double gSum0;//第一類灰度總值
  10.  double gSum1;//第二類灰度總值
  11.  double N0 = 0;//前景畫素數
  12.  double N1 = 0;//背景畫素數
  13.  double u0 = 0;//前景畫素平均灰度
  14.  double u1 = 0;//背景畫素平均灰度
  15.  double w0 = 0;//前景畫素點數佔整幅影象的比例為ω0
  16.  double w1 = 0;//背景畫素點數佔整幅影象的比例為ω1
  17.  double u = 0;//總平均灰度
  18.  double tempg = -1;//臨時類間方差
  19.  double g = -1;//類間方差
  20.  double Histogram[256]={0};// = new double[256];//灰度直方圖
  21.  double
    N = width*height;//總畫素數
  22.  for(int i=0;i<height;i++)
  23.  {//計算直方圖
  24.   for(int j=0;j<width;j++)
  25.   {
  26.    double temp =data[i*step + j * 3] * 0.114 + data[i*step + j * 3+1] * 0.587 + data[i*step + j * 3+2] * 0.299;
  27.    temp = temp<0? 0:temp;
  28.    temp = temp>255? 255:temp;
  29.    Histogram[(int)temp]++;
  30.   }
  31.  }
  32.  //計算閾值
  33.  for (int
    i = 0;i<256;i++)
  34.  {
  35.   gSum0 = 0;
  36.   gSum1 = 0;
  37.   N0 += Histogram[i];   
  38.   N1 = N-N0;
  39.   if(0==N1)break;//當出現前景無畫素點時,跳出迴圈
  40.   w0 = N0/N;
  41.   w1 = 1-w0;
  42.   for (int j = 0;j<=i;j++)
  43.   {
  44.    gSum0 += j*Histogram[j];
  45.   }
  46.   u0 = gSum0/N0;
  47.   for(int k = i+1;k<256;k++)
  48.   {
  49.    gSum1 += k*Histogram[k];
  50.   }
  51.   u1 = gSum1/N1;
  52.   //u = w0*u0 + w1*u1;
  53.   g = w0*w1*(u0-u1)*(u0-u1);
  54.   if (tempg<g)
  55.   {
  56.    tempg = g;
  57.    T = i;
  58.   }
  59.  }
  60.  return T;
  61. }