Otsu算法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/kksc1099054857/article/details/78321776

1.简介:

       一维Otsu算法有计算简洁、稳定、自适应强等优点,被广泛用于图像分割中。但一维Otsu算法没有考虑图像像素点之间的关系,当图像中有噪声时,会导致分割的效果不理想。因此,刘健庄等人在1993年提出了二维的Otsu算法,提升了算法的抗噪声能力。

2.算法思想:

       同时考虑像素的灰度值分布和它们邻域像素的平均灰度值分布,因此形成的阈值是一个二维矢量,最佳的阈值在一个二维的测度准则下确定最大值时得到。

3.算法过程:

(1)设图像I(x,y),的灰度级为L级,那么图像的邻域平均灰度也分为L级。
(2)设f(x,y)为像素点(x,y)的灰度值,g(x,y)为像素点(x,y)为中心的K*K的像
        素点集合的灰度平均值。令f(x,y)=i,g(x,y)=j,然后就形成了一个二元组(i,j)。
(3)设二元组(i,j)出现的次数为fij,然后求出二元组对应的概率密度Pij,
        Pij=fij/N, i,j=1,2,…,L,其中N为图像像素点总数。
(4)任意选取一个阈值向量(s,t)选取的阈值向量将图像的二维直方图划分成4个
        区域,B、C区域代表图像的前景和背景,A、D区域代表噪声点。

(5)设C、B两个区域对应的概率分别为w1,w2,对应的均值矢量为u1,u2。整个图
        片所对应的均值矢量为uT。


4.代码实现(opencv3):

    


   
   
  1. #include "opencv.hpp"
  2. #include "imgproc.hpp"
  3. #include "highgui.hpp"
  4. #include "iostream"
  5. #include "core.hpp"
  6. using namespace cv;
  7. using namespace std;
  8. int Otsu2D(Mat srcimage); //二维Otsu算法
  9. int main()
  10. {
  11. Mat srcimage, grayimage, dstimage;
  12. srcimage = imread( "lena.jpg");
  13. namedWindow( "原图", 0);
  14. imshow( "原图", srcimage); //显示原图
  15. cvtColor(srcimage, grayimage, COLOR_RGB2GRAY); //得到灰度图
  16. double time0 = static_cast< double>(getTickCount()); //记录程序开始时间
  17. int thresholdValue = Otsu2D(grayimage); //调用二维Otsu函数
  18. time0 = (( double)getTickCount() - time0) / getTickFrequency();
  19. cout << "算法运行时间为:" << time0 << endl;
  20. cout << "Otsu阈值为:" << thresholdValue << endl;
  21. threshold(grayimage, dstimage, thresholdValue, 255, THRESH_BINARY); //将得到的阈值传入函数,得到分割效果图
  22. namedWindow( "Otsu算法结果", 0);
  23. imshow( "Otsu算法结果", dstimage);
  24. waitKey();
  25. return 0;
  26. }

   
   
  1. <code class="language-cpp">int Otsu2D(Mat srcimage)  
  2. {  
  3.     double Histogram[256][256];        //建立二维灰度直方图  
  4.     double TrMax = 0.0;                //用于存储矩阵的迹(矩阵对角线之和)  
  5.     int height = srcimage.rows;        //矩阵的行数  
  6.     int width = srcimage.cols;         //矩阵的列数  
  7.     int N = height*width;              //像素的总数  
  8.     int T;                             //最终阈值  
  9.     uchar *data = srcimage.data;  
  10.     for (int i = 0; i < 256; i++)  
  11.     {  
  12.         for (int j = 0; j < 256; j++)  
  13.         {  
  14.             Histogram[i][j] = 0;      //初始化变量  
  15.         }  
  16.     }  
  17.     for (int i = 0; i < height; i++)  
  18.     {  
  19.         for (int j = 0; j < width; j++)  
  20.         {  
  21.             int Data1 = data[i*srcimage.step + j];         //获取当前灰度值  
  22.             int Data2 = 0;                           //用于存放灰度的平均值  
  23.             for (int m = i - 1; m <= i + 1; m++)  
  24.             {  
  25.                 for (int n = j - 1; n <= j + 1; n++)  
  26.                 {  
  27.                     if ((m >= 0) && (m < height) && (n >= 0) && (n < width))  
  28.                         Data2 += data[m*srcimage.step + n];//邻域灰度值总和  
  29.                 }  
  30.             }  
  31.             Data2 = Data2 / 9;  
  32.             Histogram[Data1][Data2]++;                  //记录(i,j)的数量  
  33.         }  
  34.     }  
  35.     for (int i = 0; i < 256; i++)  
  36.         for (int j = 0; j < 256; j++)  
  37.             Histogram[i][j] /= N;     //归一化的每一个二元组的概率分布  
  38.   
  39.     double Fgi = 0.0;    //前景区域均值向量i分量  
  40.     double Fgj = 0.0;    //前景区域均值向量j分量  
  41.     double Bgi = 0.0;    //背景区域均值向量i分量  
  42.     double Bgj = 0.0;    //背景区域均值向量j分量  
  43.     double Pai = 0.0;    //全局均值向量i分量 panorama(全景)  
  44.     double Paj = 0.0;    //全局均值向量j分量  
  45.     double w0 = 0.0;     //前景区域联合概率密度  
  46.     double w1 = 0.0;     //背景区域联合概率密度  
  47.     double num1 = 0.0;   //遍历过程中前景区i分量的值  
  48.     double num2 = 0.0;   //遍历过程中前景区j分量的值  
  49.     double num3 = 0.0;   //遍历过程中背景区i分量的值  
  50.     double num4 = 0.0;   //遍历过程中背景区j分量的值  
  51.     int Threshold_s = 0; //阈值s  
  52.     int Threshold_t = 0; //阈值t  
  53.     double temp = 0.0;   //存储矩阵迹的最大值  
  54.     for(int i=0;i<256;i++)  
  55.     {  
  56.         for (int j = 0; j < 256; j++)  
  57.         {  
  58.             Pai += i*Histogram[i][j];   //全局均值向量i分量计算  
  59.             Paj += j*Histogram[i][j];   //全局均值向量j分量计算  
  60.         }  
  61.     }  
  62.     for (int i = 0; i < 256; i++)  
  63.     {  
  64.         for (int j = 0; j < 256; j++)  
  65.         {  
  66.             w0 += Histogram[i][j];        //前景的概率  
  67.             num1 += i*Histogram[i][j];    //遍历过程中前景区i分量的值  
  68.             num2 += j*Histogram[i][j];    //遍历过程中前景区j分量的值  
  69.   
  70.             w1 = 1 - w0;                  //背景的概率  
  71.             num3 = Pai - num1;            //遍历过程中背景区i分量的值  
  72.             num4 = Paj - num2;            //遍历过程中背景区j分量的值  
  73.   
  74.             Fgi = num1 / w0;                
  75.             Fgj = num2 / w1;  
  76.             Bgi = num3 / w0;  
  77.             Bgj = num4 / w1;  
  78.             TrMax = ((w0*Pai - num1)*(w0*Pai - num1) + (w0*Paj - num2)*(w0*Paj - num2)) / (w0*w1);  
  79.             if (TrMax > temp)  
  80.             {  
  81.                 temp = TrMax;  
  82.                 Threshold_s = i;  
  83.                 Threshold_t = j;  
  84.             }  
  85.         }  
  86.     }  
  87.     cout << Threshold_s << " " << Threshold_t << endl;  
  88.     T = Threshold_s;  
  89.     return T;  
  90. }  
  91. </code>  

    
    
  1. int Otsu2D(Mat srcimage)
  2. {
  3. double Histogram[ 256][ 256]; //建立二维灰度直方图
  4. double TrMax = 0.0; //用于存储矩阵的迹(矩阵对角线之和)
  5. int height = srcimage.rows; //矩阵的行数
  6. int width = srcimage.cols; //矩阵的列数
  7. int N = height*width; //像素的总数
  8. int T; //最终阈值
  9. uchar *data = srcimage.data;
  10. for ( int i = 0; i < 256; i++)
  11. {
  12. for ( int j = 0; j < 256; j++)
  13. {
  14. Histogram[i][j] = 0; //初始化变量
  15. }
  16. }
  17. for ( int i = 0; i < height; i++)
  18. {
  19. for ( int j = 0; j < width; j++)
  20. {
  21. int Data1 = data[i*srcimage.step + j]; //获取当前灰度值
  22. int Data2 = 0; //用于存放灰度的平均值
  23. for ( int m = i - 1; m <= i + 1; m++)
  24. {
  25. for ( int n = j - 1; n <= j + 1; n++)
  26. {
  27. if ((m >= 0) && (m < height) && (n >= 0) && (n < width))
  28. Data2 += data[m*srcimage.step + n]; //邻域灰度值总和
  29. }
  30. }
  31. Data2 = Data2 / 9;
  32. Histogram[Data1][Data2]++; //记录(i,j)的数量
  33. }
  34. }
  35. for ( int i = 0; i < 256; i++)
  36. for ( int j = 0; j < 256; j++)
  37. Histogram[i][j] /= N; //归一化的每一个二元组的概率分布
  38. double Fgi = 0.0; //前景区域均值向量i分量
  39. double Fgj = 0.0; //前景区域均值向量j分量
  40. double Bgi = 0.0; //背景区域均值向量i分量
  41. double Bgj = 0.0; //背景区域均值向量j分量
  42. double Pai = 0.0; //全局均值向量i分量 panorama(全景)
  43. double Paj = 0.0; //全局均值向量j分量
  44. double w0 = 0.0; //前景区域联合概率密度
  45. double w1 = 0.0; //背景区域联合概率密度
  46. double num1 = 0.0; //遍历过程中前景区i分量的值
  47. double num2 = 0.0; //遍历过程中前景区j分量的值
  48. double num3 = 0.0; //遍历过程中背景区i分量的值
  49. double num4 = 0.0; //遍历过程中背景区j分量的值
  50. int Threshold_s = 0; //阈值s
  51. int Threshold_t = 0; //阈值t
  52. double temp = 0.0; //存储矩阵迹的最大值
  53. for( int i= 0;i< 256;i++)
  54. {
  55. for ( int j = 0; j < 256; j++)
  56. {
  57. Pai += i*Histogram[i][j]; //全局均值向量i分量计算
  58. Paj += j*Histogram[i][j]; //全局均值向量j分量计算
  59. }
  60. }
  61. for ( int i = 0; i < 256; i++)
  62. {
  63. for ( int j = 0; j < 256; j++)
  64. {
  65. w0 += Histogram[i][j]; //前景的概率
  66. num1 += i*Histogram[i][j]; //遍历过程中前景区i分量的值
  67. num2 += j*Histogram[i][j]; //遍历过程中前景区j分量的值
  68. w1 = 1 - w0; //背景的概率
  69. num3 = Pai - num1; //遍历过程中背景区i分量的值
  70. num4 = Paj - num2; //遍历过程中背景区j分量的值
  71. Fgi = num1 / w0;
  72. Fgj = num2 / w1;
  73. Bgi = num3 / w0;
  74. Bgj = num4 / w1;
  75. TrMax = ((w0*Pai - num1)*(w0*Pai - num1) + (w0*Paj - num2)*(w0*Paj - num2)) / (w0*w1);
  76. if (TrMax > temp)
  77. {
  78. temp = TrMax;
  79. Threshold_s = i;
  80. Threshold_t = j;
  81. }
  82. }
  83. }
  84. cout << Threshold_s << " " << Threshold_t << endl;
  85. T = Threshold_s;
  86. return T;
  87. }

5.运行结果(得到2个阈值,取其中一个即可):


6.修改后的代码


   
   
  1. int Otsu2D(Mat srcimage)
  2. {
  3. double Histogram[ 256][ 256]; //建立二维灰度直方图
  4. double TrMax = 0.0; //用于存储矩阵的迹(矩阵对角线之和)
  5. int height = srcimage.rows; //矩阵的行数
  6. int width = srcimage.cols; //矩阵的列数
  7. int N = height*width; //像素的总数
  8. int T; //最终阈值
  9. uchar *data = srcimage.data;
  10. for ( int i = 0; i < 256; i++)
  11. {
  12. for ( int j = 0; j < 256; j++)
  13. {
  14. Histogram[i][j] = 0; //初始化变量
  15. }
  16. }
  17. for ( int i = 0; i < height; i++)
  18. {
  19. for ( int j = 0; j < width; j++)
  20. {
  21. int Data1 = data[i*srcimage.step + j]; //获取当前灰度值
  22. int Data2 = 0; //用于存放灰度的平均值
  23. for ( int m = i - 1; m <= i + 1; m++)
  24. {
  25. for ( int n = j - 1; n <= j + 1; n++)
  26. {
  27. if ((m >= 0) && (m < height) && (n >= 0) && (n < width))
  28. Data2 += data[m*srcimage.step + n]; //邻域灰度值总和
  29. }
  30. }
  31. Data2 = Data2 / 9;
  32. Histogram[Data1][Data2]++; //记录(i,j)的数量
  33. }
  34. }
  35. for ( int i = 0; i < 256; i++)
  36. {
  37. for ( int j = 0; j < 256; j++)
  38. {
  39. Histogram[i][j] /= N; //归一化的每一个二元组的概率分布
  40. }
  41. }
  42. double S[ 256]; //统计前i行概率的数组
  43. double N1[ 256]; //统计遍历过程中前景区i分量的值
  44. double N2[ 256]; //统计遍历过程中前景区j分量的值
  45. S[ 0] = 0;
  46. N1[ 0] = 0;
  47. N2[ 0] = 0;
  48. for ( int i = 1; i < 256; i++)
  49. {
  50. double x = 0,n1= 0,n2= 0;
  51. for ( int j = 0; j < 256; j++)
  52. {
  53. x += Histogram[i - 1][j];
  54. n1 += ((i -1)*Histogram[i - 1][j]); //遍历过程中前景区i分量的值
  55. n2 += (j*Histogram[i - 1][j]); //遍历过程中前景区j分量的值
  56. }
  57. S[i] = x + S[i - 1];
  58. N1[i] = n1 + N1[i - 1];
  59. N2[i] = n2 + N2[i - 1];
  60. }
  61. double Pai = 0.0; //全局均值向量i分量 panorama(全景)
  62. double Paj = 0.0; //全局均值向量j分量
  63. int Threshold_s = 0; //阈值s
  64. int Threshold_t = 0; //阈值t
  65. int M = 0; //中间变量
  66. double temp = 0.0; //存储矩阵迹的最大值
  67. double num3 = 0.0; //遍历过程中背景区i分量的值
  68. double num4 = 0.0; //遍历过程中背景区j分量的值
  69. double Fgi = 0.0; //前景区域均值向量i分量
  70. double Fgj = 0.0; //前景区域均值向量j分量
  71. double Bgi = 0.0; //背景区域均值向量i分量
  72. double Bgj = 0.0; //背景区域均值向量j分量
  73. for( int i= 0;i< 256;i++)
  74. {
  75. for ( int j = 0; j < 256; j++)
  76. {
  77. Pai += i*Histogram[i][j]; //全局均值向量i分量计算
  78. Paj += j*Histogram[i][j]; //全局均值向量j分量计算
  79. }
  80. }
  81. for ( int i = 0; i < 256; i++)
  82. {
  83. double w0 = 0.0; //前景区域联合概率密度
  84. double w1 = 0.0; //背景区域联合概率密度
  85. double num1 = 0.0; //遍历过程中前景区i分量的值 与w0一样做相关处理
  86. double num2 = 0.0; //遍历过程中前景区j分量的值
  87. if (i >= 1)
  88. {
  89. w0 += S[i - 1];
  90. num1 += N1[i - 1];
  91. num2 += N2[i - 1];
  92. }
  93. for ( int j = 0; j < 256; j++)
  94. {
  95. w0 += Histogram[i][j]; //前景的概率
  96. num1 += i*Histogram[i][j]; //遍历过程中前景区i分量的值
  97. num2 += j*Histogram[i][j]; //遍历过程中前景区j分量的值
  98. w1 = 1 - w0; //背景的概率
  99. num3 = Pai - num1; //遍历过程中背景区i分量的值
  100. num4 = Paj - num2; //遍历过程中背景区j分量的值
  101. }
  102. Fgi = num1 / w0;
  103. Fgj = num2 / w1;
  104. Bgi = num3 / w0;
  105. Bgj = num4 / w1;
  106. TrMax = ((w0*Pai - num1)*(w0*Pai - num1) + (w0*Paj - num2)*(w0*Paj - num2)) / (w0*w1);
  107. if (TrMax > temp)
  108. {
  109. temp = TrMax;
  110. Threshold_s = i;
  111. }
  112. }
  113. cout << Threshold_s << endl;
  114. T = Threshold_s;
  115. return T;
  116. }

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值