智能车|智能车图像处理(一)阈值处理

博主使用的逐飞家的总钻风摄像头,这是一款灰度摄像头,配合逐飞的底层库,可以很快的上手。在我当时拿到总钻风的第一时间,就用逐飞的底层库,配合tft显示屏,显示简单的图像,虽然简单,当时的心情其实还是很激动的。
而这时,我们显示的,其实就是一张灰度图像。我的是120*188的灰度图像,例如:
智能车|智能车图像处理(一)阈值处理
文章图片
智能车|智能车图像处理(一)阈值处理
文章图片
智能车|智能车图像处理(一)阈值处理
文章图片

针对这些灰度图像,我们需要对它进行阈值处理以便之后的赛道信息提取。
首先,我们得知道,我们所得到了这120*188的图像,实际上也就是120*188个不同灰度值的像素点组成,对于这么多个像素点处理的最基本的思想,就是找到一个阈值,能够确保这个阈值可以把赛道和背景分离出来,而这,也就是所谓的图像二值化处理,它通过不同的算法得到图像的一个跳变阈值,将大于这个阈值的区域置为全白,小于它的阈值置为全黑。
下面给出几种得到阈值的算法思路和代码:
(一)迭代法求阈值
由于赛道的蓝色背景和白色赛道的灰度分布十分明显,我们可以用适用于整个图像的单个(全局)阈值,在大多数应用中,通常图像之间有较大变化,即使全局阈值是一种合适的方法 ,也需要有能对每幅图像自动估计阈值的算法。下面的迭代算法可用于这一目的:
基本思路:
1.为全局阈值T选择一个初始估计值。
2.用T分割该图像。这将产生两组像素: G1 由灰度值大于T的所有像素组成,G2由所有小于等于的像素组成。
3.对G和G2的像素分别计算平均灰度值(均值) m1和m2。
4.计算一个新的阈值: T=1/2(m1+m2)。
5.重复步骤2到步骤4,直到连续迭代中的T值间的差小于一个预定义的参数ΔT为止。

#define Thres128//阈值 #define ERROR2//误差int16EdgeThres = 18; //晚上20白天25 18 floatBlackThres = 58; //黑白阈值//迭代法计算阈值 void Iteration_Threshould(void) { uint16_t i = 0,j = 0,N0 = 0,N1 = 0,flag = 0; float T0,T1,T2,T_center; uint32_t S0 = 0,S1 = 0; T2 = BlackThres; do{ for(i=0; i ERROR){ flag = 1; } else{ flag = 0; } } else{ if((T2 - T_center) > ERROR){ flag = 1; } else{ flag = 0; } } T2 =T_center; BlackThres = T2; } while(flag); }

当与物体和背景相关的直方图模式间存在一个相当清晰的波谷时,这个简单的算法工作得很好。
在速度是一个重要因素的情形下, 参数ΔT用于控制这代的次数。通常,ΔT越大,则算法执行的迭代次数越少。所选的初始阈值必须大于图像中的最小灰度级而小于最大灰度级。图像的平均灰度对于T来说是较好的初始选择。
(二)基于Otus的最佳全局阈值处理
阈值处理可以视为一种统计决策理论问题, 其目的是在把像素分配给两个或多个组(也称为分类)的过程中使引人的平均误差最小。
Otus,也称大津法, 该方法在类间方差最大的情况下是最佳的。基本概念是,好阈值分类就其像素灰度值而论,应是截然不同的,反过来说,就其灰度值而言给出最好的类间分离的阈值就是最好的(最佳的)阈值。除了其最佳性之外,大津法还有一个重要的特性,即它完全以在一幅图像的直方图上执行计算为基础,直方图是很容易得到的一维阵列。
基本思路:
1.计算灰度级中每个像素在整幅图像中的个数
2.计算每个像素值的点在整幅图像中的概率
3.计算全局灰度均值
4.计算类间方差
5.得到Otus阈值
#define GrayScale 256int pixelCount[GrayScale]; float pixelPro[GrayScale]; uint8 my_adapt_threshold(uint8 *image, uint16 col, uint16 row) {uint16 width = col; uint16 height = row; int i, j, pixelSum = width * height/4; uint8* data = https://www.it610.com/article/image; //指向像素数据的指针 for (i = 0; i < GrayScale; i++) { pixelCount[i] = 0; pixelPro[i] = 0; }uint32 gray_sum=0; //统计灰度级中每个像素在整幅图像中的个数 for (i = 0; i < height; i+=2) { for (j = 0; j < width; j+=2) { pixelCount[(int)data[i * width + j]]++; //将当前的点的像素值作为计数数组的下标 gray_sum+=(int)data[i * width + j]; //灰度值总和 } }//计算每个像素值的点在整幅图像中的比例for (i = 0; i < GrayScale; i++) { pixelPro[i] = (float)pixelCount[i] / pixelSum; }//遍历灰度级[0,255] float w0, w1, u0tmp, u1tmp, u0, u1, u, deltaTmp, deltaMax = 0; w0 = w1 = u0tmp = u1tmp = u0 = u1 = u = deltaTmp = 0; for (j = 0; j < GrayScale; j++) {w0 += pixelPro[j]; //背景部分每个灰度值的像素点所占比例之和即背景部分的比例 u0tmp += j * pixelPro[j]; //背景部分每个灰度值的点的比例 *灰度值 w1=1-w0; u1tmp=gray_sum/pixelSum-u0tmp; u0 = u0tmp / w0; //背景平均灰度 u1 = u1tmp / w1; //前景平均灰度 u = u0tmp + u1tmp; //全局平均灰度 deltaTmp = w0 * pow((u0 - u), 2) + w1 * pow((u1 - u), 2); if (deltaTmp> deltaMax) { deltaMax = deltaTmp; threshold = j; } if (deltaTmp < deltaMax) { break; } } return threshold; }

通过以上两种算法可以得到全局阈值,接下来我们可以直接根据得到的阈值选择去二值化
void Binarization() { for(int i=0; i< MT9V03X_CSI_H; i++) {for(int j=0; jthreshold) { image_deal[i][j]=white; } else { image_deal[i][j]=black; } image_showlcd[i][j]=image_deal[i][j]; } } }

以上步骤,我们可以得到一帧稳定的二值化图像
智能车|智能车图像处理(一)阈值处理
文章图片
智能车|智能车图像处理(一)阈值处理
文章图片
智能车|智能车图像处理(一)阈值处理
文章图片

智能车|智能车图像处理(一)阈值处理
文章图片
智能车|智能车图像处理(一)阈值处理
文章图片


至此,针对一般的赛道场地,以上两种二值化的方法完全可以适用,但我们不排除会出现一些极端的情况,例如阳光的影响,万一一道上帝之光照射到赛道上,造成了赛道部分的灰度值发生畸变,这时候,基于全局的阈值算法似乎就显得有些无法应对,也可以采用逐飞的差比和灰度处理。但目前来看一般赛场不会出现特别极端的情况,以上两种算法其实完全够用
【智能车|智能车图像处理(一)阈值处理】

    推荐阅读