16番会議室「玉石混淆みんなで作るSample蔵」に寄せられたサンプル
"ビットマップ誤差拡散で減色する"
この発言に対し以下のコメントが寄せられています
#00923 河邦 正 さん RE:ビットマップ誤差拡散で減色する
#00925 河邦 正 さん RE:ビットマップを誤差拡散で減色する(C++)
#00926 河邦 正 さん RE:ビットマップ誤差拡散で減色する
#00929 河邦 正 さん RE:ビットマップ誤差拡散で減色する
#00930 河邦 正 さん RE:ビットマップ誤差拡散で減色する(C++)
【ビットマップの誤差拡散による固定パレットへの減色】
誤差拡散法の原理については、該当する分野の解説書を探してください。
下記のサンプルでは、原理は誤差拡散ですが、変法(誤差収集?)を使用し
ています。
変法(誤差収集)のメリットは、
1:誤差データの記録が単純化する。
正統派(?)の誤差拡散では、周囲n点に誤差データを‘加算’する
ので、[誤差値の分割]+[読み出し]+[加算]+[書き込み]の3ステップ
をn回実行しますが、誤差収集法では計算した誤差値の[書き込み]だ
けで済みます。
2:誤差のバッファに書きこむ回数が減る。
その分、読み出す回数が増えるが、ライトキャッシュに対応する L1、
L2 は少数派なので、キャッシュメモリによる高速化が期待できます。
(1:の理由と関係大)
3:誤差を周囲に割り振る際に生じる値の切り捨てが少なくなる。
正統派の誤差拡散では、周囲n点に誤差データを割り振る際に端数の
切り捨てが生じます。これを元色データ側で吸収するためには元色の
データ型を拡張(例:BYTE型からInteger型)しなくてはならないが、
誤差収集法では、誤差バッファのデータ型を拡張して、元色のデータ
は型変換せずに利用できる。
でも、このメリットは肉眼では差が無いかもしれません(^^;。
でしょうか?
function ReduceBitmapColors(Dst, Src: TBitmap; NumStepsR, NumStepsG,
NumStepsB: ShortInt): Boolean;
var
Stream: TMemoryStream;
pbiDst: PBitmapInfo;
i, x, y, DstLineSize, R, G, B: Integer;
pSrc, pDst: PBYTE;
type
TRGBDiff = record
R, G, B: Integer;
end;
TRGBDiff3 = array[0..2]of TRGBDiff;
PRGBDiff3 = ^TRGBDiff3;
var
pDiffBuffer, pDiff: PRGBDiff3;
begin
i := NumStepsR * NumStepsG * NumStepsB;
Result :=
(NumStepsR > 1)and(NumStepsG > 1)and(NumStepsB > 1)and
(i <= 256)and
// Assigned(Bitmap) and
(not Src.Empty) and
(Src.PixelFormat in [pf15bit, pf16bit, pf24bit, pf32bit]);
if Result then
begin
// 減色したデータを入れるメモリストリームを作成する
Stream := TMemoryStream.Create;
try
// ストリームを減色済みビットマップの大きさにする
DstLineSize := (Src.Width + 3)and -4;
Stream.Size := sizeof(TBitmapFileHeader) // ファイルヘッダ
+ sizeof(TBitmapInfoHeader) // ビットマップヘッダ
+ (sizeof(TRGBQuad) * i) // パレットデータ
+ (DstLineSize * Src.Height); // ビットデータ
ZeroMemory(Stream.Memory, Stream.Size);
// ファイルヘッダの書き込み
with PBitmapFileHeader(Stream.Memory)^ do
begin
bfType := $4d42;
bfSize := Stream.Size;
bfOffBits := sizeof(TBitmapFileHeader)
+ sizeof(TBitmapInfoHeader)
+ sizeof(TRGBQuad) * i;
end;
// ビットマップヘッダの書き込み
pbiDst :=
Pointer(DWORD(Stream.Memory)+sizeof(TBitmapFileHeader));
with pbiDst^ do
begin
with bmiHeader do
begin
biSize := sizeof(TBitmapInfoHeader);
biWidth := Src.Width;
biHeight := Src.Height;
biPlanes := 1;
biBitCount := 8;
biClrUsed := i;
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;
Src.PixelFormat := pf24Bit;
// 誤差データ用バッファの確保
pDiffBuffer := AllocMem(sizeof(TRGBDiff) * (Src.Width + 2));
try
for y := 0 to Src.Height - 1 do
begin
// 減色したデータの書きこみ先(ポインタ)を計算する
pDst := Pointer(DWORD(Stream.Memory)
+ PBitmapFileHeader(Stream.Memory)^.bfOffBits
+ DWORD(DstLineSize * y));
// 元ビットマップの参照先(ポインタ)を記録する
pSrc := Src.ScanLine[Src.Height - y - 1];
// 誤差データのポインタ(pDiff)をバッファの先頭にする
pDiff := pDiffBuffer;
for x := 0 to Src.Width - 1 do
begin
B := pSrc^ + // 元の青色に周辺3点の誤差を加える
((pDiff^[0].B +pDiff^[1].B)* 3 +pDiff^[2].B * 2 +4)div 8;
Inc(pSrc);
G := pSrc^ + // 元の緑色に周辺3点の誤差を加える
((pDiff^[0].G +pDiff^[1].G)* 3 +pDiff^[2].G * 2 +4)div 8;
Inc(pSrc);
R := pSrc^ + // 元の赤色に周辺3点の誤差を加える
((pDiff^[0].R +pDiff^[1].R)* 3 +pDiff^[2].R * 2 +4)div 8;
Inc(pSrc);
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(pDst);
Inc(Integer(pDiff), sizeof(TRGBDiff));
end;
end;
finally
FreeMem(pDiffBuffer); // 誤差データ用バッファの解放
end;
// 減色したデータを出力先の TBitmap にロードする
Stream.Position := 0;
Dst.LoadFromStream(Stream);
finally
// 減色したデータを入れたメモリストリームの解放
Stream.Free;
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/08/28、河邦 正(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
|