迭代法求影象的最佳閥值
阿新 • • 發佈:2019-02-02
在《影象的取樣與量化及直方圖》中講述瞭如何計算影象的灰度直方圖及對影象進行二值化處理,在這一文章中講述的二值化處理的閥值都是自己設定的,自己設定的閥值往往不準確,而且不同的影象的最佳閥值是不一樣的。那麼能不能讓計算機來計算影象的最佳閥值呢?答案是肯定的,下面就介紹一種迭代法計算影象閥值的方法:
演算法思想
迭代法是基於逼近的思想,其步驟如下:
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; }