zsm http://i.cnblogs.com/EditPosts.aspx?opt=1
摘要
人類所獲得的信息大約70%來自於圖像,數字圖像處理是計算機對采樣量化的圖像進行去除噪聲、增強、復原、分割、提前特征等處理的方法和技術,它對一個物體的數字表示施加一系列的操作,以得到所期望的結果。這種技術在航空航天、生物醫學、通信工程、軍事、文化、電子商務等方面應用廣泛。本實驗根據課堂所學的關於數字圖像點運算的知識實現對數字圖像的灰度直方圖均衡處理和分段線性拉伸處理。
關鍵詞
數字圖像 點運算 灰度直方圖 直方圖均衡 分段線性拉伸
一、 任務說明
用Java編程語言實現一個帶GUI的數字圖像處理程序,實現讀出數字圖像並進行灰度直方圖均衡處理和分段線性拉伸處理的功能。
需要注意的是,在本程序中,對於分段線性拉伸處理,程序中會先根據源圖像的灰度直方圖找出集中分布區間(像素個數之和占圖像總像素個數85%以上的區間中長度最短的區間),然后讓用戶輸入一個要拉伸到的區間,再進行分段線性拉伸。拉伸前后灰度范圍都默認為 [0,255]。
二、 算法原理
(一)直方圖均衡
1、 背景意義
直方圖反映的是一幅圖像的灰度級與出現這種灰度級概率之間關系的圖形。直方圖均衡的目的是使所有灰度級出現的相對概率相同,此時圖像的熵最大,圖像包含的信息最大。經過直方圖均衡處理后,圖像的對比度會增強,特別是對於背景或前景都太亮或太暗的圖像非常有用。例如,其可以帶來X光圖像中更好的骨骼結構顯示以及曝光過度或者曝光不足照片中更好的細節。
直方圖均衡的主要優勢是它是一個相當直觀的技術並且是可逆操作,如果已知均衡化函數,那么就可以恢復原始的直方圖,並且計算量也不大。這種方法的一個缺點是它對處理的數據不加選擇,它可能會增加背景噪聲的對比度並且降低有用信號的對比度。
2、 基本算法
若灰度級概率分布函數為 ,則此時直方圖均衡的變換函數 s=T[r] 應該滿足如下條件:
(a) T[r]為單值單調遞增函數
(b) 0<T[r]<1,保證變換后像素灰度級仍在允許范圍內
由轉換原理可求得
則原灰度值 r 經變換后對應的新灰度值為
3、 擴展算法
上述基本算法是對於灰度級連續的情況而言的,而在計算機中圖像的灰度級是離散的,實際上與連續情況類似,我們可以得到離散情況下的變換函數:
假定離散情況下共有L個灰度級,第k個灰度級出現的像素個數為,圖像的總像素個數為N,則第k個灰度級出現的概率為:
從而均衡化的變換函數為:
則原灰度值r對應的新灰度值為
因此通過轉換原圖像每個像素的灰度值就可實現直方圖均衡處理。
(二)分段線性拉伸
1、 背景意義
一般成像系統只具有一定的亮度響應范圍,亮度的最大值與最小值之比稱為對比度。由於成像系統的限制,常常出現對比度不足的問題,使人眼觀看圖像時的視覺效果較差,可以采用灰度變換(灰度修正)方法提高圖像的對比度,增強圖像。常用的灰度修正方法有:
(1) 線性變換
(2) 分段線性變換
(3) 非線性變換,如指數變換、對數變換等
這里采用的是 分段線性變換。
2、 基本算法
為了突出感興趣的目標或灰度區間,相對抑制不感興趣的灰度區間,可以采用分段線性變換。常用三段線性變換方法。
設原灰度直方圖的灰度級的分布范圍為[0,],集中分布在[a,b],又設變換后的直方圖灰度級的分布范圍為[0,],集中分布在[c,d],則變換公式如下:
對 :
對 :
對 :
3、 擴展算法
在本實驗中,和都當成255,而原灰度級的集中分布范圍 [a,b] 是通過程序算出來的,即從原圖像的灰度直方圖中找出一個連續灰度區間 [a,b] ,這個區間滿足兩個條件:
(1) 在此灰度級區間內的像素個數和占總像素個數的85%以上
(2) 此區間是滿足(1)的區間中區間長度最短的
注:上述比例85%是在程序中默認的,其實應該做成能讓用戶自己輸入一個比例,然后程序找出這個比例下灰度級的集中分布區間,但由於時間有限,程序中只默認為85%,沒有實現讓用戶輸入比例的功能。
三、 算法實現
功能函數:功能說明,輸入參數說明,輸出參數說明,算法流程(代碼或偽代碼,注釋)
(一) 使用語言
本程序采用Java語言編寫,開發平台為Eclipse (Version: Juno)
(二) 編譯環境
java version "1.7.0_25"
Java(TM) SE Runtime Environment (build 1.7.0_25-b17)
(三) 功能函數
1、private BufferedImage getGrayPicture(BufferedImage originalImage)
(1) 功能說明:此函數獲得輸入圖像的灰度圖像,以及對應的灰度分布
(2) 輸入參數說明:originalImage為輸入的圖像,類型為BufferedImage
(3) 輸出參數說明:調用此函數后得到輸入圖像對應的灰度圖像,並把個灰度級的像素個數存入全局數組huidunum[]中,供后面處理之用
(4) 算法流程:
遍歷每個像素——>
獲得該像素RGB值——>
獲得R、G、B分量——>
得到灰度值——>
以灰度值為新R、G、B分量,得到新RGB值——>
新像素
// 獲得圖像的灰度圖,並得到該圖像個灰度級的像素個數 huidunum[]
private BufferedImage getGrayPicture(BufferedImage originalImage) {
int rgb, gray, alph, red, green, blue;
int imgwidth = originalImage.getWidth();
int imgheight = originalImage.getHeight();
BufferedImage huiduimg = new BufferedImage(imgwidth, imgheight,
originalImage.getType());// 不在原圖像上修改,而是創建同樣大小的一張圖片
for (int i = 0; i < 256; i++)
huidunum[i] = 0; // 灰度分布初始化為0,即各灰度值像素個數初始化為0
for (int i = 0; i < imgwidth; i++) {
for (int j = 0; j < imgheight; j++) {// 遍歷圖像所以像素
rgb = originalImage.getRGB(i, j);// 獲得該像素的rgb值,為一個4字節的整數,從高到低各字節值
// 分別表示
// 透明度appha、red、green、blue
alph = rgb >> 24; // 得到appha的值
red = (rgb & 0xff0000) >> 16;// 得到red值
green = (rgb & 0xff00) >> 8;// 得到green值
blue = (rgb & 0xff);// 得到blue值
gray = (int) (red * 299 + green * 587 + blue * 114) / 1000;// 根據公式由rgb三分量值得到灰度值gray
huidunum[gray]++;// 該灰度值像素個數加一
rgb = (alph << 24) | (gray << 16) | (gray << 8) | gray;// 由灰度值轉rgb值,三分量都是gray值
huiduimg.setRGB(i, j, rgb);// 新rgb值
}
}
return huiduimg;
}
2、private BufferedImage hisEqual(BufferedImage originalImage)
(1) 功能說明:此函數實現直方圖均衡化的功能
(2) 輸入參數說明:originalImage為讀入的圖像
(3) 輸出參數說明:獲得對應的經過直方圖均衡處理后的圖像
(4) 算法流程:
遍歷每個灰度值的像素個數huidunum[]——>
得到灰度值范圍[0,當前灰度值]內的像素個數temp——>
該區間與總像素個數的比值——>
新灰度值=原灰度值×比值——>
以新灰度值為R、G、B分量得到RGB值——>
新像素
// 直方圖均衡處理,不改變源圖像
private BufferedImage hisEqual(BufferedImage originalImage) {
int alph, rgb, red, green, blue, gray;
int width = originalImage.getWidth();
int height = originalImage.getHeight();
double temp = 0.0;
BufferedImage transimg = new BufferedImage(width, height,
originalImage.getType());// 創建一張同樣大小的新圖片
for (int i = 0; i < 256; i++) {//遍歷灰度直方圖
temp = 0.0;
for (int j = 0; j <= i; j++) {//
temp = (double) huidunum[j] + temp;
}
temp = (temp / (double) width) / (double) height;
transhuidu[i] = (int) (255.0 * temp);
}
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
rgb = originalImage.getRGB(i, j);
alph = rgb >> 24;
red = (rgb & 0xff0000) >> 16;
green = (rgb & 0xff00) >> 8;
blue = (rgb & 0xff);
gray = (int) (red * 299 + green * 587 + blue * 114) / 1000;
gray = transhuidu[gray];// 新灰度值
rgb = (alph << 24)|(gray << 16)|(gray << 8) | gray;//新RGB值
transimg.setRGB(i, j, rgb);
}
}
return transimg;
}
3、private void getRecommendRange(int width, int height, int huidunum[])
(1) 功能說明:獲得像素集中分布的灰度值范圍
(2) 輸入參數說明:三個參數分別為圖像寬、高、各灰度值的像素個數
(3) 輸出參數說明:函數得到像素集中分別的灰度值范圍,下界、上界分別存入全局變量recommendstart、recommendend
(4) 算法流程:
初始化待求區間為[0,255]——>
以i遍歷huidunum[]——>
以j遍歷huidunum[i,255]——>
以k遍歷huidunum[i,j] 並統計此區間內像素總個數sum——>
若sum不少於總像素個數的85%且區間[i,j]長度小於上次得到的區間長度,則取[i,j]。如此循環直到完。
// 獲得源圖像中占大部分的連續灰度值區間 [recommendstart,recommendend]
private void getRecommendRange(int width, int height, int huidunum[]) {
double sum = 0.0;
recommendstart = 0;
recommendend = 255;
for (int i = 0; i < 256; i++) {
for (int j = i; j < 256; j++) {
sum = 0.0;
for (int k = i; k <= j; k++) {
sum = sum + (double) huidunum[k];
}
if (sum > percentage * width * height) {
if (j - i < recommendend - recommendstart) {
recommendstart = i;
recommendend = j;
}
}
}
}
}
4、private int getNewGrayValue(int f, int a, int b, int c, int d)
(1) 功能說明:此函數實現灰度值轉換
(2) 輸入參數說明:f為原灰度值,[b,c]為原圖像灰度值集中范圍,[c,d]為要拉伸到的范圍,由用戶輸入
(3) 輸出參數說明:函數得到變換后的新灰度值
(4) 算法流程:此部分就是 二(二)2的編程實現,比較簡單不再贅述
private int getNewGrayValue(int f, int a, int b, int c, int d) {
int newGrayValue = f;
if (f < a) {
newGrayValue = (int) ((double) c * (double) f / (double) a);
} else if (f > b) {
newGrayValue = (int) ((double) (f - b) * (double) (255 - d)
/ (double) (255 - b) + d);
} else {
newGrayValue = (int) ((double) (f - a) * (double) (d - c)
/ (double) (b - a) + c);
}
return newGrayValue;
}
5、 private BufferedImage Linearstretch(BufferedImage originalImage, int newgraystart, int newgrayend)
(1) 功能說明:根據用戶輸入的灰度值范圍[newgraystart, newgrayend]進行分段線性拉伸
(2) 輸入參數說明:originalImage為原圖像,[newgraystart, newgrayend]為用戶輸入的灰度值范圍
(3) 輸出參數說明:函數執行成功后得到原圖像經分段線性拉伸后的圖像
(4) 算法流程:
執行getRecommendRange函數獲得原圖像計組像素集中分布的灰度值范圍——>
遍歷原圖像的像素——>
獲得該像素的rgb值——>
獲得rgb值的分量r、g、b——>
獲得灰度值gray——>
執行getNewGrayValue函數獲得對應的新灰度值——>
以gray為三個分量獲得新rgb值——>
得到新像素
private BufferedImage Linearstretch(BufferedImage originalImage,
int newgraystart, int newgrayend) {
int newhuidunum[] = new int[256];
int alph, rgb, red, green, blue, gray;
int width = originalImage.getWidth();
int height = originalImage.getHeight();
BufferedImage transimg = new BufferedImage(width, height,
originalImage.getType());
// 獲得灰度大部分集中的范圍 [recommendstart, recommendend]
getRecommendRange(width, height, huidunum);
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
rgb = originalImage.getRGB(i, j);
alph = rgb >> 24;
red = (rgb & 0xff0000) >> 16;
green = (rgb & 0xff00) >> 8;
blue = (rgb & 0xff);
gray = (int) (red * 299 + green * 587 + blue * 114) / 1000;
// 根據拉伸變換得到新的灰度值
gray = getNewGrayValue(gray, recommendstart, recommendend,
newgraystart, newgrayend);
rgb = (alph << 24) | (gray << 16) | (gray << 8) | gray;
transimg.setRGB(i, j, rgb);
newhuidunum[gray]++;
}
}
return transimg;
}
四、 實驗
(一)灰度直方圖均衡
- 1. 實驗與結果
- 2. 結果分析
圖像經過直方圖均衡處理后,圖像的對比度會增強,特別是對於背景或前景都太亮或太暗的圖像非常有用。
均衡化后的各灰度級更加均衡,對於灰度范圍小,直方圖分布極不均勻的圖像,可人為的適當的擴大灰度范圍,均衡化后能取得較好的層次感,使圖像信息變得更清晰。
(二)分段線性拉伸
(1)
實驗與結果
(2) 結果分析
從以上實驗及其結果對比可看出,線性變換可以把原圖像相對較集中的灰度級拉伸到到指定的灰度級范圍,當灰度級范圍比原范圍低時,圖像將變暗,反之則變亮。
五、 結論
直方圖均衡和線性拉伸是對數字圖像進行處理的方法中的兩種,是點運算。
直方圖均衡處理可以使所有灰度級出現的相對概率相同,經過直方圖均衡處理后,圖像的對比度會增強,會更有層次感,特別是對於背景或前景都太亮或太暗的圖像非常有用。
線性拉伸可以把原圖像集中的灰度值區間拉伸到指定的區間上,這樣可以突出感興趣的目標或灰度區間,相對抑制不感興趣的灰度區間。
參考文獻
[1] 岡薩雷斯. 數字圖像處理[M]. 北京:電子工業出版社, 2011.