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' を含めるといいかも知れません.
この分野は,研究はまだまだされてはいますが,ほぼ確立されている内容も多いのも事実です.したがって,インターネット上には,参考となる記事が少ないかも知れません.出版物であれば,多くの書籍が入手可能です.

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