16番会議室「玉石混淆みんなで作るSample蔵」に寄せられたサンプル
"RE:ビットマップ誤差拡散で減色する(C++)"
この発言は #00922 河邦 正 さんのビットマップ誤差拡散で減色する に対するコメントです
【ビットマップの誤差拡散による固定パレットへの減色】
*:#922で紹介したサンプルコードの C++Bulder 版です(8/28)。
*:上記(#923)の一部修正です。負の誤差値の評価が不適切な式でした。
出力画像に与える影響は少ないですが... 念のため(8/30)。
*:上記(#925)の追加修正です。Delphi用サンプルコードの実行速度に
かなり近づきました(register追加、など)。
誤差拡散法の原理については、該当する分野の解説書を探してください。
下記のサンプルでは、原理は誤差拡散ですが、変法(誤差収集?)を使用し
ています。
変法(誤差収集)のメリットは、
1:誤差データの記録が単純化する。
正統派(?)の誤差拡散では、周囲n点に誤差データを‘加算’する
ので、[誤差値の分割]+[読み出し]+[加算]+[書き込み]の3ステップ
をn回実行しますが、誤差収集法では計算した誤差値の[書き込み]だ
けで済みます。
2:誤差のバッファに書きこむ回数が減る。
その分、読み出す回数が増えるが、ライトキャッシュに対応する L1、
L2 は少数派なので、キャッシュメモリによる高速化が期待できます。
(1:の理由と関係大)
3:誤差を周囲に割り振る際に生じる値の切り捨てが少なくなる。
正統派の誤差拡散では、周囲n点に誤差データを割り振る際に端数の
切り捨てが生じます。これを元色データ側で吸収するためには元色の
データ型を拡張(例:BYTE型からInteger型)しなくてはならないが、
誤差収集法では、誤差バッファのデータ型を拡張して、元色のデータ
は型変換せずに利用できる。
でも、このメリットは肉眼では差が無いかもしれません(^^;。
でしょうか?
typedef struct{ int R, G, B; } TRGBDiff;
bool ReduceBitmapColors(Graphics::TBitmap *Dst, Graphics::TBitmap
*Src,
BYTE NumStepsR, BYTE NumStepsG, BYTE NumStepsB)
{
bool Result = FALSE;
if(Dst && Src && !Src->Empty &&
(NumStepsR > 1)&&(NumStepsG > 1)&&(NumStepsB > 1)&&
(NumStepsR * NumStepsG * NumStepsB <= 256))
{
// 元ビットマップのフォーマットを(勝手に ^^;)変更
Src->PixelFormat = pf24bit;
// 作業用メモリ(誤差データ & ビットマップ情報)の確保
int i =
// 誤差データ用バッファの大きさ
sizeof(TRGBDiff) * (Src->Width + 2)
// 出力用ビットマップの情報を入れる大きさ
+ sizeof(BITMAPINFOHEADER)
+ sizeof(TRGBQuad) * NumStepsR * NumStepsG * NumStepsB;
BYTE* pTemp = new BYTE[i];
try
{
ZeroMemory(pTemp, i);
BITMAPINFO* pbiDst =
(BITMAPINFO*)(pTemp +sizeof(TRGBDiff)*(Src->Width +2));
// ビットマップヘッダの書き込み
BITMAPINFOHEADER* pbih = (BITMAPINFOHEADER*)pbiDst;
pbih->biSize = sizeof(TBitmapInfoHeader);
pbih->biWidth = Src->Width;
pbih->biHeight = Src->Height;
pbih->biPlanes = 1;
pbih->biBitCount = 8;
pbih->biClrUsed = NumStepsR * NumStepsG * NumStepsB;
// パレットデータの書き込み
RGBQUAD *pColors = pbiDst->bmiColors;
for(int R = 0; R < NumStepsR; R++)
for(int G = 0; G < NumStepsG; G++)
for(int B = 0; B < NumStepsB; B++)
{
i = (B * NumStepsG + G) * NumStepsR + R;
pColors[i].rgbRed = (BYTE)(R * 255 / (NumStepsR - 1));
pColors[i].rgbGreen = (BYTE)(G * 255 / (NumStepsG - 1));
pColors[i].rgbBlue = (BYTE)(B * 255 / (NumStepsB - 1));
}
// 出力用ビットマップの作成(減色データは後から書き込む)
void *pDstBits;
HBITMAP hbmpDst = CreateDIBSection(
(HDC)0, pbiDst, DIB_RGB_COLORS, &pDstBits, 0, 0);
if(hbmpDst && pDstBits)
try
{
// 出力用ビットマップの1ラインのバイト数を求めておく
int DstWidthBytes = (Src->Width + 3) & -4;
for(int y = 0; y < Src->Height; y++)
{
// 減色したデータの書きこみ先(ポインタ)を計算する
BYTE* pDst = (BYTE*)((int)pDstBits + DstWidthBytes * y);
// 元ビットマップの参照先(ポインタ)を記録する
BYTE* pSrc = (BYTE*)Src->ScanLine[Src->Height - y - 1];
// 誤差データのポインタ(pDiff)をバッファの先頭にセットする
register
TRGBDiff* pDiff = (TRGBDiff*)pTemp;
for(int x = 0; x < Src->Width; x++)
{
register
int B = *pSrc++ + // 元の青色に周辺3点の誤差を加える
((pDiff->B +pDiff[1].B)* 3 +pDiff[2].B * 2)/ 8;
register
int G = *pSrc++ + // 元の緑色に周辺3点の誤差を加える
((pDiff->G +pDiff[1].G)* 3 +pDiff[2].G * 2)/ 8;
register
int R = *pSrc++ + // 元の赤色に周辺3点の誤差を加える
((pDiff->R +pDiff[1].R)* 3 +pDiff[2].R * 2)/ 8;
// 周辺3点の誤差込みで近似色のパレットを計算する
*pDst = (BYTE)
(((B * (NumStepsB - 1) + 128) / 256 * NumStepsG +
(G * (NumStepsG - 1) + 128) / 256) * NumStepsR +
(R * (NumStepsR - 1) + 128) / 256);
pDiff++;
// 理想の色とパレットの色との差を誤差として記録する
RGBQUAD* prgb = pColors + *pDst;
pDiff->R = R - prgb->rgbRed;
pDiff->G = G - prgb->rgbGreen;
pDiff->B = B - prgb->rgbBlue;
pDst++;
} // for(int x = 0;...
} // for(int y = 0;...
Result = TRUE;
}__finally{
// 減色ビットマップを出力先の TBitmap にセットする
Dst->Handle = hbmpDst;
}
}__finally{
delete pTemp; // 作業用メモリの解放
}
}
return Result;
}
実行例は、
if(OpenPictureDialog1->Execute())
{
Image1->Picture->Bitmap->LoadFromFile(
OpenPictureDialog1->FileName);
ReduceBitmapColors(
Image1->Picture->Bitmap, // 出力
Image1->Picture->Bitmap, // 入力
6, 8, 5); // この位がお勧め
}
です。
1999/09/03、河邦 正(GCC02240@nifty.ne.jp)
(http://member.nifty.ne.jp/kht0000/ 自作ComponentのNifty外へ公開用)
Original document by 河邦 正 氏 ID:(GCC02240)
ここにあるドキュメントは NIFTY SERVEの Delphi Users' Forum の16番会議室「玉石混淆みんなで作るSample蔵」に投稿されたサンプルです。これらのサンプルはボーランド株式会社がサポートする公式のものではありません。また、必ずしも動作が検証されているものではありません。これらのサンプルを使用したことに起因するいかなる損害も投稿者、およびフォーラムスタッフはその責めを負いません。使用者のリスクの範疇でご使用下さい。
Copyright 1996-2002 Delphi Users' Forum
|