OTSU方法又叫做最大类间方差,自适应选择一个合理的阈值,根据此阈值将灰度图像转换为二值图像,例如在车牌识别过程中,将摄像头拍到的图像转化为一副灰度图像,在提取车牌的有效信息中就需要图像的二值化化。
基础知识:
对于随机变量X,它的方差为E(X-EX)2,EX为数学期望,相当于均值,进行类比。
OTSU算法的步骤:
1.选定一个阈值T,将[0,255]的256个灰度值分为A,B两部分,即为A,B两类。
2.用P(A)表示A部分像素点所占总像素的比例,依次计算出P(B)。
3.用u表示整个图像的灰度均值,用u(A)表示A部分的像素均值,同理计算出u(B).
4.类间方差为P(A)(u-u(A))(u-u(A))+P(B)(u-u(B))(u-u(B))。
5.阈值T在[0,255]遍历去寻找最大类间方差。
int Otsu(Mat img)
{ //灰度图像的channels为1
if (img.channels() != 1)
{
printf("请输入灰度图像");
return -1;
}
int T = 0;//算法的阈值
double MaxVariacne = 0;//
double w0 = 0;//前景像素点数所占的比例
double w1 = 0;//背景像素所占的比例
double u0 = 0;//前景的平均灰度
double u1 = 0;//背景的平均灰度
int Histogram[256] = { 0 };//256个发光等级
uchar* data = img.data;//得到矩阵的首地址
//得到像素的总和
int totalNum = img.cols*img.rows;
//统计每个灰度等级中像素的个数
for (int i = 0; i < img.rows; i++)
{
uchar* data = img.ptr<uchar>(i);//得到i行的首地址 得到的data是首地址
for (int j = 0; j < img.cols; j++)
{ //发光等级 data
Histogram[data[j]] += 1;
}
}
for (int i = 0; i<255; i++)
{
//每次遍历的之前初始化各类变量
w1 = 0; u1 = 0; w0 = 0; u0 = 0;
//背景各分量的计算 当灰度级别取2 为分割点的时候
for (int j = 0; j <= i; j++)
{
w1 = w1 + Histogram[j];//背景各部分像素点的总数
u1 = u1 + j*Histogram[j]; //计算出灰度数值之和
}
if (w1 == 0)
{
continue;
}
u1 = u1 / w1;//背景的平均灰度数值
w1 = w1 / totalNum;//背景部分像素点所占的比例
//上面即是背景各分量计算
//前景各部分之间的计算
for (int k = i + 1; k < 255; k++)
{
w0 = w0 + Histogram[k];//前景各部分像素点的总和 计算总灰度
u0 = u0 + k*Histogram[k];
}
//计算平局灰度
u0 = u0 / w0;
//计算像素所占的比例
w0 = w0 / totalNum;
if (w0 == 0)
{
continue;
}
double variance = w0*w1*(u1 - u0)*(u1 - u0);
if (MaxVariacne < variance)
{
MaxVariacne = variance;
T = i;
}
}
printf("经过计算最大的类方差为%lf\n", MaxVariacne);
return T;
}