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+ 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] でダイアログを閉じる
|
Delphi 2009 GDI+ Library には、多くのデモプログラムが添付されています。その中に、下図のようなフォルダに入ったデモがあります。
以下のサンプルは、この [Bitmap Effects] にある uDemoBlur.pas のコードを、ほんの少しだけ修正して使用したものです。
これらのデモ用のソースコードは、添付のデモプログラムでも使用されていて、TDemo というクラスの派生クラスとなっています。今回のテストでは、この部分のコードは必要ないので削除しています。 |
 |
図2
デモ用のコード類 |
作成したサンプルを実行した結果です。
このサンプルと同じような方法で、他のデモ用のソースコードも利用できます。 |
 |
図3
設計時画面 |
 |
図4
実行画面 |
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
あるペンギンだけを特別扱い |
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
部分的に「ぼかす」 |
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.
ガウスぼかし、画像の平滑化、輪郭抽出等は、画像のフィルタリングと呼ばれています。
これらの画像処理には、コンポリューション (畳み込み・重畳積分) という数学的な方法がよく用いられています。コンポリューションは、空間周波数領域の演算を実空間で行うものです。と言っても、この方面に関係ない方には意味がありませんが、検索エンジンで検索する際、'画像処理' の他に 'Convolution' を含めるといいかも知れません。
この分野は、研究はまだまだされてはいますが、ほぼ確立されている内容も多いのも事実です。したがって、インターネット上には、参考となる記事が少ないかも知れません。出版物であれば、多くの書籍が入手可能です。
余談になりますが、筆者が医用画像処理関係の研究をしている頃は、コンピュータの性能が急速に発展している時代でした。コンピュータのベンチマークテストの一部に、このコンポリューションの計算を使っていました。
画素数が増加すると、コンポリューションの計算は、級数的に時間がかかるようになります。このため、いろいろなアルゴリズムが考えだされています。 |
|