OTSU二值化方法是一种全局阈值分割方法,它基于类间方差最大原理。
C代码如下:
#include<stdlib.h>
bool tobinary(unsigned char *pImage, int width, int height, int biBitCount)
{
int i, j, k;
int h[256]={0};
unsigned int ip1, ip2, is1, is2=0; //存储前景和背景的灰度总和及像素个数
double w0, w1=0; //前景和背景像素的比例
double mean1, mean2=0; //前景和背景的平均灰度
double g[245]={0}; //存储类间方差
double gmax=0; //最大类间方差
unsigned char th=0; //二值化阈值
//定义变量,计算图像每行像素所占的字节数(必须是4的倍数)
int lineByte=(width * biBitCount/8+3)/4*4;
/********类间方差最大法求阈值**********/
for(i=0; i<height; i++) //灰度直方图
for(j=0; j<width; j++)
{
k = *(pImage+i*lineByte+j);
h[k]++;
}
for(i=10; i<245; i++) //阈值一般不会超出这个范围
{
ip1=0; ip2=0; is1=0; is2=0;
w0=0; w1=0;
for(j=0; j<i; j++)
{
ip1+=h[j]*j; is1+=h[j];
}
mean1=ip1/is1;
w0=double(is1)/double(width*height);
for (j=i; j<256; j++)
{
ip2+=h[j]*j; is2+=h[j];
}
mean2=ip2/is2;
w1=1-w0;
g[i]=w0*w1*(mean1-mean2)*(mean1-mean2);
}
gmax=g[10];
th=10;
for(i=11; i<245; i++)
if(g[i]>gmax)
{
gmax=g[i]; th=i;
}
/******* **二值化*********/
for(i=0; i<height; i++)
for(j=0; j<width; j++)
*(pImage+i*lineByte+j)=*(pImage+i*lineByte+j)>th?255:0;
return 1;
}
这个语句要注意 w0=double(is1)/double(width*height); 因为如果不加强制转换的话,右边一个小的整数除以一个大的整数结果为0。
程序中有这么一句 int lineByte=(width * biBitCount/8+3)/4*4; 这是因为Windows系统中一行像素所占的字节数为4的倍数,因此不是4的倍数时要补充为4的倍数,这样在寻址每一个像素的时候,像素的地址为 pImage+i*lineByte+j ;而循环控制语句for(j=0; j<width; j++)中仍用width,不用lineByte,因为补充的数据不是原图像数据,我们不需要处理。
该函数在Eclipse上调试通过。