?? 圖像處理.txt
字號:
DstLineBytes-y*DstLineBytes);
for(x=0;x<bi.biWidth;x++){
//R,G,B各取4位
Blue=(int)(*(lpPtr++) & 0xf0);
Green=(int)(*(lpPtr++) & 0xf0);
Red=(int)(*(lpPtr++) & 0xf0);
//拼成一個12位整數
ClrIndex=(Blue<<4) + Green +(Red >>4);
for (i = 0; i < PalCounts;i++)
if (ClrIndex == ColorIndex[i]){
//根據12索引值取得對應的調色板中的索引值
*(lpTempPtr++)=(unsigned char)ColorHits[i];
break;
}
}
}
if(hBitmap!=NULL)
DeleteObject(hBitmap);
//產生新的位圖
hBitmap=CreateDIBitmap(hDc, (LPBITMAPINFOHEADER)lpTempImgData,
(LONG)CBM_INIT, (LPSTR)lpTempImgData+sizeof(BITMAPINFOHEADER) +
256*sizeof(RGBQUAD), (LPBITMAPINFO)lpTempImgData, DIB_RGB_COLORS);
if(hPalette && hPrevPalette){
SelectPalette(hDc,hPrevPalette,FALSE);
RealizePalette(hDc);
}
hf=_lcreat("c:\\256.bmp",0);
_lwrite(hf,(LPSTR)&DstBf,sizeof(BITMAPFILEHEADER));
_lwrite(hf,(LPSTR)lpTempImgData,DstBufSize);
_lclose(hf);
//釋放內存和資源
ReleaseDC(hWnd,hDc);
LocalUnlock(hTempImgData);
LocalFree(hTempImgData);
GlobalUnlock(hImgData);
return TRUE;
}
以下我們將要介紹灰度變換,針對的都是256級灰度圖。
4.對比度擴展(contrast stretching)
假設有一幅圖,由于成象時光照不足,使得整幅圖偏暗,(例如,灰度范圍從0到63);或者成象時光照過強,使得整幅圖偏亮,(例如,灰度范圍從200到255),我們稱這些情況為低對比度,即灰度都擠在一起,沒有拉開。灰度擴展的意思就是把你所感興趣的灰度范圍拉開,使得該范圍內的像素,亮的越亮,暗的越暗,從而達到了增強對比度的目的。我們可以用如下的圖來說明對比度擴展的原理。
圖5. 對比度擴展的原理
圖中的橫坐標gold表示原圖的灰度值,縱坐標gnew表示gold經過對比度擴展后得到了新的灰度值。a,b,c為三段直線的斜率,因為是對比度擴展,所以斜率b>1。g1old和g2old表示原圖中要進行對比度擴展的范圍,g1new和g2new表示對應的新值。用公式表示為
a*gold 當0<=gold<g1old時
gnew= b*(gold-g1old)+g1new 當g1old<=gold<g2old時
c*(gold-g2old)+g2new 當g2old<gold<=255時
顯然要得到對比度擴展后的灰度,我們需要知道a,b,c,g1old,g2old五個參數。由于有新圖的灰度級別也是255這個約束,所以滿足a*g1old+b*(g2old-g1old)+c(255-g2old)=255這個方程。這樣,我們只需給出四個參數,而另一個可以可以代入方程求得。我們假設a=c,這樣,我們只要給出b,g1old和g2old,就可以求出
a=(255-b*(g2old-g1old))/(255-(g2old-g1old))
要注意的是,給出的三個參數必須滿足1. b*(g2old-g1old)<=255;2. (g2old-g1old)<=255
這兩點是顯然的。
下圖為圖1取g1old=100;g2old=150 ;b=3.0進行對比度擴展的結果。可以看出亮的區域(雕塑)變得更亮,暗的區域(手)變得更暗。
圖6. 圖1對比度擴展后的結果
下面的這段程序實現了對比度擴展。首先出現對話框,輸入b,g1old,g2old的三個參數(在程序中分別是StretchRatio,SecondPoint,FirstPoint),然后對調色板做響應的處理,而實際的位圖數據不用動。
BOOL ContrastStretch(HWND hWnd)
{
DLGPROC dlgInputBox = NULL;
DWORD BufSize;
LPBITMAPINFOHEADER lpImgData;
LPSTR lpPtr;
HLOCAL hTempImgData;
LPBITMAPINFOHEADER lpTempImgData;
LPSTR lpTempPtr;
HDC hDc;
HFILE hf;
LOGPALETTE *pPal;
HPALETTE hPrevPalette=NULL;
HLOCAL hPal;
DWORD i;
unsigned char Gray;
float a,g1,g2,g;
if( NumColors!=256){ //必須是256級灰度圖
MessageBox(hWnd,"Must be a 256 grayscale bitmap!","Error Message",MB_OK|
MB_ICONEXCLAMATION);
return FALSE;
}
//出現對話框,輸入三個參數
dlgInputBox = (DLGPROC) MakeProcInstance ( (FARPROC)InputBox, ghInst );
DialogBox (ghInst, "INPUTBOX", hWnd, dlgInputBox);
FreeProcInstance ( (FARPROC) dlgInputBox );
if( StretchRatio*(SecondPoint-FirstPoint) > 255.0){ //參數不合法
MessageBox(hWnd,"StretchRatio*(SecondPoint-FirstPoint) can not be larger than 255!","Error Message",MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}
if( (SecondPoint-FirstPoint) >=255){ //參數不合法
MessageBox(hWnd,"The area you selected can not be the whole scale!",
"Error Message",MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}
//計算出第一和第三段的斜率a
a=(float)((255.0-StretchRatio*(SecondPoint-FirstPoint))
/(255.0-(SecondPoint-FirstPoint)));
//對比度擴展范圍的邊界點所對應的新的灰度
g1=a*FirstPoint;
g2=StretchRatio*(SecondPoint-FirstPoint)+g1;
//新開的緩沖區的大小
BufSize=bf.bfSize-sizeof(BITMAPFILEHEADER);
if((hTempImgData=LocalAlloc(LHND,BufSize))==NULL)
{
MessageBox(hWnd,"Error alloc memory!","Error Message",MB_OK|
MB_ICONEXCLAMATION);
return FALSE;
}
lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData);
lpTempImgData=(LPBITMAPINFOHEADER)LocalLock(hTempImgData);
//拷貝頭信息和實際位圖數據
memcpy(lpTempImgData,lpImgData,BufSize);
hDc=GetDC(hWnd);
//lpPtr指向原圖數據緩沖區,lpTempPtr指向新圖數據緩沖區
lpPtr=(char *)lpImgData+sizeof(BITMAPINFOHEADER);
lpTempPtr=(char *)lpTempImgData+sizeof(BITMAPINFOHEADER);
//為新的邏輯調色板分配內存
hPal=LocalAlloc(LHND,sizeof(LOGPALETTE) + NumColors* sizeof(PALETTEENTRY));
pPal =(LOGPALETTE *)LocalLock(hPal);
pPal->palNumEntries =(WORD) NumColors;
pPal->palVersion = 0x300;
for (i = 0; i < 256; i++) {
Gray=(unsigned char )*lpPtr;
lpPtr+=4;
//進行對比度擴展
if(Gray<FirstPoint) g=(float)(a*Gray);
else if (Gray<SecondPoint) g=g1+StretchRatio*(Gray-FirstPoint);
else g=g2+a*(Gray-SecondPoint);
pPal->palPalEntry[i].peRed=(BYTE)g;
pPal->palPalEntry[i].peGreen=(BYTE)g;
pPal->palPalEntry[i].peBlue=(BYTE)g;
pPal->palPalEntry[i].peFlags=0;
*(lpTempPtr++)=(unsigned char)g;
*(lpTempPtr++)=(unsigned char)g;
*(lpTempPtr++)=(unsigned char)g;
*(lpTempPtr++)=0;
}
if(hPalette!=NULL)
DeleteObject(hPalette);
//產生新的邏輯調色板
hPalette=CreatePalette(pPal);
LocalUnlock(hPal);
LocalFree(hPal);
if(hPalette){
hPrevPalette=SelectPalette(hDc,hPalette,FALSE);
RealizePalette(hDc);
}
if(hBitmap!=NULL)
DeleteObject(hBitmap);
//產生新的位圖
hBitmap=CreateDIBitmap(hDc, (LPBITMAPINFOHEADER)lpTempImgData,
(LONG)CBM_INIT, (LPSTR)lpTempImgData+sizeof(BITMAPINFOHEADER) +
NumColors*sizeof(RGBQUAD),(LPBITMAPINFO)lpTempImgData, DIB_RGB_COLORS);
if(hPalette && hPrevPalette){
SelectPalette(hDc,hPrevPalette,FALSE);
RealizePalette(hDc);
}
hf=_lcreat("c:\\stretch.bmp",0);
_lwrite(hf,(LPSTR)&bf,sizeof(BITMAPFILEHEADER));
_lwrite(hf,(LPSTR)lpTempImgData,BufSize);
_lclose(hf);
//釋放內存和資源
ReleaseDC(hWnd,hDc);
LocalUnlock(hTempImgData);
LocalFree(hTempImgData);
GlobalUnlock(hImgData);
return TRUE;
}
5.削波(cliping)
削波可以看作是對比度擴展的一個特例,我們用如下的圖說明削波的原理。
圖7. 削波的原理
不難看出,只要令對比度擴展中的a=c=0就實現了削波。我們只要給出范圍的兩個端點,斜率b就可以用方程b*(g2old-g1old)=255求出。
下圖為圖1取g1old=150;g2old=200 進行削波的結果。把亮的區域(雕塑)提取了出來。
圖8. 圖1削波處理后的結果
削波的程序和對比度擴展的程序很類似,就不再給出了。
6.閾值化(thresholding)
閾值化可以看作是削波的一個特例,我們用如下的圖說明閾值化的原理。
圖9. 閾值化的原理
不難看出,只要令削波中的g1old=g2old就實現了閾值化。閾值就象個門檻,比它大就是白,比它小就是黑。經過閾值化處理后的圖象變成了黑白二值圖,所以說閾值化是灰度圖轉二值圖的一種常用方法(我們以前介紹過圖案化和抖動的方法)。閾值化只需給出閾值點,即g1old即可。
下圖為圖1閾值取128,閾值化處理后的結果,得到了一幅黑白圖。
圖10. 圖1閾值化處理后的結果
閾值化的程序和對比度擴展的程序很類似,就不再給出了。
7.灰度窗口變換(slicing)
灰度窗口變換是將某一區間的灰度級和其它部分(背景)分開。我們用如下的圖說明灰度窗口變換的原理。其中[g1old,g2old]稱為灰度窗口。
圖11. 清除背景的灰度窗口變換的原理
圖12. 保留背景的灰度窗口變換的原理
灰度窗口變換有兩種,一種是清除背景的,一種是保留背景的。前者把不在灰度窗口范圍內的像素都賦值為0,在灰度窗口范圍內的像素都賦值為255,這也能實現灰度圖的二值化;后者是把不在灰度窗口范圍內的像素保留原灰度值,在灰度窗口范圍內的像素都賦值為255。
灰度窗口變換可以檢測出在某一灰度窗口范圍內的所有像素,是圖象灰度分析中的一個有力工具。下面有三幅圖,圖13為原圖,圖14是經過清除背景的灰度窗口變換處理后的圖(灰度窗口取[200-255]),將夜景中大廈里的燈光提取了出來。圖15是經過保留背景的灰度窗口變換處理后的圖(灰度窗口取[200-255]),將夜景中大廈里的燈光提取了出來,同時保留了大廈的背景,可以看出它們的差別還是很明顯的。
圖13. 原圖 圖14. 圖13經過清除背景 圖15. 圖13經過保留背景的
的灰度窗口變換處理后的圖 灰度窗口變換處理后的圖
灰度窗口變換的程序和對比度擴展的程序很類似,就不再給出了。
我突然想起了不久前在一本科學雜志上看到的一篇文章,非常有趣,是介紹電影〈阿甘正傳〉特技制作的。其中有一項就用到了類似灰度窗口變換的思想。相信看過這部電影的讀者都會對那個斷腿的丹尼上校有深刻的印象。他的斷腿是怎么拍出來的呢?其實方法很簡單,先拍一幅沒有演員出現的背景畫面,然后拍一幅有演員出現,其它不變的畫面。要注意的是,此時演員的腿用藍布包裹。把前后兩幅圖輸入計算機進行處理。第二幅圖中凡是遇到藍色的像素,就用第一幅圖中對應位置的背景像素代替。這樣,一位斷腿的上校就逼真的出現在屏幕上了。這就是電影特技中經常用到的“藍幕”技術。
說點題外話,其實現代電影,特別是好萊塢的電影,越來越離不開計算機及圖象處理技術。最近引起轟動的大片<Titanic>中的很多特技鏡頭就是利用了龐大的SGI圖形工作站機群沒日沒夜的計算產生的。圖象處理技術和我們所喜愛的電影藝術緊密的結合了起來,這更增加了我們學習它的興趣。
8.灰度直方圖統計(histogram)
有時我們需要知道一幅圖中的灰度分布情況,這時就可以采用灰度直方圖來表示,圖中的橫坐標表示灰度值,縱坐標表示該灰度值出現的次數(頻率)。圖16為圖13的灰度直方圖,可見,低灰度的像素占了絕大部分。
圖16. 圖13的灰度直方圖
下面的程序顯示一幅圖的灰度直方圖。有兩段程序,第一段統計出每個灰度的像素個數,存放在數組GrayTable[]中,然后產生一個新的窗口,把統計結果顯示出來。第二段程序就是該窗口的消息處理函數。要注意的是,由于各灰度出現的頻率可能相差很大,所以如何將結果顯示在有限的窗口范圍內,是一個必須考慮的問題。我們這里的做法是,在所有出現的灰度中,統計出一個最大值max和一個最小值min,假設能顯示的窗口最大坐標為270,最小坐標為5,按成比例顯示,這樣,灰度出現的次數和顯示坐標之間呈線形關系,設 a*grayhits+b=coordinate,其中grayhits為灰度出現的次數,coordinate為顯示坐標,a和b為兩個常數。我們將max和min代入,應該滿足a*max+b=270 ; a*min+b=5
可以解得a=265/(max-min); b=270.0-a* max 。
還有一點,不要忘了在WinMain函數中注冊那個新產生窗口的窗口類。
int GrayTable[256];
int MaxGrayNum;
int MinGrayNum;
BOOL Histogram(HWND hWnd)
{
DWORD OffBits,BufSize;
LPBITMAPINFOHEADER lpImgData;
LPSTR lpPtr;
int x,y;
int grayindex;
HWND hPopupWnd;
int temp;
//計數器清零
for(grayindex=0;grayindex<256;grayindex++)
GrayTable[grayindex]=0;
//OffBits為到實際位圖數據的偏移值
OffBits=bf.bfOffBits-sizeof(BITMAPFILEHEADER);
//BufSize為緩沖區的大小
BufSize=bf.bfSize-sizeof(BITMAPFILEHEADER);
lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData);
for(y=0;y<bi.biHeight;y++){
lpPtr=(char *)lpImgData+(BufSize-LineBytes-y*LineBytes);
for(x=0;x<bi.biWidth;x++){
grayindex=(unsigned char)*(lpPtr++);
GrayTable[grayindex]++; //對應的顏色計數值加1
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -