Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2012年12月17日] [更新 2017年05月15日] GDI+ 関係サンプル
GDI+ 関係サンプル 
G120_ガウシアンぼかしと Delphi 2009 GDI+ Library
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
G120_GDIPlus_GaussianBlur.zip [1,179 KB] 2017年05月15日版 (EXE 同梱)







Delphi 2009 GDI+ Library


Delphi 2009 GDI+ Library は、Delphi 2009 以降で、GDI+ 1.1 の機能を利用するためのライブラリです。これを使用してガウシアンぼかしの処理を行うサンプルです。

ガウシアンぼかし (GaussianBlur) の処理用のクラス、TBlur 等は、GDI+ 1.1 で追加された機能です。GDI+ 1.1 は Windows Vista 以降で使用可能となっています。
サンプルのプロジェクトでは、Delphi 2009 GDI+ Library を提供しているサイトの記事の説明に従って、[プロジェクト] [オプション] で以下の設定を行っています。
  • [Delphi コンパイラ] [条件定義] に [GDIP_0110]を追加
  • [アプリケーション] [ランタイムテーマを有効にする] のチェックを外す
この設定は、GDI+ 1.1 で追加された機能を使用しなければ必要ありません。

Delphi の関数やメソッド類は、バージョンが違っていても、同じ名前であれば、ほとんど場合、変更なしに使用できます。一方、GDI+ 1.0 と GDI+ 1.1 は、関数名が同じでも、引数の仕様が異なることがあります。移植には注意が必要です。
筆者の個人的な感想としては、後発の GDI+ 1.1 の方が使いやすいようです。


図1
条件定義を追加
  • ビルド構成で [Base] を選択
  • [条件定義] に GDIP_0110 を追加
  • 追加したら、[OK] でダイアログを閉じる





01_ガウシアンぼかし ( ガウスぼかし )


Delphi 2009 GDI+ Library には、多くのデモプログラムが添付されています。その中に、下図のようなフォルダに入ったデモがあります。
以下のサンプルは、この [Bitmap Effects] にある uDemoBlur.pas のコードを、ほんの少しだけ修正して使用したものです。

これらのデモ用のソースコードは、添付のデモプログラムでも使用されていて、TDemo というクラスの派生クラスとなっています。今回のテストでは、この部分のコードは必要ないので削除しています。


図2
デモ用のコード類

作成したサンプルを実行した結果です。
このサンプルと同じような方法で、他のデモ用のソースコードも利用できます。


図3
設計時画面
図4
実行画面

リスト1
ガウシアンぼかし ( ガウスぼかし )
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, GdiPlus, ComCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    RadioGroup1: TRadioGroup;
    CheckBox1: TCheckBox;
    procedure FormCreate(Sender: TObject);
    procedure FormPaint(Sender: TObject);
    procedure RadioGroup1Click(Sender: TObject);
    procedure CheckBox1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Self.Caption := 'GDI+  ガウシアンぼかし';
  DoubleBuffered := True;

  RadioGroup1.ItemIndex := 0;
end;

//=============================================================================
//  RadioGroup1のOnClickイベント
//=============================================================================
procedure TForm1.RadioGroup1Click(Sender: TObject);
begin
  Invalidate;
end;

//=============================================================================
//  CheckBox1のOnClickイベント
//=============================================================================
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
  Invalidate;
end;

//=============================================================================
//  OnPaintイベント
//  実際の描画コード
//=============================================================================
procedure TForm1.FormPaint(Sender: TObject);
var
  LGPraphics    : IGPGraphics;
  LGPBitmap     : IGPBitmap;
  LGPBlur       : IGPBlur;
  LGPBlurParams : TGPBlurParams;
begin
  //このCanvasを描画先にしてTGPGraphicsを生成
  LGPraphics := TGPGraphics.Create(Canvas.Handle);

  //画像ファイルを指定して、TGPBitmapのオブジェクトに読み込んで表示
  LGPBitmap := TGPBitmap.Create('PenguinsB.jpg');
  LGPraphics.DrawImage(LGPBitmap, 10, 90, LGPBitmap.Width, LGPBitmap.Height);

  //ぼかし用のオブジェクトを生成
  LGPBlur := TGPBlur.Create;

  //RadioGroup1のItemIndexに応じて半径を設定
  case RadioGroup1.ItemIndex of
    0:   LGPBlurParams.Radius := 5;
    1:   LGPBlurParams.Radius := 10;
    2:   LGPBlurParams.Radius := 20;
    3:   LGPBlurParams.Radius := 30;
  end;

  //ExpandEdgeの設定
  LGPBlurParams.ExpandEdge := CheckBox1.Checked;
  //設定したパラメータをTBlurオブジェクトに渡す
  LGPBlur.Parameters := LGPBlurParams;

  //画像に、TBlurの内容を適用する
  LGPBitmap.ApplyEffect(LGPBlur);
  //画像を表示する
  LGPraphics.DrawImage(LGPBitmap,
                       10,
                       LGPBitmap.Height + 100,
                       LGPBitmap.Width,
                       LGPBitmap.Height);
  //インターフェイス型のオブジェクトなので、解放処理は不要
