智能车摄像头算法——寻线

寻线

  • 1.灰度图像二值化
  • 2.找边线
  • 3.获得中线

1.灰度图像二值化

如果使用的是小钻风摄像(二值化摄像头),就不用再进行软件二值化。
使用灰度摄像头,就需要这步。

以下展示常用的大津法

(1)首先获得分割的阈值

uint8 otsuThreshold(uint8 *GrayImage,uint32 ImageSize)
 {
     int T;                  //T为动态阈值
     uint8 threshold = 0;    //最佳阈值
     uint32 N0 = 0,N1 = ImageSize;      //图像中像素小于阈值T的像素个数记作N0,像素大于阈值T的像素个数记作N1
     float w0,w1;    //w0为前景像素点占整幅图像的比例 w0 = N0/ImageSize (ImageSize为图像大小)
                     //w1为前景像素点占整幅图像的比例 w1 = N1/ImageSize (ImageSize为图像大小)
     uint32 u0den = 0,u1den = 0;  //图像中像素小于阈值T的像素加权和记作u0den
                                  //图像中像素大于阈值T的像素加权和记作u1den
     float u0,u1;    //u0为前景像素点平均灰度 u0 = u0den/N0
                     //u1为前景像素点平均灰度 u1 = u1den/N1
     float deltaTmp;         //类间方差 计算公式 w0*w1*(u0-u1)^2
                             //类间方差最大的阈值T为所求最佳阈值

     uint32 itemp,*pPixelCount = pixelCount;
     float deltaMax = 0;

     //获取灰度图像直方图
     _Getting_GrayHistogram(pPixelCount,GrayImage,ImageSize);

     //计算动态阈值
     for (T = 0;T < 255;T ++)
     {
         itemp = *(pPixelCount + T);
         u1den += T * itemp;
     }
     for (T = 0; T < 255; T++)
     {
         itemp = *(pPixelCount + T);
         N0 += itemp;
         N1 -= itemp;
         if (N0 == 0 || N1 == 0)
             continue ;
         itemp = itemp * T;
         u0den += itemp;
         u1den -= itemp;

         w0 = (float)N0 / (float)ImageSize;
         w1 = (float)N1 / (float)ImageSize;
         u0 = (float)u0den / (float)N0;
         u1 = (float)u1den / (float)N1;

         deltaTmp = w0*w1*(u0-u1)*(u0-u1);

         if (deltaTmp > deltaMax)
         {
             deltaMax = deltaTmp;
             threshold = T;
         }
     }
     if(deltaMax < 80)
     {
         if(threshold >= 50)
             threshold = 10;
         else if(threshold < 50)
             threshold = 100;
     }
     return threshold;
 }

(2)再进行二值化(white=255 black =0)

 //***********************************************//
 //函数名称: image_Gray2Binary                    //
 //函数版本: V1.0                                 //
 //函数说明: 灰度图片转化为二值化图片             //
 //函数输入: BinImage  : 二值化图像指针           //
 //          GrayImage : 灰度图像指针             //
 //          ImageSize : 输入图像尺寸(长度*宽度)  //
 //          threshold : 图像转化阈值             //
 //函数输出: BinImage  : 二值化图像指针           //
 //***********************************************//
 void image_Gray2Binary(uint8 *Binimg,uint8 *GrayImage,uint32 ImageSize,uint8 threshold)
 {
     uint32 z;
     uint32 temp,temp_1 = white,temp_2 = black;
     for (z = 0;z < ImageSize;z++)
     {
         temp=*(GrayImage + z);
         if (temp >= threshold)
             *(Binimg + z) = temp_1;
         else
             *(Binimg + z) = temp_2;
     }
 }

使用:

//mt9v03x_image是灰度图像数组     
pixel=otsuThreshold(&mt9v03x_image[0][0],MT9V03X_W*MT9V03X_H);
//img是二值化后得到的图像
image_Gray2Binary(&img[0][0],&mt9v03x_image[0][0],MT9V03X_W*MT9V03X_H,pixel);//img二值化图像

