智能汽车竞赛摄像头处理(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

#include "zf_common_headfile.h"uint8  mt9v03x_image_BandW[MT9V03X_H][MT9V03X_W];/*begin  大津法比赛   begin*/
//快速大津法二值化 pixelSum = width * height/4;
//-------------------------------------------------------------------------------------------------------------------
//  @brief      快速大津
//  @return     uint8
//  @since      v1.1
//  Sample usage:   OTSU_Threshold = otsuThreshold(mt9v03x_image_dvp[0]);//大津法阈值
//-------------------------------------------------------------------------------------------------------------------
uint8 otsuThreshold_fast(uint8 *image)   //注意计算阈值的一定要是原图像
{
#define GrayScale 256int Pixel_Max=0;int Pixel_Min=255;uint16 width = MT9V03X_W;   //宽100uint16 height = MT9V03X_H;  //高80int pixelCount[GrayScale];  //各像素GrayScale的个数pixelCount 一维数组float pixelPro[GrayScale];  //各像素GrayScale所占百分比pixelPro 一维数组int i, j, pixelSum = width * height/4;  //pixelSum是获取总的图像像素个数的1/4,相应下面轮询时高和宽都是以2为单位自增uint8 threshold = 0;
//    uint8 last_threshold = 0;uint8* data = image;  //指向像素数据的指针//清零for (i = 0; i < GrayScale; i++){pixelCount[i] = 0;pixelPro[i] = 0;}uint32 gray_sum=0;  //每次执行到这会将gray_sum清零//统计灰度级中每个像素在整幅图像中的个数for (i = 0; i < height; i+=2)   //高{for (j = 0; j < width; j+=2)    //宽{pixelCount[(int)data[i * width + j]]++;  //将当前的点的像素值作为计数数组的下标gray_sum+=(int)data[i * width + j];       //灰度值总和if(data[i * width + j]>Pixel_Max)   Pixel_Max=data[i * width + j];if(data[i * width + j]<Pixel_Min)   Pixel_Min=data[i * width + j];}}//计算每个像素值的点在整幅图像中的比例for (i = Pixel_Min; i < Pixel_Max; i++){pixelPro[i] = (float)pixelCount[i] / pixelSum;}//遍历灰度级[0,255]float w0, w1, u0tmp, u1tmp, u0, u1, u, deltaTmp, deltaMax = 0;w0 = w1 = u0tmp = u1tmp = u0 = u1 = u = deltaTmp = 0;for (j = Pixel_Min; j < Pixel_Max; j++){w0 += pixelPro[j];  //背景部分每个灰度值的像素点所占比例之和   即背景部分的比例u0tmp += j * pixelPro[j];  //背景部分 每个灰度值的点的比例 *灰度值w1=1-w0;u1tmp=gray_sum/pixelSum-u0tmp;u0 = u0tmp / w0;              //背景平均灰度u1 = u1tmp / w1;              //前景平均灰度u = u0tmp + u1tmp;            //全局平均灰度deltaTmp = (float)(w0 *w1* (u0 - u1)* (u0 - u1)) ;if (deltaTmp > deltaMax){deltaMax = deltaTmp;threshold = (uint8)j;}if (deltaTmp < deltaMax){break;}}return threshold;
}
/*end  大津法比赛   end*//*begin  大津法学习   begin*/
//------------------摄像头参数--------------//
uint8 image_threshold = 46;  //图像阈值 0~255
uint8 dis_image[60][80];uint8 otsuThreshold(uint8 *image, uint16 width, uint16 height)
{#define GrayScale 256int pixelCount[GrayScale] = {0};//每个灰度值所占像素个数float pixelPro[GrayScale] = {0};//每个灰度值所占总像素比例int i,j;int Sumpix = width * height;   //总像素点uint8 threshold = 0;uint8* data = image;  //指向像素数据的指针//统计灰度级中每个像素在整幅图像中的个数for (i = 0; i < height; i++){for (j = 0; j < width; j++){pixelCount[(int)data[i * width + j]]++;  //将像素值作为计数数组的下标//   pixelCount[(int)image[i][j]]++;    若不用指针用这个}}float u = 0;for (i = 0; i < GrayScale; i++){pixelPro[i] = (float)pixelCount[i] / Sumpix;   //计算每个像素在整幅图像中的比例u += i * pixelPro[i];  //总平均灰度}float maxVariance=0.0;  //最大类间方差float w0 = 0, avgValue  = 0;  //w0 前景比例 ,avgValue 前景平均灰度for(i = 0; i < 256; i++)     //每一次循环都是一次完整类间方差计算 (两个for叠加为1个){w0 += pixelPro[i];  //假设当前灰度i为阈值, 0~i 灰度像素所占整幅图像的比例即前景比例avgValue  += i * pixelPro[i];float variance = pow((avgValue/w0 - u), 2) * w0 /(1 - w0);    //类间方差if(variance > maxVariance){maxVariance = variance;threshold = (uint8)i;}}return threshold;
}/*end  大津法学习   end*///图像二值化
//0 - 255
//黑 - 白
void Set_image_towvalues(uint8 value)
{uint8 temp_valude;//暂存灰度值for(uint8 i = 0;i < MT9V03X_H;i++)//高{for(uint8 j = 0;j < MT9V03X_W;j++)//宽{temp_valude = mt9v03x_image[i][j];if(temp_valude < value){mt9v03x_image_BandW[i][j] = 0;//黑}else{mt9v03x_image_BandW[i][j] = 255;//白}}}
}

images.h

#ifndef CODE_IMAGES_H_
#define CODE_IMAGES_H_extern uint8  mt9v03x_image_BandW[MT9V03X_H][MT9V03X_W];void Set_image_towvalues(uint8 value);
uint8 otsuThreshold(uint8 *image, uint16 width, uint16 height);
uint8 otsuThreshold_fast(uint8 *image);
#endif /* CODE_IMAGES_H_ */

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

cpu1.c

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/450430.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL亿级数据的查询优化-历史表该如何建

前端时间在知乎上看到一个问题&#xff0c;今天有空整理并测试了一下&#xff1a; 这个问题很具体&#xff0c;所以还是可以去尝试优化一下&#xff0c;我们基于InnoDB并使用自增主键来讲。 比较简单的做法是将历史数据存放到另一个表中&#xff0c;与最近的数据分开。那是不是…

参考数据集INRIA Holidays dataset

Download datasets 很贴心,MATLAB访问代码: % This function reads a siftgeo binary file % % Usage: [v, meta] = siftgeo_read (filename, maxdes) % filename the input filename % maxdes maximum number of descriptors to be loaded % (default=unlimit…

java05 数组

一 概念介绍 指的是一种容器&#xff0c;可以同来存储同种数据类型的多个值。 但是数组容器在存储数据的时候&#xff0c;需要结合隐式转换考虑。 比如&#xff1a; 定义了一个int类型的数组。那么boolean。double类型的数据是不能存到这个数组中的&#xff0c; 但是byte类…

1895_分离进程的能力

1895_分离进程的能力 全部学习汇总&#xff1a; g_unix: UNIX系统学习笔记 (gitee.com) 有些理念可能在控制类的嵌入式系统中不好实施&#xff0c;尤其是没有unix这样的系统搭载的情况下。如果是考虑在RTOS的基础上看是否有一些理念可以做尝试&#xff0c;我觉得还是可以有一定…

绝世唐门:霍挂六个十万年魂环,一穿七灭团再现,淘汰赛顺利晋级

Hello,小伙伴们&#xff0c;我是拾荒君。 国漫《斗罗大陆2绝世唐门》第32期超前爆料&#xff0c;霍雨浩开局便释放六个十万年魂环&#xff0c;以绝对的气场碾压天灵学院代表队。首次参与高级魂师大赛&#xff0c;霍雨浩便大放异彩秀出超级霍挂&#xff0c;此等操作就连当初的唐…

[Python] scikit-learn中数据集模块介绍和使用案例

sklearn.datasets模块介绍 在scikit-learn中&#xff0c;可以使用sklearn.datasets模块中的函数来构建数据集。这个模块提供了用于加载和生成数据集的函数。 API Reference — scikit-learn 1.4.0 documentation 以下是一些常用的sklearn.datasets模块中的函数 load_iris() …

数字巨轮航行大数据海洋:数据可视化引领时代潮流

在大数据时代的潮流中&#xff0c;数据可视化如同一艘畅行无阻的科技巨轮&#xff0c;引领我们穿越数字浩瀚的大海&#xff0c;使我们在信息的航程中游刃有余。下面我就从可视化从业者的角度&#xff0c;来简单说说数据可视化是如何帮助我们在大数据时代畅行无阻的。 数据可视化…

1、安全开发-Python爬虫EDUSRC目标FOFA资产Web爬虫解析库

用途&#xff1a;个人学习笔记&#xff0c;有所借鉴&#xff0c;欢迎指正 前言&#xff1a; 主要包含对requests库和Web爬虫解析库的使用&#xff0c;python爬虫自动化&#xff0c;批量信息收集 Python开发工具&#xff1a;PyCharm 2022.1 激活破解码_安装教程 (2022年8月25日…

CentOS 8 下载

https://mirrors.bfsu.edu.cn/centos/8-stream/isos/x86_64/ 下载地址&#xff1a; https://mirrors.bfsu.edu.cn/centos/8-stream/isos/x86_64/CentOS-Stream-8-x86_64-latest-dvd1.iso

【第二十二课】最短路:多源最短路floyd算法(acwing-852 spfa判断是否存在负环 / acwing-854 / c++代码)

目录 acwing-852 代码如下 一些解释 acwing-854 foyld算法思想 代码如下 一些解释 acwing-852 在spfa求最短路的算法基础上进行修改。 代码如下 #include<iostream> #include<cstring> #include<algorithm> #include<queue> using names…

适用于 Windows 7/8/10/11电脑的 12 款顶级数据恢复软件

很多时候&#xff0c;我们在 Windows 7 /8/10/11下不小心按了删除键&#xff0c;从而丢失了硬盘或 USB 驱动器中的重要文件和数据。在某些情况下&#xff0c;病毒或软件错误可能会损坏您的硬盘&#xff0c;从而影响您的文件。在这种情况下&#xff0c;您迫切需要一款适用于 Win…

坚持刷题|二叉树的前、中、后序遍历(递归迭代)

文章目录 题目思考递归实现迭代实现前序遍历后序遍历中序遍历 在前、中、后序的迭代遍历中&#xff0c;为什么都采用栈来模拟递归&#xff0c;而非队列&#xff1f; Hello&#xff0c;大家好&#xff0c;我是阿月。坚持刷题&#xff0c;老年痴呆追不上我&#xff0c;今天刷&…