当前位置:   article > 正文

智能汽车竞赛摄像头处理(3)——动态阈值二值化(大津法)_全国智能车竞赛大津法

全国智能车竞赛大津法

前言

(1)在上一节中,我们学习了对图像的固定二值化处理,可以将原始图像处理成二值化的黑白图像,这里面的本质就是将原来的二维数组进行了处理,处理后的二维数组里的元素都是0和255两个值。

(2)固定阈值二值化的使用是比赛过程中,每一处地方的二值化阈值都是同一个值,而赛道的不同地方以同一个阈值二值化出来的图像可能不合适,甚至不能正常循迹。

(3)为了适应赛道上的不同环境,很多同学会采用动态二值化,即对于采集到的不同图像,通过算法计算出合适的阈值来进行二值化处理,最后的二值化效果可能会比固定阈值化好一些(因为也有不少同学在比赛中采用固定阈值二值化处理,最后拿到了国奖,所以哪个方法更好,还真不好说,主要根据实际情况看自己的作品)。

(4)毋庸置疑,大津法会增加算法处理,处理时间肯定比固定阈值二值化长。

概念

对于图像处理入门,建议大家可以去看这篇文章

详解-OTUS(大津法-最大类间方差)原理及C语言代码实现-CSDN博客

但是,你不想看也没关系,请继续看我后面的内容,你只需要理解到一个点:大津法就是一个对二维数组处理后会得到一个值的算法,而这个值就是我们二值化要的阈值。想要搞清楚大津法原理,请移步上面的文章进行学习,我这里只进行简要介绍。

大津法

        大津法阈值采用最大类间方差的原理,适合于图像灰度分布整体呈现“双峰”的情况。大津法会自动找出一个阈值,使得分割后的两部分类间方差最大。

        最大类间方差是由日本学者大津(Nobuyuki Otsu)于1979年提出,是一种确定图像二值化分割阈值的算法。算法假设图像像素能够根据全局阈值,被分成背景[background]和目标[objects]两部分。然后,计算该最佳阈值来区分这两类像素,使得两类像素区分度最大。

大津法阈值采用最大类间方差的原理,适合于图像灰度分布整体呈现“双峰”的情况。大津法会自动找出一个阈值,使得分割后的两部分类间方差最大。

特性:

  1. 大津法对噪音十分敏感,在处理之前应对图片进行去噪处理。如果图像有存在局部噪声,则会影响大津法的判断
  2. 当目标与背景的面积比例悬殊的时候,类间方差函数可能呈现双峰或者多峰,这个时候 大津法的效果不好

(1)双峰图像,目标和背景面积差距不大,可以很好的判断,如下:

(2)当图像中的目标与背景的面积相差很大时,灰度直方图没有明显的双峰,或者两个峰的大小相差很大,分割效果不佳:

代码实现

一般大津法代码如下:

(你只需要将我的代码复制粘贴到images.c文件中,把这个函数在cpu1.c中调用即可)

