16番会議室「玉石混淆みんなで作るSample蔵」に寄せられたサンプル
"RE:ビットマップ誤差拡散で減色する"
この発言は #00922 河邦 正 さんのビットマップ誤差拡散で減色する に対するコメントです
【ビットマップの誤差拡散による固定パレットへの減色】
*:#922の一部修正です。負の誤差値の評価が不適切な式でした。
出力画像に与える影響は少ないですが... 念のため。
*:#926 からやや汎用性のあるコードに変更(Streamを使わない)
誤差拡散法の原理については、該当する分野の解説書を探してください。
下記のサンプルでは、原理は誤差拡散ですが、変法(誤差収集?)を使用し
ています。
変法(誤差収集)のメリットは、
1:誤差データの記録が単純化する。
正統派(?)の誤差拡散では、周囲n点に誤差データを‘加算’する
ので、[誤差値の分割]+[読み出し]+[加算]+[書き込み]の3ステップ
をn回実行しますが、誤差収集法では計算した誤差値の[書き込み]だ
けで済みます。
2:誤差のバッファに書きこむ回数が減る。
その分、読み出す回数が増えるが、ライトキャッシュに対応する L1、
L2 は少数派なので、キャッシュメモリによる高速化が期待できます。
(1:の理由と関係大)
3:誤差を周囲に割り振る際に生じる値の切り捨てが少なくなる。
正統派の誤差拡散では、周囲n点に誤差データを割り振る際に端数の
切り捨てが生じます。これを元色データ側で吸収するためには元色の
データ型を拡張(例:BYTE型からInteger型)しなくてはならないが、
誤差収集法では、誤差バッファのデータ型を拡張して、元色のデータ
は型変換せずに利用できる。
でも、このメリットは肉眼では差が無いかもしれません(^^;。
でしょうか?
function ReduceBitmapColors(Dst, Src: TBitmap; NumStepsR, NumStepsG,
NumStepsB: ShortInt): Boolean;
var
pbiDst: PBitmapInfo;
pDstBits: Pointer;
hbmpDst: HBITMAP;
x, y, R, G, B, DstWidthBytes: Integer;
pSrc: PRGBQuad;
pDst: PBYTE;
type
TRGBDiff = record
R, G, B: Integer;
end;
TRGBDiff3 = array[0..2]of TRGBDiff;
PRGBDiff3 = ^TRGBDiff3;
var
pTemp, pDiff: PRGBDiff3;
begin
Result := FALSE;
if Assigned(Dst)and Assigned(Src)and(not Src.Empty)and
(NumStepsR > 1)and(NumStepsG > 1)and(NumStepsB > 1)and
(NumStepsR * NumStepsG * NumStepsB <= 256)then
begin
// 元ビットマップのフォーマットを(勝手に ^^;)変更する
Src.PixelFormat := pf24Bit;
// 作業用メモリ(誤差データ & ビットマップ情報)の確保
pTemp := AllocMem(
// 誤差データ用バッファの大きさ
sizeof(TRGBDiff) * (Src.Width + 2)
// 出力用ビットマップの情報を入れる大きさ
+ sizeof(TBitmapInfoHeader)
+ sizeof(TRGBQuad) * NumStepsR * NumStepsG * NumStepsB);
try
pbiDst := Pointer(Integer(pTemp) +
sizeof(TRGBDiff) * (Src.Width + 2));
// 出力用ビットマップの情報を用意する
with pbiDst^ do
begin
// ヘッダ情報の書き込み
with bmiHeader do
begin
biSize := sizeof(TBitmapInfoHeader);
biWidth := Src.Width;
biHeight := Src.Height;
biPlanes := 1;
biBitCount := 8;
biClrUsed := NumStepsR * NumStepsG * NumStepsB;
end;
// パレットデータの書き込み
for R := 0 to NumStepsR - 1 do
for G := 0 to NumStepsG - 1 do
for B := 0 to NumStepsB - 1 do
with bmiColors[(B * NumStepsG + G) * NumStepsR + R] do
begin
rgbRed := R * 255 div (NumStepsR - 1);
rgbGreen := G * 255 div (NumStepsG - 1);
rgbBlue := B * 255 div (NumStepsB - 1);
end;
end; // with pbiDst^ do
// 出力用ビットマップの作成(減色データは後から書き込む)
hbmpDst := CreateDIBSection(
HDC(0), pbiDst^, DIB_RGB_COLORS, pDstBits, 0, 0);
if(hbmpDst <> HBITMAP(0))and(pDstBits <> nil)then
try
// 出力用ビットマップの1ラインのバイト数を求めておく
DstWidthBytes := (Src.Width + 3) and -4;
for y := 0 to Src.Height - 1 do
begin
// 減色したデータの書きこみ先(ポインタ)を計算する
pDst := Pointer(Integer(pDstBits) + DstWidthBytes * y);
// 元ビットマップの参照先(ポインタ)を記録する
pSrc := Src.ScanLine[Src.Height - y - 1];
// 誤差データのポインタ(pDiff)をバッファの先頭にする
pDiff := pTemp;
for x := 0 to Src.Width - 1 do
begin
B := pSrc^.rgbBlue + // 元の青色に周辺3点の誤差を加える
((pDiff^[0].B +pDiff^[1].B)* 3 +pDiff^[2].B * 2)div 8;
G := pSrc^.rgbGreen + // 元の緑色に周辺3点の誤差を加える
((pDiff^[0].G +pDiff^[1].G)* 3 +pDiff^[2].G * 2)div 8;
R := pSrc^.rgbRed + // 元の赤色に周辺3点の誤差を加える
((pDiff^[0].R +pDiff^[1].R)* 3 +pDiff^[2].R * 2)div 8;
pDst^ := // 周辺3点の誤差込みで近似色のパレットを求める
((B * (NumStepsB - 1) + 128) div 256 * NumStepsG +
(G * (NumStepsG - 1) + 128) div 256) * NumStepsR +
(R * (NumStepsR - 1) + 128) div 256;
// 理想の色とパレットの色との差を誤差として記録する
with pbiDst^.bmiColors[pDst^] do
begin
pDiff^[1].R := R - rgbRed;
pDiff^[1].G := G - rgbGreen;
pDiff^[1].B := B - rgbBlue;
end;
// ポインタを次のピクセルに移動する
Inc(Integer(pSrc), 3);
Inc(pDst);
Inc(Integer(pDiff), sizeof(TRGBDiff));
end; // for x := 0 to...
end; // for y := 0 to...
Result := TRUE;
finally
// 減色ビットマップを出力先の TBitmap にセットする
Dst.Handle := hbmpDst;
end;
finally
FreeMem(pTemp); // 作業用メモリの解放
end;
end;
end;
実行例は、
with PictureDialog1 do
if Execute then
begin
Image1.Picture.Bitmap.LoadFromFile(FileName);
ReduceBitmapColors(
Image1.Picture.Bitmap, // 出力
Image1.Picture.Bitmap, // 入力
6, 8, 5 // これくらいがお勧めです。
);
end;
です。
P.S.
私作のコンポーネント、BlendImage で公開した実装より少しだけ高速化
(10〜20%)しています。
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
|