图像处理第二步:二值化
图像处理第二步:二值化
第一种分类:(局部与整体)
1、全局二值化
设定一个全局的阈值T,用T将图像的数据分成两部分:大于T的像素群和小于T的像素群。将大于T的像素群的像素值设定为白色(或者黑色),小于T的像素群的像素值设定为黑色(或者白色)。
2、局部自适应二值化
局部二值化的方法就是按照一定的规则将整幅图像划分为N个窗口,对这N个窗口中的每一个窗口再按照一个统一的阈值T将该窗口内的像素划分为两部分,进行二值化处理。
第二种分类:(按照阈值选取方法不同来分类)
1、固定阈值
直接设定一个阈值,阈值的选取或者根据所处理问题的实际情况,或者毫不讲理心血来潮随便设定一个中间值,这种算法意义不大。
2、自适应阈值
1)、以图像的平均灰度值作为阈值。
使用平均值作为二值化阈值同样有个致命的缺点,可能导致部分对象像素或者背景像素丢失。二值化结果不能真实反映源图像信息。
2)、使用直方图方法来寻找二值化阈值。
直方图方法选择二值化阈值主要是发现图像的两个最高的峰,然后在阈值取值在两个峰之间的峰谷最低处。该方法相对前面两种方法而言稍微精准一点点。结果也更让人可以接受。
3)、使用近似一维Means方法寻找二值化阈值该方法的大致步骤如下:
1. 一个初始化阈值T,可以自己设置或者根据随机方法生成。
2. 根据阈值图每个像素数据P(n,m)分为对象像素数据G1与背景像素数据G2。(n为行,m为列)
3. G1的平均值是m1, G2的平均值是m2
4. 一个新的阈值T’ = (m1 + m2)/2
5. 回到第二步,用新的阈值继续分像素数据为对象与北京像素数据,继续2~4步,
-
package com.gloomyfish.filter.study;
-
-
import java.awt.image.BufferedImage;
-
import java.util.ArrayList;
-
import java.util.List;
-
-
public
class ThresholdBinaryFilter extends GrayFilter {
-
-
@Override
-
public BufferedImage filter(BufferedImage src, BufferedImage dest) {
-
int width = src.getWidth();
-
int height = src.getHeight();
-
-
if ( dest ==
null )
-
dest = createCompatibleDestImage( src,
null );
-
-
int[] inPixels =
new
int[width*height];
-
int[] outPixels =
new
int[width*height];
-
src =
super.filter(src,
null);
// we need to create new one
-
getRGB( src,
0,
0, width, height, inPixels );
-
int index =
0;
-
int means = getThreshold(inPixels, height, width);
-
for(
int row=
0; row<height; row++) {
-
int ta =
0, tr =
0, tg =
0, tb =
0;
-
for(
int col=
0; col<width; col++) {
-
index = row * width + col;
-
ta = (inPixels[index] >>
24) &
0xff;
-
tr = (inPixels[index] >>
16) &
0xff;
-
tg = (inPixels[index] >>
8) &
0xff;
-
tb = inPixels[index] &
0xff;
-
if(tr > means) {
-
tr = tg = tb =
255;
//white
-
}
else {
-
tr = tg = tb =
0;
// black
-
}
-
outPixels[index] = (ta <<
24) | (tr <<
16) | (tg <<
8) | tb;
-
}
-
}
-
setRGB( dest,
0,
0, width, height, outPixels );
-
return dest;
-
}
-
-
private int getThreshold(int[] inPixels, int height, int width) {
-
// maybe this value can reduce the calculation consume;
-
int inithreshold =
127;
-
int finalthreshold =
0;
-
int temp[] =
new
int[inPixels.length];
-
for(
int index=
0; index<inPixels.length; index++) {
-
temp[index] = (inPixels[index] >>
16) &
0xff;
-
}
-
List<Integer> sub1 =
new ArrayList<Integer>();
-
List<Integer> sub2 =
new ArrayList<Integer>();
-
int means1 =
0, means2 =
0;
-
while(finalthreshold != inithreshold) {
-
finalthreshold = inithreshold;
-
for(
int i=
0; i<temp.length; i++) {
-
if(temp[i] <= inithreshold) {
-
sub1.add(temp[i]);
-
}
else {
-
sub2.add(temp[i]);
-
}
-
}
-
means1 = getMeans(sub1);
-
means2 = getMeans(sub2);
-
sub1.clear();
-
sub2.clear();
-
inithreshold = (means1 + means2) /
2;
-
}
-
long start = System.currentTimeMillis();
-
System.out.println(
"Final threshold = " + finalthreshold);
-
long endTime = System.currentTimeMillis() - start;
-
System.out.println(
"Time consumes : " + endTime);
-
return finalthreshold;
-
}
-
-
private static int getMeans(List<Integer> data) {
-
int result =
0;
-
int size = data.size();
-
for(Integer i : data) {
-
result += i;
-
}
-
return (result/size);
-
}
-
-
}
4)、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;
-
}
-
}
-
}
5)、Kittle算法
-
//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\n",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;[
3]
-
}
-
}