FC2ブログ

ホーム > DIBファイルフォーマット > 誤差分散法によるディザ合成

誤差分散法によるディザ合成



前回の「メディアンカット法による画像の減色」での減色処理ではどうしても色が少なくなるために、グラデーションが潰れて汚くなるのは避けられません。そこで画像に誤差分散法によるディザをかけることて、グラデーションをよりきれいに見えるようにします。



誤差分散を計算する際に使用する整数形のRGBTRIPLE構造体です。

//整数形のRGBTRIPLE
typedef struct tagRGBINT{
int blue;
int green;
int red;
}RGBINT;


最大値、最小値と与えられた数値の中から、真ん中の値を得るマクロ定義です。

//最大値、最小値と与えられた数値の中から、真ん中の値を得る
#define MEDIAN(minimum,value,maximum) max(min((value),(maximum)),(minimum))


理論パレットを作成する関数です。

//理論パレットの作成関数
HPALETTE __stdcall _CreateLogicalPalette(UINT cEntries,RGBQUAD* prgbq)
{
if (cEntries<1) return FALSE;
LOGPALETTE* lpPalette=
(LOGPALETTE*)(new BYTE[sizeof(LOGPALETTE)+(cEntries-1)*sizeof(PALETTEENTRY)]);
lpPalette->palVersion=0x0300;
lpPalette->palNumEntries=(WORD)cEntries;
for (UINT i=0;i<cEntries;i++){
lpPalette->palPalEntry[i].peRed =prgbq[i].rgbRed;
lpPalette->palPalEntry[i].peGreen=prgbq[i].rgbGreen;
lpPalette->palPalEntry[i].peBlue =prgbq[i].rgbBlue;
lpPalette->palPalEntry[i].peFlags=0;
}
HPALETTE hPalette=::CreatePalette(lpPalette);
delete[]lpPalette;
return hPalette;
}


与えられたパレットDIB誤差分散法(Floyd Steinberg型)ディザ処理を施します。

