第十八届全国大学生智能汽车竞赛——摄像头算法(附带个人经验)

news2024/12/23 14:13:47

文章目录

  • 前言
  • 一、摄像头图像处理
    • 1、摄像头图像采集
    • 2、图像二值化与大津算法
  • 二、左右边界,中线扫描

前言

参加了第十六,十七和第十八届全国大学生智能车竞赛,对摄像头的学习有部分心得,分享给大家,三届车赛,车赛生涯也算是到了尽头。打算从基础的算法开始,给各位一些个人看法,也是对车赛的一次总结。

一、摄像头图像处理

闲话:其实摄像头的算法有很多种,弄了两年摄像头,也只是学会了其中很小的一部分,但最终,作用都是大同小异的,也不必太过于追求算法上的完美。只需要达到能稳定提取特征,识别元素其实就够用了。(个人用的是普通大津+二值化+八领域做边界提取)

1、摄像头图像采集

打开摄像头相关例程,可以发现其实最终摄像头所采集的数据都存入了一个二维数组中,工作方式也很简单:图像采集,将图像采集标志置一。手动清零,就可以达到重复采集的目的。(没有相关例程的可以去找客服要,记得B站上也有逐飞的摄像头摄像头图像采集视频讲解)

逐飞科技CH32V307摄像头例程

2、图像二值化与大津算法

智能车使用的摄像头所采集的图像一般都是灰度图像,将图像分割、数字化成一个个的数(一个个的像素点),0~255,像素点颜色越白数字越大。我们可以看出图像信息很丰富,图像处理方法自然也就多种多样了,首先我们可以将图像二值化

二值化就是将图像上的灰度点分别设置为0,255(0xFF)(有点像二级分化)。图像直观上就会变为黑白图像。我们该按照何种规则分黑和白呢?这时候就需要我们找出黑、白的分界值(也就是阈值)(大于阈值即为白,反之为黑),然后可以遍历图像数组中每一个像素点,大于阈值设为0XFF(白),反之设为0(黑)。(二值化有一个进阶的思想:图像每一个点都需要二值化嘛,我们能不能只将要使用的点二值化呢,这样速度是不是就快了,这个其实开始没有必要弄,有一张完整的二值化图也方便我们理解)

:1,数组均从零开始。
2,二值化不能作用于摄像头采集原图上,应该重新定义一个同大小的数组,专门存放二值化后的图像信息。(防止在进行图像处理时,摄像头重新采集数据,覆盖之前数据)

/*
mt9v03x_image_dvp : 原图像   		 存放摄像头灰度原图
mt9v03x_image_baz :二值化图像        用于存放图像右边界 
WHTIE  宏定义  替换作用 与0xff相同
*/
#define WHTIE 0xff //255
#define BLACK 0x00
 //使用宏 通过单词代替抽象的数字 
    for(uint8_t i=0;i<MT9V03X_DVP_H;i++)             //MT9V03X_DVP_H:图像高
    {
        for(uint8_t j=0;j<MT9V03X_DVP_W;j++)         //MT9V03X_DVP_W:图像宽
        {
            if(mt9v03x_image_dvp[i][j]>=threshold )  //threshold:图像阈值
            {
               mt9v03x_image_baz[i][j]=WHITE;
            }
            else
            {
               mt9v03x_image_baz[i][j]=BLACK;


            }
        }
    }

后面就是确定图像的阈值了,因为真实环境亮度是变化的,单纯的给定阈值二值化就显得不够稳定,在图像的基础上动态的计算阈值适应性更强。我选择的是大津算法,也是常用且基础的一种了,缺点就是运算时间有点稍长。

大津算法的相关我就不说了,网上有很多相关介绍。(其实我理解的也不太透彻, 手动狗头保命)

二、左右边界,中线扫描

在二值化之后,我们得到了一个由黑和白组成的二维数组,而我们的目的是让小车时刻处于赛道中间位置,也就是图像中间位置。我们想要小车不出赛道,如果能让赛道的中线始终贴合图像数组的中间(此处图像数组的中间我们可以想象为图像的宽/2),那我们的小车是不是就不会出赛道了。图像数组的中间就像PID中的理论值,实际赛道的中线就像实际值。这样我们是不是就可以将图像与PID联系起来。
说回边界扫描,想直接找赛道中线还是比较难的,主要是没有明显特征,我们不妨先寻找左右边界(左右边界黑白相夹),左右相加除二求得中线。这样就得到比较真实的中线坐标,再对比理想中线坐标(图像中间,也就是图像宽/2),从而求出中线偏差,用于PID控制。思路大抵都是这样,但找的方法就很多种多样了。

基础的方法就是从中间往两边找,缺点是遍历了整幅图像,消耗时间稍长,而进阶的就有对普通两边找线方法的优化,(如:双最长白列法)。再进一步稍微复杂一点的有八邻域,迷宫法等等。

我们先来看看普通的两边找线法:
在这里插入图片描述

基本思路就是从最下行往上 (利于对之后对中线的处理),每一行从中间往两边,分别寻找左右边界(特征:黑白跳变),存入左右边界数组。
同时存下中线坐标,用于下一行中线的扫描。(这样减小运算量的同时还可以加大中线扫描时的连续性)左右边界相加除2求出该行中线坐标,存入中线数组。
注:

/* 
        last_mid:                   边界扫描起始坐标 每行边界从此开始 起始行为图像宽/2 后为前一行图像中线坐标
        MT9V03X_DVP_W:              图像宽
        MT9V03X_DVP_H:              图像高
        mt9v03x_image_baz[][]:      二值化图像数组
        left_flag[] :               左边界存在数组 左边界存在 标志置1
        left_flag[] :               右边界存在数组 右边界存在 标志置1
        left_border[]:              左边界数组 存放左边界坐标
        right_border[]:             右边界数组 存放右边界坐标
        Mid_border[]:               中线左边数组 存放中线坐标
*/

last_mid = MT9V03X_DVP_W / 2;    
for (int i = MT9V03X_DVP_H - 1; i >= 0; i--)//从下往上扫描
{
    left_flag[i] = 1;
    right_flag[i] = 1;


    for (int j = last_mid; j > 1; j--)//中间往左边扫描
    {

        if (mt9v03x_image_baz[i][j] == 0xff && mt9v03x_image_baz[i][j-1] == 0x00 &&  mt9v03x_image_baz[i][j-2]==0x00)//黑黑白认为找到左边界
        {

            left_border[i] = j;                       //将左边界存入左边界数组
            left_flag[i] = 1;                         //左边界找到,标志置0
            break; 				//跳出循环
        }

    }
    if (left_flag[i]==0) left_border[i]=0;              //补线标志未置一,此行左丢线,取图像左边界

    for (int j = last_mid; j < MT9V03X_DVP_W-2; j++)  //往右扫描
    {
    
        if (mt9v03x_image_baz[i][j]==0xff && mt9v03x_image_baz[i][j+1]==0x00 && mt9v03x_image_baz[i][j+2]==0x00)//白黑黑认为找到右边界
        {

            right_border[i] = j;                        //将右边界存入右边界数组
            right_flag[i] = 1;                         //右边界找到,标志置0
            break; 				//跳出循环
        }
    }
    if (right_flag[i]==0) right_border[i] = MT9V03X_DVP_W-1;           //补线标志未置一,此行右丢线,取图像右边界

    Mid_border[i] = (left_border[i] + right_border[i]) / 2;		//中线坐标
    mt9v03x_image_baz[i][left_border[i]-2] = BLACK; 	//左边界涂黑
    mt9v03x_image_baz[i][Mid_border[i]] = BLACK; 	//中线涂黑
    mt9v03x_image_baz[i][right_border[i]+2] = BLACK; 	//右边界涂黑       
   /* 注意  左右边界-2 +2 很容易存在数组越界 导致程序卡住 如  左边界点  left_border[i]=0;   [right_border[i]=MT9V03X_DVP_W-1
   自己使用时可以加个限制如
   mt9v03x_image_baz[i][(left_border[i]<2?2:left_border[i])-2] = BLACK; 	//左边界涂黑
   */
    last_mid = Mid_border[i];		//中线查找开始点,方便中线寻找
}

可以在图像上把边界显示出来,方便后面观察图像,像这样:
边界显示
后续给各位说说我对八领域的理解,欢迎大家关注!!!




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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

explain关键字的用法(mysql高级部分)

文章目录 简介explain关键字分析 简介 explain主要是用来分析sql语句的&#xff0c;当你的系统中出现慢查询SQL后&#xff0c;你可以使用explain关键字对该语句进行分析。通过使用explain&#xff0c;我们可以得到以下结果 表的读取顺序 哪些索引可能使用 哪些索引被实际使用…

有c语言基础,如何快速学会C++核心知识?

有c语言基础&#xff0c;如何快速学会C核心知识&#xff1f; 在开始前我分享下我的经历&#xff0c;我刚入行时遇到一个好公司和师父&#xff0c;给了我机会&#xff0c;一年时间从3k薪资涨到18k的&#xff0c; 我师父给了一些 电气工程师学习方法和资料&#xff0c;让我不断提…

9:00面试,9:06就出来了,问的实在是太变态了

我从一家小公司转投到另一家公司&#xff0c;期待着新的工作环境和机会。然而&#xff0c;新公司的加班文化让我有些始料未及。虽然薪资相对较高&#xff0c;但长时间的工作和缺乏休息使我身心俱疲。 就在我逐渐适应这种高强度的工作节奏时&#xff0c;公司突然宣布了一则令人…

力扣● 1143.最长公共子序列 ● 1035.不相交的线 ● 53. 最大子序和 动态规划

● 1143.最长公共子序列 1.dp数组含义。 dp[i][j]&#xff1a;数组1[0,i-1]范围的子数组和数组2[0,j-1]的子数组的公共子序列最长长度。注意这里不需要一定以A[i-1]/B[j-1]结尾&#xff0c;原因在下面有说明。 动态规划求子序列的问题&#xff0c;一般都是dp的下标相对于数组…

系统运维网络知识汇总

一、系统运维中网络方面的规划与思考 系统运维建立在网络的基础之上&#xff0c;如果没有一个相对合理的网络架构&#xff0c;恐怕系统运维做起来也不是那么的顺手。一个公司基本上都会把网络和服务器独立开来&#xff0c;划分不同的区域摆放设备&#xff0c;很多时候都是物理…

YOLOv8_seg-Openvino和ONNXRuntime推理【CPU】

纯检测系列&#xff1a; YOLOv5-Openvino和ONNXRuntime推理【CPU】 YOLOv6-Openvino和ONNXRuntime推理【CPU】 YOLOv8-Openvino和ONNXRuntime推理【CPU】 YOLOv7-Openvino和ONNXRuntime推理【CPU】 YOLOv9-Openvino和ONNXRuntime推理【CPU】 跟踪系列&#xff1a; YOLOv5/6/7-O…

力扣L5----- 58. 最后一个单词的长度(2024年3月11日)

1.题目 2.知识点 注1&#xff1a; lastIndexOf()它用于查找指定字符或子字符串在当前字符串中最后一次出现的位置。它的作用是从字符串的末尾向前搜索指定字符或子字符串&#xff0c;并返回其最后一次出现的位置的索引。 &#xff08;1&#xff09;例如&#xff0c;在字符串 …

银河麒麟V10SP3操作系统-网络时间配置

1、动态网络配置 打开终端&#xff0c;以网口 eth0 为例&#xff1a; nmcli conn add connection.id eth0-dhcp type ether ifname eth0 ipv4.method auto其中“eth0-dhcp”为连接的名字&#xff0c;可以根据自己的需要命名方便记忆和操作 的名字&#xff1b;“ifname eth0”…

鞋服品牌如何计算门店盈亏平衡?

在鞋服品牌的运营中&#xff0c;门店盈亏平衡是衡量门店经营效果的重要指标。盈亏平衡点意味着门店在达到这一销售水平时&#xff0c;既能够覆盖所有固定和变动成本&#xff0c;又能实现零利润或零亏损。计算门店盈亏平衡有助于品牌更好地理解门店的经营状况&#xff0c;制定合…

Springboot进行web开发

创建springboot工程&#xff0c;基于2022版idea pom.xml文件中的插件爆红&#xff1a; 解决方法&#xff1a;给插件加<version>版本号</version> 版本号和<parent></parent>中的版本号一样。 另外有人说重启也可以解决爆红&#xff0c;可以试一下&a…

SpringBoot(容器功能)

文章目录 1.Configuration 添加/注入bean1.注入bean1.编写一个JavaBean&#xff0c;Monster.java2.创建一个config文件夹&#xff08;名字任意&#xff09;&#xff0c;用于存放配置Bean的类&#xff08;相当于配置文件&#xff09;3.BeanConfig.java4.测试使用 MainApp.java2.…

高中信息技术教资学习

一、几种排序方法的基本思想 1、直接插入排序&#xff08;假设按照从小到大进行排序&#xff09; 默认第一个元素是有序的&#xff0c;从有序的元素末尾开始&#xff0c;与要插入的元素进行比较&#xff0c;如果要插入的元素比有序的末尾元素小的话&#xff0c;就将有序末尾元…

适合一个人开的实体店:创业新选择与经营秘籍大公开

大家好&#xff0c;我是一名开鲜奶吧5年的实体店创业者&#xff0c;在行业里摸爬滚打多年&#xff0c;积累了丰富的经验。今天&#xff0c;我想和大家分享一些关于适合一个人开的实体店的创业新选择和经营秘籍。 首先&#xff0c;我们来聊一聊适合一个人开的实体店有哪些。这类…

多线程案例及常用模式

一.单例模式——经典的设计模式 什么是单例模式&#xff1a;就是规定一个类只能创建一个对象&#xff0c;也就是保证某个类在程序中只存在唯一一个实例&#xff0c;而不会创建出多个实例 根据对象创建的时机不同&#xff0c;可以分为饿汉模式和懒汉模式 1.饿汉模式 在类加载…

基与HTML5的塔防游戏设计与实现

目 录 摘 要 I Abstract II 引 言 1 1 项目背景与相关技术 3 1.1 背景与发展简介 3 1.2 HTML5技术及其优势 4 1.3 JavaScript开发的优势与劣势 4 1.4 CSS样式表在开发中的用处 5 1.5 本章小结 6 2 系统分析 7 2.1 需求分析 7 2.2 问题分析 7 2.3 流程设计 7 2.3 功能分析 8 2.…

【Git】Github 上commit后,绿格子contribution却不显示?不知道怎么弥补?解决方法在这里

github 上commit后&#xff0c;绿格子&#xff08;contribution&#xff09;却不显示 问题描述 今天一直在github上面commit代码&#xff0c;但是github中并没有显示自己的contribution&#xff08;没有绿色的格子&#xff09;&#xff0c;全是空白&#xff0c;网上一查是因为…

点一下即可任意调整静态图片:这个开源AI图片项目你需要了解一下

项目简介 合成满足用户需求的视觉内容通常需要对生成对象的姿势、形状、表情和布局进行灵活而精确的控制。现有的方法通过手动注释的训练数据或先前的3D模型来获得生成对抗网络&#xff08;GAN&#xff09;的可控性&#xff0c;这通常缺乏灵活性、精确性和通用性。在这项工作中…

电玩城游戏大厅计时软件怎么用,佳易王计时计费管理系统软件定时语音提醒操作教程

电玩城游戏大厅计时软件怎么用&#xff0c;佳易王计时计费管理系统软件定时语音提醒操作教程 一、前言 以下软件操作教程以 佳易王电玩计时计费软件V18.0为例 说明 软件文件下载可以点击最下方官网卡片——软件下载——试用版软件下载 1、软件计时计费&#xff0c;只需点击开…

学c还行,学Python很累,还有其他语言适合我吗?

学c还行&#xff0c;学Python很累&#xff0c;还有其他语言适合我吗&#xff1f; 在开始前我分享下我的经历&#xff0c;我刚入行时遇到一个好公司和师父&#xff0c;给了我机会&#xff0c;一年时间从3k薪资涨到18k的&#xff0c; 我师父给了一些 电气工程师学习方法和资料&a…

Java详解:单列 | 双列集合 | Collections类

○ 前言&#xff1a; 在开发实践中&#xff0c;我们需要一些能够动态增长长度的容器来保存我们的数据&#xff0c;java中为了解决数据存储单一的情况&#xff0c;java中就提供了不同结构的集合类&#xff0c;可以让我们根据不同的场景进行数据存储的选择&#xff0c;如Java中提…