OTSU,最大类间方差法 。是一种动态二值化方法。其基本思想是:假设一个阈值,将灰度图像的灰度统图分成两部分(前景部分和背景部分,也成为亮区和暗区),在这两类的类间方差最大的时候,得到的阈值是最优的二值化阈值。
性能:
1)类间方差法对噪音十分敏感,在处理之前应对图片进行去噪处理。
2)当目标与背景的大小比例悬殊的时候,类间方差函数可能呈现双峰或者多峰,这个时候 效果不好。慎用!
处理步骤:
1.对图片进行灰度化处理,并且计算其灰度值统计表N(假设图像大小为P×Q);
2.计算前景像素点数(灰度大于假设阈值T)占整个图像的比例,记做W0
N(i)表示统计图第i列的数值;
计算前景像素点的平均灰度值U0
3.同理求出背景的W1和U1(累加的时候从T加到灰度的最大值)
4.求整个图像的平均灰度值:U=W0×U0 + W1×U1
5.从L个灰度级遍历T,使得T为某个值的时候,前景和背景的方差最大, 则 这个 T 值便是我们要求得的阈值。其中,方差的计算公式如下:
G=W0 × (U0 - U) × (U0 - U) + w1 × (U1 - U) × (U1 - U)
此公式计算量较大,可以采用:
G= W0 × W1 × (U0 - U1) × (Uo - U1)
上代码:
//设t为设定的阈值。
//wo: 分开后 前景像素点数占图像的比例
//uo: 分开后 前景像素点的平均灰度
//w1:分开后 被景像素点数占图像的比例
//u1: 分开后 被景像素点的平均灰度
//u=w0*u0 + w1*u1 :图像总平均灰度
//
//从L个灰度级遍历t,使得t为某个值的时候,前景和背景的方差最大, 则 这个 t 值便是我们要求得的阈值。
//其中,方差的计算公式如下:
//g=wo * (uo - u) * (uo - u) + w1 * (u1 - u) * (u1 - u)
//[此公式计算量较大,可以采用:g = wo * w1 * (uo - u1) * (uo - u1)
int OTSU_THRESHVALU(IplImage *image)
{
//函数处理的图像需要是灰度图(通道数为1)
//灰度图像
IplImage *gray_image = cvCreateImage(cvGetSize(image),image->depth,1);
//判断图像是否为灰度图
if (image->nChannels == 3)
{
cvCvtColor(image, gray_image, CV_RGB2GRAY);
}
else
{
cvCopy(image, gray_image);
}
//直方图均衡化
cvEqualizeHist(gray_image, gray_image);
//存放灰度统计数组
int bin[255];
//初始化
memset(bin,0,sizeof(bin));
CvScalar scalar_Valu;
//灰度跨度的最大值和最小值
int max_num = 0;
int min_num = 255;
//计算图像的灰度值直方图
for (int i = 0; i < gray_image->height; i++)
{
for (int j = 0; j < gray_image->width; j++)
{
scalar_Valu = cvGet2D(gray_image, i, j);
int num = scalar_Valu.val[0];
bin[num]++;
//计算图像的灰度跨度
if (num > max_num) max_num = num;
if (num < min_num) min_num = num;
}
}
//最大的G;最合适的阈值
double max_G = 0.0;
int thresh_num = 0;
//计算w1,u1,w2,u2
for (int i = min_num; i <= max_num; i++)
{
//灰度值大于I的
long sum_up_bin = 0;
long sum_up_num = 0;
for (int j = i; j <= max_num; j++)
{
sum_up_bin += bin[j];
sum_up_num += (j * bin[j] * 1.0);
}
double u1 = sum_up_num / (sum_up_bin * 1.0);
double w1 = (sum_up_bin * 1.0) / (gray_image->width * gray_image->height * 1.0);
//计算灰度小于I的部分
long sum_down_bin = 0;
long sum_down_num = 0;
for (int j = min_num; j < i; j++)
{
sum_down_bin += bin[j];
sum_down_num += (j * bin[j] * 1.0);
}
double u2 = sum_down_num / (sum_down_bin * 1.0);
double w2 = (sum_down_bin * 1.0) / (gray_image->width * gray_image->height);
//计算G值
double u = (w1 * u1 + w2 * u2);
double G = w1 * (u1 - u) * (u1 - u) + w2 * (u2 - u) * (u2 - u);
//判断G值的大小,并计算出最佳阈值
if (G > max_G)
{
max_G = G;
thresh_num = i;
}
}
return thresh_num;
}