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

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

Delphi で SmoothEdge

2007-12-19 20:22:30 | Delphi


C# でやったように、マスクを作って切り抜き、貼り付けをおこなう Crop SmoothEdge および
SetAlpha32 に相当する関数を Delphi でつくった。



上の画像は、マスクのエッジをぼかして滑らかにし、しかも重ね描き部分を一様にアルファ値を80%に
したものである。

GDI+ と違って、従来からの GDI では画像にアルファ値を含めて描画することはできない。
したがって、C# のときと同じ論理では Delphi では同じことはできない。 しかし、ここ
やったように、マスクさえうまく加工することができれば、画像同士の間では自前でアルファブレンドできる。
したがって、すでにライブラリに入っている MaskedOverlay() に与えるマスク画像をどうやってつくるか、
が問題である。

今回作ったのは Crop() と同じ論理でマスクをつくり、それをグレースケール専用の
GaussianBlur でぼかして SmoothEdge と同じ効果を与えるための関数二つと、
できたマスクを一様に薄めることによって SetAlpha32() と同じことをする関数、計3つ
である。

この関数をテストするために簡単なアプリをつくった。



図中の画像は、左からアルファ値が 100%、75%、50% である。また、マスクのエッジをぼかす
割合を可変にすると



左から、SmoothArea が ゼロ、1、3 である。 はめ込み部分のエッジが滑らかになっている。


今回つくった3つの関数部分のソースを記録しておく。

uses
  Clipbrd, VCLImageUtils;

function GaussianBlur8(bmp: TBitmap; zone:integer = 2): Boolean;
var
  tmp: TBItmap;
  src, dst: TBmpData8;
  d: PByte;
  w, h, i, x, y, ix, iy, range: integer;
  count, dd, gauss: single;
  gf: array of single;
begin
  result := false;
  if bmp.PixelFormat <> pf8bit then exit;
  if (zone < 1) or (zone > 30) then exit;

  w := bmp.Width;
  h := bmp.Height;

  range := zone*3;
  SetLength(gf,range+1);

  for i := 0 to range do
    gf[i] := exp(-i*i/(2*zone*zone));

  tmp := BmpClone(bmp);

  src := TBmpData8.Create(tmp);
  dst := TBmpData8.Create(bmp);


  // at first, bmp -> tmp (x blur)
  for y := 0 to h-1 do
    for x := 0 to w-1 do
    begin
      count := 0;
      dd := 0;
      for ix := x-range to x+range do
      begin
        if (ix<0) or (ix>w-1) then continue;
        d := dst[ix,y];
        gauss := gf[abs(ix-x)];
        dd := dd+d^*gauss;
        count := count+gauss;
      end;
      src[x,y]^ := AdjustByte(dd/count);
    end;

  // second, tmp -> bmp (y blur)
  for y := 0 to h-1 do
    for x := 0 to w-1 do
    begin
      count := 0;
      dd := 0;
      for iy := y-range to y+range do
      begin
        if (iy<0) or (iy>h-1) then continue;
        d := src[x, iy];
        gauss := gf[abs(iy-y)];
        dd := dd+d^*gauss;
        count := count+gauss;
      end;
      dst[x,y]^ := AdjustByte(dd/count);
    end;

  src.Free;
  dst.Free;
  tmp.Free;

  result := true;
end;

function SetAlphaOnMask8(mask8: TBitmap; Alphapercent: integer): Boolean;
var
  w, h, y, x: integer;
  m: TBmpData8;
begin
  result := false;
  if (AlphaPercent < 0) or (AlphaPercent > 100) then exit;
  if mask8.PixelFormat <> pf8bit then exit;

  w := mask8.Width;
  h := mask8.Height;

  m := TBmpData8.Create(mask8);

  for y := 0 to h-1 do
    for x := 0 to w-1 do
    if m[x, y]^ <> 0 then
      m[x, y]^ := AdjustByte(m[x, y]^ * AlphaPercent / 100.0);

  m.Free;

  result := true;
end;

function MakeMask8(maskBmp: TBitmap; maskColor: TColor; Invert: Boolean;
  AlphaPercent: integer; Tolerance: integer; SmoothArea : integer = 1): TBitmap;
var
  mask8: TBitmap;
  w, h, y, x, rr, gg, bb: integer;
  rgb: LongInt;
  s: TBmpData24;
  m: TBmpData8;
  ps: PRGBTriple;
  sa: double;
begin
  mask8 := TBitmap.Create;
  mask8.PixelFormat := pf8Bit;
  w := maskBmp.Width;
  h := maskBmp.Height;
  mask8.Width := w;
  mask8.Height := h;
  SetGrayPalette(mask8);

  rgb := ColorToRGB(maskColor);
  rr := GetRValue(rgb);
  gg := GetGValue(rgb);
  bb := GetBValue(rgb);

  s := TBmpData24.Create(maskBmp);
  m := TBmpData8.Create(mask8);

  if Invert then
  begin
    for y := 0 to h-1 do
      for x := 0 to w-1 do
      begin
        ps := s[x, y];
        sa := Sqrt((rr - ps^.rgbtRed)*(rr - ps^.rgbtRed) +
                   (gg - ps^.rgbtGreen)*(gg - ps^.rgbtGreen) +
                   (bb - ps^.rgbtBlue)*(bb - ps^.rgbtBlue));
        if sa > Tolerance then
          m[x, y]^ := 0
        else
          m[x, y]^ := 255;
      end;
  end
  else
  begin
    for y := 0 to h-1 do
      for x := 0 to w-1 do
      begin
        ps := s[x, y];
        sa := Sqrt((rr - ps^.rgbtRed)*(rr - ps^.rgbtRed) +
                   (gg - ps^.rgbtGreen)*(gg - ps^.rgbtGreen) +
                   (bb - ps^.rgbtBlue)*(bb - ps^.rgbtBlue));
        if sa > Tolerance then
          m[x, y]^ := 255
        else
          m[x, y]^ := 0;
      end;
  end;

  s.Free;
  m.Free;

  if SmoothArea > 0 then GaussianBlur8(mask8, SmoothArea);

  if AlphaPercent < 100 then SetAlphaOnMask8(mask8, AlphaPercent);

  result := mask8;
end;



MakeMask8 に与える maskBmp は以下のようなもの。





また、base 画像と overlay 画像は既出のものを流用した。

base:




overlay:





[MakeMask8 SetAlphaOnMask8 GaussianBlur8]


Comments (4)

NotGraffiti10

2007-12-19 00:27:59 | processed





[rinkaku LuminanceToAlpha SetAlpha32]


Comment

walls

2007-12-19 00:10:08 | 写真


県立美術館、吾妻2、つくば市






水上レストハウス、中央公園、つくば市



[DFA Macro 100mmF2.8]

Comment

R

2007-12-19 00:03:28 | rinkaku




Comment