这篇文章主要讲述如何实现中线的扫描和二次扫线。
基础扫线效果图片如下
图像处理专栏中所使用的各种变量定义如下
//各种定义开始
//拟合
float parameterB; //斜率
float parameterA; //截距
//模拟赋值
int monileft[70];
int moniright[70];
//偏方左右
int pianfangleft = 0;
int pianfangright = 0;
//左右双断点
int16 leftduan1 = 0;
int16 leftduan2 = 0;
int16 newopenleft = 0;
int16 truedoubleleft = 0;
int16 rightduan1 = 0;
int16 rightduan2 = 0;
int16 newopen = 0;
int16 truedoubleright = 0;
//偏方满足
int16 truepianfangflag = 0;
int16 truepianfangflagforleft=0;
//扫中线
int16 zhongold=93;
int16 leftold=0;
int16 rightold=0;
int16 rightflag[70]={0,};
int16 leftflag[70]={0,};
int16 duanhang0=0;
int16 duanhang1=0;
int16 duanhangju=0;
int16 break_hangshu=0;
int16 countprotect=0;
int16 turepodaoflag = 0;
int16 rukuflag = 0;
int16 protect=0;
//左右线连续
int16 continueleft = 0;
int16 continueright = 0;
//坡道
float LK0end = 0;
float LK020 = 0;
float RK0end = 0;
float RKB020 = 0;
int16 podaoflag = 0;
int16 podaoflag2=0;
int16 kuandubreak = 0;
int16 podaotype2 = 0;
int16 lasttruepodaoflag = 0;
int16 countpodao = 0;
int16 truelongflag=0;
//入库
int16 type=0;
int firstku=0;
int haveleftku = 0;
int leftkuhang = 0;
int lastduanhang1 = 0;
int Llastduanhang1 = 0;
int countruku = 0;
int stopruku = 0;
int count3=0;
//宽度计算
int calkuan[70];
//环岛右
int flag_find_huan_leftdown_point = 0;
int flag_find_huan_rightdown_point = 0;
int flag_find_huan_leftmiddle_point = 0;
int flag_find_huan_rightmiddle_point = 0;
int flag_find_huan_rightup_point = 0;
int flag_find_huan_leftup_point = 0;
int ROUNDISLAND = 1; //识别十字
int huandao_memory=0;
int Rhave0=0;
int right_turn_down[2]={0,};
int right_turn_up[2]={0,};
int left_turn_down[2]={0,};
int left_turn_up[2]={0,};
int right_turn_middle[2];
int left_turn_middle[2];
int huandao_flag_R_L=0;
int huandao_flag = 0;
int huandao_procedure_variable = 0;
int huandao_memoryforleft=0;
int flag_rukou2 = 0;
int flag_rukou = 0;
int last_memory=0;
//右环岛左线
int jiansu_flag = 0;
int flag_blank_out_huandao = 0;
//右环岛右线
//左环岛主体
int lcenter_5=0;
int last_memoryforleft=0;
//斜入十字
int rightdownflag3=0;
int leftdownflag3=0;
int rightupflag3=0;
int leftupflag3=0;
int firstmid=0;
int8 CROSSROAD = 0;
//正入十字
int rightdownflag=0;
int rightupflag=0;
int leftdownflag=0;
int leftupflag=0;
int secondmid=0;
int buzhongxianmax = 0;
int buzhongxianmin = 0;
//小s
int turesmallsflag=0;
int countstoplongg = 0;
int flag_centre_right_point = 0;
int flag_centre_left_point = 0;
int centre_left_point[2];
int centre_right_point[2];
int swrong = 0;
int overflag = 0;
int flag_middle_S=0;
int flag_small_S=0;
int findturn = 0;
int finddirection = 0;//1左转弯 2右转弯
int min;
int max;
int LongLCenter[70]={0,};
//中s
int trueflag_middle_S = 0;
int last1trueflag_middle_S=0;
int last2trueflag_middle_S=0;
//长直道
int countstoplong = 0;
int stoplong= 0;
int overlong=0;
int overlongwrong=0;
int changzhenshu=24;
int lastturesmallsflag=0;
int stopsmalls=0;
int countstopsmalls=0;
int truelong1=0;
int truelong2=0;
int turedian=0;
int kthird=0;
int lasttruelong2 = 0;
int longflag=0;
int thirdmid=0;
int trueshortflag=0;
//环岛快速进入
float R020K = 0;
float L020K=0;
//十字防断
int bannothing=0;
int LCenterrecord[70];
int leftrecord[70];
int rightrecord[70];
int countbannothing = 0;
//二次十字
int se_thrmid = 0;
//三岔路口
int16 threemode=0;
//各种计数
int huandaotype=0;
//累差
int16 cumulants1;
int16 cumulants2;
int16 openright;
int16 endright;
void zhongxian()
{
int hang;
int lie;
int guixian = 0;
int zhongold2 = 0;
//第一次扫线,获取0-50行左右线的值
for (hang = 0; hang < 50; hang++)
{
for (lie = zhongold; lie >= 1; lie--)
{
if (lie >= (int)185) lie = 184;
if (Pixels[hang][ lie - 1] == 0 && Pixels[hang][ lie] == 0 && Pixels[hang][lie + 1] == 1) //黑白
{
R_black[hang] = (unsigned char)(lie + 1);
rightold = (int)(lie + 1);
rightflag[hang] = 1;
break;
}
else
{
R_black[hang] = 0;
rightflag[hang] = 0;
}
}
for (lie = zhongold; lie < 185; lie++)
{
if (lie == 0) lie = 1;
if (Pixels[hang][ lie + 1] == 0 && Pixels[hang][ lie] == 0 && Pixels[hang][ lie - 1] == 1)
{
L_black[hang] = (unsigned char)(lie - 1);
leftold = (int)(lie - 1);
leftflag[hang] = 1;
break;
}
else
{
L_black[hang] = 186;
leftflag[hang] = 0;
}
}
//下一行的扫线在上一行的基础上向左右扫
zhongold = (int)((L_black[hang] + R_black[hang]) / 2);
}
for (hang = 0; hang < 50; hang++)
{ LCenter[hang] = (unsigned char)((L_black[hang] + R_black[hang]) / 2); }
/*二次扫线*/
duanhang0 = 0;
duanhang1 = 0;
for (hang = 1; hang < 50; hang++) //扫断点0
{
/*
断行0的原理:由于摄像头视角,呈现出的赛道应该是近大远小的,如果远
处的行 宽度比近处的大,那么认为出现第一个断行。(大家可以想一下十
字路口的下方 赛道宽度变宽)
*/
if ((L_black[hang] - R_black[hang]) <= (L_black[hang - 1] - R_black[hang - 1])) { };
if ((L_black[hang] - R_black[hang]) - (L_black[hang - 1] - R_black[hang - 1]) >= 4&&hang>=13)
{
duanhang0 = hang - 1;
duanhangju = (L_black[hang - 1] - R_black[hang - 1]);
break;
}
}
if (duanhang0 > 10)
{
/*
如果扫描到断行0,那么从断行0开始的图像可能有问题,
所以这里选择固定终止往下扫线,寻找断行1的存在。
(断行1的原理,当宽度再次变小且小于断行0处的宽度,
认为是正确的断行1,大家可以想一下十字路口的上半段)
*/
advanced_regression(0, duanhang0 - 9, duanhang0 - 7, duanhang0 - 5, duanhang0 - 3); //显示断行处斜率
zhongold2 = LCenter[duanhang0 - 7];
for (hang = (int)(duanhang0 + 3); hang < 50; hang++) //固定中值扫线
{
for (lie = (int)zhongold2; lie >= 1; lie--) //扫右线
{
if (lie >= 185) lie = 184;
if (Pixels[hang][ lie - 1] == 0 && Pixels[hang][ lie] == 0 && Pixels[hang][ lie + 1] == 1) //白黑黑
{
R_black[hang] = (unsigned char)(lie + 1);
rightold = (int)(lie + 1);
rightflag[hang] = 1;
break;
}
else
{
R_black[hang] = 0;
rightflag[hang] = 0;
}
}
for (lie = (int)zhongold2; lie < 185; lie++) //扫左线
{
if (lie == 0) lie = 1;
if (Pixels[hang][ lie + 1] == 0 && Pixels[hang][ lie] == 0 && Pixels[hang][ lie - 1] == 1)
{
L_black[hang] = (unsigned char)(lie - 1);
leftold = (int)(lie - 1);
leftflag[hang] = 1;
break;
}
else
{
L_black[hang] = 186;
leftflag[hang] = 0;
}
}
}
}
for (hang = (int)(duanhang0 + 3); hang < 50; hang++)//扫断点1
{
/*
计算是否存在断行1
*/
if ((L_black[hang] - R_black[hang]) < duanhangju-15&&L_black[hang]<=180&&R_black[hang]>=5)
{
duanhang1 = (signed short int)hang;
// SetText(" 找到断行1 " + duanhang1);
// SetText(" duanhangju1 " + calkuan[duanhang1]);
break;
}
}
if (duanhang0 > 10 && duanhang1 != 0)
{
/*
找到断行1,开始进入二次扫线模式
二次扫线思路:从断行1处开始使用断行0的中值进行扫线,动态继承中值往下扫。
*/
zhongold2 = LCenter[duanhang0 - 7];
int gudingtime = 1;
for (hang = (int)(duanhang1); hang < 50; hang++) //二次扫线
{
for (lie = (int)zhongold2; lie >= 1; lie--) //扫右线
{
if (lie >= 185) lie = 184;
if (Pixels[hang][ lie - 1] == 0 && Pixels[hang][ lie] == 0 && Pixels[hang][lie + 1] == 1) //白黑黑
{
R_black[hang] = (unsigned char)(lie + 1);
rightold = (int)(lie + 1);
rightflag[hang] = 1;
break;
}
else
{
R_black[hang] = 0;
rightflag[hang] = 0;
}
}
for (lie = (int)zhongold2; lie < 185; lie++) //扫左线
{
if (lie == 0) lie = 1;
if (Pixels[hang][ lie + 1] == 0 && Pixels[hang][ lie] == 0 && Pixels[hang][lie - 1] == 1)
{
L_black[hang] = (unsigned char)(lie - 1);
leftold = (int)(lie - 1);
leftflag[hang] = 1;
break;
}
else
{
L_black[hang] = 186;
leftflag[hang] = 0;
}
}
if (gudingtime != 0) gudingtime = gudingtime + 1;
if (gudingtime == 8) gudingtime = 0;
if (gudingtime != 0) zhongold2 = LCenter[duanhang0 - 7];
if (gudingtime == 0) zhongold2 = (L_black[hang] + R_black[hang]) / 2;
}
}
// 终止
//存入显示数组中
for (hang = 0; hang < 69; hang++) //去掉杂点
{
LCenter[hang] = (unsigned char)((L_black[hang] + R_black[hang]) / 2);
if (hang > 5)
{
if (Pixels[hang][ LCenter[hang]] == 0 && Pixels[hang + 1][LCenter[hang]] == 0&&huandao_memory!=4&&huandao_memoryforleft!=4)
{
for (guixian = hang; guixian < 70; guixian++)
{
LCenter[hang] = LCenter[hang - 1];
L_black[guixian] = 0;
R_black[guixian] = 0;
}
break;
}
}
}
int j;
break_hangshu = 0;
for (j = 0; j < 50; j++)
{
if ((Pixels[j][ LCenter[j]]) == 0 && (Pixels[j + 1][ LCenter[j]]) == 0&& huandao_memory != 4 && huandao_memoryforleft != 4)
{
break_hangshu = (int16)j;
//last_break_hangshu = break_hangshu;
//也就是说二十行一下是不会break的
if (break_hangshu >= 20) //防止在一开始就break
{
break;
}
}
if ((Pixels[j][ LCenter[j]]) == 0 && (Pixels[j + 1][ LCenter[j+1]]) == 0 && (Pixels[j + 2][ LCenter[j+2]]) == 0 && (Pixels[j + 3][ LCenter[j+3]]) == 0 && (huandao_memory == 4 || huandao_memoryforleft == 4))
{
break_hangshu = (int16)j;
//last_break_hangshu = break_hangshu;
//也就是说二十行一下是不会break的
if (break_hangshu >= 20) //防止在一开始就break
{
break;
}
}
}
if (break_hangshu == 0) break_hangshu = 50;
zhongold = LCenter[4];
if (break_hangshu >= 4)
{
int calendleft =0;
int calendright = 0;
int i;
for (i = break_hangshu - 1; i >= 4; i--)
{
if (My_Abs(L_black[i], L_black[i - 1]) <= 4&&L_black[i]!=186)
{
//计算左边线的最终有效行数
calendleft = i-1;
// SetText("calendleft " + calendleft);
break;
}
}
for (i = calendleft; i >= 4; i--)
{
if (L_black[i] == L_black[i - 1]) calendleft = i - 1;
if (L_black[i] != L_black[i - 1]) break;
}
for (i = break_hangshu - 1; i >= 4; i--)
{
if (My_Abs(R_black[i], R_black[i - 1]) <= 4&&R_black[i]!=0)
{
//计算右边线的最终有效行数
calendright = i-1;
// SetText("calendright " + calendright);
break;
}
}
for (i = calendright; i >= 4; i--)
{
if (R_black[i] == R_black[i - 1]) calendright = i - 1;
if (R_black[i] != R_black[i - 1]) break;
}
// SetText("break_hangshu " + break_hangshu);
// SetText("calendleft " + calendleft);
// SetText("calendright " + calendright);
if (calendleft >= 45) calendleft = 45;
if (calendright >= 45) calendright = 45;
//求开始点
//int calbeginleft=0;
//int calbeginright=0;
//for (i = 0; i <= 12; i++)
//{
// if (R_black[i] != 0)
// {
// calbeginright = i;
// break;
// }
//}
//for (i = 0; i <= 12; i++)
//{
// if (L_black[i] != 186)
// {
// calbeginleft = i;
// break;
// }
//}
//SetText("calbeginleft " + calbeginleft);
// SetText("calbeginright " + calbeginright);
// SetText(" L_black[calbeginleft] " + L_black[calbeginleft]);
// SetText("L_black[calendleft] " + L_black[calendleft]);
//SetText(" R_black[calbeginright] " + R_black[calbeginright]);
//SetText(" R_black[calendright] " + R_black[calendright]);
/*
下面的代码是用来计算环岛的相关参数,进行辅助判断。
*/
if (calendleft >= 20)
{
pianfangcal(0, calendleft, 1);
}
if (calendleft < 20)
{
pianfangcal(0, break_hangshu - 3, 1);
}
if (calendright >= 20)
{
pianfangcal(0, calendright, 2);
}
if (calendright < 20)
{
pianfangcal(0, break_hangshu - 3, 2);
}
}
countprotect = 0;
if (turepodaoflag == 0 && rukuflag == 0) //坡道不退出 入库刹车10帧后自行退出
{
for (j = 0; j <= 185; j++) //退出
{
if ((Pixels[0][ j]) == 0)
{
countprotect = countprotect + 1;
}
if (countprotect == 186) { protect = 1; break; }
}
}
rightopen = R_black[0];
leftend = L_black[0];
}
void advanced_regression(int type, int startline1, int endline1, int startline2, int endline2)
{
int i = 0;
int sumlines1 = endline1 - startline1;
int sumlines2 = endline2 - startline2;
int sumX = 0;
int sumY = 0;
float averageX = 0;
float averageY = 0;
float sumUp = 0;
float sumDown = 0;
if (type == 0) //拟合中线
{
/**计算sumX sumY**/
for (i = startline1; i < endline1; i++)
{
sumX += i;
sumY += LCenter[i];
}
for (i = startline2; i < endline2; i++)
{
sumX += i;
sumY += LCenter[i];
}
averageX =(float)( sumX / (sumlines1 + sumlines2)); //x的平均值
averageY = (float)(sumY / (sumlines1 + sumlines2)); //y的平均值
for (i = startline1; i < endline1; i++)
{
sumUp += (LCenter[i] - averageY) * (i - averageX);
sumDown += (i - averageX) * (i - averageX);
}
for (i = startline2; i < endline2; i++)
{
sumUp += (LCenter[i] - averageY) * (i - averageX);
sumDown += (i - averageX) * (i - averageX);
}
if (sumDown == 0) parameterB = 0;
else parameterB = sumUp / sumDown;
parameterA = averageY - parameterB * averageX;
}
else if (type == 1) //拟合左线
{
/**计算sumX sumY**/
for (i = startline1; i < endline1; i++)
{
sumX += i;
sumY += L_black[i];
}
for (i = startline2; i < endline2; i++)
{
sumX += i;
sumY += L_black[i];
}
averageX =(float)( sumX / (sumlines1 + sumlines2)); //x的平均值
averageY = (float)(sumY / (sumlines1 + sumlines2)); //y的平均值
for (i = startline1; i < endline1; i++)
{
sumUp += (L_black[i] - averageY) * (i - averageX);
sumDown += (i - averageX) * (i - averageX);
}
for (i = startline2; i < endline2; i++)
{
sumUp += (L_black[i] - averageY) * (i - averageX);
sumDown += (i - averageX) * (i - averageX);
}
if (sumDown == 0) parameterB = 0;
else parameterB = sumUp / sumDown;
parameterA = averageY - parameterB * averageX;
}
else if (type == 2) //拟合右线
{
/**计算sumX sumY**/
for (i = startline1; i < endline1; i++)
{
sumX += i;
sumY += R_black[i];
}
for (i = startline2; i < endline2; i++)
{
sumX += i;
sumY += R_black[i];
}
averageX =(float)( sumX / (sumlines1 + sumlines2)); //x的平均值
averageY = (float)(sumY / (sumlines1 + sumlines2)); //y的平均值
for (i = startline1; i < endline1; i++)
{
sumUp += (R_black[i] - averageY) * (i - averageX);
sumDown += (i - averageX) * (i - averageX);
}
for (i = startline2; i < endline2; i++)
{
sumUp += (R_black[i] - averageY) * (i - averageX);
sumDown += (i - averageX) * (i - averageX);
}
if (sumDown == 0) parameterB = 0;
else parameterB = sumUp / sumDown;
parameterA = averageY - parameterB * averageX;
}
}
void regression(int type, int startline, int endline)//最小二乘法拟合曲线,分别拟合中线,左线,右线,type表示拟合哪几条线 xy 颠倒
{
int i = 0;
int sumlines = endline - startline;
int sumX = 0;
int sumY = 0;
float averageX = 0;
float averageY = 0;
float sumUp = 0;
float sumDown = 0;
if (type == 0) //拟合中线
{
for (i = startline; i < endline; i++)
{
sumX += i;
sumY += LCenter[i];
}
if (sumlines != 0)
{
averageX = (float)(sumX / sumlines); //x的平均值
averageY =(float)( sumY / sumlines); //y的平均值
}
else
{
averageX = 0; //x的平均值
averageY = 0; //y的平均值
}
for (i = startline; i < endline; i++)
{
sumUp += (LCenter[i] - averageY) * (i - averageX);
sumDown += (i - averageX) * (i - averageX);
}
if (sumDown == 0) parameterB = 0;
else parameterB = sumUp / sumDown;
parameterA = averageY - parameterB * averageX;
}
else if (type == 1)//拟合左线
{
for (i = startline; i < endline; i++)
{
sumX += i;
sumY += L_black[i];
}
if (sumlines == 0) sumlines = 1;
averageX = (float)(sumX / sumlines); //x的平均值
averageY =(float)( sumY / sumlines); //y的平均值
for (i = startline; i < endline; i++)
{
sumUp += (L_black[i] - averageY) * (i - averageX);
sumDown += (i - averageX) * (i - averageX);
}
if (sumDown == 0) parameterB = 0;
else parameterB = sumUp / sumDown;
parameterA = averageY - parameterB * averageX;
}
else if (type == 2)//拟合右线
{
for (i = startline; i < endline; i++)
{
sumX += i;
sumY += R_black[i];
}
if (sumlines == 0) sumlines = 1;
averageX = (float)(sumX / sumlines); //x的平均值
averageY =(float)( sumY / sumlines); //y的平均值
for (i = startline; i < endline; i++)
{
sumUp += (R_black[i] - averageY) * (i - averageX);
sumDown += (i - averageX) * (i - averageX);
}
if (sumDown == 0) parameterB = 0;
else parameterB = sumUp / sumDown;
parameterA = averageY - parameterB * averageX;
}
}
//求两数之差绝对值开始
int My_Abs(int a, int b)
{
if ((a - b) > 0)
return ((int)(a - b));
else return ((int)(b - a));
}
void pianfangcal(int begin, int end, int type)
{
int i = 0;
if (type == 1)//左线拟合差平方计算
{
pianfangleft = 0;
regression (1, begin, end);
monileftfuzhi(parameterA, parameterB,(int) begin,(int) end);
for (i = begin; i <= end; i++)
{
pianfangleft = pianfangleft + (L_black[i] - monileft[i]) * (L_black[i] - monileft[i]);
}
pianfangleft = pianfangleft / (end - begin + 1);
}
if (type == 2)//右线拟合差平方计算
{
pianfangright = 0;
regression(2, begin, end);
monirightfuzhi(parameterA, parameterB,(int) begin,(int) end);
for (i = begin; i <= end; i++)
{
pianfangright = pianfangright + (R_black[i] - moniright[i]) * (R_black[i] - moniright[i]);
}
pianfangright = pianfangright / (end - begin + 1);
}
if (type == 0)//右线拟合差平方计算
{
pianfangmid = 0;
regression(0, begin, end);
monizhongfuzhi(parameterA, parameterB, (int)begin, (int)end);
int fangjun = 0;
int junfang = 0;
for (i = begin; i <= end; i++)
{
fangjun = fangjun + (LCenter[i]) * (LCenter[i]);
}
fangjun= fangjun / (end - begin + 1);
for (i = begin; i <= end; i++)
{
junfang = junfang + (LCenter[i]);
}
junfang = junfang / (end - begin + 1);
junfang = junfang * junfang;
pianfangmid = fangjun - junfang;
}
}
void monileftfuzhi(float A, float B, int start_point, int end_point)
{
int m;
for (m = start_point; m <= end_point; m++)
{
if((B * m + A)>=255) monileft[m]=255;
if((B * m + A)<=0) monileft[m]=0;
else if(0<(B * m + A)&&(B * m + A)<255)monileft[m] = (int)(B * m + A);
}
}
void monirightfuzhi(float A, float B, int start_point, int end_point)
{
int m;
for (m = start_point; m <= end_point; m++)
{
if((B * m + A)>=255) moniright[m]=255;
if((B * m + A)<=0) moniright[m]=0;
else if(0<(B * m + A)&&(B * m + A)<255)moniright[m] = (int)(B * m + A);
}
}
void monizhongfuzhi(float A, float B, int start_point, int end_point)
{
int m;
for (m = start_point; m <= end_point; m++)
{
if ((B * m + A) >= 255) monimiddle[m] = 255;
if ((B * m + A) <= 0) monimiddle[m] = 0;
else if (0 < (B * m + A) && (B * m + A) < 255) monimiddle[m] = (int)(B * m + A);
}
}
总结
1.配合上辅助函数便是完整代码,如有不懂请留言哦。