images.c

  1. #include "zf_common_headfile.h"
  2. uint8 mt9v03x_image_BandW[MT9V03X_H][MT9V03X_W];
  3. /*begin 大津法比赛 begin*/
  4. //快速大津法二值化 pixelSum = width * height/4;
  5. //-------------------------------------------------------------------------------------------------------------------
  6. // @brief 快速大津
  7. // @return uint8
  8. // @since v1.1
  9. // Sample usage: OTSU_Threshold = otsuThreshold(mt9v03x_image_dvp[0]);//大津法阈值
  10. //-------------------------------------------------------------------------------------------------------------------
  11. uint8 otsuThreshold_fast(uint8 *image) //注意计算阈值的一定要是原图像
  12. {
  13. #define GrayScale 256
  14. int Pixel_Max=0;
  15. int Pixel_Min=255;
  16. uint16 width = MT9V03X_W; //宽100
  17. uint16 height = MT9V03X_H; //高80
  18. int pixelCount[GrayScale]; //各像素GrayScale的个数pixelCount 一维数组
  19. float pixelPro[GrayScale]; //各像素GrayScale所占百分比pixelPro 一维数组
  20. int i, j, pixelSum = width * height/4; //pixelSum是获取总的图像像素个数的1/4,相应下面轮询时高和宽都是以2为单位自增
  21. uint8 threshold = 0;
  22. // uint8 last_threshold = 0;
  23. uint8* data = image; //指向像素数据的指针
  24. //清零
  25. for (i = 0; i < GrayScale; i++)
  26. {
  27. pixelCount[i] = 0;
  28. pixelPro[i] = 0;
  29. }
  30. uint32 gray_sum=0; //每次执行到这会将gray_sum清零
  31. //统计灰度级中每个像素在整幅图像中的个数
  32. for (i = 0; i < height; i+=2) //高
  33. {
  34. for (j = 0; j < width; j+=2) //宽
  35. {
  36. pixelCount[(int)data[i * width + j]]++; //将当前的点的像素值作为计数数组的下标
  37. gray_sum+=(int)data[i * width + j]; //灰度值总和
  38. if(data[i * width + j]>Pixel_Max) Pixel_Max=data[i * width + j];
  39. if(data[i * width + j]<Pixel_Min) Pixel_Min=data[i * width + j];
  40. }
  41. }
  42. //计算每个像素值的点在整幅图像中的比例
  43. for (i = Pixel_Min; i < Pixel_Max; i++)
  44. {
  45. pixelPro[i] = (float)pixelCount[i] / pixelSum;
  46. }
  47. //遍历灰度级[0,255]
  48. float w0, w1, u0tmp, u1tmp, u0, u1, u, deltaTmp, deltaMax = 0;
  49. w0 = w1 = u0tmp = u1tmp = u0 = u1 = u = deltaTmp = 0;
  50. for (j = Pixel_Min; j < Pixel_Max; j++)
  51. {
  52. w0 += pixelPro[j]; //背景部分每个灰度值的像素点所占比例之和 即背景部分的比例
  53. u0tmp += j * pixelPro[j]; //背景部分 每个灰度值的点的比例 *灰度值
  54. w1=1-w0;
  55. u1tmp=gray_sum/pixelSum-u0tmp;
  56. u0 = u0tmp / w0; //背景平均灰度
  57. u1 = u1tmp / w1; //前景平均灰度
  58. u = u0tmp + u1tmp; //全局平均灰度
  59. deltaTmp = (float)(w0 *w1* (u0 - u1)* (u0 - u1)) ;
  60. if (deltaTmp > deltaMax)
  61. {
  62. deltaMax = deltaTmp;
  63. threshold = (uint8)j;
  64. }
  65. if (deltaTmp < deltaMax)
  66. {
  67. break;
  68. }
  69. }
  70. return threshold;
  71. }
  72. /*end 大津法比赛 end*/
  73. /*begin 大津法学习 begin*/
  74. //------------------摄像头参数--------------//
  75. uint8 image_threshold = 46; //图像阈值 0~255
  76. uint8 dis_image[60][80];
  77. uint8 otsuThreshold(uint8 *image, uint16 width, uint16 height)
  78. {
  79. #define GrayScale 256
  80. int pixelCount[GrayScale] = {0};//每个灰度值所占像素个数
  81. float pixelPro[GrayScale] = {0};//每个灰度值所占总像素比例
  82. int i,j;
  83. int Sumpix = width * height; //总像素点
  84. uint8 threshold = 0;
  85. uint8* data = image; //指向像素数据的指针
  86. //统计灰度级中每个像素在整幅图像中的个数
  87. for (i = 0; i < height; i++)
  88. {
  89. for (j = 0; j < width; j++)
  90. {
  91. pixelCount[(int)data[i * width + j]]++; //将像素值作为计数数组的下标
  92. // pixelCount[(int)image[i][j]]++; 若不用指针用这个
  93. }
  94. }
  95. float u = 0;
  96. for (i = 0; i < GrayScale; i++)
  97. {
  98. pixelPro[i] = (float)pixelCount[i] / Sumpix; //计算每个像素在整幅图像中的比例
  99. u += i * pixelPro[i]; //总平均灰度
  100. }
  101. float maxVariance=0.0; //最大类间方差
  102. float w0 = 0, avgValue = 0; //w0 前景比例 ,avgValue 前景平均灰度
  103. for(i = 0; i < 256; i++) //每一次循环都是一次完整类间方差计算 (两个for叠加为1个)
  104. {
  105. w0 += pixelPro[i]; //假设当前灰度i为阈值, 0~i 灰度像素所占整幅图像的比例即前景比例
  106. avgValue += i * pixelPro[i];
  107. float variance = pow((avgValue/w0 - u), 2) * w0 /(1 - w0); //类间方差
  108. if(variance > maxVariance)
  109. {
  110. maxVariance = variance;
  111. threshold = (uint8)i;
  112. }
  113. }
  114. return threshold;
  115. }
  116. /*end 大津法学习 end*/
  117. //图像二值化
  118. //0 - 255
  119. //黑 - 白
  120. void Set_image_towvalues(uint8 value)
  121. {
  122. uint8 temp_valude;//暂存灰度值
  123. for(uint8 i = 0;i < MT9V03X_H;i++)//高
  124. {
  125. for(uint8 j = 0;j < MT9V03X_W;j++)//宽
  126. {
  127. temp_valude = mt9v03x_image[i][j];
  128. if(temp_valude < value)
  129. {
  130. mt9v03x_image_BandW[i][j] = 0;//黑
  131. }
  132. else
  133. {
  134. mt9v03x_image_BandW[i][j] = 255;//白
  135. }
  136. }
  137. }
  138. }

