前言:我们知道,当我们在进行赛道循迹时,需要把摄像头得到的灰度图像进行二值化处理。这个二值化处理当中要用到阈值来进行黑白分割。但是如果只是固定的一个阈值没办法适应不同环境光线的变化,所以我们要用到大津法来计算一个随环境变化的阈值。
一、大津法的理论公式及C语言代码实现
大津法本质上就是一个通过求解一个方差值来对当前阈值选择的好坏进行一个判断。当通过遍历求得一个阈值对应最大方差时,这个阈值就是当前最佳阈值。
首先我们知道摄像头得到的灰度图是存储在一个数组里的,所以为了能够计算方差,我们要先将数组变换为一个直方图。具体方法下面代码演示。
uint8_t HistoGram[256];//要在别的函数中使用直方图,就要在头文件里面声明为全局变量
void Histogram()//将灰度图变成灰度直方图
{
for (int i = 0; i < 256; i++)
{
HistoGram[i] = 0;
}//需要注意的是,在C语言中,未初始化的全局变量的数组默认值为0,不需要手动初始化为0,但是局部变量要
//所以此处的初始化可以根据实际传入的histogram数组类型进行取舍
for (unsigned int i = 0; i < MT9V03X_H; i++)
{
for (unsigned int j = 0; j <MT9V03X_W; j++)
{
HistoGram[mt9v03x_image[i][j]]++;//数据分布直方图代表的是0到255的灰度中每个灰度出现几次
}
}
}
在将数组变成直方图之后就可以进行大津法的计算了。网上这类理论介绍的很多,我这边贴出来给大家,我们重点还是放在代码的讲解上。
代码原理讲解:https://blog.csdn.net/as480133937/article/details/120572867
uint8_t OTSU() //大津法 反馈一个动态的最优的阈值
{
uint8_t 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; //类间方差
uint8_t MinValue, MaxValue;
uint8_t Threshold = 0; //最终得出的阈值
Histogram();//将灰度图变成灰度直方图
for (MinValue = 0; MinValue < 256 && HistoGram[MinValue] == 0; MinValue++) ; //获取最小灰度的值
for (MaxValue = 255; MaxValue > MinValue && HistoGram[MaxValue] == 0; MaxValue--) ; //获取最大灰度的值
if (MaxValue == MinValue)
return MaxValue; // 图像中只有一个颜色
if (MinValue + 1 == MaxValue)
return MinValue; // 图像中只有二个颜色
for (j = MinValue; j <= MaxValue; j++)
Amount += HistoGram[j]; //Amount应该为总像素数120*188
PixelIntegral = 0;
for (j = MinValue; j <= MaxValue; j++)
{
PixelIntegral += HistoGram[j] * j; //灰度值与其对应的数目的乘积 应该是用来计算整体灰度平均值 用来算方差的
}
SigmaB = -1; //
for (j = MinValue; j < MaxValue; j++) //这里的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; //返回最佳阈值;
}
在运用的时候只需要调用OTSU();即可返回当前最佳阈值。
然后将其放在二值化的当中,将二值化的阈值改为OTSU()的返回值就搞定。