整理智能车中使用到的摄像头图像处理算法

19年的智能车竞赛给我留下了很深远的影响,不管是算法思路还是解决问题的方法对我来说都受益匪浅。今天整理资料时又翻出这些代码,回忆起做车时不分日夜地在赛道上调试,回忆起尽管小车已经完整跑完赛道却依旧想着如何去优化得更快更好的自己。
现在把自己的整理分享给有用的人,希望能少走写弯路吧。
注意单片机性能有限,在执行一些算法时效率会很低。

S_CV.h

#ifndef _S_CV_H
#define _S_CV_H
#include "include.h"//图像大小定义
/*
#define S_img_H     120
#define S_img_W     188*/
#define S_img_H     60
#define S_img_W     94#define S_S_Custom    0xAAvoid S_BinaryImage(uint8_t in_IMG[S_img_H][S_img_W], uint8_t out_IMG[S_img_H][S_img_W], uint8_t ThresholdV);
void S_SE_Operation(uint8_t in_IMG[S_img_H][S_img_W], uint8_t out_IMG[S_img_H][S_img_W]);
void S_SE_OperationBIG(uint8_t in_IMG[120][188], uint8_t out_IMG[120][188]);/**********************************形态学处理**************************************/
//腐蚀
void S_Erode(uint8_t in_IMG[S_img_H][S_img_W], uint8_t out_IMG[S_img_H][S_img_W]);
//膨胀
void S_Dilate(uint8_t in_IMG[S_img_H][S_img_W], uint8_t out_IMG[S_img_H][S_img_W]);
//开操作
void S_Open(uint8_t in_IMG[S_img_H][S_img_W], uint8_t out_IMG[S_img_H][S_img_W]);
//关操作
void S_Close(uint8_t in_IMG[S_img_H][S_img_W], uint8_t out_IMG[S_img_H][S_img_W]);/**********************************图像显示**************************************/
#define S_MINI  0
#define S_BIG       1
//显示小图像到TFT模块
void S_Draw_TFT_MINI(uint8_t in_IMG[S_img_H][S_img_W]);
//60*94图像显示成188*120
void S_DisplayMINI_Big(uint8_t in_IMG[PixMini_H][PixMini_W]);
//显示大图像到TFT模块
void S_Draw_TFT_BIG(uint8_t in_IMG[IMAGEH][IMAGEW]);
//显示灰度图像60*90   可选大小
void S_Gray_DisplayMINI(uint8_t in_IMG[PixMini_H][PixMini_W], u8 size);
//显示灰度图像120*188
void S_Gray_Display(uint8_t in_IMG[IMAGEH][IMAGEW]);
//三线图显示
void S_DisplayRunWay(RunWay_* in_Array);
//三线图显示 大图
void S_DRW_Big(RunWay_* in_Array);/**********************************其他操作**************************************/
// 图像  抽取压缩188X120 to 94X60
void S_Image_zip(uint8_t in_IMG[IMAGEH][IMAGEW], uint8_t out_IMG[S_img_H][S_img_W]);
// 图像 填充放大 94X60  to  188X120
void S_Image_larger(uint8_t in_IMG[S_img_H][S_img_W], uint8_t out_IMG[IMAGEH][IMAGEW]);
//三线图检测
void DetectionLine(uint8_t in_IMG[PixMini_H][PixMini_W], RunWay_ * out_Array, u8 LMP, u8 Mod);
//三线图定制
void DetectionLine_Custom(uint8_t in_IMG[PixMini_H][PixMini_W], RunWay_* out_Array, u8 LMP);/*********************************求阈值***************************************/
//大津法求阈值
uint8_t S_GetOSTU(uint8_t tmImage[S_img_H][S_img_W]);       //修改
uint8 S_Other_OSTU(int width, int height, uint8* Image);
//均值比例求阈值
u8 S_Get_01_Value(uint8_t tmImage[S_img_H][S_img_W]);/*********************************滤波***************************************/
//加权滤波
void S_WeightedFiltering(uint8_t in_IMG[S_img_H][S_img_W], uint8_t out_IMG[S_img_H][S_img_W]);
//注入填充算法(Flood Fill Algorithm)
void S_FloodSeedFill(uint8_t in_IMG[S_img_H][S_img_W], u8 x, u8 y, u8 old_color, u8 new_color);
//修改注入填充算法      使用for循环  非递归  不溢出
void SX_FloodSeedFill(uint8_t in_IMG[40][63], u8 x, u8 y, u8 old_color, u8 new_color);
//在一幅图像中判定  横穿图像的白块         未实现
u8 S_Linear(u8 height, u8 width, uint8* in_IMG);/**********************************图像边缘检测算法**************************************/
//Robert算子
void S_Robert(uint8_t in_IMG[PixMini_H][PixMini_W], uint8_t out_IMG[PixMini_H][PixMini_W]);
//S_Sobel算子
void S_Sobel(uint8_t in_IMG[PixMini_H][PixMini_W], uint8_t out_IMG[PixMini_H][PixMini_W], u8 Threshold);
//sobel 定制
void S_Sobel_Custom(uint8_t in_IMG[PixMini_H][PixMini_W], uint8_t out_IMG[PixMini_H][PixMini_W], u8 Threshold);#endif

S_CV.c