images.h

  1. #ifndef CODE_IMAGES_H_
  2. #define CODE_IMAGES_H_
  3. extern uint8 mt9v03x_image_BandW[MT9V03X_H][MT9V03X_W];
  4. void Set_image_towvalues(uint8 value);
  5. uint8 otsuThreshold(uint8 *image, uint16 width, uint16 height);
  6. uint8 otsuThreshold_fast(uint8 *image);
  7. #endif /* CODE_IMAGES_H_ */

但是不优化大津法的代码,图像处理时间较长,所以大家又将“大津法”优化成了“快速大津法”,代码也在上面公布了,照我如下调用即可。

cpu1.c

  1. void core1_main(void)
  2. {
  3. disable_Watchdog(); // 关闭看门狗
  4. interrupt_global_enable(0); // 打开全局中断
  5. // 此处编写用户代码 例如外设初始化代码等
  6. mt9v03x_init();//初始化摄像头
  7. // 此处编写用户代码 例如外设初始化代码等
  8. cpu_wait_event_ready(); // 等待所有核心初始化完毕
  9. while (TRUE)
  10. {
  11. // 此处编写需要循环执行的代码
  12. TFT180_SHOW();
  13. if(mt9v03x_finish_flag) //一幅图像完全采集完毕后,再进行图像的显示判断和显示
  14. {
  15. //Set_image_towvalues(150); //固定阈值二值化
  16. BandW_threshold = otsuThreshold_fast(mt9v03x_image[0]);//大津法得到动态阈值BandW_threshold
  17. Set_image_towvalues(BandW_threshold); //动态阈值二值化得到二维数组mt9v03x_image_BandW
  18. tft180_displayimage03x(mt9v03x_image_BandW[0],MT9V03X_W,MT9V03X_H);//显示二值化后的图像
  19. mt9v03x_finish_flag = 0;//图像显示完成后才对标志位清零
  20. }
  21. // 此处编写需要循环执行的代码
  22. }
  23. }

通过调用大津法处理函数,将原始图像进行动态阈值二值化处理,也是会在显示屏上得到黑白图像,固定阈值二值化和动态阈值二值化处理哪个效果好,还得看实际情况。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/479818
推荐阅读
相关标签
  

闽ICP备14008679号