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' を含めるといいかも知れません.
この分野は,研究はまだまだされてはいますが,ほぼ確立されている内容も多いのも事実です.したがって,インターネット上には,参考となる記事が少ないかも知れません.出版物であれば,多くの書籍が入手可能です.
余談になりますが,筆者が医用画像処理関係の研究をしている頃は,コンピュータの性能が急速に発展している時代でした.コンピュータのベンチマークテストの一部に,このコンポリューションの計算を使っていました.
画素数が増加すると,コンポリューションの計算は,級数的に時間がかかるようになります.このため,いろいろなアルゴリズムが考えだされています. |
|