在《图像的采样与量化及直方图》中讲述了如何计算图像的灰度直方图及对图像进行二值化处理,在这一文章中讲述的二值化处理的阀值都是自己设定的,自己设定的阀值往往不准确,而且不同的图像的最佳阀值是不一样的。那么能不能让计算机来计算图像的最佳阀值呢?答案是肯定的,下面就介绍一种迭代法计算图像阀值的方法:
迭代法是基于逼近的思想,其步骤如下:
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,迭代计算。
/** * 用迭代法 求最佳阀值 * @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; }