?? 圖像處理.txt
字號:
pPal->palPalEntry[i].peBlue=Gray;
pPal->palPalEntry[i].peFlags=0;
*(lpTempPtr++)=(unsigned char)Gray;
*(lpTempPtr++)=(unsigned char)Gray;
*(lpTempPtr++)=(unsigned char)Gray;
*(lpTempPtr++)=0;
}
if(hPalette!=NULL)
DeleteObject(hPalette);
//生成新的邏輯調色板
hPalette=CreatePalette(pPal);
LocalUnlock(hPal);
LocalFree(hPal);
hDc=GetDC(hWnd);
if(hPalette){
hPrevPalette=SelectPalette(hDc,hPalette,FALSE);
RealizePalette(hDc);
}
if(NumColors==0) //真彩色圖才需要處理位圖數據
for(y=0;y<bi.biHeight;y++){
lpPtr=(char *)lpImgData+(SrcBufSize-LineBytes-
y*LineBytes);
lpTempPtr=(char *)lpTempImgData+(DstBufSize-
DstLineBytes-y*DstLineBytes);
for(x=0;x<bi.biWidth;x++){
Blue=(unsigned char )(*lpPtr++);
Green=(unsigned char )(*lpPtr++);
Red=(unsigned char )(*lpPtr++);
Y=(float)(Red*0.299+Green*0.587+Blue*0.114);
//從位圖數據計算得到Y值,寫入新圖中
Gray=(BYTE)Y;
*(lpTempPtr++)=(unsigned char)Gray;
}
}
if(hBitmap!=NULL)
DeleteObject(hBitmap);
//產生新的位圖
hBitmap=CreateDIBitmap(hDc,
(LPBITMAPINFOHEADER)lpTempImgData, (LONG)CBM_INIT,
(LPSTR)lpTempImgData+sizeof(BITMAPINFOHEADER) +
NewNumColors*sizeof(RGBQUAD),(LPBITMAPINFO)lpTempImgData,
DIB_RGB_COLORS);
if(hPalette && hPrevPalette){
SelectPalette(hDc,hPrevPalette,FALSE);
RealizePalette(hDc);
}
hf=_lcreat("c:\\gray.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;
}
3. 真彩圖轉256色圖(true color to 256 indexed color)
我們知道,真彩圖中包含最多達2的24次方種顏色,怎樣從中選出256種顏色,又要使顏色的失真比較小,這是一個比較復雜的問題。一種簡單的做法是將R:G:B以3:3:2表示,即取R,G的高3位,B的高兩位,組成一個字節,這樣就可以表示256種顏色了,但不難想象,這種方法的失真肯定很嚴重。我們下面介紹的算法能夠比較好的實現真彩到256色的轉換。
它的思想是:準備一個長度為4096的數組,代表4096種顏色。對圖中的每一個像素,取R,G,B的最高四位,拼成一個12位的整數,對應的數組元素加1。全部統計完后,就得到了這4096種顏色的使用頻率。這其中,可能有一些顏色一次也沒用到,即對應的數組元素為零(假設不為零的數組元素共有PalCounts個)。將這些為零的數組元素清除出去,使得前PalCounts個元素都不為零。將這PalCounts個數按從大到小的順序排列(這里我們使用起泡排序),這樣,前256種顏色就是用的最多的顏色,它們將作為調色板上的256種顏色。對于剩下的PalCounts-256種顏色并不是簡單的丟棄,而是用前256種顏色中的一種來代替,代替的原則是找有最小平方誤差的那個。再次對圖中的每一個像素,取R,G,B的最高四位,拼成一個12位的整數,如果對應值在前256種顏色中,則直接將該索引值填入位圖數據中,如果是在后PalCounts-256種顏色中,則用代替色的索引值填入位圖數據中。
下面的兩幅圖,圖3是原真彩圖,圖4是用上面的算法轉成的256色圖,可以看出,效果還不錯。
圖3. 原真彩圖 圖4. 轉換后的256色圖
下面是上述算法的源程序。
BOOL Trueto256(HWND hWnd)
{
DWORD
SrcBufSize,OffBits,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;
WORD i,j;
int Red,Green,Blue,ClrIndex;
DWORD ColorHits[4096];
WORD ColorIndex[4096];
DWORD PalCounts,temp;
long ColorError1,ColorError2;
if(NumColors!=0){ //NumColors不為零,所以不是真彩圖
MessageBox(hWnd,"Must be a true color bitmap!","Error
Message",MB_OK|
MB_ICONEXCLAMATION);
return FALSE;
}
//由于顏色位數有可能發生了改變,所以要重新計算每行占用的字節數以及新圖
//的緩沖區大小
DstLineBytes=(DWORD)WIDTHBYTES(bi.biWidth*8);
DstBufSize=(DWORD)(sizeof(BITMAPINFOHEADER)+256
*sizeof(RGBQUAD)+
(DWORD)DstLineBytes*bi.biHeight
);
//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)(256*sizeof(RGBQUAD)+sizeof(
BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER));
DstBi.biClrUsed=0;
DstBi.biBitCount=8;
//OffBits為到實際位圖數據的偏移值
OffBits=bf.bfOffBits-sizeof(BITMAPFILEHEADER);
//SrcBufSize為原圖緩沖區的大小
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(hImgData);
lpTempImgData=(LPBITMAPINFOHEADER)LocalLock(hTempImgData);
//拷貝位圖數據
memcpy(lpTempImgData,lpImgData,OffBits);
//用新的頭信息取代舊的頭信息
memcpy(lpTempImgData,(char*)&DstBi,sizeof(BITMAPINFOHEADER));
//ColorHits為記錄顏色使用頻率的數組,ColorIndex為記錄顏色索引值的數組
//先全部清零
memset(ColorHits,0,4096*sizeof(DWORD));
memset(ColorIndex,0,4096*sizeof(WORD));
for(y=0;y<bi.biHeight;y++){
lpPtr=(unsigned char *)lpImgData+(SrcBufSize-LineBytes-y*LineBytes);
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);
//相應的數組元素加1
ColorHits[ClrIndex]++;
}
}
PalCounts=0;
//將為零的元素清除出去
for (ClrIndex = 0; ClrIndex < 4096; ClrIndex++)
{
if(ColorHits[ClrIndex]!=0){
ColorHits[PalCounts]=ColorHits[ClrIndex];
//注意調整相應的索引值
ColorIndex[PalCounts]=ClrIndex;
PalCounts++; //顏色數加1
}
}
//用起泡排序將PalCounts種顏色按從大到小的順序排列
for (i = 0; i < PalCounts-1; i++)
for (j = i + 1; j < PalCounts; j++){
if (ColorHits[j] > ColorHits[i]){
temp = ColorHits[i];
ColorHits[i] = ColorHits[j];
ColorHits[j] = temp;
//注意調整相應的索引值
temp = ColorIndex[i];
ColorIndex[i] = ColorIndex[j];
ColorIndex[j] = (WORD)temp;
}
}
//為新的調色板分配內存
hPal=LocalAlloc(LHND,sizeof(LOGPALETTE) + 256* sizeof(PALETTEENTRY));
pPal =(LOGPALETTE *)LocalLock(hPal);
pPal->palNumEntries =(WORD) 256;
pPal->palVersion = 0x300;
lpTempPtr=(char *)lpTempImgData+sizeof(BITMAPINFOHEADER);
for (i = 0; i < 256; i++) {
//由12位索引值得到R,G,B的最高4位值
pPal->palPalEntry[i].peRed=(BYTE)((ColorIndex[i] & 0x00f) << 4);
pPal->palPalEntry[i].peGreen=(BYTE)((ColorIndex[i] & 0x0f0));
pPal->palPalEntry[i].peBlue=(BYTE)((ColorIndex[i] & 0xf00) >> 4);
pPal->palPalEntry[i].peFlags=(BYTE)0;
*(lpTempPtr++)=(unsigned char)((ColorIndex[i] & 0xf00) >> 4);
*(lpTempPtr++)=(unsigned char)((ColorIndex[i] & 0x0f0));
*(lpTempPtr++)=(unsigned char)((ColorIndex[i] & 0x00f) << 4);
*(lpTempPtr++)=0;
//ColorHits作為顏色記數的作用已經完成了,下面的作用是記錄12位索引值對應
//的調色板中的索引值
ColorHits[i]=i;
}
//其余的顏色依據最小平方誤差近似為前256中最接近的一種
if (PalCounts > 256){
for (i = 256; i < PalCounts; i++){
//ColorError1記錄最小平方誤差,一開始賦一個很大的值
ColorError1=1000000000;
//由12位索引值得到R,G,B的最高4位值
Blue = (long)((ColorIndex[i] & 0xf00) >> 4);
Green = (long)((ColorIndex[i] & 0x0f0));
Red = (long)((ColorIndex[i] & 0x00f) << 4);
ClrIndex = 0;
for (j = 0; j < 256; j++){
//ColorError2計算當前的平方誤差
ColorError2=(long)(Blue-pPal->palPalEntry[j].peBlue)*
(Blue-pPal->palPalEntry[j].peBlue)+(long)(Green-pPal->palPalEntry[j].peGreen)*
(Green-pPal->palPalEntry[j].peGreen)+(long)(Red-pPal->palPalEntry[j].peRed)*
(Red-pPal->palPalEntry[j].peRed);
if (ColorError2 < ColorError1){ //找到更小的了
ColorError1 = ColorError2;
ClrIndex = j; //記錄對應的調色板的索引值
}
}
//ColorHits記錄12位索引值對應的調色板中的索引值
ColorHits[i] = ClrIndex;
}
}
if(hPalette!=NULL)
DeleteObject(hPalette);
//產生新的邏輯調色板
hPalette=CreatePalette(pPal);
LocalUnlock(hPal);
LocalFree(hPal);
hDc=GetDC(hWnd);
if(hPalette){
hPrevPalette=SelectPalette(hDc,hPalette,FALSE);
RealizePalette(hDc);
}
for(y=0;y<bi.biHeight;y++){
lpPtr=(char *)lpImgData+(SrcBufSize-LineBytes-
y*LineBytes);
lpTempPtr=(char *)lpTempImgData+(DstBufSize-
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -