?? 圖像處理.txt
字號:
第五講 直方圖修正和彩色變換
這一講,我們主要和調色板打交道。先從最簡單的反色講起。
1. 反色(invert)
反色就是形成底片效果。如下圖所示,圖2為圖1反色后的結果
圖1. 原圖
圖2. 圖1反色后的結果
反色有時是很有用的,比如說,圖1中黑色區域占絕大多數,這樣打印起來很費墨,我們可以先進行反色處理再打印。反色的實際含義是將R,G,B值反轉。若顏色的量化級別是256,則新圖的R,G,B值為255減去原圖的R,G,B值。這里針對的是所有圖,包括真彩圖,帶調色板的彩色圖(又稱為偽彩色圖),和灰度圖。針對不同種類有不同的處理。
先看看真彩圖。我們知道真彩圖不帶調色板,每個像素用3個字節,表示R,G,B三個分量。所以處理很簡單,把反轉后的R,G,B值寫入新圖即可。再來看看帶調色板的彩色圖,我們知道位圖中的數據只是對應調色板中的一個索引值,我們只需要將調色板中的顏色反轉,形成新調色板,而位圖數據不用動,就能夠實現反轉。灰度圖是一種特殊的偽彩色圖,只不過調色板中的R,G,B值 都是一樣的而已。所以反轉的處理和上面講的一樣。
這里,我想澄清一個概念。過去我們講二值圖時,一直都說成黑白圖。二值位圖一定是黑白的嗎?答案是不一定。我們安裝Windows95時看到的那幅setup.bmp是由藍色和黑色組成的,但它實際上是二值圖。原來,它的調色板中的兩種顏色是黑與藍,而不是黑與白。所以說二值圖也可以是彩色的,只不過一般情況下是黑白圖而已。
下面的程序實現了反色,注意其中真彩圖和調色板位圖處理時的差別。
BOOL Invert(HWND hWnd)
{
DWORD BufSize;
LPBITMAPINFOHEADER lpImgData;
LPSTR lpPtr;
HLOCAL hTempImgData;
LPBITMAPINFOHEADER lpTempImgData;
LPSTR lpTempPtr;
HDC hDc;
HFILE hf;
LONG x,y;
LOGPALETTE *pPal;
HPALETTE hPrevPalette=NULL;
HLOCAL hPal;
DWORD i;
unsigned char Red,Green,Blue;
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(hImgDa
ta);
lpTempImgData=(LPBITMAPINFOHEADER)LocalLock(hTe
mpImgData);
//拷貝頭信息
memcpy(lpTempImgData,lpImgData,BufSize);
hDc=GetDC(hWnd);
if(NumColors!=0){ //NumColors不為0說明是帶調色板的
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 < NumColors; i++) {
Blue=(unsigned char )(*lpPtr++);
Green=(unsigned char )(*lpPtr++);
Red=(unsigned char )(*lpPtr++);
lpPtr++;
//反轉調色板中的顏色,存入新的調色板
pPal->palPalEntry[i].peRed=(BYTE)(255-Red);
pPal->palPalEntry[i].peGreen=(BYTE)(255-Green);
pPal->palPalEntry[i].peBlue=(BYTE)(255-Blue);
pPal->palPalEntry[i].peFlags=0;
*(lpTempPtr++)=(unsigned char)(255-Blue);
*(lpTempPtr++)=(unsigned char)(255-Green);
*(lpTempPtr++)=(unsigned char)(255-Red);
*(lpTempPtr++)=0;
}
if(hPalette!=NULL)
DeleteObject(hPalette);
hPalette=CreatePalette(pPal); //產生新的調色板
LocalUnlock(hPal);
LocalFree(hPal);
if(hPalette){
hPrevPalette=SelectPalette(hDc,hPalette,FALSE);
RealizePalette(hDc);
}
}
else{ //不帶調色板,說明是真彩色圖
for(y=0;y<bi.biHeight;y++){
lpPtr=(char *)lpImgData+(BufSize-LineBytes-
y*LineBytes);
lpTempPtr=(char *)lpTempImgData+(BufSize-
LineBytes-y*LineBytes);
for(x=0;x<bi.biWidth;x++){
Blue=(unsigned char )(*lpPtr++);
Green=(unsigned char )(*lpPtr++);
Red=(unsigned char )(*lpPtr++);
//反轉位圖數據中的顏色,存入新的位圖數據中
*(lpTempPtr++)=(unsigned char)(255-Blue);
*(lpTempPtr++)=(unsigned char)(255-Green);
*(lpTempPtr++)=(unsigned char)(255-Red);
}
}
}
if(hBitmap!=NULL)
DeleteObject(hBitmap);
hBitmap=CreateDIBitmap(hDc,
(LPBITMAPINFOHEADER)lpTempImgData,
(LONG)CBM_INIT,
(LPSTR)lpTempImgData+sizeof(BITMAPINFOHEADE
R) +
NumColors*sizeof(RGBQUAD),
(LPBITMAPINFO)lpTempImgData, DIB_RGB_COLORS);
if(hPalette && hPrevPalette){
SelectPalette(hDc,hPrevPalette,FALSE);
RealizePalette(hDc);
}
hf=_lcreat("c:\\invert.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;
}
2. 彩色圖轉灰度圖(color to grayscale)
我們在第二講時提到了YUV的顏色表示方法,知道在這種表示方法中,Y分量的物理含義就是亮度,它含了灰度圖的所有信息,只用Y分量就完全能夠表示出一幅灰度圖來。YUV和RGB之間有著如下的對應關系
我們利用上式,根據R,G,B的值求出Y值后,將R,G,B值都賦值成Y,就能表示出灰度圖來,這就是彩色圖轉灰度圖的原理。
先看看真彩圖。我們知道真彩圖不帶調色板,每個像素用3個字節,表示R,G,B三個分量。所以處理很簡單,根據R,G,B的值求出Y值后,將R,G,B值都賦值成Y,寫入新圖即可。再來看看帶調色板的彩色圖,我們知道位圖中的數據只是對應調色板中的一個索引值,我們只需要將調色板中的彩色變成灰度,形成新調色板,而位圖數據不用動,就可以了。
下面的程序實現了彩色圖到灰度圖的轉換,注意其中真彩圖和調色板位圖處理時的差別。
BOOL ColortoGrayScale(HWND hWnd)
{
DWORD
SrcBufSize,DstBufSize,DstLineBytes;
LPBITMAPINFOHEADER lpImgData;
LPSTR lpPtr;
HLOCAL hTempImgData;
LPBITMAPINFOHEADER lpTempImgData;
LPSTR lpTempPtr;
HDC hDc;
HFILE hf;
LONG x,y;
BITMAPFILEHEADER DstBf;
BITMAPINFOHEADER DstBi;
LOGPALETTE *pPal;
HPALETTE hPrevPalette;
HLOCAL hPal;
DWORD NewNumColors;
WORD NewBitCount;
float Y;
DWORD i;
unsigned char Red,Green,Blue,Gray;
NewNumColors=NumColors; //NewNumColors為新圖的顏色數
NewBitCount=bi.biBitCount; //NewBitCount為新圖的顏色位數
if(NumColors==0) //真彩圖
{
NewNumColors=256;
NewBitCount=8;
}
//由于顏色位數有可能發生了改變,所以要重新計算每行占用的字節數以及新圖
//的緩沖區大小
DstLineBytes=(DWORD)WIDTHBYTES(bi.biWidth*NewBit
Count);
DstBufSize=(DWORD)(sizeof(BITMAPINFOHEADER)+Ne
wNumColors*
sizeof(RGBQUAD)+(DWORD)DstLineBytes*bi.bi
Height);
//DstBf和DstBi為新的BITMAPFILEHEADER和
BITMAPINFOHEADER
//拷貝原來的頭信息
memcpy((char *)&DstBf,(char
*)&bf,sizeof(BITMAPFILEHEADER));
memcpy((char *)&DstBi,(char
*)&bi,sizeof(BITMAPINFOHEADER));
//做必要的改變
DstBf.bfSize=DstBufSize+sizeof(BITMAPFILEHEADER);
DstBf.bfOffBits=(DWORD)(NewNumColors*sizeof(RGBQUAD)+
sizeof(BITMAPFILEHEADER) +sizeof(BITMAPINFOHEADER));
DstBi.biClrUsed=0;
DstBi.biBitCount=NewBitCount;
//原圖的緩沖區的大小
SrcBufSize=bf.bfSize-sizeof(BITMAPFILEHEADER);
if((hTempImgData=LocalAlloc(LHND,DstBufSize))==NULL)
{
MessageBox(hWnd,"Error alloc memory!","Error
Message",MB_OK|
MB_ICONEXCLAMATION);
return FALSE;
}
lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgDa
ta);
lpTempImgData=(LPBITMAPINFOHEADER)LocalLock(hTe
mpImgData);
//拷貝頭信息和位圖數據
memcpy(lpTempImgData,lpImgData,DstBufSize);
//用新的BITMAPINFOHEADER替換原來的頭信息
memcpy(lpTempImgData,(char
*)&DstBi,sizeof(BITMAPINFOHEADER));
//lpPtr指向原圖的數據
lpPtr=(char *)lpImgData+sizeof(BITMAPINFOHEADER);
//lpTempPtr指向新圖的數據
lpTempPtr=(char
*)lpTempImgData+sizeof(BITMAPINFOHEADER);
//為新的調色板分配內存
hPal=LocalAlloc(LHND,sizeof(LOGPALETTE) +
NewNumColors
* sizeof(PALETTEENTRY));
pPal =(LOGPALETTE *)LocalLock(hPal);
pPal->palNumEntries =(WORD) NewNumColors;
pPal->palVersion = 0x300;
if(NumColors==0) //真彩色
for (i = 0; i < 256; i++) { //灰度從(0,0,0)到(255,255,255)
pPal->palPalEntry[i].peRed=(BYTE)i;
pPal->palPalEntry[i].peGreen=(BYTE)i;
pPal->palPalEntry[i].peBlue=(BYTE)i;
pPal->palPalEntry[i].peFlags=(BYTE)0;
*(lpTempPtr++)=(unsigned char)i;
*(lpTempPtr++)=(unsigned char)i;
*(lpTempPtr++)=(unsigned char)i;
*(lpTempPtr++)=0;
}
else
for (i = 0; i < NewNumColors; i++) { //帶調色板的彩色圖
Blue=(unsigned char )(*lpPtr++);
Green=(unsigned char )(*lpPtr++);
Red=(unsigned char )(*lpPtr++);
Y=(float)(Red*0.299+Green*0.587+Blue*0.114);
Gray=(BYTE)Y;
lpPtr++;
//從原來的調色板中的顏色計算得到Y值,寫入新的調色板
pPal->palPalEntry[i].peRed=Gray;
pPal->palPalEntry[i].peGreen=Gray;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -