1. 程式人生 > >迭代法求影象的最佳閥值

迭代法求影象的最佳閥值

在《影象的取樣與量化及直方圖》中講述瞭如何計算影象的灰度直方圖及對影象進行二值化處理,在這一文章中講述的二值化處理的閥值都是自己設定的,自己設定的閥值往往不準確,而且不同的影象的最佳閥值是不一樣的。那麼能不能讓計算機來計算影象的最佳閥值呢?答案是肯定的,下面就介紹一種迭代法計算影象閥值的方法:

演算法思想

迭代法是基於逼近的思想,其步驟如下:

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;
	}