フレームフィルタの記述: 2値化
ここではライブ画像に2値化処理をかけるためのフレームフィルタの作成方法を紹介します。
フレームフィルタは迅速かつ能率的に動く必要があるため、プレーンポインタを取り扱うことのできる言語(Managed C++ かunsafeブロックのC# )でのみ実装が可能となっています。
今回の例ではC#だけを使います。
以下の手順について紹介します
- FrameFilterImplクラスライブラリリファレンス>クラス>FrameFilterImplからクラスを 派生させ、純粋仮想関数を実装する。
- ライブ画像にのみフィルタをかける
- フレームフィルタパラメータの変更
フレームフィルタの実装
フレームフィルタの実装は全てFrameFilterImplクラスライブラリリファレンス>クラス>FrameFilterImplによって行われる必要があります。
public class BinarizationFilter : FrameFilterImpl
2つのメンバ変数が現在のフレームフィルタ設定を保持します。m_bEnabledはそのフィルタが2値化を適用するかどうかをコントロールし、m_bEnabledは2値化におけるしきい値を決定します。
private bool _enabled = false;
private int _threshold = 127;
フレームフィルタを作成するためには、以下の3つのメソッドを実装する必要があります。
:GetSupportedInputTypesクラスライブラリリファレンス>クラス>FrameFilterImpl>FrameFilterImpl.GetSupportedInputTypes Method, GetTransformOutputTypesクラスライブラリリファレンス>クラス>FrameFilterImpl>FrameFilterImpl.GetTransformOutputTypes Method, Transformクラスライブラリリファレンス>クラス>FrameFilterImpl>FrameFilterImpl.Transform Method
GetSupportedInputTypesメソッド内のArrayListには FrameTypeクラスライブラリリファレンス>クラス>FrameTypeオブジェクトが納められています。これらはフレームフィルタがインプットとして受け入れるフレームタイプ(カラーフォーマット)を記述したものです。分かりやすくするために、今回はRGB8とY800の8bitのフォーマットのみを使用する事にします。画像取り込みデバイスのビデオフォーマットが変更された時、その画像データはフレームフィルタに渡される前の段階で自動的に変換されます。
public override void GetSupportedInputTypes( System.Collections.ArrayList frameTypes )
{
// このフィルタは 8bitのグレースケール画像にのみ作用します
frameTypes.Add( new FrameType(MediaSubtypes.Y800 ) );
}
GetTransformOutputTypesメソッドはArrayList outTypesにフレームタイプを格納していきます。これらのフレームタイプは指定された入力フレームタイプからフィルタが作り出せるものです。2値化フィルタはフレームタイプやサイズを変えることはいしないため、リストには単に入力タイプを挿入します。
public override bool GetTransformOutputTypes( FrameType inType, System.Collections.ArrayList outTypes )
{
// 画像タイプの変更はしない output = input
outTypes.Add( inType );
return true;
}
フレームごとにTransformメソッドが呼び出されます。現在の設定に従って2値化をすることが役割となります。
Transformメソッドはユーザーインターフェースとは別のスレッドで実行されるので、m_bEnabledと m_thresholdのメンバ変数へのアクセス においてアプリケーションプログラムによる並行処理が起こらないようにしておく必要があります。
最初に FrameFilterImpl.BeginParamTransferクラスライブラリリファレンス>クラス>FrameFilterImpl>FrameFilterImpl.BeginParamTransfer Methodが呼び出されます。このメソッドは内部的に重要な部分に入っていくものです。パラメータを設定したい場合にはFrameFilter::BeginParamTransferを呼び出さなければなりません。
これが同じロック機能を取得するものです。FrameFilterImpl.EndParamTransferクラスライブラリリファレンス>クラス>FrameFilterImpl>FrameFilterImpl.EndParamTransfer Methodは反対に出ていくためのもので、メンバ変数の値はローカルスタック変数にコピーされ、それらの値は変換の過程において定数であり続けることになります。
2値化自体はそれほど複雑な処理ではありません:各ピクセルのグレー値をしきい値と比較していきます。もしそのグレー値が閾値よりも大きければそのピクセルの値を最大グレー値に設定し、逆であればゼロを設定します。public override bool Transform( IFrame src, IFrame dest )
{
unsafe
{
// destination frame が利用可能かどうかをチェック
if ( dest.Ptr == null ) return false;
// メンバ変数を関数のスタックにコピーしてsetThreshold()等への
// 並行呼び出しによる上書きを防ぐ
//
// beginParamTransfer/endParamTransfer が各メンバ変数からの値に矛盾が
// ないことを確認。※ユーザーはbeginParamTransfer/endParamTransferにも
// ライティングパラメータアクセスを入れる必要があるため
BeginParameterTransfer();
int threshold = _threshold;
bool enabled = _enabled;
EndParameterTransfer();
byte* pIn = src.Ptr;
byte* pOut = dest.Ptr;
// 2値化が有効化どうかをのチェック
if ( enabled )
{
// 入力バッファ内の各バイトに対してしきい値以上かどうかをチェック
int bufferSize = src.FrameType.BufferSize;
while ( bufferSize-- > 0 )
{
if (*pIn++ >= threshold)
{
*pOut++ = 255;
}
else
{
*pOut++ = 0;
}
}
}
else
{
// 2値化は無効: 画像データをそのままコピー
dest.CopyFrom( src );
}
}
return true;
}
フレームフィルタを使う
まず最初に、BinarizationFilterのインスタンスを作成し、FrameFilter.Createクラスライブラリリファレンス>クラス>FrameFilter>FrameFilter.Create Methodに渡します。
そのメソッドは特別なオーバーロードを持ち、FrameFilterImplクラスライブラリリファレンス>クラス>FrameFilterImplオブジェクトを 取り入れてFrameFilterクラスライブラリリファレンス>クラス>FrameFilterオブジェクトを返します。これはICImagingControl.DeviceFrameFiltersクラスライブラリリファレンス>クラス>ICImagingControl>ICImagingControl.DeviceFrameFilters Property, ICImagingControl.DisplayFrameFiltersクラスライブラリリファレンス>クラス>ICImagingControl>ICImagingControl.DeviceFrameFilters Propertyとそのシンクタイプで使用することができます。
ダイアログクラスのメンバ変数内にFrameFilterクラスライブラリリファレンス>クラス>FrameFilterオブジェクトへの参照を保存します。これで後からフィルタのパラメータにアクセスすることができるようになります。
ICImagingControlクラスライブラリリファレンス>クラス>ICImagingControlオブジェクトが初期化される時、フレームフィルタの設定用にICImagingControl.DisplayFrameFilters.Addクラスライブラリリファレンス>クラス>ICImagingControl>ICImagingControl.DisplayFrameFilters Propertyが呼び出されます。
// overlay bitmap を無効にする
icImagingControl1.OverlayBitmapPosition = TIS.Imaging.PathPositions.None;
// フレームフィルタ実装のインスタンスを作成
BinarizationFilter binFilterImpl = new BinarizationFilter();
// 実装をラッピングしたFrameFilter オブジェクトを作成
_frameFilter = TIS.Imaging.FrameFilter.Create( binFilterImpl );
// FrameFilterをディスプレイフレームフィルタとしてセット
icImagingControl1.DisplayFrameFilters.Add( _frameFilter );
// ライブモードの開始
icImagingControl1.LiveStart();
ライブモードが開始されると、IC Imaging Control によって表示される前の段階で全ての画像が2値化されます。
パラメータインターフェースの実装
フレームフィルタは上記のコードで機能しますが、ホストアプリケーションがフィルタの振る舞いを変更することはできません。アプリケーションをフィルタの設定にアクセスさせるためには、SetBoolParameterクラスライブラリリファレンス>クラス>FrameFilter>FrameFilter.SetBoolParameter MethodやGetIntParameterクラスライブラリリファレンス>クラス>FrameFilter>FrameFilter.GetIntParameter MethodなどFrameFilterクラスライブラリリファレンス>クラス>FrameFilterの汎用的機能を使ってアクセスが可能な一連のパラメータ をフィルタで定義することができます。
フィルターのオンとオフを切り替えるブーリアン型のパラメータを作成するために、フィルターコンストラクタ内の FrameFilterImpl.AddBoolParamクラスライブラリリファレンス>クラス>FrameFilterImpl>FrameFilterImpl.AddBoolParam Methodを呼び出します。パラメータを追加してしきい値を調整するためには、 AddIntParamクラスライブラリリファレンス>クラス>FrameFilterImpl>FrameFilterImpl.AddIntParam Method を呼び出します。
public BinarizationFilter()
{
AddBoolParam( "enable", new SetBoolParam( setEnable ), new GetBoolParam( getEnable ) );
AddIntParam( "threshold", new SetIntParam( setThreshold ), new GetIntParam( getThreshold ) );
}
これら2つのメソッドは3つのパラメータを取ります。最初はパラメータの名前で、パラメータに変更を加える前に指定する必要があります。例えばFrameFilter.SetIntParameterクラスライブラリリファレンス>クラス>FrameFilter>FrameFilter.SetIntParameter Methodのようなメソッドです。2つ目、3つ目のパラメータはSetBoolParamとGetBoolParameterもしくはSetIntParameterとGetIntParameterのデリゲートです。
これらのデリゲートがパラメータとフィルタ実装のメンバメソッドを結びつけます。
void setEnable( bool enable )
{
_enabled = enable;
}
bool getEnable()
{
return _enabled;
}
void setThreshold( int threshold )
{
_threshold = threshold;
}
int getThreshold()
{
return _threshold;
}
フレームフィルタパラメータへのアクセス
2値化の有効、無効を設定するチェックボックス用のイベントハンドラではFrameFilter.SetBoolParameterが呼び出されます。
フィルタパラメータを変更するメソッドへのコールは全てFrameFilter.BeginParameterTransferクラスライブラリリファレンス>クラス>FrameFilter>FrameFilter.BeginParameterTransfer Method と FrameFilter.EndParameterTransferクラスライブラリリファレンス>クラス>FrameFilter>FrameFilter.EndParameterTransfer Method の間に挟む必要があります。
private void chkEnable_CheckedChanged( object sender, EventArgs e )
{
_frameFilter.BeginParameterTransfer();
_frameFilter.SetBoolParameter( "enable", chkEnable.Checked );
sldThreshold.Enabled = _frameFilter.GetBoolParameter( "enable" );
lblThreshold.Enabled = _frameFilter.GetBoolParameter( "enable" );
_frameFilter.EndParameterTransfer();
}
しきい値スライダーのイベントハンドラではFrameFilter.SetIntParameterがコールされます。
このコールもまたFrameFilter.BeginParameterTransferクラスライブラリリファレンス>クラス>FrameFilter>FrameFilter.BeginParameterTransfer Method と FrameFilter.EndParameterTransferクラスライブラリリファレンス>クラス>FrameFilter>FrameFilter.EndParameterTransfer Methodの間に挟む必要があります。
private void sldThreshold_Scroll( object sender, EventArgs e )
{
_frameFilter.BeginParameterTransfer();
_frameFilter.SetIntParameter( "threshold", sldThreshold.Value );
lblThreshold.Text = _frameFilter.GetIntParameter( "threshold" ).ToString();
_frameFilter.EndParameterTransfer();
}