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

寻线

  • 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。当然还是要根据自己摄像头采集到的图像特征来灵活运用

    物联沃分享整理
    物联沃-IOTWORD物联网 » 智能车摄像头算法——寻线

    发表评论