//誤差分散法(Floyd Steinberg型)
BOOL __stdcall _FloydSteinberg(HBITMAP hbmPal,HBITMAP hbmRgb24,COLORREF crMask)
{
//減色前の画像のDIBSECTION構造体を取得し、
//DIBではないか、24ビットRGBDIBでない場合は、
//FALSEを返して終了する。

DIBSECTION dsRgb={0};
if ((!::GetObject(hbmRgb24,sizeof(DIBSECTION),&dsRgb))||
(dsRgb.dsBmih.biSize!=sizeof(BITMAPINFOHEADER))||
(dsRgb.dsBmih.biBitCount!=24))
return FALSE;

//減色済み画像のDIBSECTION構造体を取得し、
//DIBではないか、パレットDIBでない場合は、
//FALSEを返して終了する。

DIBSECTION dsPal={0};
if ((!::GetObject(hbmPal,sizeof(DIBSECTION),&dsPal))||
(dsPal.dsBmih.biSize!=sizeof(BITMAPINFOHEADER))||
(dsPal.dsBmih.biBitCount>8))
return FALSE;

//減色済み画像のサイズを求めて、
//減色前の画像のサイズと違えば
//FALSEを返して終了する。

UINT width=dsPal.dsBmih.biWidth;
UINT height=abs(dsPal.dsBmih.biHeight);
UINT nBPP=dsPal.dsBmih.biBitCount;
if (width!=dsRgb.dsBmih.biWidth) return FALSE;
if (height!=abs(dsRgb.dsBmih.biHeight)) return FALSE;

//減色済み理論パレットの作成
UINT cEntries=(dsPal.dsBmih.biClrUsed)?dsPal.dsBmih.biClrUsed:1<<nBPP;
RGBQUAD* pColorTable=new RGBQUAD[cEntries];

//DIBのカラーテーブルからRGB(赤、緑、青)カラーの値を取得
HDC hMemDC=::CreateCompatibleDC(0);
HGDIOBJ hOldObj=::SelectObject(hMemDC,hbmPal);
UINT uResult=::GetDIBColorTable(hMemDC,0,cEntries,pColorTable);
::SelectObject(hMemDC,hOldObj);
::DeleteDC(hMemDC);
HPALETTE hPalette=_CreateLogicalPalette(cEntries,pColorTable);

//マスクの準備
RGBQUAD mask;
if (!crMask) crMask=0x00FFFFFF;
mask.rgbBlue =(BYTE)((crMask>>16)&0xFF);
mask.rgbGreen=(BYTE)((crMask>>8 )&0xFF);
mask.rgbRed =(BYTE)( crMask &0xFF);

RGBQUAD nMask;
nMask.rgbBlue=~mask.rgbBlue;
nMask.rgbGreen=~mask.rgbGreen;
nMask.rgbRed=~mask.rgbRed;

//誤差データをライン単位で一時確保するラインバッファ
RGBINT* piBuf=new RGBINT[(width+4)*2];
::ZeroMemory(piBuf,(width+4)*2*sizeof(RGBINT));

//ディザ付けループ
// | |着目|7/16|
// |3/16|5/16|1/16|

for(UINT y=0;y<height;y++)
{
PBYTE src=(PBYTE)dsRgb.dsBm.bmBits+y*dsRgb.dsBm.bmWidthBytes;
PBYTE dst=(PBYTE)dsPal.dsBm.bmBits+y*dsPal.dsBm.bmWidthBytes;
PBYTE pdc=dst;
for(UINT x=0;x<width;x++)
{
BYTE b=*src++;
BYTE g=*src++;
BYTE r=*src++;
int index=CLR_INVALID;
int indexPal=0;
switch(nBPP){
case 8:
indexPal=*pdc++;
break;
case 4:
indexPal=(x%2==0)? *pdc>>4:(*pdc++)&0xF;
break;
case 1:
indexPal=((*pdc)>>(7-(x%8)))&0x1;
if (x%8==7) pdc++;
break;
}

//誤差分散法で新しい色の成分を算出する
int wActual=width+2;
int odd=y%2;
int here=(y%2)*wActual+x+1; //着目位置
r=MEDIAN(0,(int)r+(piBuf[here].red )-(int)nMask.rgbRed,UCHAR_MAX);
g=MEDIAN(0,(int)g+(piBuf[here].green)-(int)nMask.rgbGreen,UCHAR_MAX);
b=MEDIAN(0,(int)b+(piBuf[here].blue )-(int)nMask.rgbBlue,UCHAR_MAX);

//カラーテーブル上のインデックス番号を求める
index=::GetNearestPaletteIndex(hPalette,RGB(r,g,b));
if (index==CLR_INVALID) index=indexPal;

//インデックス番号をピクセルビットに書き込む
switch(nBPP){
case 8:
*dst++=index;
break;
case 4:
if (x%2==0) *dst=index<<4;
else *dst++|=index;
break;
case 1:
if (x%8==0) *dst=(index&1)<<7;
else *dst|=(index&1)<<(7-(x%8));
if (x%8==7) dst++;
break;
}

//原画との誤差を算出してラインバッファに保存する。
BYTE nb=pColorTable[index].rgbBlue;
BYTE ng=pColorTable[index].rgbGreen;
BYTE nr=pColorTable[index].rgbRed;
RGBINT ierr; //誤差データ
ierr.blue =(int)b-(int)nb;
ierr.green=(int)g-(int)ng;
ierr.red =(int)r-(int)nr;
r=nr;g=ng;b=nr;

//ラインバッファ上での書き込み先のポインタ
int right=here+1;          //右側
int underLeft=(((y%2)+1)%2)*wActual+x; //左下
int under=underLeft+1; //真下
int underRight=under+1; //右下

//端数の7/16を右側に加算

piBuf[right].blue +=((ierr.blue*7) >>4);
piBuf[right].green+=((ierr.green*7)>>4);
piBuf[right].red +=((ierr.red*7) >>4);

//x=0の場合は下側を初期化
if (x==0){
piBuf[under].blue =
piBuf[under].green=
piBuf[under].red =0;
}

//端数の5/16を下側に加算
piBuf[under].blue +=((ierr.blue*5) >>4);
piBuf[under].green+=((ierr.green*5)>>4);
piBuf[under].red +=((ierr.red*5) >>4);

//端数を1/16にして右下に代入
piBuf[underRight].blue =ierr.blue >>4;
piBuf[underRight].green=ierr.green>>4;
piBuf[underRight].red =ierr.red >>4;

//端数の3/16を左下側に加算
piBuf[underLeft].blue +=((ierr.blue*3)>>4);
piBuf[underLeft].green+=((ierr.green*3)>>4);
piBuf[underLeft].red +=((ierr.red*3)>>4);
}
}
::DeleteObject(hPalette);
delete[]piBuf;
delete[]pColorTable;
return TRUE;
}


