zoukankan      html  css  js  c++  java
  • 基于阈值的图像分割方法

    1. 直方图双峰法(mode 法)

      Prewitt 等人于六十年代中期提出的直方图双峰法(也称 mode 法) 是典型的全局单阈值分割方法。该方法的基本思想是:假设图像中有明显的目标和背景,则其灰度直方图呈双峰分布,当灰度级直方图具有双峰特性时,选取两峰之间的谷对应的灰度级作为阈值。如果背景的灰度值在整个图像中可以合理地看作为恒定,而且所有物体与背景都具有几乎相同的对比度,那么,选择一个正确的、固定的全局阈值会有较好的效果。例如图4.1所示:

    图4.1原始灰度图像

    图4.2灰度直方图

    选定阈值M为100

    算法实现:找到第一个峰值和第二个峰值, 再找到第一和第二个峰值之间的谷值,谷值就是那个阀值了。

    2. 固定阈值分割

      就是设定一个固定的值, 像素灰度大于就该像素编程0或者255或者其他的,小于的又等于什么的。

     1 for (int i = 0; i < nWidth; ++i)
     2 {
     3     for (int j = 0; j < nHigh; ++j)
     4     {
     5         if (Image[i][j] >= 阈值)
     6         {
     7             Image[i][j] = 255;
     8         }
     9         else
    10         {
    11             Image[i][j] = 0;
    12         }
    13     }
    14 }

      这个阈值选什么值呢, 1中的双峰法就是一个阈值产生的方法。

    3. 半阈值分割

     1 for (j = 0; j < height; j++)
     2 {
     3     for (i = 0; i < wide; i++)
     4     {
     5         lpSrc = p_data + wide*j + i;
     6         lpDst = temp + wide*j + i;
     7 
     8         if ((*lpSrc - 阈值) < 30)
     9             *lpDst = *lpSrc;
    10         else
    11             *lpDst = 255;
    12     }
    13 }

      不知道为什么这么做, 为什么这样就叫做半阈值?

    4. 迭代阈值图像分割

    http://topic.csdn.net/u/20080402/10/d3cb6789-fa60-4758-b232-7a89926f07b9.html

    迭代法是基于逼近的思想,其步骤如下: 

    1. 求出图象的最大灰度值和最小灰度值,分别记为ZMAX和ZMIN,令初始阈值T0=(ZMAX+ZMIN)/2; 

    2. 根据阈值TK将图象分割为前景和背景,分别求出两者的平均灰度值ZO和ZB 

    3. 求出新阈值TK+1=(ZO+ZB)/2; 

    4. 若TK==TK+1,则所得即为阈值;否则转2,迭代计算。 

    我想问下,ZO和ZB怎么求??

    1. 统计图像灰度直方图

    2. 找到最大灰度值ZMAX和最小灰度值ZMIN,并计算T0 =(ZMAX+ZMIN)/2

    3. 计算小于T0的所有灰度的均值ZO和大于T0的所有灰度的均值ZB(用直方图求就可以)。

    例如,你的直方图从10到250有值,则T0 = 260/2 = 130.

    1 ZO = Sum(nHist[i] * i) / Sum(nHist[i]); 10 <= i <= 130
    2 BO = Sum(nHist[i] * i) / Sum(nHist[i]); 131 <= i <= 250

      /////////////////////////////////////////////////////////////////////////////////////////////////////////////////

     1 ZO = .0, ZB = .0;
     2 int nB = 0, nO = 0;
     3 BYTE bytVal = 0;
     4 
     5 while (还有图像数据没读完)
     6 {
     7     bytVal = ReadNextPixel();
     8     if (bytVal > T0)
     9     {
    10         ZB += bytVal;
    11         ++nB;
    12     }
    13     else
    14     {
    15         ZO += bytVal;
    16         ++nO;
    17     }
    18 }
    19 ZO /= nO;
    20 ZB /= nB;

      //////////////////////////////////////////////////////////////////////////////////////////////////////////

    伪代码1

    A. 找到灰度图中最大灰度nZmax和最小灰度nZmin(代码略)

    B. 求T0。

    1 T0 = (nZmax + nZmin) / 2;

    C. 迭代了求出阈值

     1 int i;
     2 while (true)
     3 {
     4     // 计算下一个迭代阀值
     5     for (i = 0; i < T0 + 1; i++)
     6     {
     7         Temp0 += tongji[i] * i;
     8         Temp1 += tongji[i];
     9     }
    10     for (i = T0 + 1; i < 256; i++)
    11     {
    12         Temp2 += tongji[i] * i;
    13         Temp3 += tongji[i];
    14     }
    15     // (大于T0的灰度均值 + 小于T0的灰度均值) / 2
    16     T2 = (Temp0 / Temp1 + Temp2 / Temp3) / 2;
    17     // 看迭代结果是否已收敛
    18     if (T0 == T2)
    19         break;
    20     else
    21         T0 = T2;
    22 }

    D. 根据上一步求到的T2阈值进行图像分割

     1 // 对各像素进行灰度转换
     2            // 对各像素进行灰度转换
     3        for (j = 0; j < height; j ++)
     4        {
     5               for (i = 0; i < wide; i ++)
     6               {
     7                      // 读取像素
     8                      unsigned char temp = *((unsigned char *)p_data + wide * j + i);
     9                      // 判断像素灰度值是否超出范围
    10                      if (temp < T0)
    11                             temp = 0;
    12                      else
    13                             temp = 255;
    14                      // 回写处理完的像素
    15                      *((unsigned char *)p_data + wide * j + i) = temp;
    16               }
    17        }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////

    伪代码2

    C. 找到灰度图中最大灰度iMaxGrayValue和最小灰度iMinGrayValue (代码略)

    D.求iNewThreshold。

    iNewThreshold = (iMaxGrayValue + iMinGrayValue) / 2;

    C. 迭代了求出阈值

     1 //迭代求最佳阈值
     2        iNewThreshold = (iMinGrayValue + iMaxGrayValue)/2;
     3        iThreshold = 0;
     4        for(iIterationTimes = 0; iThreshold != iNewThreshold && iIterationTimes < 100;iIterationTimes ++)
     5        {
     6               iThreshold = iNewThreshold;
     7               lP1 =0;
     8               lP2 =0;
     9               lS1 = 0;
    10               lS2 = 0;
    11               //求两个区域的灰度平均值
    12               for (i = iMinGrayValue;i < iThreshold;i++)
    13               {
    14                      lP1 += lHistogram[i]*i;
    15                      lS1 += lHistogram[i];
    16               }
    17               iMean1GrayValue = (unsigned char)(lP1 / lS1);
    18               for (i = iThreshold+1;i < iMaxGrayValue;i++)
    19               {
    20                      lP2 += lHistogram[i]*i;
    21                      lS2 += lHistogram[i];
    22               }
    23               iMean2GrayValue = (unsigned char)(lP2 / lS2);
    24               iNewThreshold =  (iMean1GrayValue + iMean2GrayValue)/2;
    25        }

    // 这里限制的迭代次数不大于100,考虑到效率吧。

    D. 根据上一步求到的iNewThreshold阈值进行图像分割

     1 //根据阈值将图像二值化
     2        for (i = 0;i < lHeight ;i++)
     3        {
     4                      for(j = 0;j < lWidth ;j++)
     5                      {
     6                             // 指向源图像倒数第j行,第i个象素的指针               
     7                             lpSrc = (char *)lpDIBBits + lLineBytes *i + j;
     8              
     9                             // 指向目标图像倒数第j行,第i个象素的指针                   
    10                             lpDst = (char *)lpNewDIBBits + lLineBytes *i + j;
    11  
    12                             pixel = (unsigned char)*lpSrc;
    13                            
    14                             if(pixel <= iThreshold)
    15                             {
    16                                    *lpDst = (unsigned char)0;
    17                             }
    18                             else
    19                             {
    20                                    *lpDst = (unsigned char)255;
    21                             }
    22                      }
    23        }

    5. 自适应阈值图像分割

         在许多情况下,物体和背景的对比度在图象中不是各处一样的,这时很难用统一的一个阈值将物体与背景分开。这时可以根据图象的局部特征分别采用不同的阈值进行分割。实际处理时,需要按照具体问题将图象分成若干子区域分别选择阈值,或者动态地根据一定的邻域范围选择每点处的阈值,进行图象分割。

    1). 大津法(OTSU)

    最大类间方差法是由日本学者大津于1979年提出的,是一种自适应的阈值确定的方法,又叫大津

    法,简称OTSU。它是按图像的灰度特性,将图像分成背景和目标2部分。背景和目标之间的类间方差

    越大,说明构成图像的2部分的差别越大,当部分目标错分为背景或部分背景错分为目标都会导致2部

    分差别变小。因此,使类间方差最大的分割意味着错分概率最小。

    对于图像I(x,y),前景(即目标)和背景的分割阈值记作T, 属于前景的像素点数占整幅图像的比例记为ω0,其平均灰度μ0;背景像素点数占整幅图像的比例为ω1,其平均灰度为μ1。图像的总平均灰度记为μ,类间方差记为g。

    假设图像的背景较暗,并且图像的大小为M×N,

    图像中像素的灰度值小于阈值T的像素个数记作N0,像素灰度大于阈值T的像素个数记作N1,则有:

    1 ω0 = N0/ M×N        (1)
    2 ω1 = N1/ M×N        (2)
    3 N0 + N1 = M×N       (3)
    4 ω0 + ω1 = 1          (4)
    5 μ= ω0 * μ0 + ω1 * μ1   (5)
    6 g = ω0 (μ0 -μ) ^ 2 + ω1 (μ1 - μ)^2    (6)

    将式(5)代入式(6),得到等价公式:

    1 g = ω0 ω1 (μ0 - μ1) ^ 2    (7)

    采用遍历的方法得到使类间方差最大的阈值T,即为所求。

    Otus算法使用的是聚类的思想,即把图像的灰度数按灰度级分成2个部分,使2个部分的之间的灰度值差异最大,每个部分之内的灰度差异最小的,找到这样的一个灰度级t划分。通过方差的计算实现,即方差最小的值对应的t即是理想的划分。

    http://hi.baidu.com/cwynamespace/blog/item/896ed529955c61f998250a47.html

    伪代码1)

     1 FLOAT result;
     2     
     3      int cnt0;
     4      int cnt1;
     5      FLOAT max=0.0;
     6      for (thre = 1; thre < 255; thre++)
     7      {
     8          cnt0=0;
     9          cnt1=0;
    10          pixeltotalC0=0.0;
    11          pixeltotalC1=0.0;
    12          // 计算背景与目标的像素数各是多少
    13          // 计算背景与目标的像素值总和各是多少
    14          for (i=0; i<lHeight; i++)
    15          {
    16               for (j=0; j<lWidth; j++)
    17               {
    18                    if (ImageSrc[i][j] <= thre)
    19                    {
    20                        cnt0++;
    21                        pixeltotalC0 += ImageSrc[i][j];
    22                    }
    23                    else
    24                    {
    25                        cnt1++;
    26                        pixeltotalC1 += ImageSrc[i][j];
    27                    }
    28               }
    29          }
    30          cnt0=cnt0;
    31          cnt1=cnt1;
    32  
    33          rateC0 = 1.0 * cnt0 / (lHeight * lWidth); // 计算背景的面积比例
    34          rateC1 = 1 - rateC0;                           // 计算目标的面积比例
    35  
    36          // 计算背景平均灰度
    37          if (cnt0 != 0)
    38          {
    39               pixelaverC0 = pixeltotalC0 / cnt0;       
    40          }
    41          else
    42          {
    43               pixelaverC0 = 0;
    44          }
    45  
    46          // 计算目标平均灰度
    47          if (cnt1 !=0)
    48          {
    49               pixelaverC1 = pixeltotalC1 / cnt1;
    50          }
    51          else
    52          {
    53               pixelaverC1 = 0;
    54          }
    55  
    56          // 计算类间方差
    57          result = rateC0 * rateC1 * (pixelaverC0 - pixelaverC1) * (pixelaverC0 - pixelaverC1);
    58  
    59          // 找到最大的类间方差, 就找到最佳的阈值了
    60          if(result > max)
    61          {
    62               max = result;
    63               threbest = thre;
    64          }
    65      }
    66     
    67      // 进行二值化
    68      for (i=0; i<lHeight; i++)
    69      {
    70          for (j=0; j<lWidth; j++)
    71          {
    72               if (ImageSrc[i][j] >= threbest)
    73               {
    74                    ImageDst[i][j] = (unsigned char)255;
    75               }
    76               else
    77               {
    78                    ImageDst[i][j] = (unsigned char)0;
    79               }
    80          }
    81       }

    明显这段代码的效率会低一点,它是怎对每一个灰度值在图像中的所有点进行计算。

    看下面代码,效率会高一点。

    伪代码2)

    http://fcwhx007.bokewu.com/blog173376.htm

     1 /*
     2 OTSU 算法可以说是自适应计算单阈值(用来转换灰度图像为二值图像)的简单高效方法。下面的代码最早由 Ryan Dibble提供,此后经过多人Joerg.Schulenburg, R.Z.Liu 等修改,补正。
     3 算法对输入的灰度图像的直方图进行分析,将直方图分成两个部分,使得两部分之间的距离最大。划分点就是求得的阈值。
     4 parameter: *image --- buffer for image
     5 rows, cols --- size of image
     6 x0, y0, dx, dy --- region of vector used for computing threshold
     7 vvv --- debug option, is 0, no debug information outputed
     8 */
     9 /*======================================================================*/
    10 /* OTSU global thresholding routine */
    11 /* takes a 2D unsigned char array pointer, number of rows, and */
    12 /* number of cols in the array. returns the value of the threshold */
    13 /*======================================================================*/
    14 // 这段代码可以针对图像的区域
    15 int otsu (unsigned char *image, int rows, int cols, int x0, int y0, int dx, int dy)
    16 {
    17      unsigned char *np; // 图像指针
    18      int thresholdValue=1; // 阈值
    19      int ihist[256]; // 图像直方图,个点
    20  
    21      int i, j, k; // various counters
    22      int n, n1, n2, gmin, gmax;
    23      double m1, m2, sum, csum, fmax, sb;
    24  
    25      // 对直方图置零...
    26      memset(ihist, 0, sizeof(ihist));
    27  
    28      gmin=255; gmax=0;
    29      // 生成直方图
    30      // 求出最大像素值和最小像素值
    31      // 求出图像中各个灰度值的个数存于数组ihist中
    32      for (i = y0 + 1; i < y0 + dy - 1; i++)
    33      {
    34          np = &image[i*cols+x0+1];
    35          for (j = x0 + 1; j < x0 + dx - 1; j++)
    36           {
    37               ihist[*np]++;
    38               if(*np > gmax) gmax=*np;
    39               if(*np < gmin) gmin=*np;
    40               np++; /* next pixel */
    41          }
    42      }
    43  
    44      // set up everything
    45      sum = csum = 0.0;
    46      n = 0;
    47  
    48      // 不知道这个有什么用?
    49      for (k = 0; k <= 255; k++)
    50      {
    51          // 图像的总灰度值
    52          sum += (double) k * (double) ihist[k]; /* x*f(x) 质量矩*/
    53          // 总像素点数? 不就是等于宽*高吗
    54          n += ihist[k]; /* f(x) 质量*/
    55      }
    56  
    57      if (!n)
    58      {
    59          // if n has no value, there is problems...
    60          fprintf (stderr, "NOT NORMAL thresholdValue = 160/n";
    61          return (160);
    62      }
    63  
    64      // do the otsu global thresholding method
    65      fmax = -1.0;
    66      n1 = 0;
    67      for (k = 0; k < 255; k++)
    68      {
    69          n1 += ihist[k];
    70          if (!n1)
    71          {
    72               continue;
    73          }
    74          n2 = n - n1;
    75          if (n2 == 0)
    76          {
    77               break;
    78          }
    79          csum += (double) k *ihist[k];
    80          m1 = csum / n1;
    81          m2 = (sum - csum) / n2;
    82          sb = (double) n1 *(double) n2 *(m1 - m2) * (m1 - m2);
    83          /* bbg: note: can be optimized. */
    84          if (sb > fmax)
    85          {
    86               fmax = sb;
    87               thresholdValue = k;
    88          }
    89      }
    90      // at this point we have our thresholding value
    91      return(thresholdValue);
    92 }

    http://hi.baidu.com/flyingmooding/blog/item/a434e134e3139bd7a2cc2b63.html

    2). 均值法

    思想很简单,就是把图像分成m*n块子图,求取每一块子图的灰度均值(就是所有像素灰度值之和除以像素点的数量),这个均值就是阈值了。

    这种方法明显不比大津法好,因为均值法和大津法都是从图像整体来考虑阈值的,但是大津法找了一个类间方差最大值来求出最佳阈值的;这两种方法子图越多应该分割效果会好一点,但效率可能会变慢。

    6. 最佳阈值

         阈值的选择需要根据具体问题来确定,一般通过实验来确定。对于给定的图象,可以通过分析直方图的方法确定最佳的阈值,例如当直方图明显呈现双峰情况时,可以选择两个峰值的中点作为最佳阈值。

    所谓最佳阈值就是根据一定的方法(例如双峰法),找出图像中目标与背景的分割最佳阈值就是了。方法多种多样,对不同的图片可以有不同的方法(因为不同的图片有不同的特点)。方法是多种多样的,答案是丰富多彩的。

    转自:http://blog.csdn.net/bagboy_taobao_com/article/details/5645425

    附:OpenCv中实现了三种跟图像分割相关的算法(http://www.cnblogs.com/xrwang/archive/2010/02/28/ImageSegmentation.html)

  • 相关阅读:
    jqgrid content-type datatype
    github删除
    springcolud文章收藏
    springboot无法加载oracle驱动终极解决
    当多线程并发遇到Actor
    spring.boot 无法加载oracle驱动的可能原因
    MobileNet V2深入理解
    转载:从loss处理图像分割中类别极度不均衡的状况---keras
    6D姿态估计
    An overview of semantic image segmentation
  • 原文地址:https://www.cnblogs.com/wangduo/p/5556903.html
Copyright © 2011-2022 走看看