时隔一年,对全国大学生智能车竞赛做段总结(三)

图像的黑白二值化处理

OK,前文说到哪儿了?对,说到摄像头已经采集好原始的图像数据了。那么采集到的数据我们要做什么用呢?分析数据,提取赛道元素。是直线,就一一定速度继续前行,是弯道,就给电机两边输出不同脉冲宽度的PWM波,实现差速转向。

对我们采集到的图像进行黑白处理。灰度图像的灰度级0~255,共256个灰度级。0为最黑,255为最白。再看看赛道,它有三个sai,蓝色,黑色,以及白色。蓝色的是赛道之外的区域,黑色的是赛道边缘贴着的黑色胶带,白色则是赛道区域。对于我们采集到的188*120个像素点来说,赛道区域的灰度级绝对是最高的。而我们二值化的目的呢,让采集到的图像中,赛道区域全白,非赛道区域全黑。当然,这只是其中一种处理方式,至少我还知道另一种:边沿提取——赛道边沿全白,其他地方全黑。

对于二值化有几种简单的实现方式,最简单的一种是什么呢?是对所有像素点的灰度值做个累加,遍历二维数组中的每一个像素点,将像素点的灰度值求和再取平均数。然后,接着遍历一遍数组,灰度值大于这个平均值的就是白,小于这个平均值的就是黑。这里的这个平均值呢,我们称之为阈值。(yu第四声)

不过这种手法实在是过于粗糙了,虽然没试过,但基本上想必很难拿出手。不过无所谓了,作为一名高级的CV工程师,你应该懂得搬迁和借鉴。

下面给出一段大津法的代码:

/************************************************************************
函数名:获取阈值
返回值:阈值
功能:大津法确定阈值
************************************************************************/
uint8 otsuThreshold(uint8 *image, uint16 col, uint16 row)
{
    #define GrayScaleMAX 170//110
    #define GrayScaleMIN 100  //50
    uint16 width = col;
    uint16 height = row;
    int pixelCount[GrayScaleMAX];
    float pixelPro[GrayScaleMAX];
    int i, j, pixelSum = width * height/8;
    uint8 threshold = 0;
    uint8* data = image;  //指向像素数据的指针
    for (i =GrayScaleMIN; i < GrayScaleMAX; i++)
    {
        pixelCount[i] = 0;
        pixelPro[i] = 0;
    }
    
    uint32 gray_sum=0;
    //统计灰度级中每个像素在整幅图像中的个数  
    for (i = 0; i < height; i+=2)
    {
        for (j = 0; j < width; j+=4)
        {
          if(data[i * width + j]>=GrayScaleMAX) data[i * width + j]=GrayScaleMAX;
          else if(data[i * width + j]<=GrayScaleMIN) data[i * width + j]=GrayScaleMIN; 
          
            pixelCount[(int)data[i * width + j]]++;  //将当前的点的像素值作为计数数组的下标
            gray_sum+=(int)data[i * width + j];       //灰度值总和
        }
    }
                      
    //计算每个像素值的点在整幅图像中的比例  
  
    for (i = GrayScaleMIN; i < GrayScaleMAX; 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 = GrayScaleMIN; j < GrayScaleMAX; 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 = w0 * pow((u0 - u), 2) + w1 * pow((u1 - u), 2);
                if (deltaTmp > deltaMax)
                {
                    deltaMax = deltaTmp;
                    threshold = j;
                }
                if (deltaTmp < deltaMax)
                {
                break;
                }
          
         }

    return threshold;
}

这个算法如果记得没错的话,好像是一个日本人提出来的,不过无所*谓,该用用,有更好的并且是你使得出来的,那你就大可以换掉它。另外,这种灰度图像求阈值的算法,无论是在matlab还是opencv里都有很多现成的算法。opencv那边我没找到底层的代码,而matlab这边的两三个算法我试着改写成c语言代码。改是改了,编译一下也是0error,0warnning,但是跑出来放在显示屏上,它就是依托答辩。现在想来大抵是卷积计算那里出了点问题,因为matlab可以直接调用卷积这边的函数,而我C语言代码中的卷积运算是自己凑的。想想当时复变函数学得也是依托答辩,那卷积运算的代码多半食油点大饼,导致最后算出的那个阈值,大于图像每一个像素点的灰度值。所以,结果就是全黑图像。

图像二值化

宏定义

宏定义黑和白,这样做没有别的目的,单纯就是看起来,无论是自己阅读还是别人阅读都舒服点。不然整个代码中全是255和0这样的数字,看起来多不爽啊。当然,爽不爽还是写代码的人说的算,但在我写着的时候,我是觉得不爽的,因为这个255和0出现的频率太高了,什么时候代指颜色,什么时候代指其他的,并不确定。当然,另一方面代指白sai的不一定要255,只要是1~255之间的数字就行。

#define  black  0 //定义黑sai
#define  white  255  //宏定义白sai,单纯就是为了看起来舒服,其实不顶啥用

既然黑白分清楚了,阈值有了,那就是将图像二值化,这部分可以自己单独封装成一个函数,到时候再放进主函数中使用。

还得定义个存储二值化图像的数组

uint8 image[120][188];

示例:

void 二值化函数名(void) //毕竟不需要返回值嘛
{
    //先放大津法得到阈值
    uint16 Threshold=otsuThreshold(mt9v03x_image[0],MT9V03X_W,MT9V03X_H);
    //遍历二维数组图像
    uint16 i,j;
    for(i=0;i<120;i++)
    {
        for(j=0;j<188;j++)
        {
            if(原图像数组(i,j)这个点的灰度值大于阈值)
                定义存储二值化图像的数组这个位置的点就是白点
            else
                定义存储二值化图像的数组这个位置的点就是黑点
        }
    }
}

后续就是分析处理过的图像,提取我们需要的信息了。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沈千曦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值