//#include <cv.h>
//#include <highgui.h>
#include "S_CV.h"u8 temp_IMG[S_img_H][S_img_W];   //过渡图像/***************************************************************
*
* 函数名称:void S_BinaryImage(uint8_t tmImage[IMAGEH][IMAGEW], uint8_t ThresholdV)
* 功能说明:图像数据二值化
* 参数说明:tmImage 二值化数据存储、 ThresholdV 阈值
* 函数返回:void
* 修改时间:2019年4月6日
* 备 注:
***************************************************************/
void S_BinaryImage(uint8_t in_IMG[S_img_H][S_img_W], uint8_t out_IMG[S_img_H][S_img_W], uint8_t ThresholdV)
{int i = 0, j = 0;for (i = 0; i < S_img_H; i++)for (j = 0; j < S_img_W; j++){if (in_IMG[i][j] > ThresholdV)out_IMG[i][j] = 1;elseout_IMG[i][j] = 0;}
}//SE   操作
//          1
//      1   X   1
//          1
void S_SE_Operation(uint8_t in_IMG[S_img_H][S_img_W], uint8_t out_IMG[S_img_H][S_img_W])
{u8 i, j;//限制长宽避免溢出     使用图像小一圈u8 img_H = S_img_H - 1;u8 img_W = S_img_W - 1;u8 S_UP, S_DN, S_LL, S_RR;//输出数据初始化for (i = 0; i < S_img_H; i++)for (j = 0; j < S_img_W; j++)out_IMG[i][j] = 0;for (i = 1; i < img_H; i++){S_UP = i - 1;S_DN = i + 1;for (j = 1; j < img_W; j++){S_LL = j - 1;S_RR = j + 1;if (in_IMG[i][j]){out_IMG[i][j]++;out_IMG[S_UP][j]++;      //UPout_IMG[S_DN][j]++;       //DNout_IMG[i][S_LL]++;       //LLout_IMG[i][S_RR]++;       //RR}}}
}void S_SE_OperationBIG(uint8_t in_IMG[120][188], uint8_t out_IMG[120][188])
{u8 i, j;//限制长宽避免溢出     使用图像小一圈u8 img_H = 120 - 1;u8 img_W = 188 - 1;u8 S_UP, S_DN, S_LL, S_RR;//输出数据初始化for (i = 0; i < 120; i++)for (j = 0; j < 188; j++)out_IMG[i][j] = 0;for (i = 1; i < img_H; i++){S_UP = i - 1;S_DN = i + 1;for (j = 1; j < img_W; j++){S_LL = j - 1;S_RR = j + 1;if (in_IMG[i][j]){out_IMG[i][j]++;out_IMG[S_UP][j]++;      //UPout_IMG[S_DN][j]++;       //DNout_IMG[i][S_LL]++;       //LLout_IMG[i][S_RR]++;       //RR}}}
}//腐蚀
void S_Erode(uint8_t in_IMG[S_img_H][S_img_W], uint8_t out_IMG[S_img_H][S_img_W])
{S_SE_Operation(in_IMG, out_IMG);S_BinaryImage(out_IMG, out_IMG, 3);                //交集
}//膨胀
void S_Dilate(uint8_t in_IMG[S_img_H][S_img_W], uint8_t out_IMG[S_img_H][S_img_W])
{S_SE_Operation(in_IMG, out_IMG);S_BinaryImage(out_IMG, out_IMG, 0);                //并集
}//开操作
void S_Open(uint8_t in_IMG[S_img_H][S_img_W], uint8_t out_IMG[S_img_H][S_img_W])
{S_Erode(in_IMG, temp_IMG);             //腐蚀S_Dilate(temp_IMG, out_IMG);            //膨胀
}//关操作
void S_Close(uint8_t in_IMG[S_img_H][S_img_W], uint8_t out_IMG[S_img_H][S_img_W])
{S_Dilate(in_IMG, temp_IMG);                //膨胀S_Erode(temp_IMG, out_IMG);             //腐蚀
}//显示大图像到TFT模块
void S_Draw_TFT_BIG(uint8_t in_IMG[IMAGEH][IMAGEW])
{u8 i = 0, j = 0;//二值化图像显示TFTSPI_Set_Pos(0, 0, 160 - 1, 120 - 1);          //定位字符显示区域              for (i = 0; i < 120; i++){for (j = 0; j < 160; j++){if (in_IMG[i][j])TFTSPI_Write_Word(0xffff);elseTFTSPI_Write_Word(0x0000);}}
}//显示小图像到TFT模块
void S_Draw_TFT_MINI(uint8_t in_IMG[S_img_H][S_img_W])
{u8 i, j;//二值化图像显示TFTSPI_Set_Pos(0, 0, S_img_W - 1, S_img_H - 1);          //定位字符显示区域              for (i = 0; i < S_img_H; i++){for (j = 0; j < S_img_W; j++){if (in_IMG[i][j])TFTSPI_Write_Word(0xffff);elseTFTSPI_Write_Word(0x0000);}}
}// 图像 抽取压缩188X120 to 94X60
void S_Image_zip(uint8_t in_IMG[IMAGEH][IMAGEW], uint8_t out_IMG[S_img_H][S_img_W])
{u8 i, j;for (i = 0; i < S_img_H; i++)  //120行,每2行采集一行,for (j = 0; j < S_img_W; j++)  //188,out_IMG[i][j] = in_IMG[i * 2][j * 2];
}// 图像 填充放大 94X60  to  188X120
void S_Image_larger(uint8_t in_IMG[S_img_H][S_img_W], uint8_t out_IMG[IMAGEH][IMAGEW])
{u8 i, j, iq, jq;for (i = 0; i < S_img_H; i++){iq = i * 2;for (j = 0; j < S_img_W; j++){jq = j * 2;out_IMG[iq][jq] = out_IMG[iq + 1][jq + 1] =out_IMG[iq][jq + 1] = out_IMG[iq + 1][jq] =in_IMG[i][j];}}
}/***************************************************************
*
* 函数名称:uint8_t GetOSTU(uint8_t tmImage[S_img_H][S_img_W])
* 功能说明:求阈值大小
* 参数说明:
* 函数返回:阈值大小
* 修改时间:2018年3月27日
* 备 注:
参考:https://blog.csdn.net/zyzhangyue/article/details/45841255https://www.cnblogs.com/moon1992/p/5092726.htmlhttps://www.cnblogs.com/zhonghuasong/p/7250540.html
Ostu方法又名最大类间差方法,通过统计整个图像的直方图特性来实现全局阈值T的自动选取,其算法步骤为:
1) 先计算图像的直方图,即将图像所有的像素点按照0~255共256个bin,统计落在每个bin的像素点数量
2) 归一化直方图,也即将每个bin中像素点数量除以总的像素点
3) i表示分类的阈值,也即一个灰度级,从0开始迭代
4) 通过归一化的直方图,统计0~i 灰度级的像素(假设像素值在此范围的像素叫做前景像素) 所占整幅图像的比例w0,并统计前景像素的平均灰度u0;统计i~255灰度级的像素(假设像素值在此范围的像素叫做背景像素) 所占整幅图像的比例w1,并统计背景像素的平均灰度u1;
5) 计算前景像素和背景像素的方差 g = w0*w1*(u0-u1) (u0-u1)
6) i++;转到4),直到i为256时结束迭代
7)将最大g相应的i值作为图像的全局阈值
缺陷:OSTU算法在处理光照不均匀的图像的时候,效果会明显不好,因为利用的是全局像素信息。
***************************************************************/
uint8_t S_GetOSTU(uint8_t tmImage[S_img_H][S_img_W])
{int16_t i, j;uint32_t Amount = 0;uint32_t PixelBack = 0;uint32_t PixelIntegralBack = 0;uint32_t PixelIntegral = 0;int32_t PixelIntegralFore = 0;int32_t PixelFore = 0;double OmegaBack, OmegaFore, MicroBack, MicroFore, SigmaB, Sigma; // 类间方差; int16_t MinValue, MaxValue;uint8_t Threshold = 0;uint8_t HistoGram[256];              //  for (j = 0; j < 256; j++)  HistoGram[j] = 0; //初始化灰度直方图 for (j = 0; j < S_img_H; j++){for (i = 0; i < S_img_W; i++){HistoGram[tmImage[j][i]]++; //统计灰度级中每个像素在整幅图像中的个数}}for (MinValue = 0; MinValue < 256 && HistoGram[MinValue] == 0; MinValue++);        //获取最小灰度的值for (MaxValue = 255; MaxValue > MinValue && HistoGram[MinValue] == 0; MaxValue--); //获取最大灰度的值if (MaxValue == MinValue)     return MaxValue;         // 图像中只有一个颜色    if (MinValue + 1 == MaxValue)  return MinValue;        // 图像中只有二个颜色for (j = MinValue; j <= MaxValue; j++)    Amount += HistoGram[j];        //  像素总数PixelIntegral = 0;for (j = MinValue; j <= MaxValue; j++){PixelIntegral += HistoGram[j] * j;//灰度值总数}SigmaB = -1;for (j = MinValue; j < MaxValue; j++){PixelBack = PixelBack + HistoGram[j];    //前景像素点数PixelFore = Amount - PixelBack;         //背景像素点数OmegaBack = (double)PixelBack / Amount;//前景像素百分比OmegaFore = (double)PixelFore / Amount;//背景像素百分比PixelIntegralBack += HistoGram[j] * j;  //前景灰度值PixelIntegralFore = PixelIntegral - PixelIntegralBack;//背景灰度值MicroBack = (double)PixelIntegralBack / PixelBack;   //前景灰度百分比MicroFore = (double)PixelIntegralFore / PixelFore;   //背景灰度百分比Sigma = OmegaBack * OmegaFore * (MicroBack - MicroFore) * (MicroBack - MicroFore);//计算类间方差if (Sigma > SigmaB)                    //遍历最大的类间方差g //找出最大类间方差以及对应的阈值{SigmaB = Sigma;Threshold = j;}}return Threshold;                        //返回最佳阈值;
}//按照均值的比例进行二值化
u8 S_Get_01_Value(uint8_t tmImage[S_img_H][S_img_W])
{int i = 0, j = 0;u8 GaveValue;u32 tv = 0;//累加for (i = 0; i < S_img_H; i++){for (j = 0; j < S_img_W; j++){tv += tmImage[i][j];   //累加  }}GaveValue = tv / S_img_H / S_img_W;     //求平均值,光线越暗越小,全黑约35,对着屏幕约160,一般情况下大约100 //按照均值的比例进行二值化GaveValue = GaveValue * 8 / 10 + 10;        //此处阈值设置,根据环境的光线来设定 return GaveValue;
}void S_WeightedFiltering(uint8_t in_IMG[S_img_H][S_img_W], uint8_t out_IMG[S_img_H][S_img_W])
{s8 i, j;s8 UP, DN, L, R;for (i = 0; i < S_img_H; i++){//防溢出UP = i - 1;if (UP < 0)    UP = 0;DN = i + 1;if (DN > S_img_H)   DN = S_img_H;for (j = 0; j < S_img_W; j++){//防溢出L = j - 1;if (L < 0) UP = 0;R = j + 1;if (R > S_img_W) DN = S_img_W;if ((in_IMG[i][j]) && (in_IMG[i][L] + in_IMG[i][R] + in_IMG[UP][j] + in_IMG[DN][j] + in_IMG[UP][L] + in_IMG[UP][R] + in_IMG[DN][L] + in_IMG[DN][R] > 4))out_IMG[i][j] = 1;elseout_IMG[i][j] = 0;}}
}extern u8 SS_ZipF[40][63];                             //压缩图像数组        滤波使用
/*
---------------------
作者:吹泡泡的小猫
来源:CSDN
原文:https://blog.csdn.net/orbit/article/details/7323090
版权声明:本文为博主原创文章,转载请附上博文链接!*/
typedef struct tagDIRECTION
{int x_offset;int y_offset;
}DIRECTION;DIRECTION direction_8[] = { {-1, 0}, {-1, 1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}, {-1, -1} };
//DIRECTION direction_4[] = { {-1, 0}, {0, 1}, {1, 0}, {0, -1} };
DIRECTION direction_4[] = { {-1, 0}, {1, 0}, {0, 1}, {0, -1} };
#define directionXX direction_4
//注入填充算法(Flood Fill Algorithm)
void S_FloodSeedFill(uint8_t in_IMG[S_img_H][S_img_W], u8 x, u8 y, u8 old_color, u8 new_color)
{if (x > 58 || y > 92 || x < 1 || y < 1)        return;         //避免溢出if (in_IMG[x][y] == old_color){in_IMG[x][y] = new_color;//ImgZip_FLU[x][y] = 1;for (int i = 0; i < 4; i++)          // i <COUNT_OF(directionXX){S_FloodSeedFill(in_IMG, x + directionXX[i].x_offset,y + directionXX[i].y_offset, old_color, new_color);}}
}//不使用递归  不会溢出版本
void SX_FloodSeedFill(uint8_t in_IMG[40][63], u8 x, u8 y, u8 old_color, u8 new_color)
{u8 i, j, k;u8 return_sign;in_IMG[x][y] = 255;     //过渡颜色for (;;){return_sign = 0;for (i=0;i<40;i++)for (j = 0; j < 63; j++){if (in_IMG[i][j] == 255)       //过渡颜色{in_IMG[i][j] = new_color;       //实际赋值SS_ZipF[i][j] = 1;for (k = 0; k < 4; k++){x = i + directionXX[k].x_offset;       y = j + directionXX[k].y_offset;if (x < 40 && y < 63 && x >= 0 && y >= 0)       //避免溢出if (in_IMG[x][y] == old_color){in_IMG[x][y] = 255;     //过渡颜色return_sign = 1;}}}}if (return_sign == 0)      return;}
}//在一幅图像中判定  横穿图像的白块            未实现
u8 S_Linear(u8 height, u8 width, uint8* in_IMG)
{u8 i, j, m, n, up, dn;u8 getONE = 0;u8 LL_h, LL_w;u8 RR_h, RR_w;for (i=20;i< height-20;i++){j = 0;while (j<5)       //在5点内寻得白点{if (in_IMG[i * width + j++]){LL_h = i;       LL_w = j - 1;      //记录左点坐标up = i - 7;            dn = i + 7;for (m = up; m < dn; m++){n = width - 1;while (n > width + 5)       //在5点内寻得白点{if (in_IMG[m * width + n--]){RR_h = m;     RR_w = n + 1;     //记录右点坐标float kk, bb;kk = (float)(LL_h - RR_h) / (LL_w - RR_w);bb = (float)(LL_h - kk * LL_w);u8 ww, ww_COUNT = 0;for (ww = LL_w + 10; ww < RR_w - 10; ww += 10){if (!in_IMG[(u8)(kk * ww + bb) * width + ww])          //如果黑ww_COUNT = 1;}if (ww_COUNT == 0)return 1;break;}}}      break;}}}return 0;
}//参数解释:宽 高 图像指针 起始行  起始列 处理行大小  处理列大小
#define GrayScale 256   //frame灰度级
uint8 pixel[GrayScale];
uint8 S_Other_OSTU(int width, int height, uint8* Image)
{int threshold = 0;int32_t sum_gray = 0;int32_t sum_pix_num = 0;int32_t pl_pix_num = 0;int32_t p2_pix_mum = 0;int32_t p1_sum_gray = 0;float m1 = 0;float m2 = 0;float V = 0;float variance = 0;int i, j, k = 0;for (i = 0; i < 256; i++)pixel[i] = 0;//统计每个灰度级中像素的个数   for (i = 0; i < height; i++){for (j = 0; j < width; j++){pixel[(int)Image[i * width + j]]++;}}for (k = 0; k < GrayScale; k++){sum_gray += k * pixel[k];//灰度直方图质量矩sum_pix_num += pixel[k];//总像素个数}for (k = 0; k < GrayScale - 1; k++){pl_pix_num += pixel[k];//第一部分像素个数p2_pix_mum = sum_pix_num - pl_pix_num;//第二部分像素个数p1_sum_gray += k * pixel[k];   //第一部分质量矩m1 = (float)p1_sum_gray / pl_pix_num;//第一部分灰度均值m2 = (float)(sum_gray - p1_sum_gray) / p2_pix_mum;//第二部分灰度均值V = pl_pix_num * p2_pix_mum * (m1 - m2) * (m1 - m2);if (V > variance)//将类间方差较大时的灰度值作为阈值{variance = V;threshold = k;}}return threshold;
}/************************************Robert算子************************************
**          Gx={   {1,  0},                       Gy={    {  0,  1},
**                  {0,-1}}                             {-1,  0}}
**      最右、最底边缘舍去
**      G(x,y)=abs(f(x,y)-f(x+1,y+1))+abs(f(x,y+1)-f(x+1,y))
***********************************************************************************/
#define Robert_G(addr, y, x)    (abs(addr[y, x] - addr[DN, RR]) + abs(addr[y, RR] - addr[DN, x]))
void S_Robert(uint8_t in_IMG[PixMini_H][PixMini_W], uint8_t out_IMG[PixMini_H][PixMini_W])
{u8 i, j;u8 DN, RR;for (i = 0; i < 59; i++){DN = i + 1;for (j = 0; j < 93; j++){RR = j + 1;out_IMG[i][j] = Robert_G(in_IMG, i, j);}}
}/************************************Sobel算子************************************
**          Gx={   {-1,  0,  1},                   Gy={   {  1,  2,  1},
**                  {-2,  0,  2},                           {  0,  0,  0},
**                  {-1,  0,  1}}                           { -1, -2, -1}}
**      最上下左右边缘舍去
**
**      Gx = (-1)*f(x-1, y-1) + 0*f(x,y-1) + 1*f(x+1,y-1)
**            +(-2)*f(x-1,y) + 0*f(x,y)+2*f(x+1,y)
**            +(-1)*f(x-1,y+1) + 0*f(x,y+1) + 1*f(x+1,y+1)
**      = [f(x+1,y-1)+2*f(x+1,y)+f(x+1,y+1)]-[f(x-1,y-1)+2*f(x-1,y)+f(x-1,y+1)]
**
**      Gy =1* f(x-1, y-1) + 2*f(x,y-1)+ 1*f(x+1,y-1)
**            +0*f(x-1,y) 0*f(x,y) + 0*f(x+1,y)
**            +(-1)*f(x-1,y+1) + (-2)*f(x,y+1) + (-1)*f(x+1, y+1)
**      = [f(x-1,y-1) + 2f(x,y-1) + f(x+1,y-1)]-[f(x-1, y+1) + 2*f(x,y+1)+f(x+1,y+1)]
**      若Threshold=0则输出灰度图     其他则二值化
***********************************************************************************/
#define Sobel_Gx(addr, y, x)    (addr[UP][RR]+2*addr[y][RR]+addr[DN][RR]-(addr[UP][LL]+2*addr[y][LL]+addr[DN][LL]))
#define Sobel_Gy(addr, y, x)    (addr[UP][LL]+2*addr[UP][x]+addr[UP][RR]-(addr[DN][LL]+2*addr[DN][x]+addr[DN][RR]))
#define Sobel_G(addr, y, x)     (abs(Sobel_Gx(addr, y, x)) + abs(Sobel_Gy(addr, y, x)))
void S_Sobel(uint8_t in_IMG[PixMini_H][PixMini_W], uint8_t out_IMG[PixMini_H][PixMini_W],u8 Threshold)
{u8 i, j;u8 UP, DN, LL, RR;if (Threshold == 0){for (i = 1; i < 59; i++){DN = i + 1;       UP = i - 1;for (j = 1; j < 93; j++){RR = j + 1;        LL = j - 1;out_IMG[i][j] = Sobel_G(in_IMG, i, j);}}}else{for (i = 1; i < 59; i++){DN = i + 1;     UP = i - 1;for (j = 1; j < 93; j++){RR = j + 1;        LL = j - 1;out_IMG[i][j] = (Sobel_G(in_IMG, i, j) >= Threshold ? 1 : 0);}}}}
//sobel 定制
//在已有二值化的图像中标记边缘
void S_Sobel_Custom(uint8_t in_IMG[PixMini_H][PixMini_W], uint8_t out_IMG[PixMini_H][PixMini_W], u8 Threshold)
{u8 i, j;u8 UP, DN, LL, RR;u16 TempVal;for (i = 1; i < 58; i++)       //HIGH  只计算前50行{DN = i + 1;       UP = i - 1;for (j = 1; j < 93; j++)  //WIDE{RR = j + 1;        LL = j - 1;TempVal = Sobel_G(in_IMG, i, j);if (TempVal >= Threshold){out_IMG[i][j] = S_S_Custom;}}}}/******************************60*94图像显示成188*120***********************************
**
*******************************************************************************************/
void S_DisplayMINI_Big(uint8_t in_IMG[PixMini_H][PixMini_W])
{u8 i, j, k;u16 color;   TFTSPI_Set_Pos(0, 0, 188 - 1, 120 - 1);          //定位字符显示区域     for (i = 0; i < PixMini_H; i++){for (k = 0; k < 2; k++)for (j = 0; j < PixMini_W; j++){if (in_IMG[i][j] == 1)color = u16WHITE;else if (in_IMG[i][j] == S_S_Custom)color = u16PURPLE;elsecolor = u16BLACK;TFTSPI_Write_Word(color);TFTSPI_Write_Word(color);}}
}/******************************60*94图像显示灰度图***********************************
**      size==S_BIG       显示大图                                                                                            **
**      size==S_MINI  显示小图                                                                                            **
***************************************************************************************/
void S_Gray_DisplayMINI(uint8_t in_IMG[PixMini_H][PixMini_W], u8 size)
{u8 i, j, k;u16 color, temp;if (size== S_MINI)            //显示小图{TFTSPI_Set_Pos(0, 0, 94 - 1, 60 - 1);          //定位字符显示区域              for (i = 0; i < 60; i++){for (j = 0; j < 94; j++){temp = in_IMG[i][j];color = (0x001f & ((temp) >> 3)) << 11;color = color | (((0x003f) & ((temp) >> 2)) << 5);color = color | (0x001f & ((temp) >> 3));TFTSPI_Write_Word(color);}}}else                                //显示大图{TFTSPI_Set_Pos(0, 0, 188 - 1, 120 - 1);          //定位字符显示区域              for (i = 0; i < 60; i++){for (k = 0; k < 2; k++)for (j = 0; j < 94; j++){temp = in_IMG[i][j];color = (0x001f & ((temp) >> 3)) << 11;color = color | (((0x003f) & ((temp) >> 2)) << 5);color = color | (0x001f & ((temp) >> 3));TFTSPI_Write_Word(color);                TFTSPI_Write_Word(color);}}}
}/******************************120*188图像显示灰度图************************************
**                                                                                  **
**************************************************************************************/
void S_Gray_Display(uint8_t in_IMG[IMAGEH][IMAGEW])
{u8 i, j;u16 color, temp;TFTSPI_Set_Pos(0, 0, 188 - 1, 120 - 1);          //定位字符显示区域              for (i = 0; i < 120; i++){for (j = 0; j < 188; j++){temp = in_IMG[i][j];color = (0x001f & ((temp) >> 3)) << 11;color = color | (((0x003f) & ((temp) >> 2)) << 5);color = color | (0x001f & ((temp) >> 3));TFTSPI_Write_Word(color);}}
}/*********************************三线图检测识别**************************************
**                              Mod=0  寻二值化    Mod=2  寻sobel                                                      **
***************************************************************************************/
void DetectionLine( uint8_t in_IMG[PixMini_H][PixMini_W],       //输入图像RunWay_* out_Array,           //输出数组u8 LMP,                           //输入中点u8 Mod)                           //检测模式
{u8 i, j;u8 M_Point, L_Point, R_Point;u8 get_Point;M_Point = LMP;                         //取输入的底部中点for (i = 58; i > 0; i--)                   //行数{/**///寻左点j = M_Point + 5;if (j < 99 && j>92)       j = 92;while (!((!in_IMG[i][j + Mod]) && in_IMG[i][j + 1]) && (j > 1)) { j--; }L_Point = j;//寻右点j = M_Point - 5;if (j > 99)                      j = 1;while (!((!in_IMG[i][j - Mod]) && in_IMG[i][j - 1]) && (j < 92)) { j++; }R_Point = j;/*//寻左点get_Point = 0;for (j = M_Point + 5; j > 0; j--){//防溢出//if (j < 0)         j = 0;//if (j > 187)        j = 187;if (in_IMG[i][j]== Mod){L_Point = j;get_Point = 1;     //取得点break;}}if (get_Point==0)                    //取最左边的黑点{L_Point = 0;}//寻右点get_Point = 0;for (j = M_Point - 5; j < 59; j++){//防溢出//if (j < 0)        j = 0;//if (j > 187)       j = 187;if (in_IMG[i][j] == Mod){R_Point = j;get_Point = 1;        //取得点break;}}if (get_Point == 0)                      //取最右边的黑点{R_Point = 93;}
*/M_Point = (L_Point + R_Point) / 2;                         //取上一行中点out_Array->M[i] = M_Point;out_Array->L[i] = L_Point;out_Array->R[i] = R_Point;/**///归线if (i <= 55){//断开丢线xu8 fg_no_c = 0;if (i <= 30)if (abs(M_Point - out_Array->M[i + 1]) >= 5)           //连续两行中点相差5点以上{if (M_Point >= 50)fg_no_c = 1;else if (M_Point <= 45)fg_no_c = 2;}if ((M_Point < 25) || (fg_no_c == 2)){for (; i > 0; i--){out_Array->M[i] = 3;out_Array->L[i] = 3;out_Array->R[i] = 3;}break;}else if ((M_Point > 69) || (fg_no_c == 1)){for (; i > 0; i--){out_Array->M[i] = 90;out_Array->L[i] = 90;out_Array->R[i] = 90;}break;}}}//TFTSPI_P6X8Int(20, 5, L_Point, u16WHITE, u16BLACK);//TFTSPI_P6X8Int(20, 6, R_Point, u16WHITE, u16BLACK);//S_DisplayRunWay(out_Array);
}/******************************三线图检测识别结合sobel定制********************************
**                                                                                  **
***************************************************************************************/
void DetectionLine_Custom(  uint8_t in_IMG[PixMini_H][PixMini_W],       //输入图像RunWay_* out_Array,           //输出数组u8 LMP)                           //输入中点
{u8 i, j;u8 M_Point, L_Point, R_Point;u8 get_Point;M_Point = LMP;                         //取输入的底部中点for (i = 57; i > 0; i--)                   //行数{/**///寻左点j = M_Point + 2;if (j < 99 && j>90)       j = 90;        //图像右侧2点像素不可用while (!(in_IMG[i][j] == S_S_Custom) && (j > 1)) { j--; }L_Point = j;//寻右点j = M_Point - 2;if (j > 99)                        j = 1;while (!(in_IMG[i][j] == S_S_Custom) && (j < 92)) { j++; }R_Point = j;/*//寻左点get_Point = 0;for (j = M_Point + 5; j > 0; j--){//防溢出//if (j < 0)         j = 0;//if (j > 187)        j = 187;if (in_IMG[i][j]== Mod){L_Point = j;get_Point = 1;      //取得点break;}}if (get_Point==0)                    //取最左边的黑点{L_Point = 0;}//寻右点get_Point = 0;for (j = M_Point - 5; j < 59; j++){//防溢出//if (j < 0)        j = 0;//if (j > 187)       j = 187;if (in_IMG[i][j] == Mod){R_Point = j;get_Point = 1;        //取得点break;}}if (get_Point == 0)                      //取最右边的黑点{R_Point = 93;}
*/M_Point = (L_Point + R_Point) / 2;                         //取上一行中点out_Array->M[i] = M_Point;out_Array->L[i] = L_Point;out_Array->R[i] = R_Point;/**///归线if (i <= 55){//断开丢线xu8 fg_no_c = 0;if (i <= 30)if (abs(M_Point - out_Array->M[i + 1]) >= 5)           //连续两行中点相差5点以上{if (M_Point >= 50)fg_no_c = 1;else if (M_Point <= 45)fg_no_c = 2;}if ((M_Point < 25) || (fg_no_c == 2)){for (; i > 0; i--){out_Array->M[i] = 3;out_Array->L[i] = 3;out_Array->R[i] = 3;}break;}else if ((M_Point > 69) || (fg_no_c == 1)){for (; i > 0; i--){out_Array->M[i] = 90;out_Array->L[i] = 90;out_Array->R[i] = 90;}break;}}}//TFTSPI_P6X8Int(20, 5, L_Point, u16WHITE, u16BLACK);//TFTSPI_P6X8Int(20, 6, R_Point, u16WHITE, u16BLACK);//S_DisplayRunWay(out_Array);
}RunWay_ TempArray;
void S_DisplayRunWay(RunWay_* in_Array)
{u8 i, j;for (i = 0; i < 60; i++){if (i % 10 == 0)      //画线标记  刻度{TFTSPI_Draw_Dot(0, i, u16RED);TFTSPI_Draw_Dot(1, i, u16RED);TFTSPI_Draw_Dot(2, i, u16RED);}//抹上次点TFTSPI_Draw_Dot(TempArray.M[i], i, 0x0000);TFTSPI_Draw_Dot(TempArray.L[i], i, 0x0000);TFTSPI_Draw_Dot(TempArray.R[i], i, 0x0000);//记录旧点TempArray.L[i] = in_Array->L[i];TempArray.M[i] = in_Array->M[i];TempArray.R[i] = in_Array->R[i];//显示新点TFTSPI_Draw_Dot(in_Array->M[i], i, u16YELLOW);TFTSPI_Draw_Dot(in_Array->L[i], i, u16YELLOW);TFTSPI_Draw_Dot(in_Array->R[i], i, u16YELLOW);}
}void S_DRW_Big(RunWay_* in_Array)
{u8 i, j, k, kk;for (i = 0; i < 60; i++){for (k = 0; k < 2; k++){kk = 2 * i + k;if (kk % 10 == 0)       //画线标记  刻度{TFTSPI_Draw_Dot(0, kk, u16RED);TFTSPI_Draw_Dot(1, kk, u16RED);TFTSPI_Draw_Dot(2, kk, u16RED);}//抹上次点TFTSPI_Draw_Dot(TempArray.M[i] * 2, kk, 0x0000);TFTSPI_Draw_Dot(TempArray.L[i] * 2, kk, 0x0000);TFTSPI_Draw_Dot(TempArray.R[i] * 2, kk, 0x0000);//显示新点TFTSPI_Draw_Dot(in_Array->M[i] * 2, kk, u16CYAN);TFTSPI_Draw_Dot(in_Array->L[i] * 2, kk, u16ORANGE);TFTSPI_Draw_Dot(in_Array->R[i] * 2, kk, u16GREEN);}//记录旧点TempArray.L[i] = in_Array->L[i];TempArray.M[i] = in_Array->M[i];TempArray.R[i] = in_Array->R[i];}
}

整理智能车中使用到的摄像头图像处理算法相关推荐

  1. 时雨月五| AI机器学习实战の电磁导航智能车中神经网络应用的问题与思考

    "不愤不启,不悱不发.举一隅不以三隅反,则不复也". – <论语·述而> 再次将论语中的这句"不愤不启,不悱不发"引用在这里,说明学生的学习的活动部 ...

  2. PID控制介绍以及在智能车中的应用

    第一篇博客--PID控制 一. PID介绍 二.PID在智能车中的应用 假期留在学校进行电赛培训,顺便跟着同学搞了几个项目.因为电赛和项目都需要用到PID技术,对PID有了一些了解,半分享半记录于此. ...

  3. 大津法优化之在飞卡智能车中的应用

    此优化算法是本人前两年参加智能车比赛,在总钻风摄像头例程基础上优化的(分辨率为188*70,工程文件:含优化算法的小车程序).移植方便,亲测只需2ms(逐飞给的例程是很常规的,网上都能找到,需要40m ...

  4. 【智能车】上海交通大学AuTop战队开源算法提纲备忘

    写在前面 本文是作者在学习上海交通大学AuTop战队开源算法时列的提纲备忘,并做了很多资料的链接,像是一个目录,分享给大家一起学习, 如有侵权,联系删除: 参考:https://github.com/ ...

  5. 第十六届智能车竞赛全国总决赛究竟该怎么举办讨论中的“混沌”现象

    简 介: 汇总有对全国总决赛比赛意见推文之后,将在推文"三思而后行"之后的留言进行汇总分析.可以看到现在正值参赛同学等待总决赛名单公布百无聊赖之际,大家在利益攸关.意见远离平衡态下 ...

  6. 【智能车学习】电磁循迹中的基本控制算法

    文章目录 前言 采样信号滤波 什么是滤波 常见的软件滤波办法 限幅法 算术平均值滤波 归一化 什么是归一化 归一化的代码实现 PID控制 什么是PID控制 PID算法的代码实现 舵机控制 电机控制 目 ...

  7. 第十六届全国大学生智能车竞赛技术报告 | 单车拉力组- 上海海事大学-骑摩托的蒙娜丽莎

    简 介: 本文设计的智能车系统以 STC16F40K128微控制器为核心控制单元,通过车体前方的电感检测赛道电磁信息,通过编码器检测智能车的实时速度,利用陀螺仪检测小车姿态,使用PID 控制算法调节电 ...

  8. 智能车竞赛技术报告 | 智能车视觉 - 上海大学 - 猫耳麻花

    简 介: 本文设计的智能车系统以NXP RT1064微控制器为核心控制单元,通过车体前方的摄像头检测赛道信息:通过齿轮编码器检测模型车的实时速度,使用PID 控制算法调节驱动左右电机的转速,PD算法控 ...

  9. 智能车竞赛技术报告 | 智能车视觉 - 太原工业学院 - 晋速-轩辕星

    简 介: 本文详细介绍了太原工业学院"晋速-轩辕星"在第十六届全国大学生智能汽车竞赛智能视觉组的系统方案.本次比赛采用大赛组委会统一指定的新 C 型车模,以NXP公司生产的RT10 ...

最新文章

  1. 独家 | ​采用BERT的无监督NER(附代码)
  2. CSS3--选择器、动画效果
  3. 解决go包管理代理网址无法访问:proxy.golang.org 换成goproxy.cn
  4. NHibernate 学习总结 开篇
  5. 魔兽世界转服务器显示完成,魔兽世界怀旧服:TAQ变简单了,为什么还是出现了AFK大潮?...
  6. node.js 爬虫入门总结
  7. Ubuntu 14.04 或者16.04开启root账户登录和图形界面登录root时候的报错解决方法
  8. MongoDB索引类型
  9. WinForm 中自定义文件与自己的应用程序相关联
  10. 注入器 过检测_福特全顺V348检测车报价
  11. ubuntu下python3及idle3的安装
  12. 条码扫描二维码扫描——ZXing android 源码简化
  13. 怎样批量修改图片格式
  14. 个人中心网页设计html,超全面!个人中心页面从思考到设计全过程
  15. 安利一个小众又逆天的副业,轻松月入过万
  16. phpredis中文手册(使用方法)——《redis中文手册》 php版
  17. Java学生签到考勤请假系统源码
  18. 老大告诉我不要用字符串存IP地址,不兴~
  19. 恐龙快打无限子弹修改方案
  20. RabbitMQ(六)——持久化和权重分配消息

热门文章

  1. 令克软件与纳斯达克交易所达成战略合作,携手助力金融IT发展
  2. 帝国cms后台登陆显示Cann‘t connect to DB 解决方法
  3. 显示器可以远程管理Linux吗,Ubuntu无显示器情况下的远程控制的方法及命令
  4. 动手学深度学习(十一) NLP循环神经网络
  5. 十进制整数转为十六进制整数(C++实现)
  6. 国产手机高价就提升档次了?超越苹果是痴人说梦
  7. hive -- stddev , stddev_pop , stddev_samp , var_pop , var_samp(计算方差标准差等)
  8. move_base/Control loop missed its desired rate报错
  9. 骑行适合戴什么耳机?开放双耳的骨传导耳机更安全
  10. 哪些企业需要云服务器?这6类更需要使用云服务器!