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

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

Y

2007-10-25 19:20:06 | rinkaku





Comment

TOKEI

2007-10-24 00:18:15 | 写真


谷田部総合運動公園、つくば市

この写真、なんか違和感があるなぁー、とずっと気になっていたんですが、
いまやっと気づいた。

時計の時針が変なんですね。
Exif によれば、撮影時刻は午後3:30分なんですが、分針はあっているものの、
時針が4をさしている。3と4の間になくてはならないのに。


Comment

Y

2007-10-24 00:03:39 | rinkaku





Comment

カミナリ電機

2007-10-23 00:34:08 | 写真







以上、谷田部、つくば市

個人営業の商店は、スーパーや郊外の大型店に押されて、いつのまにかなくなって
しまった。子供の頃には、普通に肉屋、魚屋、豆腐屋、八百屋、いろんな店が
あったものだが。谷田部の中心街には、まだすこしだが、これらの商店が
残っていて、なつかしいのと、すこしうれしかった。



Comment

S

2007-10-23 00:19:48 | rinkaku




Comment

Delphi で Rinkaku Application その10

2007-10-23 00:09:04 | Delphi


前回に引き続き、Turbo Delphi で Rinkaku Application をつくる、の10回目。

今回は、前回までに得られた画像のコントラスト調整をする RinkakuContrast() を
作ってテストする。

RinkakuUtils.pas に以下の関数を追加する。

function RinkakuContrast(var bmp: TBitmap; factor: integer): Boolean;
var
  w, h, x, y, i: integer;
  a: double;
  src: TBmpData8;
  d: array[0..255] of byte;
begin
  result := false;
  if bmp.PixelFormat <> pf8bit then exit;

  if (factor > 80) or (factor < 0) then exit;

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

  a := (127.5 + factor * 1.8) / (127.5 * 127.5) ;

  for i := 0 to 255 do
    if i < 128 then
      d[i] := AdjustByte( -(i-127.5)*(i-127.5)*a + 127.5 - factor)
    else
      d[i] := AdjustByte((i-127.5)*(i-127.5)*a + 127.5 - factor);


  src := TBmpData8.Create(bmp);

  for y := 0 to h-1 do
    for x := 0 to w-1 do
      src[x,y]^ := d[ src[x,y]^];

  src.Free;

  result := true;
end;


テストコードをしめす。

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, 25)
     and Contrast8(tmp, 105, 0.05)
     and AntiBlackOut(bmp, tmp, 55, 30, 0.5)
     and Kuwahara8Ex(tmp, 1, true)
     and Median8(tmp, 1)
     and SetTwoColorGrayScalePalette(tmp, RGB(44, 51, 0), RGB(255, 244, 238))
     //and RinkakuContrast(tmp, 50)
  then
  begin
    Canvas.Draw(5, 35, tmp);
    Clipboard.Assign(tmp);
  end;

  bmp.Free;
  tmp.Free;
end;



RinkakuContrast() なし。


これは、意図的に薄く塗ってある。

RinkakuContrast(tmp, 25)


RinkakuContrast(tmp, 50)


RinkakuContrast(tmp, 75)



今回つくった RinkakuContrast() の合理的な論理はない。
Rinkaku 画像は、エッジ抽出による線画に、元画像の濃淡を薄く重ねたものである。
したがって、輝度のヒストグラムは、線画部分の暗い領域がほんの少し、あとは
うすく塗りつぶした部分の比較的明るい部分が圧倒的に多い。コントラストを強調する
ためには、この比較的明るい部分の分布を拡大し、全体に暗いほうへ分布をシフトさせると
よい。これを簡単な数式と、パラメータを一つだけつかって実現した一例が
RinkakuContrast() フィルタである。

このフィルタの効果を PaintShopPro4 のヒストグラムで見てみよう。

RinkakuContrast() なし。


RinkakuContrast(tmp, 75)


だいたいねらったとおりになっているのが分かるだろう。

今回で Rinkaku Application を作るためのフィルタづくりは終わり。
次回はこのシリーズ最後で、Application を Turbo Delphi でつくる。




Comment

ハハハ

2007-10-22 00:29:40 | 写真






千歳通りフラワー公園、谷田部、つくば市


Comments (2)

H

2007-10-22 00:10:03 | rinkaku




Comment

Delphi で Rinkaku Application その9

2007-10-22 00:09:14 | Delphi


前回に引き続き、Turbo Delphi で Rinkaku Application をつくる、の9回目。

今回は、Kuwahara8() を拡張した Kuwahara8Ex() とグレースケール専用のメディアン
フィルタ Median8() をつくって、引き続きノイズの低減を目指す。さらに、
グレースケールのパレットの両端を任意の色に設定する SetTwoColorGrayScalePalette()
をつくる。

ここで説明したように、普通の Kuwahara() はカレントピクセルを含む斜め上下の
4区画について、最小の分散区画をもとめ、その区画の色の平均値をカレントの
色とする。この論理を拡張して、斜め上下と、直接の上下区画を4つ加えて、
カレントピクセルを囲む全部で8つの区画について同様にする Kuwahara8Ex() を
試してみよう。

以下の関数を RinkakuUtils.pas に追加する。

function Kuwahara8Ex(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;

  sig: array[0..7] of integer;
  sum: array[0..7] of integer;

  Xini: array[0..7] of integer;
  Yini: array[0..7] 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;
      Xini[4] := x - nBlock; Yini[4] := y - sblock;     // upper
      Xini[5] := x; Yini[5] := y - nBlock;              // right
      Xini[6] := x - nBlock; Yini[6] := y;              // lower
      Xini[7] := x - sblock; Yini[7] := y - nBlock;     // left

      for i := 0 to 7 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 7 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;


テストコードは前回と同じで Kuwahara8() を Kuwahara8Ex() に変えただけである。

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 Kuwahara8Ex(tmp, 1, true)
  then
  begin
    Canvas.Draw(5, 35, tmp);
    Clipboard.Assign(tmp);
  end;

  bmp.Free;
  tmp.Free;
end;


結果をしめす。



これは、Kuwahara8() よりわずかに良いようだ。


次に、ノイズ除去フィルタの定番である Median8() を試してみる。すでに、Delphi で
pf24bit のカラー画像用には、ここで作った。今回は、Rinkaku 画像用にグレースケール
専用の Median8() をつくる。

RinkakuUtils.pas に以下の関数を追加する。

function ByteSort(Item1, Item2: Pointer): Integer;
begin
  result := byte(Item1)-byte(Item2);
end;

function Median8(var bmp: TBitmap; area: integer = 1):Boolean;
var
  tmp:TBitmap;
  w, h, ix, iy, x, y, xx, yy: integer;
  src, dst: TBmpData8;
  ll: TList;
  md, num, indx: integer;
begin
  result := false;
  if bmp.PixelFormat <> pf8bit then exit;

  if (area<1) or (area>4) then exit;

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

  num := (2*area+1)*(2*area+1);
  md := Round(num/2);

  ll := TList.Create; ll.Capacity := num; ll.Count := num;

  tmp := BmpClone(bmp);

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

  for iy := 0 to h-1 do
    for ix := 0 to w-1 do
    begin
      indx := 0;
      for y := iy-area to iy+area do
        for x := ix-area to ix+area do
        begin
          if (y<0) or (y>h-1) then yy := iy else yy := y;
          if (x<0) or (x>w-1) then xx := ix else xx := x;

          ll[indx] := pointer(src[xx, yy]^);
          inc(indx);
        end;

      ll.Sort(ByteSort);

      dst[ix, iy]^ := byte(ll[md]);
    end;

  dst.Free;
  src.Free;

  ll.Free;

  tmp.Free;

  result := true;
end;


前回のテストコードの Kuwahara8Ex() を Median8() に変えた結果を示す。



Median8() 単独でもかなりのノイズ低減効果がある。

Kuwahara8Ex() と Median8() をこの順序で適用した結果を以下にしめす。



もうほとんど完璧かな?

元画像が良質な場合は、Kuwahara8Ex() も Median8() も適用しなくてもよい。
画像の効果として、イラスト風、絵画風を求める場合は、ノイズの有無に関係なく
Kuwahara8Ex() を適用してもよいだろう。


最後に、任意の二色をパレットの両端に設定する関数をつくる。

RinkakuUtils.pas に以下を追加する。

function SetTwoColorGrayScalePalette(var bmp: TBitmap; Dark, Bright: TColor):Boolean;
var
  i: integer;
  ct: array[0..255] of TRGBQuad;
  dr, dg, db, br, bg, bb: Byte;
begin
  result := false;
  if bmp.PixelFormat <> pf8bit then exit;

  Dark := ColorToRGB(Dark);
  Bright := ColorToRGB(Bright);
  dr := GetRValue(Dark); dg := GetGValue(Dark); db := GetBValue(Dark);
  br := GetRValue(Bright); bg := GetGValue(Bright); bb := GetBValue(Bright);
  for i := 0 to 255 do
  begin
    ct[i].rgbRed   := Round(dr+(br-dr)*i/255);
    ct[i].rgbGreen := Round(dg+(bg-dg)*i/255);
    ct[i].rgbBlue  := Round(db+(bb-db)*i/255);
    ct[i].rgbReserved := 0;
  end;
  SetDIBColorTable(bmp.Canvas.Handle,0,256,ct);
  DeleteObject(bmp.ReleasePalette);

  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);

  if Rinkaku(tmp, 15) and Contrast8(tmp, 100, 0.04) and
     AntiBlackOut(bmp, tmp, 60, 30, 0.8)
     and Kuwahara8Ex(tmp, 1, true)
     and Median8(tmp, 1)
     and SetTwoColorGrayScalePalette(tmp, RGB(50, 20, 0), RGB(255, 230, 240))
  then
  begin
    Canvas.Draw(5, 35, tmp);
    Clipboard.Assign(tmp);
  end;

  bmp.Free;
  tmp.Free;
end;


この結果は、



となる。

今回はこれまで。

次回は、いよいよフィルタづくりの最後となる Rinkaku 専用のコントラスト調整のための
フィルタをつくる。



Comment

NotGraffiti12

2007-10-21 00:29:33 | processed




[dessin + crop + smoothedge + SetAlpha]

Comment

Spirograph + InsideOut

2007-10-21 00:18:00 | processed






[Spirograph InsideOut]


Comment

Y

2007-10-21 00:03:05 | rinkaku





Comment

今日の夕暮れ

2007-10-20 21:39:31 | 写真






谷田部、つくば市


Comment

Delphi で Rinkaku Application その8

2007-10-20 00:49:08 | Delphi

前回に引き続き、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() を拡張して、より効果的なフィルタを作って試す。

Comment

羽成公園にて

2007-10-20 00:33:28 | 写真






羽成公園、観音台1、つくば市


Comment