フィルター処理


画像のフィルター処理は,「画像の処理と認識」(安居院猛,長尾智晴,昭晃堂,1992)に次のように 定義されている.

画像のフィルタリング(フィルター処理)とは,画像上の任意の画素(i,j)の 新しい階調値g(i,j)を,画素(i,j)の近傍の画素の階調値から決定する局所演算を 画像上のすべての画素に対して実行することによって,エッジの強調や雑音処理などの 画像処理を実行する操作である. このとき,各点に対する演算は全く独立に行われ,各画素の近傍は画像全体に比較して 十分小さい.

エッジの強調あるいは抽出とは,階調値が大きく変化するところ,すなわち物体の輪郭(外形線)を 強調して表示させることである. エッジが強調された画像は,ちょうど彫刻刀で彫った木版画のような感じになる. 処理の詳細は上記文献等に譲るとして,ここでは実際のコーディング例を示す.

画素(i,j)の現在の階調値f(i,j)から 新たな階調値g(i,j)を求めるフィルタとしては,以下のものが挙げられる.

フィルター処理を行う以下のような関数を作ってみた.

// フィルタの種類を表す新しい型を定義
type
  TFilterMethod = ( fmLinearX, fmLinearY, fmLinearMagnitude, fmLaplacian );

// 2次元の動的配列を定義.画素の階調値を格納する.
var
  ToneData: array of array of Byte;

function PerformFiltering( Org, Filtered: TBitmap;
                           Method: TFilterMethod; HalfTone: Boolean ): Boolean;
var
  P: PByte;
  X, Y, XDiff, YDiff, R: Integer;
begin
   // 原画像が256階調のモノクロ画像でなければ処理を中止する
   if Org.PixelFormat <> pf8bit then
   begin
      MessageDlg( '8ビットのモノクロ画像を指定してください', mtError,
          [ mbOK ], 0 );
      Result := False;
      Exit;
   end;

   // 動的配列 ToneData の大きさを確保
   SetLength( ToneData, Org.Width, Org.Height );
   try
     // ToneData に画素の階調値を代入する
     for Y := 0 to Org.Height - 1 do
     begin
        P := Org.ScanLine[ Y ];
        for X := 0 to Org.Width - 1 do
        begin
           ToneData[ X, Y ] := P^;
           Inc( P );
        end;
     end;

     // CreateCompatibleBitmap APIを使って,原画像と互換性が
     // あるビットマップを作成する
     Filtered.Handle := CreateCompatibleBitmap( Org.Canvas.Handle,
                       Org.Width, Org.Height );
     for Y := 1 to Filtered.Height - 2 do
     begin
        P := Filtered.ScanLine[ Y ];
        for X := 1 to Filtered.Width - 2 do
        begin
           // フィルタの種類に応じて新たな階調値を計算
           if Method <> fmLaplacian then
           begin
              XDiff := - ToneData[ X - 1, Y - 1 ] + ToneData[ X + 1, Y - 1 ]
                       - ToneData[ X - 1, Y ]     + ToneData[ X + 1, Y ]
                       - ToneData[ X - 1, Y + 1 ] + ToneData[ X + 1, Y + 1 ];
              YDiff := - ToneData[ X - 1, Y - 1 ] - ToneData[ X, Y - 1 ]
                       - ToneData[ X + 1, Y - 1 ] + ToneData[ X - 1, Y + 1 ]
                       + ToneData[ X , Y + 1 ]    + ToneData[ X + 1, Y + 1 ];
              case Method of
               fmLinearX: R := XDiff;
               fmLinearY: R := YDiff;
               else       R := Round( Sqrt( XDiff * XDiff + YDiff * YDiff ) );
              end;
           end
           else begin
              R := ToneData[ X, Y - 1 ] + ToneData[ X - 1, Y ]
                 + ToneData[ X + 1, Y ] + ToneData[ X, Y + 1 ]
                 - 4 * ToneData[ X, Y ];
           end;
           // HalfTone が True ならば階調値を 128 だけ増加させる
           if HalfTone then Inc( R, 128 );
           
           // R が 負の場合は強制的に 0 にする
           if R < 0 then R := 0;
           
           // R が 255 よりも大きい場合は強制的に 255 にする
           if R > 255 then R := 255;
           
           P^ := R;
           Inc( P );
        end;
     end;

    finally
      Finalize( ToneData );
    end;
    Result := True;
end;

上のコードでは,原画像のバイトデータを動的に確保した2次元配列にコピーし, 2次元配列のデータを使って画素(i,j)の新しい階調値を計算している. 原画像のCanvas.Pixelsプロパティを使えば,わざわざ配列にデータをコピーしなくても 新たな階調値を計算することはできるが,Pixelsプロパティの参照は オーバーヘッドが大きいため,フィルタ処理のように演算回数が多い場合は処理時間が長くなる. 上のコードは配列を使う分メモリの使用量は多くなるが,処理は速い.

また,新たな階調値を代入する場合も,Pixelsプロパティを使わず, ScalLineプロパティでバイトデータに直接アクセスしている.

さらに,CreateCompatibleBitmap APIを使って 原画像と互換性のあるビットマップを生成している. このAPIを使えば,原画像のグラフィックオブジェクトと同じものを持つビットマップを 作成することができる.

各フィルタによる処理結果は次のようになる. 今回の例では2次微分フィルタが最も鮮明に輪郭を抽出できているように見える.
原画像
線形1次微分フィルタ(水平方向)
線形1次微分フィルタ(垂直方向)
線形1次微分フィルタ(微分の大きさ)
線形2次微分フィルタ


お問い合わせはメールにて: akasaka@klc.ac.jp

戻る
SEO [PR] 爆速!無料ブログ 無料ホームページ開設 無料ライブ放送