関心領域の画像更新
概要
指定した領域の輝度値が変化したら表示している画像を更新するサンプルです。ライブストリーム開始後にマウスドラッグで画像の関心領域を選択し、[Set current ROI]ボタンクリックで設定します。
その後、表示画面が停止した状態になりますが、カメラからのライブストリームは継続してイメージバッファに送信され続けています。その間に、関心領域内にあるイメージバッファのピクセル輝度値を取得し、フレーム前後の輝度値の平均値がしきい値(Threshold)よりも大きい場合には、表示画面が更新されます。なお、モノクロのカラーフォーマットとなっていますので、カラーカメラでもモノクロ画像として表示されます。
サンプルプログラム
Software | IC Imaging Control 3.5, Visual Studio™ 2019 |
---|---|
サンプル(C#) | advanced_image_processing_cs_3.5.zip |
実行結果
Visual Studioのプログラム上ではすでにボタンの設置や関数は定義済ですので、IC Imaging Contorl3.5をインストールされていれば、実行ボタンだけですぐにデバッグで動作確認することができます。
画面にあるそれぞれのボタンなどのコントローラの機能は下記の通りです。
[Start]ボタン | ライブ表示を開始 |
---|---|
[Stop]ボタン | ライブ表示を停止 |
[Setting]ボタン | デバイスプロパティのダイアログ画面を表示 |
[Device]ボタン | デバイスを選択するダイアログ画面を表示 |
[Theeshold]スライダー | ピクセル輝度値の閾値を設定(ピクセル輝度の計算に使用する) |
[Set current ROI]ボタン | マウスで選択した関心領域内のピクセル輝度の変化を計算し、輝度値に変化があったら表示画面を更新([Set current ROI]ボタンをクリックしたら[Reset ROI]ボタンに変化) |
[Reset ROI]ボタン | [Set current ROI]ボタンで機能している解除 |
FrameQueueSinkクラスを使うための宣言
private IFrameQueueBuffer _currentlyDisplayedBuffer;
private FrameQueueSink _sink;
IC Imaging Controlは主に入力や動画フォーマット、画像取り込みを制御するためのメソッドを提供します。
イメージバッファ(FrameQueueSinkクラス) を制御するために、カメラから入力されたイメージデータへのアクセス(IFrameQueueSinkListener.FramesQueuedメソッド) も使用する必要がありますので、グローバル変数としてIFrameQueueBufferクラスとFrameQueueSinkクラスの変数を宣言しインスタンス化します。
画像取得したタイミングで画像を取得する方法
private void Form1_Load( object sender, EventArgs e ){
// ボタンを無効化する
cmdStart.Enabled = false;
cmdStop.Enabled = false;
cmdROICommit.Enabled = false;
cmdSettings.Enabled = false;
//FrameQueueSinkクラスを使用してカラーフォーマットY800のみ受け入れるコールバックするように設定する
_sink = new TIS.Imaging.FrameQueueSink(( arg ) => NewBufferCallback(arg), TIS.Imaging.MediaSubtypes.Y800, 5);
//使用するシンクを設定
icImagingControl1.Sink = _sink;
//ライブ表示を無効化し
//リングバッファからの画像をICImagingControlの
//コントロールウィンドウに表示する
icImagingControl1.LiveDisplay = false;
// ライブ画像上に図やテキストを描画しない
icImagingControl1.OverlayBitmapPosition = PathPositions.None;
// xmlファイルを読み込みロードする
icImagingControl1.LoadShowSaveDeviceState("lastSelectedDeviceState.xml");
//デバイス設定を保存し、ボタンを有効化する
UpdateDeviceSettings();
}
Form1がロードされたタイミングでグローバル変数として定義されたシンクオブジェクト_sinkが、シンクの状態が変化した時にコールされるように設定します。NewBufferCallbackを設定することでデバイスから画像を受け取ると、入力キューからバッファを受け取り、画像データをコピーして出力キューに配置ができます。
カラー画像を取得したい場合はTIS.Imaging.MediaSubtypes.Y800をTIS.Imaging.MediaSubtypes.RGBなどに変更してください。
イメージ取得した時にコールバックされる関数
FrameQueuedResult NewBufferCallback( IFrameQueueBuffer buffer ){
RECT region = NormalizeRect( _userROI, buffer.FrameType.Size );
if( !_userROICommited )
{
//"Set current ROI"ボタンクリックの時
ReceiveFrameInContinuousMode( buffer, region );
}
else
{
//"Reset ROI"ボタンクリックの時
ReceiveFrameInCompareMode( buffer, region );
}
return FrameQueuedResult.SkipReQueue;
}
新しい画像を受け取るときにコールされる関数オブジェクトで、カメラからのフレームを受け取るたびにIFrameQueueBufferインターフェースを受け取ります。"Set current ROI"ボタンの状態でクリックするか、"Reset ROI"ボタンの状態でクリックするかで条件分岐しています。なお、呼び出されるIFrameQueueBufferインターフェースが自動的に再キューされるかどうかを示すために、FrameQueuedResult.SkipReQueueかFrameQueuedResult.ReQueueを返す必要があります。ここでは再キューしてフレームを使用する必要がないため、FrameQueuedResult.SkipReQueueを返しています。
FrameQueueSinkについては下記のAPIリファレンスマニュアルを参考ください。
FrameQueueSink.FrameQueueSink Method
IC Imaging Control_Ver3.5(C#/VB.NET) APIリファレンスマニュアル
[Reset ROI]ボタンをクリックしたときの関数
private void ReceiveFrameInContinuousMode( IFrameQueueBuffer buffer, RECT Region )
{
if( _currentlyDisplayedBuffer != null )
{
//取得したフレームをバッファリストに入れる
_sink.QueueBuffer(_currentlyDisplayedBuffer);
}
//取得したフレームをバッファを更新
_currentlyDisplayedBuffer = buffer;
//ROIで指定した領域を線で囲いオーバーレイにする
DrawRectangleY8( buffer, Region );
//ライブ表示画面を更新
icImagingControl1.DisplayImageBuffer( _currentlyDisplayedBuffer );
}
"[Reset ROI]ボタンクリックした時の動作に呼び出される関数です。引数としてイメージバッファ"buffer"とマウスで指定したROI領域"Region"を使います。"DrawRectangleY8"関数でイメージバッファにマウスでドラックしたROI領域をオーバーレイで描画し、DisplayImageBufferメソッドでライブ画面に表示します。
[Set current ROI]ボタンをクリックしたときの関数
private void ReceiveFrameInCompareMode( IFrameQueueBuffer newFrame, RECT Region )
{
IFrameQueueBuffer oldBuffer = _currentlyDisplayedBuffer;
//oldBuffer とnewFrameが更新されているか判断
if( oldBuffer == null || CompareRegion(oldBuffer, newFrame, Region, _threshold) )
{
//イメージバッファが更新されている場合
if( oldBuffer != null )
{
//イメージバッファをキューされたバッファリストに入れる
_sink.QueueBuffer(oldBuffer);
}
_currentlyDisplayedBuffer = newFrame;
//ROIで指定した領域を線で囲いオーバーレイにする
DrawRectangleY8(newFrame, Region);
//ライブ表示を更新
icImagingControl1.DisplayImageBuffer(newFrame);
}
else
{
//更新されていない
_sink.QueueBuffer(newFrame);
}
}
新しく取得したイメージバッファ"newFrame"と前のイメージバッファ_currentlyDisplayedBufferをピクセル比較する"CompareRegion"関数で更新されているかどうか判断します。もし、更新されているようであれば、[Reset ROI]ボタンクリックしたときの関数の内容と同じく、前のフレームがFrameQueueSinkの中でキューされ、"DrawRectangleY8"関数でイメージバッファにマウスでドラックしたROI領域をオーバーレイで描画し、DisplayImageBufferメソッドでライブ表示画面を更新します。
ピクセルの輝度値を取得しライブ表示画面を更新するか計算する関数
private bool CompareRegion( IFrame buf, IFrame buf2, RECT region, int threshold )
{
//ROIで選んだ領域のピクセル数を計算
int PixelCount = (region.Bottom - region.Top) * (region.Right - region.Left);
if (PixelCount <= 0)
{
return false;
}
long greyscaleDifferenceAccu = 0;
for( int y = region.Top; y <= region.Bottom; y++ )
{
for( int x = region.Left; x <= region.Right; x++ )
{
//事前に取得して置いたイメージバッファbufと新しく取得したイメージバッファーのbuf2の輝度値の差異を計算
greyscaleDifferenceAccu += Math.Abs( GetY8PixelAt( buf, x, y ) - GetY8PixelAt( buf2, x, y ) );
}
}
//差分の平均値を算出
var greyscaleDifference = greyscaleDifferenceAccu / PixelCount;
//差分の平均値がthresholdよりも大きければtrue(画面を更新する)
if( greyscaleDifference > threshold )
{
return true;
}
else
{
return false;
}
}
画面を更新するには、下記の手順となっています。
- 関心領域を取得
- 関心領域内の事前に取得して置いたイメージバッファbufと新しく取得したイメージバッファーのbuf2の2つのバッファの輝度値の差分を取得
- 2で取得した差分の合計値からピクセル数を割り、平均値を取得する
- 平均値が設定したthresholdよりも大きければ画像更新したとみなしてtrueを返す
下記のAPIリファレンスマニュアルにもその他関数などの説明があります。
高度な画像処理を行う
IC Imaging Control_Version 3.5(C#/VB.NET) APIリファレンスマニュアル