戻り値がTRUEなら、ディザ合成は成功です。

サンプルプログラム(VisualC++net2003ソリューション)DIBFileTest07.zip

上記のzipファイルをダウンロードしてからWinRAR等で解凍し、DIBFileTest07フォルダー内にある「DIBFileTest.sln」を開きます。「F5」キーを押すと、ビルド確認のダイヤログが表示されるので「Yes」を選択してソリューションをビルドします。



ビルドが終わると直ちにプログラムが自動起動してウィンドウが表示されます。ウィンドウのメニューで「ファイル」/「開く」を選択して、「ファイルを開く」ダイヤログを表示させ、resフォルダー内の「GP500cc24bit.bmp」を読み込みます。



24ビットフルカラー画像が表示されます。



画像が表示されたところで「編集」/「ビット深度の変更」で「ビット深度の変更」ダイヤログを表示させ、「ビット深度」コンボボックスで8ビットを選択し、「メディアンカット」と「ディザ」の両方のチェックボックスにチェックを入れて、「OK」ボタンを押します。



256色パレットDIBにメディアンカット法で減色してから、誤差分散法によりディザ合成された画像が表示されます。



メディアンカット法による画像の減色」で作成した画像と比較して、色品質が向上しているか確認してください。



次に「Ctrl+Z」で24ビットフルカラー画像に戻しておいてから、再び「編集」/ビット深度の変更」で「ビット深度の変更」ダイヤログを表示させ、「ビット深度」コンボボックスで今度は4ビットを選択し、「メディアンカット」と「ディザ」の両方のチェックボックスがチェックされていることを確認してから、「OK」ボタンを押します。



16色パレットDIBにメディアンカット法で減色してから、誤差分散法によりディザ合成された画像が表示されます。



メディアンカット法による画像の減色」で作成した画像と比較して、色品質が向上しているか確認してください。



最後に「Ctrl+Z」で24ビットフルカラー画像に戻しておいてから、再び「編集」/ビット深度の変更」で「ビット深度の変更」ダイヤログを表示させ、「ビット深度」コンボボックスで今度は1ビットを選択し、「メディアンカット」と「ディザ」の両方のチェックボックスがチェックされていることを確認してから、「OK」ボタンを押します。



モノクロDIBにメディアンカット法で減色してから、誤差分散法によりディザ合成された画像が表示されます。



メディアンカット法による画像の減色」で作成した画像と比較して、色品質が向上しているか確認してください。




スポンサーサイト



コメント: 0

この記事へのコメント
ブログ著者にのみ知らせます。

Trackback+Pingback: 0

TrackBack URL for this entry
http://hiroshi0945.blog75.fc2.com/tb.php/18-4d261a94
Listed below are links to weblogs that reference
誤差分散法によるディザ合成 from マルチメディアファイルフォーマット

Home > DIBファイルフォーマット > 誤差分散法によるディザ合成

タグクラウド
ブロとも申請フォーム

この人とブロともになる

ブロとも一覧

このページの先頭へ戻る