灰度图像阈值化分割常见方法总结及VC实现

转载地址:http://blog.csdn.net/likezhaobin/article/details/6915755

在图像处理领域,二值图像运算量小,并且能够体现图像的关键特征,因此被广泛使用。将灰度图像变为二值图像的常用方法是选定阈值,然后将待处理图像的每个像素点进行单点处理,即将其灰度值与所设置的门限进行比对,从而得到二值化的黑白图。这样一种方式因为其直观性以及易于实现,已经在图像分割领域处于中心地位。本文主要对最近一段时间作者所学习的阈值化图像分割算法进行总结,全文描述了作者对每种算法的理解,并基于OpenCV和VC6.0对这些算法进行了实现。最终将源代码公开,希望大家一起进步。(本文的代码暂时没有考虑执行效率问题)

首先给出待分割的图像如下:

1、Otsu法(最大类间方差法)

该算法是日本人Otsu提出的一种动态阈值分割算法。它的主要思想是按照灰度特性将图像划分为背景和目标2部分,划分依据为选取门限值,使得背景和目标之间的方差最大。(背景和目标之间的类间方差越大,说明这两部分的差别越大,当部分目标被错划分为背景或部分背景错划分为目标都会导致这两部分差别变小。因此,使用类间方差最大的分割意味着错分概率最小。)这是该方法的主要思路。其主要的实现原理为如下:

1)建立图像灰度直方图(共有L个灰度级,每个出现概率为p)

2)计算背景和目标的出现概率,计算方法如下:

上式中假设t为所选定的阈值,A代表背景(灰度级为0~N),根据直方图中的元素可知,Pa为背景出现的概率,同理B为目标,Pb为目标出现的概率。

3)计算A和B两个区域的类间方差如下:

第一个表达式分别计算A和B区域的平均灰度值;

第二个表达式计算灰度图像全局的灰度平均值;

第三个表达式计算A、B两个区域的类间方差。

4)以上几个步骤计算出了单个灰度值上的类间方差,因此最佳分割门限值应该是图像中能够使得A与B的类间灰度方差最大的灰度值。在程序中需要对每个出现的灰度值据此进行寻优。

本人的VC实现代码如下。

  1. /*****************************************************************************
  2. *
  3. * \函数名称:
  4. *   OneDimentionOtsu()
  5. *
  6. * \输入参数:
  7. *   pGrayMat:      二值图像数据
  8. *   width:         图形尺寸宽度
  9. *   height:        图形尺寸高度
  10. *   nTlreshold:    经过算法处理得到的二值化分割阈值
  11. * \返回值:
  12. *   无
  13. * \函数说明:实现灰度图的二值化分割——最大类间方差法(Otsu算法,俗称大津算法)
  14. *
  15. ****************************************************************************/
  16. void CBinarizationDlg::OneDimentionOtsu(CvMat *pGrayMat, int width, int height, BYTE &nThreshold)
  17. {
  18. double nHistogram[256];         //灰度直方图
  19. double dVariance[256];          //类间方差
  20. int N = height*width;           //总像素数
  21. for(int i=0; i<256; i++)
  22. {
  23. nHistogram[i] = 0.0;
  24. dVariance[i] = 0.0;
  25. }
  26. for(i=0; i<height; i++)
  27. {
  28. for(int j=0; j<width; j++)
  29. {
  30. unsigned char nData = (unsigned char)cvmGet(pGrayMat, i, j);
  31. nHistogram[nData]++;     //建立直方图
  32. }
  33. }
  34. double Pa=0.0;      //背景出现概率
  35. double Pb=0.0;      //目标出现概率
  36. double Wa=0.0;      //背景平均灰度值
  37. double Wb=0.0;      //目标平均灰度值
  38. double W0=0.0;      //全局平均灰度值
  39. double dData1=0.0,  dData2=0.0;
  40. for(i=0; i<256; i++)     //计算全局平均灰度
  41. {
  42. nHistogram[i] /= N;
  43. W0 += i*nHistogram[i];
  44. }
  45. for(i=0; i<256; i++)     //对每个灰度值计算类间方差
  46. {
  47. Pa += nHistogram[i];
  48. Pb = 1-Pa;
  49. dData1 += i*nHistogram[i];
  50. dData2 = W0-dData1;
  51. Wa = dData1/Pa;
  52. Wb = dData2/Pb;
  53. dVariance[i] = (Pa*Pb* pow((Wb-Wa), 2));
  54. }
  55. //遍历每个方差,求取类间最大方差所对应的灰度值
  56. double temp=0.0;
  57. for(i=0; i<256; i++)
  58. {
  59. if(dVariance[i]>temp)
  60. {
  61. temp = dVariance[i];
  62. nThreshold = i;
  63. }
  64. }
  65. }

阈值分割结果如下图,求解所得的阈值为116.

2、一维交叉熵值法

这种方法与类间最大方差很相似,是由Li和Lee应用了信息论中熵理论发展而来。首先简要介绍交叉熵的概念。

对于两个分布P和Q,定义其信息交叉熵D如下:

这代表的物理意义是两个分布之间信息理论距离,另外一种理解是,将分布P变为Q后所带来的信息变化。那么对于图像分割来说,如果要用分割图像来替换原来的图像,最优的分割依据应该就是使得两幅图像之间的交叉熵最小。以下对最小交叉熵法的过程进行简要总结。

可以假设上文的P为源图像的灰度分布,Q为所得到的分割图像的灰度分布,其中:

上式中H为统计直方图;

N为图像总的像素点数;

L为源图像总的灰度级数;

P代表源图像,其每个元素代表每个灰度级上的灰度分布(平均灰度值);

Q为分割后的二值图像,两个u分别代表两个分割后的区域的平均灰度值,其中t为分割图像所采用的阈值。

根据以上定义,以每个灰度级上的灰度和为计算量,可以很容易根据交叉熵的公式,推导出P和Q之间的交叉熵定量表达式:

根据上文所述思路,使得D最小的t即为最小交叉熵意义下的最优阈值。

作者VC实现代码如下。

  1. /*****************************************************************************
  2. *
  3. * \函数名称:
  4. *   MiniCross()
  5. *
  6. * \输入参数:
  7. *   pGrayMat:      二值图像数据
  8. *   width:         图形尺寸宽度
  9. *   height:        图形尺寸高度
  10. *   nTlreshold:    经过算法处理得到的二值化分割阈值
  11. * \返回值:
  12. *   无
  13. * \函数说明:实现灰度图的二值化分割——最小交叉熵算法
  14. *
  15. ****************************************************************************/
  16. void CBinarizationDlg::MiniCross(CvMat *pGrayMat, int width, int height, BYTE &nThreshold)
  17. {
  18. double dHistogram[256];         //灰度直方图
  19. double dEntropy[256];           //每个像素的交叉熵
  20. int N = height*width;           //总像素数
  21. for(int i=0; i<256; i++)
  22. {
  23. dHistogram[i] = 0.0;
  24. dEntropy[i] = 0.0;
  25. }
  26. for(i=0; i<height; i++)
  27. {
  28. for(int j=0; j<width; j++)
  29. {
  30. unsigned char nData = (unsigned char)cvmGet(pGrayMat, i, j);
  31. dHistogram[nData]++;     //建立直方图
  32. }
  33. }
  34. double Pa=0.0;      //区域1平均灰度值
  35. double Pb=0.0;      //区域2平均灰度值
  36. double P0=0.0;      //全局平均灰度值
  37. double Wa=0.0;      //第一部分熵
  38. double Wb=0.0;      //第二部分的熵
  39. double dData1=0.0, dData2=0.0;  //中间值
  40. double dData3=0.0, dData4=0.0;  //中间值
  41. for(i=0; i<256; i++)     //计算全局平均灰度
  42. {
  43. dHistogram[i] /= N;
  44. P0 += i*dHistogram[i];
  45. }
  46. for(i=0; i<256; i++)
  47. {
  48. Wa=Wb=dData1=dData2=dData3=dData4=Pa=Pb=0.0;
  49. for(int j=0; j<256; j++)
  50. {
  51. if(j<=i)
  52. {
  53. dData1 += dHistogram[j];
  54. dData2 += j*dHistogram[j];
  55. }
  56. else
  57. {
  58. dData3 += dHistogram[j];
  59. dData4 += j*dHistogram[j];
  60. }
  61. }
  62. Pa = dData2/dData1;
  63. Pb = dData4/dData3;
  64. for(j=0; j<256; j++)
  65. {
  66. if(j<=i)
  67. {
  68. if((Pa!=0)&&(dHistogram[j]!=0))
  69. {
  70. double d1 = log(dHistogram[j]/Pa);
  71. Wa += j*dHistogram[j]*d1/log(2);
  72. }
  73. }
  74. else
  75. {
  76. if((Pb!=0)&&(dHistogram[j]!=0))
  77. {
  78. double d2 = log(dHistogram[j]/Pb);
  79. Wb += j*dHistogram[j]*d2/log(2);
  80. }
  81. }
  82. }
  83. dEntropy[i] = Wa+Wb;
  84. }
  85. //遍历熵值,求取最小交叉熵所对应的灰度值
  86. double temp=dEntropy[0];
  87. for(i=1; i<256; i++)
  88. {
  89. if(dEntropy[i]<temp)
  90. {
  91. temp = dEntropy[i];
  92. nThreshold = i;
  93. }
  94. }
  95. }

阈值分割结果如下图,求解所得的阈值为106.

3、二维OTSU法

这种方法是对类间最大方差法的扩展,将其从求两个一维分布最大类间方差扩充为求解类间离散度矩阵的迹的最大值,考虑像素点灰度级的基础上增加了对像素点邻域平均像素值的考虑。

以下按照本人的理解对该方法的思路以及推倒过程进行分析:

1)首先需要建立二维的灰度统计直方图P(f, g);

图像的灰度级为L级,那么其每个像素点的8邻域灰度平均值的灰度级也为L级,据此来构建直方图P。二维统计直方图的横轴为每个像素点的灰度值f(i, j),纵坐标为同一个点对应的邻域平均值g(i, j) 其中(0≤i<height, 0≤j<width, 0≤f(i, j)<L),而所对应的P(f,g)整幅图像中灰度值为f,邻域灰度均值为g的点的统计值占总像素点的比例(即为灰度值出现的联合概率密度)。其中P的每个元素满足如下公式:

n为整幅图像中灰度值为f,邻域灰度均值为g的点的统计值;

N为图像总的像素点个数;

2)对于下图所示的二维统计直方图,t代表横坐标(灰度值),s代表纵坐标(像素点邻域的灰度均值)

对已图像中的阈值点(t,s)来说,其灰度值t和其邻域内的灰度均值s不应该相差太多,如果t比s大很多(点位于上图中的II区域),说明像素的灰度值远远大于其临域的灰度均值,故而该点很可能是噪声点,反之如果t比s小很多(点位于途中的IV区域),即该点的像素值比其临域均值小很多,则说明是一个边缘点。据此我们在进行背景前景分割的时候忽略这些干扰因素,认为这两个区域内Pi,j=0。剩下的I区域和III区域则分别代表了前景和背景。以下据此来推导对于选定的阈值(t, s),进行离散度判据的最优推导。

3)推导阈值(t, s)点处的离散度矩阵判据

根据上文分析可知,由阈值(t, s)所分割的前景和背景出现的概率如下:

定义两个中间变量,方便下面推导:

据此,这两部分的灰度均值向量可以推导如下(两个分量分别根据灰度值以及每个点的灰度均值计算):

整幅图像的灰度均值向量为:

与一维的大津法一样的思路,推导类间方差,这里是二维因此要用矩阵形式。参考一维法,可同样定义类间“方差”矩阵如下:

为了在实现的时候,容易判断出这样一个矩阵的“最大值”,因此数学中采用矩阵的迹(对角线之和)来衡量矩阵的“大小”。因此以该矩阵的迹作为离散度测度,推导如下:

这样就可以通过求解使得这个参数最大时的(t,s)即为所求得的最佳阈值组合。

以下为具体算法实现过程:

1)建立二维直方图

2)对直方图进行遍历,计算每个(t,s)组合所得到的矩阵离散度,也就是一维大津法中所谓的最大类间方差。

3)求得使“类间方差”最大的(t,s),由于t代表灰度值,s代表改点在其邻域内的灰度均值,因此本人认为在选择阈值时可以选择s为最佳,当然用t也可以,因为从求解结果可以看出,这这个数值往往很接近。

具体的实现代码如下:

  1. /*****************************************************************************
  2. *
  3. * \函数名称:
  4. *   TwoDimentionOtsu()
  5. *
  6. * \输入参数:
  7. *   pGrayMat:      二值图像数据
  8. *   width:         图形尺寸宽度
  9. *   height:        图形尺寸高度
  10. *   nTlreshold:    经过算法处理得到的二值化分割阈值
  11. * \返回值:
  12. *   无
  13. * \函数说明:实现灰度图的二值化分割——最大类间方差法(二维Otsu算法)
  14. * \备注:在构建二维直方图的时候,采用灰度点的3*3邻域均值
  15. ******************************************************************************/
  16. void CBinarizationDlg::TwoDimentionOtsu(CvMat *pGrayMat, int width, int height, BYTE &nThreshold)
  17. {
  18. double dHistogram[256][256];        //建立二维灰度直方图
  19. double dTrMatrix = 0.0;             //离散矩阵的迹
  20. int N = height*width;               //总像素数
  21. for(int i=0; i<256; i++)
  22. {
  23. for(int j=0; j<256; j++)
  24. dHistogram[i][j] = 0.0;      //初始化变量
  25. }
  26. for(i=0; i<height; i++)
  27. {
  28. for(int j=0; j<width; j++)
  29. {
  30. unsigned char nData1 = (unsigned char)cvmGet(pGrayMat, i, j);  //当前的灰度值
  31. unsigned char nData2 = 0;
  32. int nData3 = 0;         //注意9个值相加可能超过一个字节
  33. for(int m=i-1; m<=i+1; m++)
  34. {
  35. for(int n=j-1; n<=j+1; n++)
  36. {
  37. if((m>=0)&&(m<height)&&(n>=0)&&(n<width))
  38. nData3 += (unsigned char)cvmGet(pGrayMat, m, n); //当前的灰度值
  39. }
  40. }
  41. nData2 = (unsigned char)(nData3/9);    //对于越界的索引值进行补零,邻域均值
  42. dHistogram[nData1][nData2]++;
  43. }
  44. }
  45. for(i=0; i<256; i++)
  46. for(int j=0; j<256; j++)
  47. dHistogram[i][j] /= N;  //得到归一化的概率分布
  48. double Pai = 0.0;      //目标区均值矢量i分量
  49. double Paj = 0.0;      //目标区均值矢量j分量
  50. double Pbi = 0.0;      //背景区均值矢量i分量
  51. double Pbj = 0.0;      //背景区均值矢量j分量
  52. double Pti = 0.0;      //全局均值矢量i分量
  53. double Ptj = 0.0;      //全局均值矢量j分量
  54. double W0 = 0.0;       //目标区的联合概率密度
  55. double W1 = 0.0;       //背景区的联合概率密度
  56. double dData1 = 0.0;
  57. double dData2 = 0.0;
  58. double dData3 = 0.0;
  59. double dData4 = 0.0;   //中间变量
  60. int nThreshold_s = 0;
  61. int nThreshold_t = 0;
  62. double temp = 0.0;     //寻求最大值
  63. for(i=0; i<256; i++)
  64. {
  65. for(int j=0; j<256; j++)
  66. {
  67. Pti += i*dHistogram[i][j];
  68. Ptj += j*dHistogram[i][j];
  69. }
  70. }
  71. for(i=0; i<256; i++)
  72. {
  73. for(int j=0; j<256; j++)
  74. {
  75. W0 += dHistogram[i][j];
  76. dData1 += i*dHistogram[i][j];
  77. dData2 += j*dHistogram[i][j];
  78. W1 = 1-W0;
  79. dData3 = Pti-dData1;
  80. dData4 = Ptj-dData2;
  81. /*          W1=dData3=dData4=0.0;   //对内循环的数据进行初始化
  82. for(int s=i+1; s<256; s++)
  83. {
  84. for(int t=j+1; t<256; t++)
  85. {
  86. W1 += dHistogram[s][t];
  87. dData3 += s*dHistogram[s][t];  //方法2
  88. dData4 += t*dHistogram[s][t];  //也可以增加循环进行计算
  89. }
  90. }*/
  91. Pai = dData1/W0;
  92. Paj = dData2/W0;
  93. Pbi = dData3/W1;
  94. Pbj = dData4/W1;   // 得到两个均值向量,用4个分量表示
  95. dTrMatrix = ((W0*Pti-dData1)*(W0*Pti-dData1)+(W0*Ptj-dData1)*(W0*Ptj-dData2))/(W0*W1);
  96. if(dTrMatrix > temp)
  97. {
  98. temp = dTrMatrix;
  99. nThreshold_s = i;
  100. nThreshold_t = j;
  101. }
  102. }
  103. }
  104. nThreshold = nThreshold_t;   //返回结果中的灰度值
  105. //nThreshold = 100;
  106. }

阈值分割结果如下图,求解所得的阈值为114.(s=114,t=117)

时间: 2024-08-02 11:03:12

灰度图像阈值化分割常见方法总结及VC实现的相关文章

《OpenCV:灰度图像阈值化分割常见方法总结及VC代码》

支持原创,拿来收藏!转载地址:http://blog.csdn.net/likezhaobin/article/details/6915755?userName=u014395105&userInfo=aWOfy4XjkeuESVqMgVdrnPewKx6gaD2TZ6xUFF%2FXs%2FeZjmZKRHLyhzVPli3izF4JpSQuVNfcdFRe6pvuXl6VvRJ%2FSmjVpClq8XgXbwl56GUA19Luch91NWA57umNAidF94p6X1kqBpQ9l4%

OpenCV笔记(十一)——图像的阈值操作(一种分割的方法)

对图像进行阈值的操作,可以认为是一种简单的分割前景和背景的方法. 这种分割的方法一般用在前景像素和背景像素的强度值反差比较明显的情况下,如下图: 我们可以看到这幅苹果的图像,代表前景的是苹果,代表背景的是白色部分.这幅图像前景背景分明,所以可以取一个阈值,如200.在图像中,凡是灰度低于200的像素,我们认作是前景,将其值设为255,凡是灰度不低于200的像素,我们认作是背景,将其值设为0.获得一张新的Mask,将原始的图像与这张Mask做与(&)运算,就能够得到分割好的图像. 一般来说,阈值化

【前端】js截取or分割字符串的常见方法

1.截取字符串 分割字符串方法 1.charAt(): 没有一种有别于字符串类型的字符数据类型,所以返回的字符是长度为 1 的字符串 例如:var str="Hello world!" document.write(str.charAt(1)) 输出:e2.substring(): var str="Hello world!" document.write(str.substring(3,7)) 输出:low 类似方法:substr() 定义:substr(star

图像阈值化-threshold、adaptivethreshold

在图像处理中阈值化操作,从一副图像中利用阈值分割出我们需要的物体部分(当然这里的物体可以是一部分或者整体).这样的图像分割方法是基于图像中物体与背景之间的灰度差异,而且此分割属于像素级的分割.opencv的二值化操作函数,如果你是一位经验丰富的专业人员,可以发现阈值化操作有很多小技巧,不只是单单调用二值化操作函数,就完成阈值化操作,往往还是结合形态学处理. 阈值化操作在图像处理中是一种常用的算法,比如图像的二值化就是一种最常见的一种阈值化操作.opencv2和opencv3中提供了直接阈值化操作

OpenCV阈值化处理

图像的阈值化就是利用图像像素点分布规律,设定阈值进行像素点分割,进而得到图像的二值图像.图像阈值化操作有多种方法,常用方法有经典的OTSU.固定阈值.自适应阈值.双阈值及半阈值化操作.这里对各种阈值化操作进行一个总结. OTSU阈值化 在阈值化处理中,常用的算法就是OTSU.发明人是Nobuyuki Ostu.这种二值化操作阈值的选取非常重要,阈值选取的不合适,可能得到的结果就毫无用处.简单的说,这种算法假设衣服图像由前景色和背景色组成.通过统计学的方法来选取一个阈值,使这个阈值可以将前景色和背

S0.4 二值图与阈值化

目录 二值图的定义 二值图的应用 阈值化 二值化/阈值化方法 1,无脑简单判断 opencv3函数threshold()实现 2,Otsu算法(大律法或最大类间方差法) OpenCV3 纯代码实现大津法 OpenCV3 threshold算法调用Otsu阈值化 改进版本 OpenCV3函数adaptiveThreshold实现自适应阈值 二值图的定义 二值图是一种特殊的灰度图,即每个像素点要么是白(0),要么是黑(255) 无论是灰度图还是二值图都是用阈值化的知识. 二值图的应用 图像的二值化使

Opencv3编程入门笔记(4)腐蚀、膨胀、开闭运算、漫水填充、金字塔、阈值化、霍夫变换

19      腐蚀erode.膨胀dilate 腐蚀和膨胀是针对图像中的白色部分(高亮部分)而言的,不是黑色的.除了输入输出图像外,还需传入模板算子element,opencv中有三种可以选择:矩形MORPH_RECT,交叉形MORPH_CROSS,椭圆形MORPH_ELLIPSE.Matlab中会有更多一点的模板. 例如: Mat element = getStructuringElement(MORPH_RECT,Size(15,15)); erode(srcImage,dstImage,

【数字图像处理】五.MFC图像点运算之灰度线性变化、灰度非线性变化、阈值化和均衡化处理具体解释

本文主要讲述基于VC++6.0 MFC图像处理的应用知识,主要结合自己大三所学课程<数字图像处理>及课件进行解说.主要通过MFC单文档视图实现显示BMP图片点运算处理.包含图像灰度线性变换.灰度非线性变换.图像阈值化处理.图像均衡化处理等知识,并结合前一篇论文灰度直方图进行展示 .同一时候文章比較具体基础,希望该篇文章对你有所帮助,尤其是刚開始学习的人和学习图像处理的学生. [数字图像处理]一.MFC具体解释显示BMP格式图片 [数字图像处理]二.MFC单文档切割窗体显示图片 [数字图像处理]

openCV—Python(10)—— 图像阈值化处理

一.函数简介 1.threshold-图像简单阈值化处理 函数原型:threshold(src, thresh, maxval, type, dst=None) src:图像矩阵 thresh:阈值 maxVal:像素最大值 type:阈值化类型 2.adaptiveThreshold-图像自适应阈值化处理 函数原型:adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C, dst=None) sr