** ## 智能车阳光算法 **

谈不上真正的阳光算法,但是对于一些光干扰的场景和一些有噪点的图像还是可以进行处理。
在之前主流的摄像头主要是ov7725这个摄像头的优点就是硬件二值化,它的处理速度比较快,但是它输出只能是0和1,即就是黑和白,这对于阳光处理,反而成了缺点,我们只能人为的修改阈值,来适应部分场景,但是在光线较恶劣的场景下,我们不可能满足所有的光线要求,所以基于此,灰度摄像头由此诞生。灰度摄像头的灵活之处在于它返回的值不在仅仅是0和1,而是0-255之间,这样我们就可以根据图像,来人为进行一些光线处理,图像处理的操作。目前智能车主流的灰度摄像头有总钻风和神眼,一个是龙丘的一个是逐飞的。下面我们以逐飞的总钻风为例,给大家讲解一下摄像头的一些阳光处理。

1.图像二值化

上一篇文章里面我们说到了,如何获取图像和解压图像,但是我们主要针对的是ov7725,这里我们在给大家讲一下总钻风的处理。
首先就是正常的图像获取,但是今天我们用的是软件二值化的摄像头,就是灰度摄像头,所以我们首先就要对采集的数组进行处理。

void photo_get()
{int num=0;for(num=0;num<120;num++){memcpy(dis_image[num],mt9v03x_csi_image[num],160);}
}

这里我们首先进行了一个操作就是想摄像头的图像数组进行复制,在这里复制的目的是:比如摄像头在进行图像采集的过程中,我们就要对图像进行操作,这样就会对原数据有一定的影响,因此我们首先是将图像进行复制,然后在对复制过来的图像进行操作。这样我们还可以在后面用原数据进行对比。这是第一步核心操作,接下来就是图像二值化了。一般图像二值化有几种常见的:大津法、和均值二值化。均值二值化是龙丘的库里面就会有,当然他的库里面也会有大津法,不过这个都需要去优化,因为一般大津法耗时比较长,所以软件二值化对芯片的要求就比较高,比如我们目前用的总钻风,就用k60带不起来,就要用主频更高的rt1064这个主频500M的芯片来配套使用。
下面给大家分享已经优化的大津法:

    #define GrayScale 256uint16 Image_Width  = col;uint16 Image_Height = row;int X; uint16 Y; uint8* data = image;int HistGram[GrayScale];for (Y = 0; Y < GrayScale; Y++) { HistGram[Y] = 0; //初始化灰度直方图 } for (Y = 0; Y <Image_Height; Y++) //Y<Image_Height改为Y =Image_Height;以便进行 行二值化{ //Y=Image_Height;for (X = 0; X < Image_Width; X++) { HistGram[(int)data[Y*Image_Width + X]]++; //统计每个灰度值的个数信息 }}uint32 Amount = 0; uint32 PixelBack = 0; uint32 PixelIntegralBack = 0; uint32 PixelIntegral = 0; int32 PixelIntegralFore = 0; int32 PixelFore = 0; double OmegaBack=0, OmegaFore=0, MicroBack=0, MicroFore=0, SigmaB=0, Sigma=0; // 类间方差; int16 MinValue=0, MaxValue=0; uint8 Threshold = 0;for (MinValue = 0; MinValue < 256 && HistGram[MinValue] == 0; MinValue++) ;        //获取最小灰度的值for (MaxValue = 255; MaxValue > MinValue && HistGram[MinValue] == 0; MaxValue--) ; //获取最大灰度的值if (MaxValue == MinValue) {return MaxValue;          // 图像中只有一个颜色    }if (MinValue + 1 == MaxValue) {return MinValue;      // 图像中只有二个颜色}for (Y = MinValue; Y <= MaxValue; Y++){Amount += HistGram[Y];        //  像素总数}PixelIntegral = 0;for (Y = MinValue; Y <= MaxValue; Y++){PixelIntegral += HistGram[Y] * Y;//灰度值总数}SigmaB = -1;for (Y = MinValue; Y < MaxValue; Y++){PixelBack = PixelBack + HistGram[Y];    //前景像素点数PixelFore = Amount - PixelBack;         //背景像素点数OmegaBack = (double)PixelBack / Amount;//前景像素百分比OmegaFore = (double)PixelFore / Amount;//背景像素百分比PixelIntegralBack += HistGram[Y] * Y;  //前景灰度值PixelIntegralFore = PixelIntegral - PixelIntegralBack;//背景灰度值MicroBack = (double)PixelIntegralBack / PixelBack;//前景灰度百分比MicroFore = (double)PixelIntegralFore / PixelFore;//背景灰度百分比Sigma = OmegaBack * OmegaFore * (MicroBack - MicroFore) * (MicroBack - MicroFore);//gif (Sigma > SigmaB)//遍历最大的类间方差g{SigmaB = Sigma;Threshold = Y;}} return Threshold;

当然还有另外一种大津法,如果感兴趣的同学可以给我留言,我和大家分享第二种方法,但是此时效果已经差不多了。

2.灰度转换成二值化

敲黑板!!!不论是硬件二值化还是软件二值化,最终我们的有效数据都是二值化的图像,所以我们最终都是以二值化的图像为准!

void img_conversion(uint8 (*src)[COL],uint8 (*dst)[COL],uint8 val)
{uint8 line=0,col=0;for(line=0;line<ROW;line++){for(col=0;col<COL;col++){if(src[line][col]>val)  dst[line][col]=250;  //!!!!敲黑板 划重点 250 不是255else dst[line][col]=0;}}
}

!!!在这里有一个地方很重要,我们将黑点都变成了250,而不是255。这里是为后面埋下伏笔。因为我们一般都是0和255,也就是黑和白,但是在这里我们先将黑点都标位疑似点,也就是在这里我不能确定这个点就是黑点,所以后面我们还是要继续判断,所以这也是一个重点。

3.黑点确认!

这就是我们的第三点,就是我们刚才上述的疑似点,这里我们就将刚才的疑似点进行处理。

void find_borders()
{int16 i,j,temp_centere; //临时中点/**********先初始化边界数组********************/for(i=0;i<ROW;i++) //64行{for(j=0;j<3;j++){fb_image[i][j]=0;}}/**********找119行的中心检测点********************/temp_centere=COL/2;         //   上一次中点位置 for(int i=0;i<=30;i++)  {if(tv_image[119][temp_centere+i]==250 || tv_image[119][temp_centere+i]==255){temp_centere=temp_centere+i;break;}if(tv_image[119][temp_centere-i]==250 || tv_image[119][temp_centere-i]==255){ temp_centere=temp_centere-i;break;}}/**********最后一行向左扫描********************/for(i=temp_centere;i>=0;i--)   {if(tv_image[119][i] !=0 ){tv_image[119][i] =255;}else break;}if(i==-1)  //for循环中1-2循环 到达0时 值已经为-1了{fb_image[119][0] = 0;}else{fb_image[119][0] = i+1; //存储数组  假设左边界是20}/**********最后一行向右扫描********************/for(i=temp_centere; i<COL; i++)        {if(tv_image[119][i] !=0 ){tv_image[119][i] =255;}else break;}if(i == 160){fb_image[119][2] = 159;}else{fb_image[119][2] = i-1; //存储数组}/****************自下往上,扫描一次 筛选跳变点*****************/for(i=119;i>=tv_min_row;i--)    //根据119行的255遍历找本行的250或者255 及边界{for(j=0;j<160;j++){if( tv_image[i+1][j] == 255 )     //根据上一行的赛道来确定本行的赛道{if( tv_image[i][j]== 250 || tv_image[i][j] == 255 ){tv_image[i][j] = 255;}if( j-1 >=0 &&( tv_image[i][j-1] == 250 || tv_image[i][j-1] == 255 )){tv_image[i][j-1] =255;}if( j+1<=159 &&(tv_image[i][j+1] == 250 || tv_image[i][j+1] == 255)){tv_image[i][j+1]= 255;}}}/**********向左扫描,寻找第一个255点********************/for(j=0;j<=159;j++)        {if(tv_image[i][j] == 255){break;}}if(j==160)  //本行无赛道,跳出{break;}for(;j>=0;j--)      //根据上面找到的255,向回找255 或者 250 核心亮点 for循环还能这样玩{if(tv_image[i][j] == 250 || tv_image[i][j] == 255){tv_image[i][j] =255;}else{fb_image[i][0] = j+1;break;}}if(j==-1)   //本行无赛道,跳出{fb_image[i][0] = 0;} /**********向左扫描,寻找第一个255点********************/for(j=159;j>=0; j--)        //同上{if(tv_image[i][j] == 255){break;}}if(j==-1){break;}for(;j<=159;j++)     //同上{if(tv_image[i][j] == 250 || tv_image[i][j] == 255){tv_image[i][j] = 255;}else{fb_image[i][2] = j-1;break;}}if(j==160){fb_image[i][2] = 159;}}/*******************************二次扫描是为了处理特殊情况****************************************************/
#if 1if(i >= tv_min_row )  //代表图像未扫描完{temp_centere=COL/2;       //   上一次中点位置for(int j=0;j<=30;j++){if(tv_image[i][temp_centere+j]==250 || tv_image[i][temp_centere+j]==255){temp_centere=temp_centere+j;break;}if(tv_image[i][temp_centere-j]==250 || tv_image[i][temp_centere-j]==255){temp_centere=temp_centere-j;break;}}if( temp_centere != COL/2)        //不相等说明找到了该行的赛道{//自下往上,扫描一次for(i=temp_centere;i>=tv_min_row;i--)    //根据159行的255遍历找本行的250或者255 及边界{for(j=0;j<159;j++){if( tv_image[i+1][j] == 255 )     //根据上一行的赛道来确定本行的赛道{if( tv_image[i][j]== 250 || tv_image[i][j] == 255){tv_image[i][j] = 255;}if( j-1 >=0 &&( tv_image[i][j-1] == 250 || tv_image[i][j-1] == 255 )){tv_image[i][j-1] =255;}if( j+1<=159 &&(tv_image[i][j+1] == 250 || tv_image[i][j+1] == 255)){tv_image[i][j+1]= 255;}}}   for(j=0;j<=159;j++)         //向左扫描,寻找第一个255点{if(tv_image[i][j] == 255){break;}}if(j==160)  //本行无赛道,跳出{break;}for(;j>=0;j--)      //根据上面找到的255,向回找255 或者 250{if(tv_image[i][j] == 250 || tv_image[i][j] == 255){tv_image[i][j] =255;}else{fb_image[i][0] = j+1;break;}}if(j==-1)   //本行无赛道,跳出{fb_image[i][0] = 0;}for(j=159;j>=0; j--)        //同上{if(tv_image[i][j] == 255){break;}}if(j==-1){break;}for(;j<=159;j++)     //同上{if(tv_image[i][j] == 250 || tv_image[i][j] == 255){tv_image[i][j] = 255;}else{fb_image[i][2] = j-1;break;}}if(j==160){fb_image[i][2] = 159;}}}}
#endif
}

下面就是我们的二次判定,主要是对刚才的疑似点再次进行判定,这样我们就可以将刚才的疑似点再次进行处理,这样就有一定的图像干扰处理。

void twoBJ()
{int i,j,k,temp_centere;//寻找63行的中点temp_centere=COL/2;       //   上一次中点位置for(int i=0;i<=30;i++){if(tv_image[119][temp_centere+i]==255){temp_centere=temp_centere+i;break;}if( tv_image[119][temp_centere-i]==255){temp_centere=temp_centere-i;break;}}for(i=ROW-1;i>=0;i--)        {for(j=temp_centere;j<COL;j++) //前一行中点位置作为本行的向右侧扫描的起点{if(tv_image[i][j]==0)      {if(j==temp_centere)      //第一个点就等于0,说明此行丢赛道,跳过{return ;}fb_image[i][2]=j-1;       //右侧扫描到第一个黑点,判定为赛道的边界break;}else         //改写赛道,消除干扰{tv_image[i][j]=255;}}if(j==COL){fb_image[i][2]=COL-1;}for(k=temp_centere;k>=0;k--)  //同上{if(tv_image[i][k]==0){if(k==temp_centere)     第一个点就等于0,说明此行丢赛道,跳过{return ;}fb_image[i][0]=k+1;break;}else{tv_image[i][k]=255;}}if(k==-1){fb_image[i][0]=0;}temp_centere=(j+k)/2;     //保存本行的中线,为下一行做参考fb_image[i][1]=(fb_image[i][0]+fb_image[i][2])/2;if((k==(COL-1)/2&&j==(COL-1)/2)||(k==0&& j==0)){break;}if( k == 119 && j == 119){break;}}}

上述就是部分的图像干扰处理,可以将部分噪点进行处理,同时也能让图像的数据更加有效,在后期我们进行赛道分析的时候,也会有很大的帮助。如果有写的不好的地方,请大佬见谅。仅自己个人观点。

智能车阳光算法(含大津法)相关推荐

  1. 算法001:大津法OTSU学习记录

    OTSU算法 一.大津法主要的工作是什么? 大多数时候,我们需要获取到一幅图像中的特定目标.如果可以根据像素值将图像进行合理的分割,例如全局阈值分割那样,找到一个阈值TTT,大于阈值TTT的赋予一个像 ...

  2. C++实现OTSU算法(大津法)

    OTSU算法,即最大类间方差,是由日本学者大津(Nobuyuki Otsu)于1979年提出的一种自适应阈值确定方法.算法假设一组数据 DDD 能够根据阈值被分为两部分,使得两类的区分度最大.两类之间 ...

  3. 基于OTSU(大津法)的图像分块的阈值分割

    一.开发环境: Qt版本:Qt5.12.3VS版本:VS2017opencv版本:opencv-4.5.1-vc14_vc15 二.要求:实现基于图像分块+OTSU的图像分割 1.OTSU大津法实现 ...

  4. 【智能车】图像二值化算法--大津法OTSU

    图像二值化算法–大津法OTSU 大津算法是一种图像二值化算法,作用是确定将图像分成黑白两个部分的阈值. 大津法是针对灰度值进行阈值分割二值化,如果是彩色图像的话需要先转化成灰度图再进行计算. 方差越大 ...

  5. 大津法优化之在飞卡智能车中的应用

    此优化算法是本人前两年参加智能车比赛,在总钻风摄像头例程基础上优化的(分辨率为188*70,工程文件:含优化算法的小车程序).移植方便,亲测只需2ms(逐飞给的例程是很常规的,网上都能找到,需要40m ...

  6. 【Matlab验证码识别】遗传算法和最大熵优化+大津法(OTSU)+自定义阈值数字验证码识别【含GUI源码 1694期】

    一.代码运行视频(哔哩哔哩) [Matlab验证码识别]遗传算法和最大熵优化+大津法(OTSU)+自定义阈值数字验证码识别[含GUI源码 1694期] 二.matlab版本及参考文献 1 matlab ...

  7. CUDA精进之路(五):图像处理——OTSU二值算法(最大类间方差法、大津法)

    引言 最近在做医疗设备相关的项目,故在项目中大量用到了各类图像分割的算法,为了在图像中分割出特定目标,用到的算法可以有很多,比如阈值分割,多通道分割,边缘分割以及一些前沿的组合分割.而对大多数图像来说 ...

  8. 详细及易读懂的 大津法(OTSU)原理 和 算法实现

    OTSU算法原理简述: 最大类间方差是由日本学者大津(Nobuyuki Otsu)于1979年提出,是一种自适应的阈值确定方法.算法假设图像像素能够根据阈值,被分成背景[background]和目标[ ...

  9. 基于阈值方法的大津法(OTSU算法)---图像分割

    基于阈值方法的大津法(OTSU算法)---图像分割 主要分为三部分去实现: 1.基本概念 大津法:属于阈值分割的范畴.阈值分割方法:利用图像中要提取的目标物体和背景在灰度上的差异,选择一个合适的阈值, ...

最新文章

  1. python字典导入mongodb_Python中的有序字典:添加到MongoDB
  2. Mybatis like查询的写法--转载
  3. 第九十三节,html5+css3移动手机端流体布局,基础CSS,头部设计,轮播设计,底部设计...
  4. POJ1265(Pick定理的应用)
  5. $lookup做关联表查询
  6. js动态增加行 删除行
  7. python object has no attribute_如何修复python中的“AttributeError:type object has no attribute”?...
  8. 数据结构之树【完善中】
  9. otis电梯服务器tt使用说明_南充私人电梯
  10. ArcGIS API for Silverlight 入门学习笔记(一)hello world 补充问题
  11. Pytorch(0)降低学习率torch.optim.lr_scheduler.ReduceLROnPlateau类
  12. 从JavaEye社区被迫改名说起(转载他人博客)
  13. “天生BUFF”华硕主板冷傲ROG
  14. 使用动态时间规整来同步时间序列数据
  15. 使用 Bitmap Style Designer 为FMX修改已有样式
  16. 室内红外线防盗报警器matlab,红外防盗报警系统毕业设计论文.doc
  17. 线性回归的scikit-learn实现
  18. win7系统屏蔽全/半角切换问题
  19. 科大讯飞语音识别_科大讯飞 语音识别_科大讯飞语音识别系统 - 云+社区 - 腾讯云...
  20. break continue 关键字 while和do while循环一维数组Day05

热门文章

  1. Perl-Can't locate Time/HiRes.pm 错误
  2. 微信添加表情提示过大怎么解决?GIF太大怎么变小?
  3. Hibernate 关联关系解除
  4. 当今几大主流服务器的操作系统简介
  5. 办公邮箱安全性哪家好,163vip.com邮箱全新域名全新体验!
  6. 无线路由器连接交换机
  7. 【GT跑车】GT跑车是什么意思 GT跑车有哪些
  8. Photoshop学习(二):换色
  9. 阿里云播放器SDK的正确打开方式 | Aliplayer Web播放器介绍及功能实现(三)
  10. 工作流应用 电子表单签核