end;

end.


パスやリージョンを使用すると、下図のように、画像の一部だけを表示し、あたかもその部分だけ「ぼかし」をしていないようにすることもできます。


図5
あるペンギンだけを特別扱い

リスト2
パスで画像の一部を描画 1
procedure TForm1.FormPaint(Sender: TObject);
var
  LGraphics     : IGPGraphics;
  LGPBitmap     : IGPBitmap;
  LGPBlur       : IGPBlur;
  LGPBlurParams : TGPBlurParams;
  LGPPath       : IGPGraphicsPath;
  LResGPCBitmap : IGPBitmap;
begin
  //このCanvasを描画先にしてTGPGraphicsを生成
  LGraphics := TGPGraphics.Create(Canvas.Handle);

  //画像ファイルを指定して、TGPBitmapのオブジェクトに読み込む
  LGPBitmap  := TGPBitmap.Create('PenguinsB.jpg');
  LResGPCBitmap := LGPBitmap.Clone;

  //ぼかし用のオブジェクトを生成してパラメータを設定
  LGPBlur := TGPBlur.Create;
  LGPBlurParams.Radius := 10;
  LGPBlurParams.ExpandEdge := False;
  //設定したパラメータをTBlurオブジェクトに渡す
  LGPBlur.Parameters := LGPBlurParams;

  //画像に、TBlurの内容を適用する
  LGPBitmap.ApplyEffect(LGPBlur);
  //画像を表示する
  LGraphics.DrawImage(LGPBitmap, 10, 10, LGPBitmap.Width, LGPBitmap.Height);

  //パスを生成してクリップ領域とし、描画
  LGPPath := TGPGraphicsPath.Create;
  LGPPath.AddEllipse(50, 20, 70, 70);
  LGraphics.SetClip(LGPPath);
  LGraphics.DrawImage(LResGPCBitmap, 10, 10, LGPBitmap.Width, LGPBitmap.Height);
end;


逆に、元の画像を表示してから、クリッピングしてぼかした画像を重ねれば、部分的に「ぼかし」たことになります。


図6
部分的に「ぼかす」

リスト3
パスで画像の一部を描画 2
procedure TForm1.FormPaint(Sender: TObject);
var
  LGPGraphics   : IGPGraphics;
  LGPBitmap     : IGPBitmap;
  LGPBlur       : IGPBlur;
  LGPBlurParams : TGPBlurParams;
  LGPPath       : IGPGraphicsPath;
begin
  //このCanvasを描画先にしてTGPGraphicsを生成
  LGPGraphics := TGPGraphics.Create(Canvas.Handle);

  //画像ファイルを指定して、TGPBitmapのオブジェクトに読み込んで表示
  LGPBitmap  := TGPBitmap.Create('PenguinsB.jpg');
  LGPGraphics.DrawImage(LGPBitmap, 10, 10, LGPBitmap.Width, LGPBitmap.Height);

  //バスを生成してクリップ
  LGPPath := TGPGraphicsPath.Create;
  LGPPath.AddEllipse(50, 20, 70, 70);
  LGPGraphics.SetClip(LGPPath);

  //ぼかし用のオブジェクトを生成してパラメータを設定
  LGPBlur := TGPBlur.Create;
  LGPBlurParams.Radius := 10;
  LGPBlurParams.ExpandEdge := False;
  LGPBlur.Parameters := LGPBlurParams;

  LGPBitmap.ApplyEffect(LGPBlur);
  LGPGraphics.DrawImage(LGPBitmap, 10, 10, LGPBitmap.Width, LGPBitmap.Height);
end;




02_Delphi 2007 以前で画像効果の機能を実装


Windows XP、あるいは Delphi 2007 以前でガウシアンぼかし等の画像処理を実装するには、自前でコーディングする必要があります。
サードパーティ製のコード類を使用する他、サンプルコードを提供しているサイトがあります。例えば Torry's DELPHI にもコンポーネント類がありますが、 TEffects - graphics effects in Delphi という、下図のコンポーネントをテストしてみました。コンポーネントとしての登録はしないで、実行時に生成しています。
これらは、TBitmap に対する操作となっています。TGPBitmap 用のコードにして利用した方がいいのですが、ここではそのまま使用しています。

JUNKI さんのサイトにもユーティリティがありますが、現在はダウンロードができない状態です。参考用に添付しておきました。


図7
TEffects コンポのファイル構成
  • [Indtall] 階層下の各フォルダ内のファイルを全て、[TEffects] の直下にコピー
  • [TEffects] フォルダにパスを通す

作成したサンプルを実行した結果です。
上の図のように、コンポ関係のファイル全てをパスの通ったフォルダに置き、uses に Effects を追加しています。
TEffects.Blur メソッドの処理半径の設定は、最大 5 となっていますが、メソッド内のコードを大きい値に変更すれば、処理可能です。

このサンプルでは、TGPGraphics を使用して描画していますが、単にビットマップを処理するだけ、あるいは、処理したビットマップを表示するだけであれば、その分のコードは不要です。メモリストリームに格納したり、別のビットマップを用意する必要はありません。
画像の一部分だけを「ぼかす」のに、GDI+ のような処理はできません。処理前と処理後のビットマップから、Halbow 資料館のサンプルのように、リージョンで処理するか、矩形領域であれば、画像の一部分だけの画像を作成して、それを処理し、CopyRect 等で描画することになるでしょう。


図8
実行結果

リスト4
TEffects コンポーネントを使用したガウシアンぼかし
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, GDIPAPI, GDIPOBJ;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormPaint(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses 
  Effects;

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Self.Caption := 'GDI+  ガウシアンぼかし TEffects';
  DoubleBuffered := True;
end;

//=============================================================================
//  OnPaintイベント
//  実際の描画コード
//=============================================================================
procedure TForm1.FormPaint(Sender: TObject);
var
  LObjWidth  : Integer;
  LObjHeight : Integer;
  LStream    : TMemoryStream;
  LBitmap    : TBitmap;
  LGPGraphic : TGPGraphics;
  LGPImage   : TGPImage;
  LTargetBmp : TBitmap;
  LEffect    : TEffects;
begin
  //ディスクから画像ファイルをTGPImageに読み込んで、幅と高さを取得
  LGPImage   := TGPImage.Create('PenguinsB.jpg');
  LObjWidth  := LGPImage.GetWidth;
  LObjHeight := LGPImage.GetHeight;

  //画像を描画するビットマップを生成
  LBitmap := TBitmap.Create;
  LBitmap.SetSize(LObjWidth, LObjHeight);

  //そのビットマップのCanvasを描画先としてTGPGraphicsを生成して描画
  //これが処理前の画像
  LGPGraphic := TGPGraphics.Create(LBitmap.Canvas.Handle);
  LGPGraphic.DrawImage(LGPImage, 0, 0);

  //処理結果用のビットマップと、そのビットマップを格納するメモリストリームを生成
  //処理結果用のビットマップと、元のビットマップは同じでもよいが、ここでは別とした
  LTargetBmp := TBitmap.Create;
  LStream    := TMemoryStream.Create;
  try
    LTargetBmp.SetSize(LObjWidth, LObjHeight);
    //TEffectsを生成してぼかし処理を実行
    LEffect := TEffects.Create(Self);
    LEffect.Blur(LBitmap, LTargetBmp, PositiveSel, 5);

    //処理結果のビットマップをメモリストリームに格納
    LTargetBmp.SaveToStream(LStream);
  finally
    LTargetBmp.Free;
  end;

  LGPImage.Free;
  LGPGraphic.Free;
  //このコントロールのCanvasを描画先にしてTGPGraphicsを生成
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);
  //メモリストリームに格納したビットマップからTGPImageを生成して、それを描画
  LGPImage   := TGPImage.Create(TStreamAdapter.Create(LStream));
  LGPGraphic.DrawImage(LGPImage, 10, 10);

  LStream.Free;
  LBitmap.Free;
  LGPGraphic.Free;
  LGPImage.Free;
  LEffect.Free;
end;

end.




03_画像のフィルタリングとコンポリューション


ガウスぼかし、画像の平滑化、輪郭抽出等は、画像のフィルタリングと呼ばれています。
これらの画像処理には、コンポリューション (畳み込み・重畳積分) という数学的な方法がよく用いられています。コンポリューションは、空間周波数領域の演算を実空間で行うものです。と言っても、この方面に関係ない方には意味がありませんが、検索エンジンで検索する際、'画像処理' の他に 'Convolution' を含めるといいかも知れません。
この分野は、研究はまだまだされてはいますが、ほぼ確立されている内容も多いのも事実です。したがって、インターネット上には、参考となる記事が少ないかも知れません。出版物であれば、多くの書籍が入手可能です。

余談になりますが、筆者が医用画像処理関係の研究をしている頃は、コンピュータの性能が急速に発展している時代でした。コンピュータのベンチマークテストの一部に、このコンポリューションの計算を使っていました。
画素数が増加すると、コンポリューションの計算は、級数的に時間がかかるようになります。このため、いろいろなアルゴリズムが考えだされています。