图像二值化
24位真彩图(1像素3字节)进行二值化为单色图(1像素1位),需要进行如下处理:
- 灰度化处理
- 选取合适的阈值,二值化处理
灰度化处理算法
灰度图:每个像素占一个字节,即RGB转换为单色的[0 ~256]之间的灰度,常用的转换公式如下:
Gray = 0.299 * R + 0.587 * G + 0.114 * B;
大律法,取自适应阈值
大津法是一种图像灰度自适应的阈值分割算法。实现思路:
-
计算0~255各灰阶对应的像素个数,保存至一个数组中,该数组下标是灰度值,保存内容是当前灰度值对应像素数
-
计算背景图像的平均灰度、背景图像像素数所占比例
-
计算前景图像的平均灰度、前景图像像素数所占比例
-
遍历0~255各灰阶,计算并寻找类间方差极大值
什么是类间方差
- ω0:前景的像素点数占整幅图像的比例
- μ0:前景平均灰度
- ω1:背景像素点数占整幅图像的比例
- μ1:背景平均灰度
- μ:图像的总平均灰度
- g:类间方差
- N: 图像总像素数(灰度图的宽*高)
- N0:图像中像素的灰度值小于阈值T的像素个数
- N1:图像中像素的灰度值大于阈值T的像素个数
- T:阈值
则有如下公式:
ω0=N0/ N —-1
ω1=N1/ N —-2
N0+N1=N —-3
ω0+ω1=1 —-4
μ=ω0μ0+ω1μ1 —-5
g=ω0(μ0-μ)^2 + ω1(μ1-μ)^2 —-6
将式(5)代入式(6),得到等价公式:
g=ω0ω1(μ0-μ1)^2 —-7 这个就是类间方差的公式表述
采用遍历的方法得到使类间方差g最大的阈值T,即为所求。
C代码实现
/**
* 二值化算法
*参数:
width 像素宽度
height 像素高度
*data8 灰度图数据
*返回:二值图数据
*/
unsigned char* otsu(int width,int height,unsigned char *data8,int nThreshold)
{
int N = height*width; //总像素数
//计算自适应阈值
if(nThreshold<0 || nThreshold>255)
{
double nHistogram[256]; //灰度直方图
double dVariance[256]; //类间方差
for(int i=0; i<256; i++)
{
nHistogram[i] = 0.0;
dVariance[i] = 0.0;
}
//获取灰度图,每一个像素点数据,计算出灰度直方图
for(int i=0; i<N; i++)
{
unsigned char nData = data8[i];
nHistogram[nData]++; //建立直方图
}
double Pa=0.0; //背景出现概率
double Pb=0.0; //目标出现概率
double Wa=0.0; //背景平均灰度值
double Wb=0.0; //目标平均灰度值
double W0=0.0; //全局平均灰度值
double dData1=0.0, dData2=0.0;
for(int i=0; i<256; i++) //计算全局平均灰度
{
nHistogram[i] /= N;
W0 += i*nHistogram[i];
}
for(int i=0; i<256; i++) //对每个灰度值计算类间方差
{
Pa += nHistogram[i];
Pb = 1-Pa;
dData1 += i*nHistogram[i];
dData2 = W0-dData1;
Wa = dData1/Pa;
Wb = dData2/Pb;
dVariance[i] = (Pa*Pb* pow((Wb-Wa), 2));
}
//遍历每个方差,求取类间最大方差所对应的灰度值
double temp=0.0;
for(int i=0; i<256; i++)
{
if(dVariance[i]>temp)
{
temp = dVariance[i];
nThreshold = i;
}
}
}
printf("阈值为:%d\n",nThreshold);
int g_size = N/8;
unsigned char *data1 = (unsigned char *)malloc(g_size);
unsigned char piexBit = 0;
int index = 0;
for(int i=0; i<N; i=i+8)
{
for(int j=0; j<8; j++)
{
if(data8[i+j] < nThreshold)
{
piexBit |= (1<<(7-j));//将piexBit的第(7-j)位, 置1
}
else
{
piexBit &= ~(1<<(7-j)); //将piexBit的第(7-j)位,置0
}
data1[index++] = piexBit;
}
return data1;
}