当然还有很多算法,例如基于sobel算子,动态阈值法等等。有需要可以评论邮箱,我发你。但是我觉得只要能把图像大体的边界信息保留下来,就可以了。并且有部分算法运算量很大,智能车比赛有些指定的单片机运用这些算法会把图像卡成PPT。

2.找边线

找边线可以就用最简单的逻辑,从中间向两边搜黑点。从距离车头较近的行开始从中间向两边搜线。

以下代码思路稍微有点变化:
左边线默认:1 右边线默认:(图像宽度-1)
从车头最近的一行开始,把这行图像单独拿出来,作为一个参考。之后的行,搜左边线时,从前一个搜的左边线的横坐标加一个值向左搜;搜右边线时,从前一个搜的右边线的横坐标减一个值向右搜。这个过程完成,就得到了一幅图像的边界数组。

/*-----------------------------------------------------------------!
  * @brief    获取边线
  *
  * @param    imageInput : 二值图像信息
  * @param    imageOut   : 边线数组
  *
  * @return   是否丢线
  *
  * @note     思路:从距离车头较近的行开始从中间向两边搜线
 ---------------------------------------------------------------------- */
uint8_t ImageGetSide(uint8_t imageInput[OV7725_UART_H][OV7725_UART_W], uint8_t imageOut[OV7725_UART_H][2])
{
    uint8_t i = 0, j = 0,k=0;
	
        for(i = OV7725_UART_H-1; i > 0; i--)
        {
            imageOut[i][left] = 1;
            imageOut[i][right] = OV7725_UART_W-1;

        }

        // 用距离小车比较近的行 判断是否丢线
        for(i = OV7725_UART_W/2; i > 1; i--)
        {
            if(!imageInput[OV7725_UART_H-1][i])
            {
                imageOut[OV7725_UART_H-1][left] = i;
                break;
            }
        }

        for(i = OV7725_UART_W/2+1; i < OV7725_UART_W-1; i++)
        {
            if(!imageInput[OV7725_UART_H-1][i])
            {
                imageOut[OV7725_UART_H-1][right] = i;
                break;
            }
        }
//-------------------------------------------------------------


    // 遍历每行
    for(i = OV7725_UART_H-2; i > 0; i--)
    {
        // 向左搜线
        for(j = imageOut[i+1][left] + 10; j > 0; j--)
        {
            if(!imageInput[i][j])
            {
                imageOut[i][left] = j;
                break;
            }
        }

        if(imageOut[i][left]  >  OV7725_UART_W/2)
         {
              imageOut[i][left]=OV7725_UART_W/2;

                for(k=i;k>0;k--)
                {
                  imageOut[k][left]=OV7725_UART_W/2;
                }

                break;
         }

    }

   //遍历每一行
   for(i = OV7725_UART_H-2; i > 0; i--)
   {
        //向右搜线
        for(j = imageOut[i+1][right] - 10; j < OV7725_UART_W-1; j++)
        {
            if(!imageInput[i][j])
            {
                imageOut[i][right] = j;
                break;
            }
        }


        if(imageOut[i][right]  <  OV7725_UART_W/2)
        {
              imageOut[i][right]=OV7725_UART_W/2;

              for(k=i;k>0;k--)
              {
                  imageOut[k][right]=OV7725_UART_W/2;
              }

              break;
        }

   }

3.获得中线

一般情况:
中线 =(左边线+右边线)/2
特殊情况:
中线 = 左边线+N 或者
中线 = 右边线-N
这个特殊处理根据实际情况自己处理就行了。比如说,在左转弯的时候弧度太大,导致左边线全部丢线,这个时候就可以令中线 = 右边线 – N。当然还是要根据自己摄像头采集到的图像特征来灵活运用

有问题可以私聊我咯~
另外承接各种单片机设计
可以加企鹅群 658476482 交流

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
乘风的头像乘风管理团队
上一篇 2023年12月20日
下一篇 2023年12月20日

相关推荐