搜上下边线,处理圆环的时,可以利用上下边线的特点。
uint8_t UpdownSideGet(uint8_t imageInput[OV7725_UART_H][OV7725_UART_W], uint8_t imageOut[2][OV7725_UART_W])
{
uint8_t i = 0, j = 0;
uint8_t last = OV7725_UART_H/2;
imageOut[0][OV7725_UART_W-1] = 0;
imageOut[1][OV7725_UART_W-1] = OV7725_UART_H-1;
//从图像中间行 从中到下 从中到上 扫描
//处理中间单独那一列的上下边线
for(i = last; i >= 0; i--)
{
if(!imageInput[i][OV7725_UART_W/2])
{
imageOut[up][OV7725_UART_W/2] = i;
break;
}
}
for(i = last; i < OV7725_UART_H; i++)
{
if(!imageInput[i][OV7725_UART_W/2])
{
imageOut[down][OV7725_UART_W/2] = i;
break;
}
}
//其他列的上下边线
//从中到左
for(i = OV7725_UART_W/2-1; i > 0; i--)//遍历每一列
{
imageOut[up][i] = 0;
imageOut[down][i] = OV7725_UART_H-1;
for(j = imageOut[0][i+1] + 5; j > 0; j--)//一列中的扫描每行 从上列的行数+10开始向上扫描
{
if(!imageInput[j][i])
{
imageOut[up][i] = j;
break;
}
}
for(j = imageOut[1][i+1] - 5; j < OV7725_UART_H; j++)
{
if(!imageInput[j][i])
{
imageOut[down][i] = j;
break;
}
}
}
//从中到右
for(i = OV7725_UART_W/2+1; i < OV7725_UART_W-1; i++)
{
imageOut[up][i] = 0;
imageOut[down][i] = OV7725_UART_H-1;
for(j = imageOut[0][i-1] + 5; j > 0; j--)
{
if(!imageInput[j][i])
{
imageOut[up][i] = j;
break;
}
}
for(j = imageOut[1][i-1] - 5; j < OV7725_UART_H; j++)
{
if(!imageInput[j][i])
{
imageOut[down][i] = j;
break;
}
}
}
return 0;
}
找凸起的弧,用于圆环的检测。如下图红色的线。
RoundaboutGetArc函数中传入的num代表着,要检测的这个圆弧大小,要求这个圆弧包含多少个点。
以左圆环为例:
先判断边线丢不丢线,不丢线再进行下一步。由于是遍历一幅图像左边线数组的每一行,我们就找这样一个特征:它下面连续递减点的个数+它上面连续递减点的个数+跟它横坐标一样大的点的个数 >=我们设定的值。这个就是我们认为的圆弧
/*!
* @brief 判断左右边线是否存在弧形
* 输出的 index 圆弧的顶点位置
* @param imageInput : 二值图像信息
* @param imageOut : 边线数组
* @param status : 1:左边线 2:右边线
* @param num : 圆弧的大小 用点数表示 (连续N个增 连续N个减)
* @return 1 有弧线 0 没弧线
*/
uint8_t RoundaboutGetArc(uint8_t imageSide[OV7725_UART_H][2], uint8_t status, uint8_t num,uint8_t* index)
{
int i = 0;
uint8_t inc = 0, dec = 0, n = 0;
switch(status)
{
case 1:
for(i = START_H-2; i > END_H; i--)
{
//没有丢线
if(imageSide[i][0] != 1 && imageSide[i+1][0] != 1)
{
if(imageSide[i][0] == imageSide[i+1][0])
{
n++;
continue;
}
if(imageSide[i][0] > imageSide[i+1][0])
{
inc++;
inc+=n;
n=0;
}
else
{
dec++;
dec+=n;
n=0;
}
/* 有弧线 */
if(inc > num && dec > num)
{
*index = i + num;
return 1;
}
}
else
{
inc = 0;
dec = 0;n=0;
}
}
break;
case 2:
for(i = START_H-2; i > END_H; i--)
{
if(imageSide[i][1] != OV7725_UART_W-1 && imageSide[i+1][1] != OV7725_UART_W-1)
{
if(imageSide[i][1] == imageSide[i+1][1])
{
n++;
continue;
}
if(imageSide[i][1] > imageSide[i+1][1])
{
inc++;
inc+=n;
n = 0;
}
else
{
dec++;
dec+=n;
n=0;
}
/* 有弧线 */
if(inc > num && dec > num)
{
*index = i + num;
return 1;
}
}
else
{
inc = 0;
dec = 0;n=0;
}
}
break;
}
return 0;
}
/*!
* @brief 补线处理
*
* @param imageSide : 边线
* @param status : 1:左边线补线 2:右边线补线
* @param startX : 起始点 列数
* @param startY : 起始点 行数
* @param endX : 结束点 列数
* @param endY : 结束点 行数
*
* @return
*
* @note endY 一定要大于 startY
*
*/
void ImageAddingLine(uint8_t imageSide[OV7725_UART_H][2], uint8_t status, uint8_t startX, uint8_t startY, uint8_t endX, uint8_t endY)
{
int i = 0;
// 直线 x = ky + b
float k = 0.0f, b = 0.0f;
switch(status)
{
case 1://左补线
{
k = (float)((float)endX - (float)startX) / (float)((float)endY - (float)startY);
b = (float)startX - (float)startY * k;
for(i = startY; i < endY; i++)
{
imageSide[i][0] = (uint8_t)(k * i + b);
}
break;
}
case 2://右补线
{
k = (float)((float)endX - (float)startX) / (float)((float)endY - (float)startY);
b = (float)startX - (float)startY * k;
for(i = startY; i < endY; i++)
{
imageSide[i][1] = (uint8_t)(k * i + b);
}
break;
}
}
}
/*!
* @brief 判断上边线是否单调
* @param X1 :起始X点
* @param X2 :终止X点 X1 < X2
* @param imageIn : 边线数组
*
* @return 0:不单调or错误, 1:单调递增, 2:单调递减
*
* @note
*
* @see
*
* @date 2021/11/30 星期二
*/
uint8_t RoadUpSide_Mono(uint8_t X1, uint8_t X2, uint8_t imageIn[2][OV7725_UART_W])
{
uint8_t i = 0, num = 0;
for(i = X1; i < X2-1; i++)
{
if(imageIn[0][i] >= imageIn[0][i+1])
num++;
else
num = 0;
if (num >= (X2-X1)*4/5)
return 1;
}
for(i = X1; i < X2-1; i++)
{
if(imageIn[0][i] <= imageIn[0][i+1])
num++;
else
num = 0;
if (num >= (X2-X1)*4/5)
return 2;
}
return 0;
}
找圆环的特征,以左圆环为例,要求“左边线有弧,右边线单调”。左边线有弧已经说过了,这里的“右边线单调”:需要右边线在一定的横坐标范围内单调的点数大于我们设定的值,才认为是这里的“右边线单调”。
uint8_t RoadIsRoundabout(uint8_t Upimage[2][OV7725_UART_W], uint8_t imageInput[OV7725_UART_H][OV7725_UART_W], uint8_t image[OV7725_UART_H][2], uint8_t *flag)
{
uint8_t i = 0;
errL=0, errR=0;
leftState = 0, rightState = 0;
count = 0;
uint8_t num = 0, py;
// 从车头往前 左边线是否单调
for(i = START_H-2; i > END_H; i--)
{
if(image[i][0] == 1)
continue;
if(image[i][0] >= image[i+1][0]) // i是Y坐标值 0 是图像左线X坐标
{
if(image[i][0]<OV7725_UART_W/2 - 5)
num++;
else
num = 0 ;
if(num == 50)
{
num = 0;
leftState = 1; // 左单调标志
break;
}
}
else
{
num = 0;
}
if(i <= END_H+1) // 清0
num = 0;
}
errL = RoundaboutGetArc(image, 1, round_size, &py);
errR = RoundaboutGetArc(image, 2, round_size, &py);
// 右边线是否单调
for(i = START_H-2; i > END_H; i--)
{
if(image[i][1] == OV7725_UART_W-1)
continue;
if(image[i][1]<= image[i+1][1])
{
if(image[i][1]>OV7725_UART_W/2 + 5)
num++;
else
num = 0 ;
if(num == 50)
{
num = 0;
rightState = 1;
break;
}
}
else
{
num = 0;
}
if(i <= END_H+1)
num = 0;
}
// 左边单调, 检测右侧是否是环岛
if(leftState == 1 && rightState == 0 && errL == 0)
{
count = 0;
if(RoundaboutGetArc(image, 2, round_size, &count))
{
*flag = 1;
return 1;
}
else
{
return 0;
}
}
/* 右边单调, 检测左侧是否是环岛 */
if(rightState == 1 && leftState == 0&& errR == 0)
{
count = 0;
if(RoundaboutGetArc(image, 1, round_size, &count))
{
*flag = 2;
return 2;
}
}
return 0;
}
右环为例,左环也是一样,对称写(右环为奇数状态,左环为偶数状态)。目前已经找到圆环,进入状态1。
状态1:找到圆环后,补一条线,保证车子直走。一直检查圆弧还在不在,直到弧消失了,就进入状态3。
状态3:让车子一直往右拐,一直拐到上线单调的时候。进入状态5
这个还没拐成,要继续拐。
拐到这种时候,就已经进入圆环口了,可以不用拐了。
在圆环内,都是这种图像,正常走就行了,之后的状态就是出圆环了。
状态5:出圆环,找右边凸起的位置+下线全是最低点这种情况,然后补一条线,让车子向左拐。一直拐到上线单调的时候,进入状态7.
一直拐
拐到上线单调了,进入状态7。
状态7:加大力度拐,拐到上线不单调的时候,就出圆环正常行驶了。
参考代码
具体要根据车子的速度和摄像头选择图像的大小来确定里面的参数
void RoundaboutProcess(uint8_t imageInput[OV7725_UART_H][OV7725_UART_W], uint8_t imageSide[OV7725_UART_H][2], uint8_t UpdowmSide[2][OV7725_UART_W], uint8_t* state)
{
uint8_t i = 0, err5 = 0;
uint8_t pointX = 0, pointY = 0, inc = 0, dec = 0;
uint8_t flag= 0, Down_flag = 0;
static uint8_t finderr = 0;
static uint8_t err1 = 0;
switch(*state)
{
//奇数为入右圆环
case 1:
// 检查弧线
err1 = RoundaboutGetArc(imageSide, 2, 3, &pointY);
// 有弧线 进行补线 连接弧线最右点 和 图像左下角
if(err1)
{
pointX = imageSide[pointY][1];
ImageAddingLine(imageSide, 2, pointX, pointY, OV7725_UART_W-1, START_H);
finderr = 1;
}
else
{
if(finderr)
*state = 3;//准备进入环岛
}
break;
/* 发现左环岛 环岛出口处补线 */
case 3:
for(i=1;i<OV7725_UART_H-1;i++)
{
ImageSide[i][0]= ImageSide[i][0]+50;
}
if(RoadUpSide_Mono(30, OV7725_UART_W-1,UpdowmSide) == 1)//上线单调增进入下一步
*state = 5;
break;
case 5 :
err5 = RoundaboutGetArc(imageSide, 1, 10, &pointY);
//检查下线
for(i = OV7725_UART_W-1; i > 0; i--)
{
if(UpdowmSide[1][i] == 119)
inc++;
else
dec++;
if( dec <= 15)
{
Down_flag = 1;
break;
}
}
flag = RoadUpSide_Mono(20, OV7725_UART_W,UpdowmSide);
if(flag && err5 && Down_flag)
{
*state = 7;
}
break;
case 7:
ImageAddingLine(imageSide, 1, 80, 10, 0, START_H);
flag = RoadUpSide_Mono(50, OV7725_UART_W,UpdowmSide);
if(flag==0)
{
*state = 0;
finderr = 0;
err1 = 0;
}
有问题可以私聊我咯~
可以加企鹅群 658476482 交流
另外承接各种单片机设计~