山木、つくば市
前回に引き続き、Turbo Delphi で Rinkaku Application をつくる、の7回目。
今回は、元画像の濃淡を利用して、前回までで得られた線画の上から
塗りつぶす AntiBlackOut() をつくる。
RinkakuUtils.pas に以下の関数を追加する。
function AntiBlackOut(originalBmp: TBitmap; var rinkakuBmp: TBitmap; threshold, alphaPercent: integer; decayPercent: double): Boolean; var w, h, sum, count, x, y, ix, iy: integer; alpha, decay, a: double; tmp: TBitmap; src, dst: TBmpData8; begin result := false; if originalBmp.PixelFormat <> pf24bit then exit; if rinkakuBmp.PixelFormat <> pf8bit then exit; if (threshold < 0) or (threshold > 255) then exit; if (alphaPercent < 0) or (alphaPercent > 100) then exit; w := originalBmp.Width; h := originalBmp.Height; tmp := BmpClone(originalBmp); GrayScale(tmp); alpha := alphaPercent / 100.0; decay := alpha * decayPercent / 100.0; src := TBmpData8.Create(tmp); dst := TBmpData8.Create(rinkakuBmp); for y := 0 to h-1 do for x := 0 to w-1 do begin sum := 0; count := 0; for iy := y-2 to y+2 do for ix := x-2 to x+2 do begin if (ix<0) or (ix>w-1) or (iy<0) or (iy>h-1) then continue; sum := sum + src[ix, iy]^; Inc(count); end; sum := sum div count; if (sum < threshold) then dst[x, y]^ := Min(dst[x, y]^, AdjustByte(dst[x, y]^ * (1.0 - alpha) + src[x, y]^ * alpha)) else begin if (decay < 0.00000001) then continue; a := alpha - decay * (sum - threshold); if (a > 0) then dst[x, y]^ := Min(dst[x, y]^, AdjustByte(dst[x, y]^ * (1.0 - a) + src[x, y]^ * a)); end; end; dst.Free; src.Free; tmp.Free; result := true; end;
最初のテストコードをしめす。
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); //Median(tmp); if Rinkaku(tmp, 15) and Contrast8(tmp, 100, 0.04) and AntiBlackOut(bmp, tmp, 60, 30, 0.8) then begin Canvas.Draw(5, 35, tmp); Clipboard.Assign(tmp); end; bmp.Free; tmp.Free; end;
この結果は、
となる。コントラストを調整すると
となる。ノイズがすこし多いことを除けば、かなり完成にちかづいた。
今回つくった AntiBlackOut() の最後の三つのパラメータは重要である。
threshold は、塗りつぶす際に、0-255の輝度のうち、元画像が
この threshold 以下の場合は、線画の濃度と比較して濃いほうを新しい
カレントピクセルの色とする。これを試してみよう。
コントラストを同じだけ調整した結果をしめす。
AntiBlackOut(bmp, tmp, 50, 30, 1.2)
AntiBlackOut(bmp, tmp, 120, 30, 1.2)
このように、髪などの暗い部分の陰影を重視するときには threshold を
小さめに、顔などの明るい部分のグラデーションを重視するときは大きめ
に設定する必要がある。
alphaPercent は、線画の上に重ねる割合を設定する。50 では、半分の濃さ
で塗りつぶす。あまり濃くすると、線画を取得した意味がないので、20から
50の間くらいが適当である。
AntiBlackOut(bmp, tmp, 60, 25, 0.8)
AntiBlackOut(bmp, tmp, 60, 45, 0.8)
このように、線画と塗りつぶしの相対的な濃さを決めるのが alphaPercent である。
decayPercent は、threshold 以上の濃さの減衰の速さを決める。これが大きいと
すぐに塗りつぶしは終わり、相対的に二値化にちかい漫画のような画像になる。
小さいと普通のグレイスケールの画像に近くなり、明るい部分のグラデーションが
相対的に濃く表現される。0.5 から 3.0 くらいの間が適当である。
AntiBlackOut(bmp, tmp, 60, 40, 0.5)
AntiBlackOut(bmp, tmp, 60, 40, 2.5)
このように、AntiBlackOut() のパラメータは、できあがりの画像に大きな影響を
及ぼすので三つのパラメータの設定を慎重に決める。
今回はここまで。
依然として、ノイズが取りきれていない。画像の濃淡を利用したノイズ除去は
S字型とコントラスト調整以上のことはできないので、カレントピクセルの
周囲の色を参照して平均化を行ってノイズを除去してみよう。
次回は、エッジを保存する平均化・ノイズ除去フィルタである Kuwahara8() と
それを拡張した Kuwahara8Ex() フィルタをつくって、今回までに得られた画像に
適用してみる。