福州大学第19届206智能车队摄像头培训,差比和如何操作?

摘要:原文于2023.10.25发布于本人CSDN主页,现同步至cnblogs 1	普通二值化的缺点 当场地光线不均时,基础的固定阈值二值化无法适应光线的变化。此外,对大量与道路无关的像素点进行二值化处理会造成算力的浪费。 2&a
原文于2023.10.25发布于本人CSDN主页,现同步至cnblogs 1 普通二值化的缺点 当场地光线不均时,基础的固定阈值二值化无法适应光线的变化。此外,对大量与道路无关的像素点进行二值化处理会造成算力的浪费。 2 使用差比和 对于MT9V03X拍摄的画面,其由0-255级灰度的像素点组成。二值化的思路是把浅色的前景部分变为255(白色),深色的背景部分变为0(黑色),所有255与0的分界线便是提取的道路边线。 那么使用差比和,则无需对图像进行变化,而是直接辨识浅色部分与深色部分的交界处。 关于差比和的原理,可参考视频: 18届智能车软件培训之差和比扫线 可以直接使用差比和,也可以先进行其他处理后再使用差比和。 参考代码如下: Main.c #include "zf_common_headfile.h" #include "camera.h" #include "screen.h" #include "track.h" #pragma section all "cpu0_dsram" #define IPS200_TYPE (IPS200_TYPE_SPI) int core0_main(void) { clock_init(); // 获取时钟频率<务必保留> debug_init(); // 初始化默认调试串口 //外设初始化代码 ips200_init(IPS200_TYPE); ips200_show_string(0, 0, "mt9v03x init."); while(1) { if(mt9v03x_init()) ips200_show_string(0, 80, "mt9v03x reinit."); else break; system_delay_ms(500); // 短延时快速闪灯表示异常 } ips200_show_string(0, 16, "init success."); //外设初始化代码 cpu_wait_event_ready(); // 等待所有核心初始化完毕 while (TRUE) { //循环执行的代码 if(mt9v03x_finish_flag) { image_boundary_process(); switch_trackline(); show_line(); ips200_show_uint(0, 200, leftline_num, 3); ips200_show_uint(0, 216, rightline_num, 3); ips200_displayimage03x((const uint8 *)mt9v03x_image, MT9V03X_W, MT9V03X_H); // 显示原始图像 //ips200_show_gray_image(0, 188, (const uint8 *)mt9v03x_image, MT9V03X_W, MT9V03X_H, 240, 180, 64); // 显示二值化图像 mt9v03x_finish_flag = 0; } //循环执行的代码 } } #pragma section all restore camera.c /* * camera.c * * Created on: 2023年10月24日 * Author: lychee */ #include "camera.h" int16 centerline[MT9V03X_H]; int16 leftline[MT9V03X_H]; int16 rightline[MT9V03X_H]; uint8 leftline_num;//左线点数量 uint8 rightline_num;//右线点数量 int16 sar_thre = 17;//差比和阈值 uint8 pix_per_meter = 20;//每米的像素数 //逐行寻找边界点 void image_boundary_process(void){ uint8 row;//行 //uint8 col = MT9V03X_W/2;//列 uint8 start_col = MT9V03X_W / 2;//各行起点的列坐标,默认为MT9V03X_W / 2 //清零之前的计数 leftline_num = 0; rightline_num = 0; for(row = MT9V03X_H - 1; row >= 1; row--){ //选用上一行的中点作为下一行计算起始点,节省速度,同时防止弯道的左右两边均出现与画面一侧 if(row != MT9V03X_H - 1){ start_col = (uint8)(0.4 * centerline[row] + 0.3 * start_col + 0.1 * MT9V03X_W);//一阶低通滤波,防止出现噪点影响下一行的起始点 } else if(row == MT9V03X_H - 1){ start_col = MT9V03X_W / 2; } //逐行作差比和 difsum_left(row,start_col); difsum_right(row,start_col); centerline[row] = 0.5 * (rightline[row] + leftline[row]); } } //差比和寻找左侧边界点 void difsum_left(uint8 y,uint8 x){ float sum,dif,sar;//和,差,比 uint8 col;//列 uint8 mov = 2;//每次作差后的移动量,默认为2,可以根据画面分辨率调整 //计算第x行的左边界 leftline[y] = 0;//未找到左边界时输出为0 for(col = x; col >= mov + 1; col -= mov){ dif = (float)((mt9v03x_image[y][col] - mt9v03x_image[y][col - mov - 1])<<8);//左移8位即乘256,可避免浮点数乘,加快速度 sum = (float)((mt9v03x_image[y][col] + mt9v03x_image[y][col - mov - 1])); sar = fabs(dif / sum);//求取差比和 if(sar > sar_thre){//差比和大于阈值代表深浅色突变 leftline[y] = (int16)(col - mov); leftline_num ++;//左线点计数+ break;//找到边界后退出 } } } //差比和寻找右侧边界点 void difsum_right(uint8 y,uint8 x){ float sum,dif,sar;//和,差,比 uint8 col;//列 uint8 mov = 2;//每次作差后的移动量,默认为2,可以根据画面分辨率调整 //计算第x行的左边界 rightline[y] = MT9V03X_W - 1;//未找到右边界时输出为187 for(col = x; col <= MT9V03X_W - mov - 1; col += mov){ dif = (float)((mt9v03x_image[y][col] - mt9v03x_image[y][col + mov + 1])<<8);//左移8位即乘256,可避免浮点数乘,加快速度 sum = (float)((mt9v03x_image[y][col] + mt9v03x_image[y][col + mov + 1])); sar = fabs(dif / sum);//求取差比和 if(sar > sar_thre){//差比和大于阈值代表深浅色突变 rightline[y] = (int16)(col + mov); rightline_num ++;//右线点计数+ break;//找到边界后退出 } } } camera.h /* * camera.h * * Created on: 2023年10月24日 * Author: lychee */ #ifndef CODE_CAMERA_H_ #define CODE_CAMERA_H_ #include "zf_common_headfile.h" #include "track.h" extern int16 centerline[MT9V03X_H]; extern int16 leftline[MT9V03X_H]; extern int16 rightline[MT9V03X_H]; extern uint8 leftline_num;//左线点数量 extern uint8 rightline_num;//右线点数量 extern int16 sar_thre;//差比和阈值 extern uint8 pix_per_meter;//每米的像素数 void image_boundary_process(void); void difsum_left(uint8 y,uint8 x); void difsum_right(uint8 y,uint8 x); #endif /* CODE_CAMERA_H_ */ track.c /* * track.c * Created on: 2023年10月24日 * Author: lychee */ #include "track.h" int16 trackline[MT9V03X_H];//跟踪线 enum track_type_e track_type = TRACK_MID;//默认循中线 //将边线赋值给循迹跟踪线 void switch_trackline(void){ if(track_type == TRACK_MID){ for(int i = 0; i < MT9V03X_H;i ++){ trackline[i] = centerline[i];//循中线 } } if(track_type == TRACK_LEFT){ for(int i = 0; i < MT9V03X_H;i ++){ trackline[i] = (leftline[i] + pix_per_meter) > 187 ? 187 : (int16)(leftline[i] + 0.2 * pix_per_meter);//循左线,左线应向右侧偏移道路宽度一半,即0.2米对应的像素数 } } if(track_type == TRACK_RIGHT){ for(int i = 0; i < MT9V03X_H;i ++){ trackline[i] = ((rightline[i]) - pix_per_meter) < 0 ? 0 : (int16)(rightline[i] - 0.2 * pix_per_meter);//循左线,右线应向左侧偏移 } } } //选择如何循线,大家可以自由发挥 void choose_tracktype(void){ //track_type = TRACK_LEFT; } track.h /* * track.h * * Created on: 2023年10月24日 * Author: lychee */ #ifndef CODE_TRACK_H_ #define CODE_TRACK_H_ #include "zf_common_headfile.h" #include "camera.h" extern int16 trackline[MT9V03X_H]; //定义巡线模式枚举类型 enum track_type_e { TRACK_LEFT,//沿左边线 TRACK_RIGHT,//沿右边线 TRACK_MID,//沿中线 }; //定义当前循线模式 extern enum track_type_e track_type; void switch_trackline(void); void choose_tracktype(void); #endif /* CODE_TRACK_H_ */ screen.c /* * screen.c * * Created on: 2023年10月24日 * Author: lychee */ #include "screen.h" void show_line(void){ for(uint16 i = 0; i < MT9V03X_H; i = i + 2){ ips200_draw_point((uint16)trackline[i], i, RGB565_BLACK);//黑色跟踪 } for(uint16 i = 0; i < MT9V03X_H; i ++){ ips200_draw_point((uint16)leftline[i], i, RGB565_RED);//红色左线 ips200_draw_point((uint16)rightline[i], i, RGB565_BLUE);//蓝色右线 ips200_draw_point((uint16)centerline[i], i, RGB565_PURPLE);//紫色中线 } } screen.h /* * screen.h * * Created on: 2023年10月24日 * Author: lychee */ #ifndef CODE_SCREEN_H_ #define CODE_SCREEN_H_ #include "zf_common_headfile.h" #include "camera.h" #include "track.h" void show_line(void); #endif /* CODE_SCREEN_H_ */ 测试效果如上图,可见在左右两侧亮度相差较大的情况下,仍可正常寻线。 3 差比和的优缺点 差和比的计算只与灰度值的对比度有关,无关灰度值本身的大小,在不同光线情况下的适应能力更强。计算至边界点时退出循环,可避免在背景像素中浪费时间进行计算。 差比和仍需对每个前景像素进行计算,仍会造成一定浪费,寻线策略有待进一步优化。