前回に引き続き、Turbo Delphi で Rinkaku Application をつくる、の8回目。
今回は、前回までで取りきれないかも知れないノイズを、エッジを保つ平均化である
Kuwahara() フィルタでつくって処理することを試す。
Kuwahara() 自体の論理は、C# ですでにここで説明したので繰り返すことはしない。
今回は、これをグレースケールに変換したここと同等なものを Turbo Delphi で
つくる。ただし、C# のときは手抜きしていた統計処理を今回は厳密に標本分散を
計算して、それが最小な区画から平均をとることにする。
さっそく実装しよう。RinkakuUtils.pas に以下の関数を追加する。
function Kuwahara8(var bmp: TBitmap; nBlock: integer; bDetail: Boolean = true): Boolean; var w, h, x, y, i, ix, iy, block, sblock, numBlock: integer; indx, min, d: integer; t: double; tmp: TBitmap; src, dst: TBmpData8; sum: array[0..3] of integer; sig: array[0..3] of integer; Xini: array[0..3] of integer; Yini: array[0..3] of integer; begin result := false; if bmp.PixelFormat <> pf8bit then exit; if (nBlock > 8) or (nBlock < 1) then exit; w := bmp.Width; h := bmp.Height; block := nBlock * 2 + 1; sblock := block - 1; numBlock := block * block; tmp := TBitmap.Create; tmp.PixelFormat := pf24bit; tmp.Width := w + sblock * 2; tmp.Height := h + sblock * 2; tmp.Canvas.Draw(sblock, sblock, bmp); GrayScale(tmp); src := TBmpData8.Create(tmp); dst := TBmpData8.Create(bmp); for y := 0 to h-1 do for x := 0 to w-1 do begin Xini[0] := x - sblock; Yini[0] := y - sblock; // upper-left Xini[1] := x; Yini[1] := y - sblock; // upper-right Xini[2] := x; Yini[2] := y; // lower-right Xini[3] := x - sblock; Yini[3] := y; // lower-left; for i := 0 to 3 do begin sum[i] := 0; for ix := Xini[i] to Xini[i] + sblock do for iy := Yini[i] to Yini[i] + sblock do sum[i] := sum[i] + src[ix+sblock, iy+sblock]^; sum[i] := sum[i] div numBlock; sig[i] := 0; for ix := Xini[i] to Xini[i] + sblock do for iy := Yini[i] to Yini[i] + sblock do begin d := src[ix+sblock, iy+sblock]^; sig[i] := sig[i] + (sum[i] - d) * (sum[i] - d); end; sig[i] := sig[i] div numBlock; end; min := 90000; indx := 0; for i := 0 to 3 do if (sig[i] < min) then begin min := sig[i]; indx:= i; end; if (bDetail) then begin t := Max(0.5, 1.0 - Sqrt(sig[indx]) / 60); dst[x, y]^ := AdjustByte(t * sum[indx] + (1 - t) * dst[x, y]^); end else dst[x, y]^ := AdjustByte(sum[indx]); end; dst.Free; src.Free; tmp.Free; result := true; end;
この実装の仕方は C# のときとは違う。カレントピクセルを含む小区画の
サイズは nBlock で設定するが、今回は nBlock*2 - 1 であり、nBlock = 1 は
C# のときの2、nBlock = 2 は C# のときの4に相当する。 通常は、nBlock = 1 で
十分である。
このテストコードを以下に示す。
uses VCLImageUtils, RinkakuUtils, Clipbrd; procedure TForm1.Button1Click(Sender: TObject); var bmp, tmp: TBitmap; begin bmp := LoadPng('C:\Home\ImgWork\RaceQueen.png'); if not Assigned(bmp) then exit; bmp.PixelFormat := pf24bit; tmp := BmpClone(bmp); if Rinkaku(tmp, 15) and Contrast8(tmp, 100, 0.04) and AntiBlackOut(bmp, tmp, 60, 30, 0.8) and Kuwahara8(tmp, 1, true) then begin Canvas.Draw(5, 35, tmp); Clipboard.Assign(tmp); end; bmp.Free; tmp.Free; end;
結果は
となる。Kuwahara8() を適用しない
と比べると、エッジの周辺、顔のぶつぶつなどが効果的に軽減されていることが分かるだろう。
なお、上の二つは少し同じだけコントラストを強めている。
bDetail = false のときには
となる。最初の画像と比べると、平均化が大きく、ノイズがより低減されているが
歯茎や毛先などのディテールが失われていて、より絵画的になっている。
今回はここまで。
次回は Kuwahara8() を拡張して、より効果的なフィルタを作って試す。
※コメント投稿者のブログIDはブログ作成者のみに通知されます