目次
一、媒介
二、直角拐点求取
1、根据生长方向找拐点
1. 左侧上转左拐点
2. 左侧右转上拐点
3. 右侧上转右拐点
4. 右侧左转上拐点
2、根据X坐标跳变寻找拐点
3、组合判断拐点
4、逆透视求角度找拐点
三、边界点求取
四、对位边界行
五、圆弧拐点求取
六、求每侧边线斜率及截距
七、拟合曲线计算方差
八、求取左右侧边线差值
九、TOF测距
十、总函数调用
十一、总结
一、媒介
本文主要讲授二维数组边线的信息提取,此中主要包罗:直角拐点、圆弧拐点、边界点个数等。
二、直角拐点求取
1、根据生长方向找拐点
岂论是常见的八邻域求边线,还是本人优化过的八向迷宫求取边线,都可以得到每个边线点的生长方向,由此我们可以判断直角拐点。
因摄像头为斜视,因此获取图像中的直角拐点并非严酷的直角拐点,而是锐角,由此我们根据生长方向判断拐点时,
此处基于本人的八向迷宫讲授拐点求取。八向迷宫的生长方向差别于八邻域的0~7,而是特别的数字用来代表(可以参考之前的文章:智能车摄像头开源—1.2 核心算法:自适应八向迷宫(下)):
直角拐点主要需求四类:上转左,上转右,左转上,右转上。这里的上、下、左、右为生长的大抵方向,比方上转左拐点,即边线大要上向上生长,经过上转左拐点后,转为大要上向左生长。此中,上转左和右转上只会在左侧边线出现,而上转右,左转上只会出现在右侧边线(二维边线)。当碰到十字路口时(正入十字,斜入另说),会碰到上述四个拐点,循环找四个拐点给标记位,即可初步判出十字元素。不可太过绝对。但同时不可太过简单,否则导致误判,在一定程度上,误判比漏判结果更为严峻。
读者可求取一张十字的图像,因摄像头斜视原因,会发现向上生长的边线为斜线,向左或向右生长的边线较趋近于平滑的直线
下基于八向迷宫的八个方向分别讲授四个拐点,每个拐点有三个判断严酷程度,每个拐点需根据前后几个点的生长方向加以判断。
1. 左侧上转左拐点
宽松判断:前方几个点的生长方向为 -2 或 1 或 4 ,后方几个点生长方向为 -2 或 -3 或 -4;
中等判断:前方几个点的生长方向为 1 或 4 ,后方几个点生长方向为 -3 或 -4;
严酷判断:前方几个点的生长方向为 1 或 4 ,后方几个点生长方向为 -3 ;
此中宽松判断会和圆弧拐点误判,中等判断常用,严酷判断要求过高,根据自身需求调用。
- /**
- * 函数功能: 寻找上转左拐点
- * 特殊说明: 分为三个严格等级,根据每个点的生长方向判断
- * 寻找方式为遍历二维数组边线的每个点
- * 形 参: uint8 Grade 选择判定严格等级,常用2,即中等等级
- *
- * 示例: Get_L_Up_Turn_Left_Point_1(2);
- * 返回值: 无
- */
- uint8 L_Up_Turn_Left_Point_1[2] = {0}; //存储上转左拐点的坐标
- uint8 L_Up_Turn_Left_Point_Flag_1 = 0; //上转左拐点存在标志位,找到时置1
- uint8 L_Up_Turn_Left_Point_Position_1 = 0; //记录位置,即是二维数组中的第几个点
- float L_Up_Turn_Left_Point_Angle_1 = 0; //记录找到的拐点的角度(逆透视求取)
- void Get_L_Up_Turn_Left_Point_1(uint8 Grade)
- {
- uint8 i = 0;
- L_Up_Turn_Left_Point_Flag_1 = 0;
- L_Up_Turn_Left_Point_1[0] = 0;
- L_Up_Turn_Left_Point_1[1] = 0;
- L_Up_Turn_Left_Point_Position_1 = 0;
- L_Up_Turn_Left_Point_Angle_1 = 0; //将各个参数清零
- switch(Grade)
- {
- case 1: //严格判断
- {
- for (i = 4; i < (L_Statics - 4); i++) //L_Statics 为左侧边线点个数
- {
- if((L_Line[i][1] >= (Y_Border_Min + 2)) && (L_Line[i][1] <= (Y_Border_Max - 2))) //L_Line为存储左侧边线的二维数组,拐点必须满足不在于左右两侧边界上
- {
- if ((L_Grow_Dir[i - 2] == 4 || L_Grow_Dir[i - 2] == 1) && (L_Grow_Dir[i - 4] == 4 || L_Grow_Dir[i - 4] == 1) &&
- (L_Grow_Dir[i + 2] == -3) && (L_Grow_Dir[i + 4] == -3) && (L_Grow_Dir[i] == -2 || L_Grow_Dir[i] == -3)) //L_Grow_Dir存储每个边线点的生长方向
- {
- L_Up_Turn_Left_Point_1[0] = L_Line[i][0];
- L_Up_Turn_Left_Point_1[1] = L_Line[i][1];
- L_Up_Turn_Left_Point_Flag_1 = 1;
- break;
- }
- }
- }
- break;
- }
- case 2: //中等判断
- {
- for (i = 4; i < (L_Statics - 4); i++)
- {
- if((L_Line[i][1] >= (Y_Border_Min + 2)) && (L_Line[i][1] <= (Y_Border_Max - 2)))
- {
- if ((L_Grow_Dir[i - 2] == 4 || L_Grow_Dir[i - 2] == 1) && (L_Grow_Dir[i - 4] == 4 || L_Grow_Dir[i - 4] == 1) &&
- (L_Grow_Dir[i + 2] == -3 || L_Grow_Dir[i + 2] == -4) && (L_Grow_Dir[i + 4] == -3 || L_Grow_Dir[i + 4] == -4) &&
- (L_Grow_Dir[i] == -2 || L_Grow_Dir[i] == -3 || L_Grow_Dir[i] == -4))
- {
- L_Up_Turn_Left_Point_1[0] = L_Line[i][0];
- L_Up_Turn_Left_Point_1[1] = L_Line[i][1];
- L_Up_Turn_Left_Point_Flag_1 = 1;
- break;
- }
- }
- }
- break;
- }
- case 3: //宽松判断
- {
- for (i = 4; i < (L_Statics - 4); i++)
- {
- if((L_Line[i][1] >= (Y_Border_Min + 2)) && (L_Line[i][1] <= (Y_Border_Max - 2)))
- {
- if ((L_Grow_Dir[i - 2] == 4 || L_Grow_Dir[i - 2] == 1 || L_Grow_Dir[i - 2] == -2) && (L_Grow_Dir[i - 4] == 4 || L_Grow_Dir[i - 4] == 1 || L_Grow_Dir[i - 2] == -2) &&
- (L_Grow_Dir[i + 2] == -3 || L_Grow_Dir[i + 2] == -4 || L_Grow_Dir[i + 2] == -2) && (L_Grow_Dir[i + 4] == -3 || L_Grow_Dir[i + 4] == -4 || L_Grow_Dir[i + 4] == -2) &&
- (L_Grow_Dir[i] == -2 || L_Grow_Dir[i] == -3 || L_Grow_Dir[i] == -4))
- {
- L_Up_Turn_Left_Point_1[0] = L_Line[i][0];
- L_Up_Turn_Left_Point_1[1] = L_Line[i][1];
- L_Up_Turn_Left_Point_Flag_1 = 1;
- break;
- }
- }
- }
- break;
- }
- }
- if(L_Up_Turn_Left_Point_Flag_1 == 1)
- {
- L_Up_Turn_Left_Point_Position_1 = i; //记录拐点的位置
- L_Up_Turn_Left_Point_Angle_1 = Get_Turn_Point_Angle(L_Line[i - 5][0], L_Line[i - 5][1], L_Line[i][0], L_Line[i][1], L_Line[i + 5][0], L_Line[i + 5][1]); //逆透视求取拐点角度,后续补充
- }
- }
复制代码 2. 左侧右转上拐点
宽松判断:前方几个点的生长方向为 2 或 3 或 4 ,后方几个点生长方向为 4 或 1 或 -2;
中等判断:前方几个点的生长方向为 2或 3 ,后方几个点生长方向为 4 或 1;
严酷判断:前方几个点的生长方向为 3 ,后方几个点生长方向为 4 或 1 ;
与左侧上转左拐点同理,下面几个都不做过多讲述。
- /**
- * 函数功能: 寻找右转上拐点
- * 特殊说明: 分为三个严格等级,根据每个点的生长方向判断
- * 寻找方式为遍历二维数组边线的每个点
- * 形 参: uint8 Grade 选择判定严格等级,常用2,即中等等级
- *
- * 示例: Get_L_Right_Turn_Up_Point_1(2);
- * 返回值: 无
- */
- uint8 L_Right_Turn_Up_Point_1[2] = {0};
- uint8 L_Right_Turn_Up_Point_Flag_1 = 0;
- uint8 L_Right_Turn_Up_Point_Position_1 = 0;
- float L_Right_Turn_Up_Point_Angle_1 = 0;
- void Get_L_Right_Turn_Up_Point_1(uint8 Grade)
- {
- uint8 i = 0;
- L_Right_Turn_Up_Point_Flag_1 = 0;
- L_Right_Turn_Up_Point_1[0] = 0;
- L_Right_Turn_Up_Point_1[1] = 0;
- L_Right_Turn_Up_Point_Position_1 = 0;
- L_Right_Turn_Up_Point_Angle_1 = 0;
- switch(Grade)
- {
- case 1:
- {
- for (i = 5; i < (L_Statics - 4); i++)
- {
- if((L_Line[i][1] >= (Y_Border_Min + 2)) && (L_Line[i][1] <= (Y_Border_Max - 2)))
- {
- if ((L_Grow_Dir[i + 2] == 4 || L_Grow_Dir[i + 2] == 1) && (L_Grow_Dir[i + 5] == 4 || L_Grow_Dir[i + 5] == 1) && (L_Grow_Dir[i + 7] == 4 || L_Grow_Dir[i + 7] == 1) &&
- (L_Grow_Dir[i - 2] == 3) && (L_Grow_Dir[i - 5] == 3) && (L_Grow_Dir[i] == 4 || L_Grow_Dir[i] == 1))
- {
- L_Right_Turn_Up_Point_1[0] = L_Line[i][0];
- L_Right_Turn_Up_Point_1[1] = L_Line[i][1];
- L_Right_Turn_Up_Point_Flag_1 = 1;
- break;
- }
- }
- }
- break;
- }
- case 2:
- {
- for (i = 5; i < (L_Statics - 4); i++)
- {
- if((L_Line[i][1] >= (Y_Border_Min + 2)) && (L_Line[i][1] <= (Y_Border_Max - 2)))
- {
- if ((L_Grow_Dir[i + 2] == 4 || L_Grow_Dir[i + 2] == 1) && (L_Grow_Dir[i + 5] == 4 || L_Grow_Dir[i + 5] == 1) && (L_Grow_Dir[i + 7] == 4 || L_Grow_Dir[i + 7] == 1) &&
- (L_Grow_Dir[i - 2] == 2 || L_Grow_Dir[i - 2] == 3) && (L_Grow_Dir[i - 5] == 2 || L_Grow_Dir[i - 5] == 3) &&
- (L_Grow_Dir[i] == 4 || L_Grow_Dir[i] == 1))
- {
- L_Right_Turn_Up_Point_1[0] = L_Line[i][0];
- L_Right_Turn_Up_Point_1[1] = L_Line[i][1];
- L_Right_Turn_Up_Point_Flag_1 = 1;
- break;
- }
- }
- }
- break;
- }
- case 3:
- {
- for (i = 5; i < (L_Statics - 4); i++)
- {
- if((L_Line[i][1] >= (Y_Border_Min + 2)) && (L_Line[i][1] <= (Y_Border_Max - 2)))
- {
- if ((L_Grow_Dir[i + 2] == 4 || L_Grow_Dir[i + 2] == 1 || L_Grow_Dir[i + 2] == -2) && (L_Grow_Dir[i + 5] == 4 || L_Grow_Dir[i + 5] == 1 || L_Grow_Dir[i + 5] == -2) &&
- (L_Grow_Dir[i + 7] == 4 || L_Grow_Dir[i + 7] == 1 || L_Grow_Dir[i + 7] == -2) &&
- (L_Grow_Dir[i - 2] == 2 || L_Grow_Dir[i - 2] == 3 || L_Grow_Dir[i - 2] == 4) && (L_Grow_Dir[i - 5] == 2 || L_Grow_Dir[i - 5] == 3 || L_Grow_Dir[i - 5] == 4) &&
- (L_Grow_Dir[i] == 4 || L_Grow_Dir[i] == 1))
- {
- L_Right_Turn_Up_Point_1[0] = L_Line[i][0];
- L_Right_Turn_Up_Point_1[1] = L_Line[i][1];
- L_Right_Turn_Up_Point_Flag_1 = 1;
- break;
- }
- }
- }
- break;
- }
- }
- if(L_Right_Turn_Up_Point_Flag_1 == 1)
- {
- L_Right_Turn_Up_Point_Position_1 = i;
- L_Right_Turn_Up_Point_Angle_1 = Get_Turn_Point_Angle(L_Line[i - 5][0], L_Line[i - 5][1], L_Line[i][0], L_Line[i][1], L_Line[i + 5][0], L_Line[i + 5][1]);
- }
- }
复制代码 3. 右侧上转右拐点
宽松判断:前方几个点的生长方向为 -2 或 1或 4 ,后方几个点生长方向为 2或 3或 4;
中等判断:前方几个点的生长方向为 -2或 1,后方几个点生长方向为 2或 3;
严酷判断:前方几个点的生长方向为 -2或 1,后方几个点生长方向为 3 ;
- /**
- * 函数功能: 寻找上转右拐点
- * 特殊说明: 分为三个严格等级,根据每个点的生长方向判断
- * 寻找方式为遍历二维数组边线的每个点
- * 形 参: uint8 Grade 选择判定严格等级,常用2,即中等等级
- *
- * 示例: Get_R_Up_Turn_Right_Point_1(2);
- * 返回值: 无
- */
- uint8 R_Up_Turn_Right_Point_1[2] = {0};
- uint8 R_Up_Turn_Right_Point_Flag_1 = 0;
- uint8 R_Up_Turn_Right_Point_Position_1 = 0;
- float R_Up_Turn_Right_Point_Angle_1 = 0;
- void Get_R_Up_Turn_Right_Point_1(uint8 Grade)
- {
- uint8 i = 0;
- R_Up_Turn_Right_Point_Flag_1 = 0;
- R_Up_Turn_Right_Point_1[0] = 0;
- R_Up_Turn_Right_Point_1[1] = 0;
- R_Up_Turn_Right_Point_Position_1 = 0;
- R_Up_Turn_Right_Point_Angle_1 = 0;
- switch(Grade)
- {
- case 1:
- {
- for (i = 4; i < (R_Statics - 4); i++)
- {
- if((R_Line[i][1] >= (Y_Border_Min + 2)) && (R_Line[i][1] <= (Y_Border_Max - 2)))
- {
- if ((R_Grow_Dir[i - 2] == -2 || R_Grow_Dir[i - 2] == 1) && (R_Grow_Dir[i - 4] == -2 || R_Grow_Dir[i - 4] == 1) &&
- (R_Grow_Dir[i + 2] == 3) && (R_Grow_Dir[i + 4] == 3) && (R_Grow_Dir[i] == 2 || R_Grow_Dir[i] == 3 || R_Grow_Dir[i] == 4))
- {
- R_Up_Turn_Right_Point_1[0] = R_Line[i][0];
- R_Up_Turn_Right_Point_1[1] = R_Line[i][1];
- R_Up_Turn_Right_Point_Flag_1 = 1;
- break;
- }
- }
- }
- break;
- }
- case 2:
- {
- for (i = 4; i < (R_Statics - 4); i++)
- {
- if((R_Line[i][1] >= (Y_Border_Min + 2)) && (R_Line[i][1] <= (Y_Border_Max - 2)))
- {
- if ((R_Grow_Dir[i - 2] == -2 || R_Grow_Dir[i - 2] == 1) && (R_Grow_Dir[i - 4] == -2 || R_Grow_Dir[i - 4] == 1) &&
- (R_Grow_Dir[i + 2] == 2 || R_Grow_Dir[i + 2] == 3) && (R_Grow_Dir[i + 4] == 2 || R_Grow_Dir[i + 4] == 3) &&
- (R_Grow_Dir[i] == 2 || R_Grow_Dir[i] == 3 || R_Grow_Dir[i] == 4))
- {
- R_Up_Turn_Right_Point_1[0] = R_Line[i][0];
- R_Up_Turn_Right_Point_1[1] = R_Line[i][1];
- R_Up_Turn_Right_Point_Flag_1 = 1;
- break;
- }
- }
- }
- break;
- }
- case 3:
- {
- for (i = 4; i < (R_Statics - 4); i++)
- {
- if((R_Line[i][1] >= (Y_Border_Min + 2)) && (R_Line[i][1] <= (Y_Border_Max - 2)))
- {
- if ((R_Grow_Dir[i - 2] == -2 || R_Grow_Dir[i - 2] == 1 || R_Grow_Dir[i - 2] == 4) && (R_Grow_Dir[i - 4] == -2 || R_Grow_Dir[i - 4] == 1 || R_Grow_Dir[i - 4] == 4) &&
- (R_Grow_Dir[i + 2] == 2 || R_Grow_Dir[i + 2] == 3 || R_Grow_Dir[i + 2] == 4) && (R_Grow_Dir[i + 4] == 2 || R_Grow_Dir[i + 4] == 3 || R_Grow_Dir[i + 4] == 4) &&
- (R_Grow_Dir[i] == 2 || R_Grow_Dir[i] == 3 || R_Grow_Dir[i] == 4))
- {
- R_Up_Turn_Right_Point_1[0] = R_Line[i][0];
- R_Up_Turn_Right_Point_1[1] = R_Line[i][1];
- R_Up_Turn_Right_Point_Flag_1 = 1;
- break;
- }
- }
- }
- break;
- }
- }
- if(R_Up_Turn_Right_Point_Flag_1 == 1)
- {
- R_Up_Turn_Right_Point_Position_1 = i;
- R_Up_Turn_Right_Point_Angle_1 = Get_Turn_Point_Angle(R_Line[i - 5][0], R_Line[i - 5][1], R_Line[i][0], R_Line[i][1], R_Line[i + 5][0], R_Line[i + 5][1]);
- }
- }
复制代码 4. 右侧左转上拐点
宽松判断:前方几个点的生长方向为 -2 或 1或 4 ,后方几个点生长方向为 2或 3或 4;
中等判断:前方几个点的生长方向为 -2或 1,后方几个点生长方向为 2或 3;
严酷判断:前方几个点的生长方向为 -2或 1,后方几个点生长方向为 3 ;
- /**
- * 函数功能: 寻找左转上拐点
- * 特殊说明: 分为三个严格等级,根据每个点的生长方向判断
- * 寻找方式为遍历二维数组边线的每个点
- * 形 参: uint8 Grade 选择判定严格等级,常用2,即中等等级
- *
- * 示例: Get_R_Left_Turn_Up_Point_1(2);
- * 返回值: 无
- */
- uint8 R_Left_Turn_Up_Point_1[2] = {0};
- uint8 R_Left_Turn_Up_Point_Flag_1 = 0;
- uint8 R_Left_Turn_Up_Point_Position_1 = 0;
- float R_Left_Turn_Up_Point_Angle_1 = 0;
- void Get_R_Left_Turn_Up_Point_1(uint8 Grade)
- {
- uint8 i =0;
- R_Left_Turn_Up_Point_Flag_1 = 0;
- R_Left_Turn_Up_Point_1[0] = 0;
- R_Left_Turn_Up_Point_1[1] = 0;
- R_Left_Turn_Up_Point_Position_1 = 0;
- R_Left_Turn_Up_Point_Angle_1 = 0;
- switch(Grade)
- {
- case 1:
- {
- for (i = 5; i < (R_Statics - 4); i++)
- {
- if((R_Line[i][1] >= (Y_Border_Min + 2)) && (R_Line[i][1] <= (Y_Border_Max - 2)))
- {
- if ((R_Grow_Dir[i + 2] == -2 || R_Grow_Dir[i + 2] == 1) && (R_Grow_Dir[i + 5] == -2 || R_Grow_Dir[i + 5] == 1) && (R_Grow_Dir[i + 7] == -2 || R_Grow_Dir[i + 7] == 1) &&
- (R_Grow_Dir[i - 2] == -3) && (R_Grow_Dir[i - 5] == -3) && (R_Grow_Dir[i] == -2 || R_Grow_Dir[i] == 1))
- {
- R_Left_Turn_Up_Point_1[0] = R_Line[i][0];
- R_Left_Turn_Up_Point_1[1] = R_Line[i][1];
- R_Left_Turn_Up_Point_Flag_1 = 1;
- break;
- }
- }
- }
- break;
- }
- case 2:
- {
- for (i = 5; i < (R_Statics - 4); i++)
- {
- if((R_Line[i][1] >= (Y_Border_Min + 2)) && (R_Line[i][1] <= (Y_Border_Max - 2)))
- {
- if ((R_Grow_Dir[i + 2] == -2 || R_Grow_Dir[i + 2] == 1) && (R_Grow_Dir[i + 5] == -2 || R_Grow_Dir[i + 5] == 1) && (R_Grow_Dir[i + 7] == -2 || R_Grow_Dir[i + 7] == 1) &&
- (R_Grow_Dir[i - 2] == -3 || R_Grow_Dir[i - 2] == -4) && (R_Grow_Dir[i - 5] == -3 || R_Grow_Dir[i - 5] == -4) && (R_Grow_Dir[i] == -2 || R_Grow_Dir[i] == 1))
- {
- R_Left_Turn_Up_Point_1[0] = R_Line[i][0];
- R_Left_Turn_Up_Point_1[1] = R_Line[i][1];
- R_Left_Turn_Up_Point_Flag_1 = 1;;
- break;
- }
- }
- }
- break;
- }
- case 3:
- {
- for (i = 5; i < (R_Statics - 4); i++)
- {
- if((R_Line[i][1] >= (Y_Border_Min + 2)) && (R_Line[i][1] <= (Y_Border_Max - 2)))
- {
- if ((R_Grow_Dir[i + 2] == -2 || R_Grow_Dir[i + 2] == 1 || R_Grow_Dir[i + 2] == 4) && (R_Grow_Dir[i + 5] == -2 || R_Grow_Dir[i + 5] == 1 || R_Grow_Dir[i + 5] == 4) &&
- (R_Grow_Dir[i + 7] == -2 || R_Grow_Dir[i + 7] == 1 || R_Grow_Dir[i + 7] == 4) &&
- (R_Grow_Dir[i - 2] == -3 || R_Grow_Dir[i - 2] == -4) && (R_Grow_Dir[i - 5] == -3 || R_Grow_Dir[i - 5] == -4) && (R_Grow_Dir[i] == -2 || R_Grow_Dir[i] == 1))
- {
- R_Left_Turn_Up_Point_1[0] = R_Line[i][0];
- R_Left_Turn_Up_Point_1[1] = R_Line[i][1];
- R_Left_Turn_Up_Point_Flag_1 = 1;;
- break;
- }
- }
- }
- break;
- }
- }
- if(R_Left_Turn_Up_Point_Flag_1 == 1)
- {
- R_Left_Turn_Up_Point_Position_1 = i;
- R_Left_Turn_Up_Point_Angle_1 = Get_Turn_Point_Angle(R_Line[i - 5][0], R_Line[i - 5][1], R_Line[i][0], R_Line[i][1], R_Line[i + 5][0], R_Line[i + 5][1]);
- }
- }
复制代码
2、根据X坐标跳变寻找拐点
X坐标跳变寻拐点,使用的是一维边线数组。对于一维边线数组来说,每行左右边线只会存储一个点,即每行只会有两个边线点,差别于二维数组每行图像可以记载多个点。当碰到十字路口时对比尤为明显。
比方在十字路口时,正常的二维数组应是如下环境:
但一维数组存储的边线,应是如下环境:(画成蓝线更明显)
可以对比上下两张图像,一维边线失去了横向的多个点,即每行每个边线只存储一个点,由此在四个拐点处,会出现当前拐点的X值与下一个点的(或前一个点的)X值出现较大变革,通过遍历每侧的一维边线每个点,可以找到四个拐点。同时需判断其与其前方(或后方)的几个点连续。下面上代码:
此中 L_Border 和 R_Border 是存储一维边线的数组。
3、组合判断拐点
可先用生长方向找拐点,再用X坐标跳变强化判断拐点,代码如下:
- //组合寻拐点
- uint8 L_Up_Turn_Left_Point[2] = {0};
- uint8 L_Up_Turn_Left_Point_Flag = 0;
- void Get_L_Up_Turn_Left_Point(void)
- {
- L_Up_Turn_Left_Point_Flag = 0;
- Get_L_Up_Turn_Left_Point_1(2);
- if(L_Up_Turn_Left_Point_Flag_1 == 1)
- {
- L_Up_Turn_Left_Point_Flag_1 = 0;
- Get_L_Up_Turn_Left_Point_2(L_Up_Turn_Left_Point_1[1] - 7, L_Up_Turn_Left_Point_1[1] + 7);
- {
- if(L_Up_Turn_Left_Point_Flag_2 == 1)
- {
- L_Up_Turn_Left_Point_Flag_2 = 0;
- L_Up_Turn_Left_Point_Flag = 1;
- L_Up_Turn_Left_Point[0] = L_Up_Turn_Left_Point_1[0];
- L_Up_Turn_Left_Point[1] = L_Up_Turn_Left_Point_1[1];
- }
- }
- }
- }
- uint8 L_Right_Turn_Up_Point[2] = {0};
- uint8 L_Right_Turn_Up_Point_Flag = 0;
- void Get_L_Right_Turn_Up_Point(void)
- {
- L_Right_Turn_Up_Point_Flag = 0;
- Get_L_Right_Turn_Up_Point_1(2);
- if(L_Right_Turn_Up_Point_Flag_1 == 1)
- {
- L_Right_Turn_Up_Point_Flag_1 = 0;
- Get_L_Right_Turn_Up_Point_2(L_Right_Turn_Up_Point_1[1] - 7, L_Right_Turn_Up_Point_1[1] + 7);
- {
- if(L_Right_Turn_Up_Point_Flag_2 == 1)
- {
- L_Right_Turn_Up_Point_Flag_2 = 0;
- L_Right_Turn_Up_Point_Flag = 1;
- L_Right_Turn_Up_Point[0] = L_Right_Turn_Up_Point_1[0];
- L_Right_Turn_Up_Point[1] = L_Right_Turn_Up_Point_1[1];
- }
- }
- }
- }
- uint8 R_Up_Turn_Right_Point[2] = {0};
- uint8 R_Up_Turn_Right_Point_Flag = 0;
- void Get_R_Up_Turn_Right_Point(void)
- {
- R_Up_Turn_Right_Point_Flag = 0;
- Get_R_Up_Turn_Right_Point_1(2);
- if(R_Up_Turn_Right_Point_Flag_1 == 1)
- {
- R_Up_Turn_Right_Point_Flag_1 = 0;
- Get_R_Up_Turn_Right_Point_2(R_Up_Turn_Right_Point_1[1] - 7, R_Up_Turn_Right_Point_1[1] + 7);
- {
- if(R_Up_Turn_Right_Point_Flag_2 == 1)
- {
- R_Up_Turn_Right_Point_Flag_2 = 0;
- R_Up_Turn_Right_Point_Flag = 1;
- R_Up_Turn_Right_Point[0] = R_Up_Turn_Right_Point_1[0];
- R_Up_Turn_Right_Point[1] = R_Up_Turn_Right_Point_1[1];
- }
- }
- }
- }
- uint8 R_Left_Turn_Up_Point[2] = {0};
- uint8 R_Left_Turn_Up_Point_Flag = 0;
- void Get_R_Left_Turn_Up_Point(void)
- {
- R_Left_Turn_Up_Point_Flag = 0;
- Get_R_Left_Turn_Up_Point_1(2);
- if(R_Left_Turn_Up_Point_Flag_1 == 1)
- {
- R_Left_Turn_Up_Point_Flag_1 = 0;
- Get_R_Left_Turn_Up_Point_2(R_Left_Turn_Up_Point_1[1] - 7, R_Left_Turn_Up_Point_1[1] + 7);
- {
- if(R_Left_Turn_Up_Point_Flag_2 == 1)
- {
- R_Left_Turn_Up_Point_Flag_2 = 0;
- R_Left_Turn_Up_Point_Flag = 1;
- R_Left_Turn_Up_Point[0] = R_Left_Turn_Up_Point_1[0];
- R_Left_Turn_Up_Point[1] = R_Left_Turn_Up_Point_1[1];
- }
- }
- }
- }
复制代码 4、逆透视求角度找拐点
使用此方法需先求得逆透视变动数组,再使用逆透视求取角度。逆透视实现方法可以参考之前的文章:智能车摄像头开源—5 逆透视处置惩罚
- //-------------------------------------------------------------------------------------------------------------------
- // @brief 逆透视知三点求形成的角度
- // @param Ax,Ay 下边点
- // @param Bx,By 要求角度的一点
- // @param Cx,Cy 上边点
- // @return
- // @since v1.0
- // Sample usage:
- // 注:使用时传入原图像的三个点,不可传入逆透视图像的三个点
- //-------------------------------------------------------------------------------------------------------------------
- float Get_Turn_Point_Angle(uint8 Ax, uint8 Ay, uint8 Bx, uint8 By, uint8 Cx, uint8 Cy)
- {
- float BA = 0.00;//向量BA的模
- float BC = 0.00;
- float SBA_BC = 0.00;//向量点乘的值
- float Angle = 0.00;
- uint8 AX = Back_Inverse_Matrix_Row[Ay * Image_X + Ax];
- uint8 AY = Back_Inverse_Matrix_Col[Ay * Image_X + Ax];
- uint8 BX = Back_Inverse_Matrix_Row[By * Image_X + Bx];
- uint8 BY = Back_Inverse_Matrix_Col[By * Image_X + Bx];
- uint8 CX = Back_Inverse_Matrix_Row[Cy * Image_X + Cx];
- uint8 CY = Back_Inverse_Matrix_Col[Cy * Image_X + Cx];
- BA = sqrt((float)((AX-BX)*(AX-BX)+(AY-BY)*(AY-BY)));
- BC = sqrt((float)((CX-BX)*(CX-BX)+(CY-BY)*(CY-BY)));
- SBA_BC = (float)((AX-BX)*(CX-BX)+(AY-BY)*(CY-BY));
- Angle = acos(SBA_BC * 1.00/ (BA * BC));
- return Angle * 57.3f;
- }
- //逆透视后的图像拐点求角度,传入已经逆透视后的三个点
- float I_Get_Turn_Point_Angle(uint8 Ax, uint8 Ay, uint8 Bx, uint8 By, uint8 Cx, uint8 Cy)
- {
- float BA = 0.00;//向量BA的模
- float BC = 0.00;
- float SBA_BC = 0.00;//向量点乘的值
- float Angle = 0.00;
- BA = sqrt((float)((Ax-Bx)*(Ax-Bx)+(Ay-By)*(Ay-By)));
- BC = sqrt((float)((Cx-Bx)*(Cx-Bx)+(Cy-By)*(Cy-By)));
- SBA_BC = (float)((Ax-Bx)*(Cx-Bx)+(Ay-By)*(Cy-By));
- Angle = acos(SBA_BC * 1.00/ (BA * BC));
- return Angle * 57.3f;
- }
复制代码 寻找时对所有的二维边线点举行遍历寻找。至于三个点的输入顺序,已经知道中间点,别的两个点可以实验两次即可确定。经本人实验,此方法并不稳定,以是后续未使用,便不过多赘述,如有兴趣可自行研究。
三、边界点求取
由于使用爬线算法,提前在图像中补了黑框,以是在碰到十字、弯道等图像时,不可避免的会有部分边线落在黑框上,在此我称之为边界点,即位于左右两侧边界上的点。
找出左右两侧边界上的点的个数,可用于判断弯道(一侧有边界点,另一侧没有边界点)、判断十字(左右两侧都有边界点),当然还有环岛等,要根据详细环境去使用,以下给出代码:
- /**
- * 函数功能: 寻找左侧边线的边界点个数
- * 特殊说明: 无
- * 形 参: 无
- *
- * 示例: Get_L_Border_Point_Num();
- * 返回值: 无
- */
- uint8 L_Border_Point_Num = 0;
- uint8 L_UP_Border_Point_Num = 0; //记录位于顶部黑框上的边界点个数
- void Get_L_Border_Point_Num(void)
- {
- uint8 i = 0;
- L_Border_Point_Num = 0;
- L_UP_Border_Point_Num = 0;
- for(i = Y_Meet; i < L_Line[0][1]; i++)
- {
- if(L_Border[i] == 2) //左右两侧用一维边线即可,2是我的左侧边界
- {
- L_Border_Point_Num ++;
- }
- }
- for(i = 40; i < L_Statics - 2; i++)
- {
- if(L_Line[i][1] == 2) //顶部的边界点使用二维边线
- {
- L_UP_Border_Point_Num ++;
- }
- }
- }
- /**
- * 函数功能: 寻找右侧边线的边界点个数
- * 特殊说明: 无
- * 形 参: 无
- *
- * 示例: Get_R_Border_Point_Num();
- * 返回值: 无
- */
- uint8 R_Border_Point_Num = 0;
- uint8 R_UP_Border_Point_Num = 0;
- void Get_R_Border_Point_Num(void)
- {
- uint8 i = 0;
- R_Border_Point_Num = 0;
- R_UP_Border_Point_Num = 0;
- for(i = Y_Meet; i < R_Line[0][1]; i++)
- {
- if(R_Border[i] == 77) //77是我的右侧边界
- {
- R_Border_Point_Num ++;
- }
- }
- for(i = 40; i < R_Statics - 2; i++)
- {
- if(R_Line[i][1] == 2)
- {
- R_UP_Border_Point_Num ++;
- }
- }
- }
复制代码 四、对位边界行
当图像的某一行,它的左侧边线点位于左侧边界上的同时,右侧边线点也位于右侧边界上,那我称之为对位边界行,可以用来辅助判断十字路口,正常环境下只有十字路口才会出现数个对位边界行。
- /**
- * 函数功能: 寻找对位边界行的个数
- * 特殊说明: 无
- * 形 参: 无
- *
- * 示例: Get_Relative_Border_Point_Num();
- * 返回值: 无
- */
- uint8 Relative_Border_Point_Num = 0; //存储对位边界行的个数
- void Get_Relative_Border_Point_Num(void)
- {
- uint8 i = 0;
- Relative_Border_Point_Num = 0; //函数每张图像边线都调用一次,每调用一次都要清零
- for(i = Y_Meet; i < L_Start_Point[1]; i++)
- {
- if(L_Border[i] == 2 && R_Border[i] == 77) //2为左侧边界的X坐标,77为右侧边界
- {
- Relative_Border_Point_Num ++;
- }
- }
- }
复制代码
五、圆弧拐点求取
圆弧拐点常见于小S弯道以及环岛,可以用于元素判断。判断直角拐点的时候轻易与圆弧拐点误判,但有差别的需求场景,详细本身真正纯熟使用后就可理解。
圆弧拐点的判断原理为找一段连续点中最突出的那个点,比方左侧内凹的圆弧拐点,其前方的数个点X坐标一直在增大,其之后的数个点X坐标值一直在减小,同时使用生长方向举行辅助判断,生长方向使用的是本人八向迷宫算法中的方向,可查看之前文章去了解:
智能车摄像头开源—1.2核心算法:自适应八向迷宫(下)
六、求每侧边线斜率及截距
将每侧边线由中间点分割为两段(后称为一段和二段),分别计算一段、二段和整条边线的斜率以及截距(一维边线),然后将三者的斜率举行比对。当一段斜率近似等于二段斜率,一段斜率近似等于整段斜率,二段斜率近似等于整段斜率时,可判断为该侧边线为直线,用于直道的判断。
- /**
- * 函数功能: 最小二乘法计算斜率
- * 特殊说明: 无
- * 形 参: uint8 begin 输入起点
- * uint8 end 输入终点
- * uint8 *border 输入需要计算斜率的一维边线数组
- * 示例: Slope_Calculate(start, end, border);
- * 返回值: Result 计算出的斜率
- */
- float Slope_Calculate(uint8 begin, uint8 end, uint8 *border) //注:begin 必须小于 end,一般 begin 位于图像上方, end 位于图像下方
- {
- float X_Sum = 0, Y_Sum = 0, XY_Sum = 0, X2_Sum = 0;
- int16 i = 0;
- float Result = 0;
- static float Result_Last;
- for(i = begin; i < end ; i++)
- {
- X_Sum += (float)i;
- Y_Sum += (float)border[i];
- XY_Sum += (float)i * (border[i]);
- X2_Sum += (float)i * i;
- }
- if((end - begin) * X2_Sum - X_Sum * X_Sum) //防止为0的情况出现
- {
- Result = ((float)(end - begin) * XY_Sum - X_Sum * Y_Sum) / ((float)(end - begin) * X2_Sum - X_Sum * X_Sum);
- Result_Last = Result;
- }
- else
- {
- Result = Result_Last;
- }
- return Result;
- }
- /**
- * 函数功能: 计算斜率截距
- * 特殊说明: 调用最小二乘法计算斜率
- * 形 参: uint8 start 输入起点
- * uint8 end 输入终点
- * uint8 *border 输入需要计算斜率的一维边线数组
- * float *slope_rate 存储斜率的变量地址
- * float *intercept 存储截距的变量地址
- * 示例: Calculate_Slope_Intercept(start, end, L_Border, &L_Straightaway_Lope_Rate_C, &L_Intercept);
- * 返回值: 无
- */
- void Calculate_Slope_Intercept(uint8 start, uint8 end, uint8 *border, float *slope_rate, float *intercept)
- {
- uint16 i, Num = 0;
- uint16 X_Sum = 0, Y_Sum = 0;
- float Y_Average = 0, X_Average = 0;
- for(i = start; i < end; i++)
- {
- X_Sum += i;
- Y_Sum += border[i];
- Num ++;
- }
- if(Num)
- {
- X_Average = (float)(X_Sum / Num);
- Y_Average = (float)(Y_Sum / Num);
- }
- *slope_rate = Slope_Calculate(start, end, border);
- *intercept = (float)(Y_Average - (*slope_rate) * X_Average);
- }
- float Max_Slope_Dif_Thre = 0.6; //实测得出,并未使用
- float Min_Slope_Dif_Thre = 0.15; //实测得出,当两段线段的斜率低于这个阈值时,可认为斜率相等
- //斜率和截距
- float R_Straightaway_Lope_Rate_A = 0; //第一段的斜率
- float R_Straightaway_Lope_Rate_B = 0; //第二段的斜率
- float R_Straightaway_Lope_Rate_C = 0; //整段边线的斜率
- float R_Intercept = 0; //整段边线的截距
- uint8 R_Straight_Flag = 0; //右侧边线是否为直线标志位
- /**
- * 函数功能: 求右侧边线斜率截距,并判断是否为直线
- * 特殊说明: 无
- * 形 参: uint8 start //右侧边线起始点Y坐标
- * uint8 end //右侧边线终止点Y坐标
- *
- * 示例: Get_R_Intercept_And_Slope(R_Line[R_Statics][1] + 5, R_Line[0][1]);
- * 返回值: 无
- */
- void Get_R_Intercept_And_Slope(uint8 start, uint8 end)
- {
- R_Straightaway_Lope_Rate_A = 0;
- R_Straightaway_Lope_Rate_B = 0;
- R_Straightaway_Lope_Rate_C = 0;
- R_Intercept = 0;
- R_Straight_Flag = 0; //所有值先清零
- R_Straightaway_Lope_Rate_A = Slope_Calculate(start, ((end - start) / 2) + start, R_Border);
- R_Straightaway_Lope_Rate_B = Slope_Calculate(((end - start) / 2) + start, end, R_Border); //计算两段斜率
- Calculate_Slope_Intercept(start, end, R_Border, &R_Straightaway_Lope_Rate_C, &R_Intercept); //计算整段边线斜率和截距
- if(My_ABS_F(R_Straightaway_Lope_Rate_A - R_Straightaway_Lope_Rate_B) <= Min_Slope_Dif_Thre &&
- My_ABS_F(R_Straightaway_Lope_Rate_B - R_Straightaway_Lope_Rate_C) <= Min_Slope_Dif_Thre &&
- My_ABS_F(R_Straightaway_Lope_Rate_A - R_Straightaway_Lope_Rate_C) <= Min_Slope_Dif_Thre) //判断是否为直线
- {
- R_Straight_Flag = 1;
- }
- }
- float L_Straightaway_Lope_Rate_A = 0;
- float L_Straightaway_Lope_Rate_B = 0;
- float L_Straightaway_Lope_Rate_C = 0;
- float L_Intercept = 0;
- uint8 L_Straight_Flag = 0;
- /**
- * 函数功能: 求左侧边线斜率截距,并判断是否为直线
- * 特殊说明: 无
- * 形 参: uint8 start //左侧边线起始点Y坐标
- * uint8 end //左侧边线终止点Y坐标
- *
- * 示例: Get_L_Intercept_And_Slope(L_Line[L_Statics][1] + 5, L_Line[0][1]);
- * 返回值: 无
- */
- void Get_L_Intercept_And_Slope(uint8 start, uint8 end)
- {
- L_Straightaway_Lope_Rate_A = 0;
- L_Straightaway_Lope_Rate_B = 0;
- L_Straightaway_Lope_Rate_C = 0;
- L_Intercept = 0;
- L_Straight_Flag = 0;
- L_Straightaway_Lope_Rate_A = Slope_Calculate(start, ((end - start) / 2) + start, L_Border);
- L_Straightaway_Lope_Rate_B = Slope_Calculate(((end - start) / 2) + start, end, L_Border);
- Calculate_Slope_Intercept(start, end, L_Border, &L_Straightaway_Lope_Rate_C, &L_Intercept);
- if(My_ABS_F(L_Straightaway_Lope_Rate_A - L_Straightaway_Lope_Rate_B) <= Min_Slope_Dif_Thre &&
- My_ABS_F(L_Straightaway_Lope_Rate_B - L_Straightaway_Lope_Rate_C) <= Min_Slope_Dif_Thre &&
- My_ABS_F(L_Straightaway_Lope_Rate_A - L_Straightaway_Lope_Rate_C) <= Min_Slope_Dif_Thre)
- {
- L_Straight_Flag = 1;
- }
- }
复制代码
七、拟合曲线计算方差
当我们求出左右两侧一维边线的斜率和截距以后,可以用斜率和截距在图像中画出一条直线,然后将实际的赛道边线的每个点(一维边线)与拟合直线的对应行的点计算方差,整条边线的方差的大小反应了赛道边线的弯曲程度。
但实际验证此方法时,效果不甚理想,会碰到各种特别环境,同时以下代码存在些许bug,作者到最终都没修复,以是更多是在此处给出一个思路,想用的话可以实验修修下列代码的bug:
- //拟合曲线
- int16 L_Fitting_Line[] = {0};
- int16 R_Fitting_Line[] = {0};
- //方差
- float L_Variance = 0;
- float R_Variance = 0;
- /**
- * 函数功能: 计算左右两侧边线的方差
- * 特殊说明: 每三行计算一次,减少计算量,但获取的信息不那么精确,可以选择计算每一行
- * 形 参: 无
- *
- * 示例: Get_Fitting_Line_And_Variance();
- * 返回值: 无
- */
- void Get_Fitting_Line_And_Variance(void)
- {
- uint8 i = 0;
- for(i = 1; i <= 19; i ++)
- {
- L_Fitting_Line[i] = (int16)(L_Straightaway_Lope_Rate_C * (float)(i * 3) + L_Intercept); //y = kx+b(把XY轴交换下就能想通了)
- L_Fitting_Line[i] = Limit_16(L_Fitting_Line[i], X_Border_Min + 2, X_Border_Max - 2);
- R_Fitting_Line[i] = (int16)(R_Straightaway_Lope_Rate_C * (float)(i * 3) + R_Intercept);
- R_Fitting_Line[i] = Limit_16(R_Fitting_Line[i], X_Border_Min + 2, X_Border_Max - 2);
- }
- Get_Variance(L_Line[L_Statics][1] + 1, L_Line[0][1], L_Fitting_Line, L_Border, &L_Variance, 3);
- Get_Variance(R_Line[R_Statics][1] + 1, R_Line[0][1], R_Fitting_Line, R_Border, &R_Variance, 3);
- }
复制代码
八、求取左右侧边线差值
用每行右侧边线点的X坐标值减去对应行左侧边线点的X坐标值(一维边线),可以得到每行的差值(大概说每行赛道的宽度),由此可以用来判断十字等元素。比方十字:图像从下往上的边线差值先变小,后突然变大,后又变小,呈现出十字的趋势。
以下代码每隔两行计算一行,并没有计算全部行的差值,但是计算每一行完全可行。
- int8 Row_Difference[20] = {0};
- uint8 Max_Row_Dif_Line_Num = 0; //当某一行左侧边线点位于左侧边界上,右侧边线点位于右侧边线上,与对位边界行同理,可以记录下来
- /**
- * 函数功能: 寻找对位边界行的个数
- * 特殊说明: 无
- * 形 参: 无
- *
- * 示例: Get_Relative_Border_Point_Num();
- * 返回值: 无
- */
- void Get_Row_Difference(uint8 *l_border, uint8 *r_border)
- {
- uint8 i = 0;
- Max_Row_Dif_Line_Num = 0;
- for(i = 0; i < 20; i ++)
- {
- Row_Difference[i] = (int8)(r_border[i * 3] - l_border[i * 3]);
- if(Row_Difference[i] == 75)
- {
- Max_Row_Dif_Line_Num ++;
- }
- }
- }
复制代码
九、TOF测距
部分组别使用TOF测距模块检测停滞、坡道等元素,那么就必要调用对应函数获取距离。逐飞官方手册中说明:测量间隔不可低于10ms。因此我们直接使用图像计数(收罗十张图像间隔绝对大于10ms,而且时间相对固定,可根据本身帧率恰当调整)
- if(Image_Num % 10 == 0) //其中Image_Num 需在复制图像时加一,即没采集一张图像加一一次
- {
- Barrier_Distance = TOF_Get_Distance_mm();
- }
复制代码
十、总函数调用
将上述所有处置惩罚举行整合,放至一个函数内,每张图像调用一次函数便可获取所有信息。对于部分元素内部环境(比方环岛、十字等特别元素会被对应的循环接管),只需调用对应的函数获取所需的信息即可。顺序可随意
- /**
- * 函数功能: 获取所有信息
- * 特殊说明: 无
- * 形 参: 无
- *
- * 示例: Get_Information();
- * 返回值: 无
- */
- void Get_Information(void)
- {
- Get_L_Border_Point_Num();
- Get_R_Border_Point_Num();
- Get_Relative_Border_Point_Num();
- Get_L_Up_Turn_Left_Point_1(2);
- Get_L_Right_Turn_Up_Point_1(2);
- Get_R_Up_Turn_Right_Point_1(2);
- Get_R_Left_Turn_Up_Point_1(2);
- Get_L_Arc_Turn_Point();
- Get_R_Arc_Turn_Point();
- Get_L_Intercept_And_Slope(L_Line[L_Statics][1] + 5, L_Line[0][1]);
- Get_R_Intercept_And_Slope(R_Line[R_Statics][1] + 5, R_Line[0][1]);
- Get_Fitting_Line_And_Variance();
- Get_Row_Difference(L_Border, R_Border);
- }
复制代码
十一、总结
以上处置惩罚后就完成了对于边线信息的提取,加上之前的处置惩罚,我们可以获取一张图像赛道的以下信息:
1、左右两侧边线的二维数组
- 每个点的X,Y坐标;
- 每个点的生长方向;
- 每个点的阈值;
2、每侧二维边线点的个数
3、左右两侧边线的一维数组
4、每侧一维边线点的个数
5、所有二维边线点的均值阈值
6、左右两侧爬线相遇点的X,Y坐标
7、左右两侧边界点的个数
8、顶部边界点的个数
9、对位边界行的个数
10、上转左、上转右、右转上、左转上四个拐点坐标及标记位
11、每侧最多三个圆弧拐点及标记位
12、每侧边线的斜率、截距以及是否为直线标记位
13、每侧边线的方差(弯曲程度)
14、左右两侧边线的差值
15、停滞物(坡道)距离
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |