在《图像的采样与量化及直方图》中讲述了如何计算图像的灰度直方图及对图像进行二值化处理,在这一文章中讲述的二值化处理的阀值都是自己设定的,自己设定的阀值往往不准确,而且不同的图像的最佳阀值是不一样的。那么能不能让计算机来计算图像的最佳阀值呢?答案是肯定的,下面就介绍一种迭代法计算图像阀值的方法:
算法思想
迭代法是基于逼近的思想,其步骤如下:
1. 求出图象的最大灰度值和最小灰度值,分别记为Pmax和Pmin,令初始阈值T0=(Pmax+Pmin)/2;
2. 根据阈值T(k)(k=0,1,2...,k)将图象分割为前景和背景,分别求出两者的平均灰度值H1和H2;
3. 求出新阈值T(k+1)=(H1+H2)/2;
4. 若T(k)=T(k+1),则所得即为阈值;否则转2,迭代计算。
源码(java)
/**
* 用迭代法 求最佳阀值
* @param pix 灰度像素数组
* @return 最佳阀值
*/
public int iterationGetThreshold(int[] pix) {
int min = pix[0], max = pix[0];
for(int i=0; i<pix.length; i++) {
if(pix[i] > 255) {
pix[i] = 255;
}
if(pix[i] < 0) {
pix[i] = 0;
}
if(min >pix[i])
min = pix[i];
if(max <pix[i])
max = pix[i];
}
double histo[] = getHisto(pix);
int threshold = 0;
int newThreshold = (int) ((min+max)/2);;
while(threshold != newThreshold) {
double sum1=0, sum2=0, w1=0, w2=0 ;
int avg1, avg2;
for(int i=min; i<newThreshold; i++) {
sum1 += histo[i]*i;
w1 += histo[i];
}
avg1 = (int) (sum1/w1);
for(int i=newThreshold; i<max; i++) {
sum2 += histo[i]*i;
w2 += histo[i];
}
avg2 = (int) (sum2/w2);
//System.out.println("avg1:" + avg1 + " avg2:" + avg2 + " newThreshold:" + newThreshold);
threshold = newThreshold;
newThreshold = (avg1+avg2)/2;
}
return newThreshold;
/*if(min==0 && max == 255) {
return (min+max)/2;
} else {
int t = (min+max)/2;
double sum1=0, sum2=0, w1=0, w2=0 ;
int avg1, avg2;
for(int i=min; i<t; i++) {
sum1 += histo[i]*i;
w1 += histo[i];
}
avg1 = (int) (sum1/w1);
for(int i=t; i<max; i++) {
sum2 += histo[i]*i;
w2 += histo[i];
}
avg2 = (int) (sum2/w2);
return (avg1+avg2)/2;
} */
}
/**
* 求图像的灰度直方图
* @param pix 一维的灰度图像像素值
* @return 0-255的 像素值所占的比率
*/
public double[] getHisto(int pix[]) {
double histo[] = new double[256];
for(int i=0; i<pix.length; i++) {
//System.out.println("pix[i]:" + pix[i]);
if(pix[i] > 255) {
pix[i] = 255;
}
if(pix[i] < 0) {
pix[i] = 0;
}
histo[pix[i]] ++;
}
for(int i=0; i<255; i++) {
histo[i] = (double)histo[i]/pix.length;
}
return histo;
}
/**
* 求二值图像
* @param pix 像素矩阵数组
* @param w 矩阵的宽
* @param h 矩阵的高
* @param threshold 阀值
* @return 处理后的数组
*/
public int[] threshold(int pix[], int threshold) {
for(int i=0; i<pix.length; i++) {
if(pix[i] <= threshold) {
pix[i] = 0;
} else {
pix[i] = 255;
}
}
return pix;
}