全フレームリアルタイム画像処理しながら、NG判定を静止画保存
概要
ICImagingControlを使用してTheImagingSourceのデバイスで取得できる画像を全フレームリアルタイムに画像処理とライブ表示を行いながら、任意のタイミングあるいは画像処理NG時に静止画保存をするためのサンプルプログラムです。FrameQueueSinkがメインのAPIです。
処理内容とタイミング | 1.ライブ表示のみ | 2.静止画保存 | 3.任意のタイミングで画像処理と静止画保存を同時に処理 | 4.録画しながら任意のタイミングで静止画保存 | 5.全フレームリアルタイム画像処理しながら、NG判定を静止画保存 | 6.全フレームリアルタイム画像処理しながらNG判定を静止画保存、同時に録画 | |
---|---|---|---|---|---|---|---|
処理タイミング
|
ライブ 表示 |
〇 LiveDisplay | 〇 LiveDisplay | 〇 LiveDisplay | 〇 LiveDisplay | - | - |
画像 処理 |
× | × | 〇 SnapSingle | × | - | - | |
静止画 保存 |
× | 〇 SnapSingle | 〇 SnapSingle |
〇
SetStringParameter (FrameFilter) |
- | - | |
動画 保存 |
× | × | × |
〇
LiveStart (MediaStreamSink) |
- | - | |
処理タイミング
|
ライブ 表示 |
- | - | - | - | 〇 DisplayImageBuffer | 〇 DisplayImageBuffer |
画像 処理 |
- | - | - | - | 〇 FrameQueueSinkQueueSink | 〇 FrameQueueSinkQueueSink | |
静止画 保存 |
- | - | - | - |
〇
SaveAsBitmap (コールバック関数内で定義) |
〇
SaveAsBitmap (コールバック関数内で定義) |
|
動画 保存 |
- | - | - | - | × | 〇 Windows API | |
メインになる ICImagingControl Sinkクラス |
FrameNotificationSink | FrameSnapsink | FrameSnapsink | MediaStreamSink | FrameQueueSink | FrameQueueSink | |
使用例 | 拡大観察してモニター表示しているものを作業者がモニターで目視チェック。 | モニター表示による拡大観察と作業者による画像保存操作。 | オフラインでの寸法測定と測定画像の保存。 | オフラインでの寸法測定と測定画像の保存と作業内容の録画。 | インラインの画像検査。NG判定画像保存でトレサビ管理。 | インラインの画像検査。NG判定画像保存でトレサビ管理と、録画によるラインの監視。 | |
sinkのオブジェクトが用意している コールバック関数 |
有り (コールバック関数はあるが全フレーム取得は保証されていない) |
無し | 無し | 無し | 有り | 有り | |
Sinkの切り替え | 無し | 無し | 無し | 有り | 無し | 無し |
サンプルプログラム
Software | IC Imaging Control 3.5, Visual Studio™ 2019 |
---|---|
サンプル(C#) | multi_process5_cs_3.5.zip |
サンプルツールの外観
フォームを立ち上げたときのイベント
private FrameFilter _saveImageFrameFilter;
private FrameQueueSink _frameQueueSink;
private int imageCounter = 0;
private bool blnDiplayOnOff = true;
private Boolean blnImageProcessON = false;
private void Form1_Load( object sender, EventArgs e )
{
// カメラの設定した解像度に合わせて表示画面を変更
icImagingControl1.LiveDisplayDefault = false;
icImagingControl1.LiveDisplaySize = icImagingControl1.Size;
icImagingControl1.LiveDisplay = false;
_frameQueueSink = new FrameQueueSink(Callback_FrameQueueSink, MediaSubtypes.RGB32, 5);
icImagingControl1.Sink = _frameQueueSink;
//静止画保存するための準備(フレームフィルタの定義)
_saveImageFrameFilter = FrameFilter.Create(""Save Image"");
if (_saveImageFrameFilter == null)
{
MessageBox.Show($""Failed to load the \""Save Image\"" frame filter!"", ""Error"", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
icImagingControl1.DeviceFrameFilters.Add(_saveImageFrameFilter);
}
最初に、グローバル変数としてFrameQueueSink、画面表示ON/OFFを制御するフラグblnDiplayOnOff、画像処理ON/OFFを制御するフラグblnImageProcessONなどを宣言しています。
次にICImagingControlのコントロールパネルのサイズを合わせるためにLiveDisplayDefault プロパティで false に設定しています。
その後icImagingControl1コントロールのSinkプロパティに、画像処理をするための新しいTIS.Imaging.FrameQueueSinkオブジェクトを設定します。
FrameQueueSinkのSinkではIC Imaging Controlが自動的にライブストリームを表示しないようにLiveDisplay = falseにします。
画面表示は後述するCallback_FrameQueueSinkコールバック関数の中でフレームを受け取るたび毎にicImagingControl1.DisplayImageBuffer(buffer);で表示するようにしています。
リアルタイム画像処理中の任意のタイミングで静止画像保存ができるように、FrameFilter.Createメソッドを使用してSave Imageフレームフィルターを作成し、icImagingControl1.DeviceFrameFiltersコレクションに追加します。
※補足 フレームフィルターとは:
通常は、SnapSingleメソッドで静止画保存をします。しかし、リアルタイムに画像処理をしながらSnapSingleメソッドを使う事はできないので、連続画像処理をしながら静止画保存をするためにフレームフィルターを用いる必要があります。フレームフィルターは様々な種類があり、レンズの歪曲収差補正、デノイズ処理、画像の回転などがあります。ここでは、Save Imageフレームフィルターを用いています。Save Imageフィルターを使う為に、以下のファイルを実行ファイルと同じディレクトリに配置する必要があります。
SaveImageFrameFilter32.ftf | Save Imageのフィルター画像処理で画像保存するための設定情報が定義されたXML形式のファイル(32bitアプリケーション向け) |
---|---|
SaveImageFrameFilter64.ftf | Save Imageのフィルター画像処理で画像保存するための設定情報が定義されたXML形式のファイル(64bitアプリケーション向け) |
tis_udshl12.dll | ftfファイルの読み込む時に必要となるファイル(32bitアプリケーション向け) |
tis_udshl12_x64.dll | ftfファイルの読み込む時に必要となるファイル(64bitアプリケーション向け) |
[デバイス選択 ShowDeviceSettingsDialog]ボタンをクリックしたときの処理
private void cmdDeviceFrameQueueSink_Click(object sender, EventArgs e)
{
if (icImagingControl1.LiveVideoRunning)
{
icImagingControl1.LiveStop();
}
// デバイスダイアログを表示する
icImagingControl1.ShowDeviceSettingsDialog();
icImagingControl1.LiveStart();
}
[デバイス選択 ShowDeviceSettingsDialog]ボタンをクリックしたときのイベントです。最初にライブモードが停止していること(LiveStartメソッドを呼んでいないこと)を確認するために、LiveVideoRunningプロパティを確認しています。ShowDeviceSettingsDialogメソッドを呼び出して、下図のようなデバイス設定ダイアログを表示し、ダイアログにてカメラの選択、ビデオフォーマットの設定、フレームレートの設定を行い、OKを押下してデバイス設定ダイアログを閉じます。その後、icImagingControl1.LiveStart();にてカメラのリアルタイムの映像を画面に表示します。
[プロパティ ShowPropertyDialog]ボタンをクリックしたときの処理
private void cmdPropertiesFrameQueueSink_Click(object sender, EventArgs e)
{
icImagingControl1.ShowPropertyDialog();
}
[プロパティ ShowPropertyDialog]ボタンをクリックしたときのイベントです。下図のようなIC Imaging Controlのプロパティダイアログを表示します。プロパティダイアログには、カメラのゲイン、露光時間、ホワイトバランス、明るさ、コントラストなどのカメラの機能に関する設定が含まれており、それらの設定をプロパティダイアログ上で変更することができます。
[画面表示ON LiveDisplay=True]ボタンをクリックしたときの処理
private void cmdDisplayON_Click(object sender, EventArgs e)
{
blnDiplayOnOff = true;
}
[画面表示ON LiveDisplay=True]ボタンを押下したときのイベントです。フォーム上のICImagingControlのコントロール(画面上)にカメラの映像を表示させるか、または非表示にするか切り替えるために使用されます。
FrameSnapSinkやMediaStreamSinkでは表示非表示をicImagingControl1.LiveDisplay で切り替えていましたが、FrameQueueSinkのときはblnDiplayOnOffでDisplayImageBufferメソッドで画面表示/非表示を切り替えています。FrameQueueSinkではコールバック関数ImageProcess内でDisplayImageBufferメソッドを呼び出しており、LiveStartを呼び出し中でもblnDiplayOnOffを切り替えることができます。
[画面表示OFF LiveDisplay=False]ボタンをクリックしたときの処理
private void cmdDisplayOFF_Click(object sender, EventArgs e)
{
blnDiplayOnOff = false;
}
[画面表示OFF LiveDisplay=False]ボタンを押下したときのイベントです。フォーム上のICImagingControlのコントロール(画面上)にカメラの映像を表示させるか、または非表示にするか切り替えるために使用されます。
FrameSnapSinkやMediaStreamSinkでは表示非表示をicImagingControl1.LiveDisplay で切り替えていましたが、FrameQueueSinkのときはblnDiplayOnOffでDisplayImageBufferメソッドで画面表示/非表示を切り替えています。FrameQueueSinkではコールバック関数ImageProcess内でDisplayImageBufferメソッドを呼び出しており、LiveStartを呼び出し中でもblnDiplayOnOffを切り替えることができます。
[ライブスタート LiveStart(FrameQueueSink)]ボタンをクリックしたときの処理
private void cmdLiveStartFrameQueueSink_Click(object sender, EventArgs e)
{
//画像処理OFF
blnImageProcessON = false;
if (!icImagingControl1.LiveVideoRunning)
icImagingControl1.LiveStart();
}
//非同期処理の定義
public delegate void ImageProcessDelegate(IFrameQueueBuffer imgBuffer);
//非同期処理の実行
private FrameQueuedResult Callback_FrameQueueSink(IFrameQueueBuffer buffer)
{
BeginInvoke(new ImageProcessDelegate(ImageProcess), buffer);
return FrameQueuedResult.ReQueue;
}
private void ImageProcess(IFrameQueueBuffer buffer)
{
//画像処理
if (blnImageProcessON) makeImageNegative(buffer);
//画面表示
if (blnDiplayOnOff) icImagingControl1.DisplayImageBuffer(buffer);
}
上記はIC Imaging ControlのLiveStartを定義したものです。
cmdLiveStartFrameQueueSink_Clickメソッドは、[ライブスタート LiveStart(FrameQueueSink)]ボタンがクリックされたときに呼び出されます。
LiveStartをすることで「■フォームを立ち上げたときのイベント」にて_frameQueueSink = new FrameQueueSink(Callback_FrameQueueSink, MediaSubtypes.RGB32, 5);と定義したコールバック関数Callback_FrameQueueSinkが呼び出されます。
//画像処理OFF
事前に【画像処理開始 FrameQueueSink】ボタンを押下していると画像処理が実行されているので、画面表示のみを行う為に画像処理ON/OFFを制御するフラグblnImageProcessONをfalseにしています。
//非同期処理の定義
画像処理、画面表示を非同期で実行する為にIFrameQueueBufferを引数に取るデリゲートとしてImageProcessDelegateを宣言し、非同期処理の定義をします。
//非同期処理の実行
Callback_FrameQueueSinkメソッドはカメラから送られた画像を受け取ったタイミングで呼び出されます。その後、Callback_FrameQueueSinkメソッド内でBeginInvokeを使用して非同期処理を実行し、ImageProcessメソッドに引き渡します。次に、Callback_FrameQueueSinkの戻り値に、コールバック関数内で処理したフレームを使用するために再びキューに入れています。
//画像処理
ImageProcessメソッド内ではcmdLiveStartFrameQueueSink_Clickメソッド内でblnImageProcessONはFalseとしているため、makeImageNegativeメソッドはスルーします。
//画面表示
次に、FrameQueueBufferに格納された画像を「 if (blnDiplayOnOff) icImagingControl1.DisplayImageBuffer(buffer);」に引き渡し、DisplayImageBufferで受け取った画像を画面表示しています。
[画像処理開始 FrameQueueSink]ボタンをクリックしたときの処理
private void cmdImageProcessStartFrameQueueSink_Click(object sender, EventArgs e)
{
//画像処理ON
blnImageProcessON = true;
if (!icImagingControl1.LiveVideoRunning)
icImagingControl1.LiveStart();
}
//非同期処理の定義
public delegate void ImageProcessDelegate(IFrameQueueBuffer imgBuffer);
//非同期処理の実行
private FrameQueuedResult Callback_FrameQueueSink(IFrameQueueBuffer buffer)
{
BeginInvoke(new ImageProcessDelegate(ImageProcess), buffer);
return FrameQueuedResult.ReQueue;
}
private void ImageProcess(IFrameQueueBuffer buffer)
{
//画像処理
if (blnImageProcessON) makeImageNegative(buffer);
//画面表示
if (blnDiplayOnOff) icImagingControl1.DisplayImageBuffer(buffer);
}
private int imageNumber = 0;
private void makeImageNegative(TIS.Imaging.IFrame imgBuffer)
{
int bytesPerLine = imgBuffer.FrameType.BytesPerLine;
int lines = imgBuffer.FrameType.Height;
double sum = 0.0;
int count = 0;
bool blnOK = true;
string fileName_imageprocess = ""Image_imageprocess "" + imageNumber + "".bmp"";
unsafe
{
//画像処理
byte* pDatabyte = imgBuffer.Ptr;
for (int y = 0; y < lines; y++)
{
for (int x = 0; x < bytesPerLine; x++)
{
*pDatabyte = (byte)(255 - *pDatabyte);
byte value = *pDatabyte;
sum += value;
count++;
pDatabyte++;
}
}
}
double average = sum / count;
//輝度値平均値が150だったらNG判定としています。
if (average > 150) blnOK = false;
else blnOK = true;
//NGのものだけを保存
if (blnOK == false)
{
TIS.Imaging.FrameExtensions.SaveAsBitmap(imgBuffer, fileName_imageprocess);
imageNumber += 1;
}
}
上記はIC Imaging ControlのLiveStartを定義したものです。
cmdImageProcessStartFrameQueueSink_Clickメソッドは、【画像処理開始 FrameQueueSink】ボタンがクリックされたときに呼び出されます。
LiveStartをすることで「■フォームを立ち上げたときのイベント」にて_frameQueueSink = new FrameQueueSink(Callback_FrameQueueSink, MediaSubtypes.RGB32, 5);と定義したコールバック関数Callback_FrameQueueSinkが呼び出されます。
//画像処理ON
画像処理を開始するために、画像処理ON/OFFを制御するフラグblnImageProcessONをONにしています。
//非同期処理の定義
画像処理、画面表示を非同期で実行する為にIFrameQueueBufferを引数に取るデリゲートとしてImageProcessDelegateを宣言し、非同期処理の定義をします。
//非同期処理の実行
Callback_FrameQueueSinkメソッドはカメラから送られた画像を受け取ったタイミングで呼び出されます。その後、Callback_FrameQueueSinkメソッド内でBeginInvokeを使用して非同期処理を実行し、ImageProcessメソッドに引き渡します。次に、Callback_FrameQueueSinkの戻り値に、コールバック関数内で処理したフレームを使用するために再びキューに入れています。
//画像処理
ImageProcessメソッド内ではcmdImageProcessStartFrameQueueSink_Clickメソッド内でblnImageProcessONはtrueとしているため、makeImageNegativeメソッドを実行します。
makeImageNegative関数内ではFrameQueueBufferに格納された画像imgBufferを使って画像処理を実行します。まず、imgBuffer.FrameType.BytesPerLineやimgBuffer.FrameType.Heightで取得した画像のピクセルサイズを取得します。次にポインタを使って画像データへ直接アクセスするためにunsafeを使っています。ポインタ使ってバッファの1画素1画素にアクセスし、その輝度値を反転させるために、バッファー内の各ピクセル値を255から引いています。そして、輝度値平均を求めて輝度値平均が150以上だったらNG判定するようにしています。NG判定の時にはBitmap形式で画像処理後のバッファを保存するようにしています。
//画面表示
次に、ImageProcessメソッド内で【画面表示ON blnDiplayOnOff=True】ボタンと【画面表示False blnDiplayOnOff=False】ボタンで切り替えられたフラグ:blnDiplayOnOffをもとに画像処理後のバッファを画面上にDisplayImageBufferメソッドで表示しています。
[画像処理停止 FrameQueueSink]ボタンをクリックしたときの処理
private void cmdImageProcessStopFrameQueueSink_Click(object sender, EventArgs e)
{
//画像処理OFF
blnImageProcessON = false;
}
cmdImageProcessStopFrameQueueSink_Clickメソッドは、【画像処理停止 FrameQueueSink】ボタンがクリックされたときに呼び出されます。 FrameQueueSinkのSinkでLiveStartした状態の時に呼び出されるコールバック関数内で画像処理しないようにblnImageProcessONをFalseにしています。
[静止画保存 FrameFilter]ボタンをクリックしたときの処理
private void cmdSnapFrameQueueSink_Click(object sender, EventArgs e)
{
if (_saveImageFrameFilter != null && icImagingControl1.DeviceValid && icImagingControl1.LiveVideoRunning)
{
imageCounter++;
string imageFileName = String.Format(""Image{0}.jpg"", imageCounter);
_saveImageFrameFilter.SetStringParameter(""ImageName"", imageFileName);
}
}
ICImagingControlのFrameFilterを使用して、静止画をキャプチャして保存しています。Form_Loadの関数内で画像保存するためのFrameFilterインスタンスを_saveImageFrameFilterで作成していますので、saveImageFrameFilterのパラメータにアクセスするだけで、フィルタの設定(画像保存)を呼び出すことができます。フィルタの設定(画像保存)を呼び出すにはSetStringParameterメソッドの引数に有効なパラメーター「ImageName」を指定することでフィルターを使用することができます。FrameFilterを使うことでFrameQueueSinkを使いながらでも静止画保存の処理を割り込んで処理することができます。
[ライブストップ LiveStop]ボタンをクリックしたときの処理
private void cmdLiveStopFrameQueueSink_Click(object sender, EventArgs e)
{
icImagingControl1.LiveStop();
}
上記はIC Imaging ControlのLiveStopを定義したものです。cmdLiveStopFrameQueueSink_Clickメソッドは、[ライブストップ LiveStop]ボタンがクリックされたときに呼び出されます。LiveStopメソッドは、カメラからのライブストリーミングを停止します。
フレームカウントやフレームレートを記載する処理
//FrameQueueSinkに対して受け取ったフレーム数とドロップ数をカウント
long _countOfFramesCopied = 0;
long _countOfFramesDropped = 0;
if (_frameQueueSink != null)
{
//このSinkの出力キューにコピーされたフレーム数を返します。
_countOfFramesCopied = _frameQueueSink.CountOfFramesCopied;
//このSinkがドロップしたフレーム数を返します。
_countOfFramesDropped = _frameQueueSink.CountOfFramesDropped;
}
lbltext2.Text = _countOfFramesCopied.ToString() + ""/"" + _countOfFramesDropped.ToString() ;
画面右上にあるカメラから送られてくるフレーム数のカウントアップやフレーム落ちのカウントはDriverFrameDropInformationのプロパティを使っています。詳細については別のサンプルページにて解説を行っておりますので下記を参照ください。
上記とは別に「FrameQueueSink」の欄に、FrameQueueSinkのSinkがコピーしたフレーム数(CountOfFramesCopied)とフレーム落ちした数(CountOfFramesDropped)の順でスラッシュ(/)区切りで表示しています。