当前位置:   article > 正文

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

智能车摄像头图像处理

前言

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

一、摄像头图像处理

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

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;


            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

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

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

二、左右边界,中线扫描

在二值化之后,我们得到了一个由黑和白组成的二维数组,而我们的目的是让小车时刻处于赛道中间位置,也就是图像中间位置。我们想要小车不出赛道,如果能让赛道的中线始终贴合图像数组的中间(此处图像数组的中间我们可以想象为图像的宽/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];		//中线查找开始点,方便中线寻找
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

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




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

闽ICP备14008679号