阈值T:按照我的理解就是一般我们用摄像头拍摄照片或者视频我们通常把拍摄的照片或者视
频转化为RGB565图像,然后通过RGB的转化算出像素通过设置一个阈值像素,大于
这个值我们把它设置为白色,小于这个值得我们把它设置为黑色。这个就是图像二值
化。
图像二值化的原理
图像的二值化处理就是将图像上的点的灰度置为0或255,也就是将整个图像
呈现出明显的黑白效果。即将256个亮度等级的灰度图像通过适当的阈值选取而获
得仍然可以反映图像整体和局部特征的二值化图像。在数字图像处理中,二值图像
占有非常重要的地位,特别是在实用的图像处理中,以二值图像处理实现而构成的
系统是很多的,要进行二值图像的处理与分析,首先要把灰度图像二值化,得到二
值化图像,这样子有利于在对图像做进一步处理时,图像的集合性质只与像素值为
0或255的点的位置有关,不再涉及像素的多级值,使处理变得简单,而且数据的
处理和压缩量小。为了得到理想的二值图像,一般采用封闭、连通的边界定义不交
叠的区域。所有灰度大于或等于阈值的像素被判定为属于特定物体,其灰度值为255
表示,则这些像素点被排除在物体区域以外,灰度值为0,表示背景或者例外的物体
区域。如果某特定物体在内部有均匀一致的灰度值,并且其处在一个具有其他等级灰
度值的均匀背景下,使用阈值法就可以得到比较的分割效果。如果物体同背景的差别
表现不在灰度值上(比如纹理不同),以将这个差别特征转换为灰度的差别,然后利用
阈值选取技术来分割该图像。动态调节阈值实现图像的二值化可动态观察其分割图像
的具体结果。
图像二值化的方法
(1)全局二值化
一幅图像包括目标物体、背景还有噪声,要想从多值的数字图像中直接提取出目标物体,
最常用的方法就是设定一个全局的阈值T,用T将图像的数据分成两部
分:大于T的像素群和小于T的像素群。将大于T的像素群的像素值设定为白色(或者
黑色),小于T的像素群的像素值设定为黑色(或者白色)。
(2)局部二值化
全局二值化,在表现图像细节方面存在很大缺陷。为了弥补这个缺陷,出现了局部
二值化方法。
局部二值化的方法就是按照一定的规则将整幅图像划分为N个窗口,对这N个窗口中
的每一个窗口再按照一个统一的阈值T将该窗口内的像素划分为两部分,进行二值
化处理。
(3)局部自适应二值化
局部二值化也有一个缺陷。这个缺陷存在于那个统一阈值的选定。这个阈值是没有
经过合理的运算得来,一般是取该窗口的平局值。这就导致在每一个窗口内仍然出
现的是全局二值化的缺陷。为了解决这个问题,就出现了局部自适应二值化方法。
局部自适应二值化,该方法就是在局部二值化的基础之上,将阈值的设定更加合理
化。该方法的阈值是通过对该窗口像素的平均值E,像素之间的差平方P,像素之间
的均方根值Q等各种局部特征,设定一个参数方程进行阈值的计算,例如:T=a*E
+b*P+c*Q,其中a,b,c是自由参数。这样得出来的二值化图像就更能表现出二值化
图像中的细节。
此篇介绍两种全局阈值方法,下篇介绍自适应方法。
1:经典算法OTSU
OTSU的中心思想是阈值T应使目标与背景两类的类间方差最大。
//用类间方差最大思想计算阈值
int Threshold(int *hist) //compute the threshold
{
float u0, u1;
float w0, w1;
int count0;
int t, maxT;
float devi, maxDevi = 0; //方差及最大方差
int i;
int sum = 0;
for (i = 0; i < 256; i++)
{
sum = sum + hist[i];
}
for (t = 0; t < 255; t++)
{
u0 = 0; count0 = 0;
//阈值为t时,c0组的均值及产生的概率
for (i = 0; i <= t; i++)
{
u0 += i * hist[i]; count0 += hist[i];
}
u0 = u0 / count0; w0 = (float)count0/sum;
//阈值为t时,c1组的均值及产生的概率
u1 = 0;
for (i = t + 1; i < 256; i++)
{
u1 += i * hist[i];
}
u1 = u1 / (sum - count0); w1 = 1 - w0;
//两类间方差
devi = w0 * w1 * (u1 - u0) * (u1 - u0);
//记录最大的方差及最佳位置
if (devi > maxDevi)
{
maxDevi = devi;
maxT = t;
}
}
return maxT;
}
//二值化处理
void OTSU(IplImage *src, IplImage *dst)
{
int i = 0, j = 0;
int wide = src->widthStep;
int high = src->height;
int hist[256] = {0};
int t;
unsigned char *p, *q;
for (j = 0; j < high; j ++)
{
p = (unsigned char *)(src->imageData + j * wide);
for (i = 0; i < wide; i++)
{
hist[p[i]]++; //统计直方图
}
}
t = Threshold(hist);
for (j = 0; j < high; j ++)
{
q = (unsigned char *)(dst->imageData + j * wide);
p = (unsigned char *)(src->imageData + j * wide);
for (i = 0; i < wide; i++)
{
q[i] = p[i] >= t ? 255 : 0;
}
}
}
2:另外一个Kittler算法,是一种快速的全局阈值法。它的效果不比OTSU差多少,但速度快好多倍,如果可以应用在图像质量不错的环境。
它的中心思想是,计算整幅图像的梯度灰度的平均值,以此平均值做为阈值。
//kittler算法
for (i=1;i<high-1;i++)
{
plineadd=src->imageData+i*wide;
pNextLine=src->imageData+(i+1)*wide;
pPreLine=src->imageData+(i-1)*wide;
for(j=1;j<wide-1;j++)
{
//求水平或垂直方向的最大梯度
Grads=MAX(abs((uchar)pPreLine[j]-(uchar)pNextLine[j]),abs((uchar)plineadd[j-1]-(uchar)plineadd[j+1])); //max(xGrads,yGrads)
sumGrads += Grads;
//梯度与当前点灰度的积
sumGrayGrads += Grads*((uchar)plineadd[j]);
}
}
threshold=sumGrayGrads/sumGrads;
// printf("%d
",threshold);
for(i=0;i<high;i++)
{
plineadd=src->imageData+i*wide;
pTempLine=kittler->imageData+i*wide;
for(j=0;j<wide;j++)
{
pTempLine[j]=(uchar)plineadd[j]>threshold?255:0;
}
}