なんとなく、ふわっと・・

写真と画像処理関係とひとりごとをなんとなく書き溜めていきたい

こっちも忙しい

2007-10-12 00:29:32 | 写真




山木、つくば市



Comment

G

2007-10-12 00:16:11 | rinkaku




Comment

Delphi で Rinkaku Application その7

2007-10-12 00:06:11 | Delphi

前回に引き続き、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() フィルタをつくって、今回までに得られた画像に
適用してみる。

Comment