リングバッファを使用したイメージシーケンスの取得と再生
概要
ICImagingControlでリングバッファを構成し、取得した複数のフレームを表示するサンプルです。
サンプルプログラム
Software | IC Imaging Control 3.5, Visual Studio™ 2019 |
---|---|
サンプル(C#) | Capture_before_and_after_event_cs_3.5.zip |
サンプルツールの外観
このサンプルでは、キャプチャボタンが押された時点から3秒前まで遡った全イメージフレームを保持します。その後、ユーザーは自動または手動で保持されたフレームを表示する事ができます。イベントレコーダーなど、様々な用途で利用する事ができるメソッドです。
このサンプルの主な構成は以下となります。
ImageBuffersから迅速にデータを引き出す方法
FrameQueueSinkにため込んだリングバッファにアクセスする方法
グローバル変数を宣言
// この属性は、IC Imaging Controlウィンドウ画面に合うようにライブ表示を拡大縮小するかどうかを制御します
private bool m_bFitImageToWindow = true;
// シンク関数を用意
private SinkListener _sinkListener = new SinkListener();
FrameQueueSink _sink;
// 3秒間キャプチャする(フレームレートの三倍分フレームを取得する)
private int _FramesToCapture = 0;
// キャプチャしたイメージを格納するリングバッファ
private IFrameQueueBuffer [] _bufferlist;
FrameQueueSinkクラスを使用するために_sinkを定位置に、なおかつFrameQueueSinkクラスでのコールバック関数を使用するために_sinkListenerもグローバル変数で宣言が必要となります。
また、取得したフレームをリングバッファに格納するために_bufferlistも宣言しておきます。
private void Form1_Load(object sender, EventArgs e)
{
icImagingControl1.LoadDeviceStateFromFile("device.xml", true);
if (m_bFitImageToWindow)
{
// ディスプレイのサイズを調整
icImagingControl1.LiveDisplayDefault = false;
icImagingControl1.LiveDisplaySize = icImagingControl1.Size;
}
// _sinkListenerをベースにシンクを作成
// Prepareボタンをクリックしたときにリングバッファを取得します。
_sink = new FrameQueueSink(_sinkListener, MediaSubtypes.RGB32);
icImagingControl1.Sink = _sink;
UpdateControls();
}
フォームロードしたタイミングでシンクをFrameQueueSinkクラスを使うのですが、FrameQueueSinkクラスによって使用されるインターフェースで状態変化や通知や新しいフレームのコールバックを使うためにクラスを定義します。
SinkListenerクラスの定義
class SinkListener : IFrameQueueSinkListener
{
public void FramesQueued(FrameQueueSink sink)
{
if( sink.InputQueueSize == 0)
{
sink.QueueBuffer(sink.PopOutputQueueBuffer());
}
}
}
IFrameQueueSinkListener
インターフェースを使用するためにクラスを定義します。
InputQueueSizeでキューがからの場合にはPopOutputQueueBuffer を使ってリングバッファからシンクの入力キューに入れます。
[Start Live]ボタンクリックした時
private void StartLiveVideo()
{
icImagingControl1.LiveStart();
btnSnapEvent.Enabled = false;
// フレームレートの3倍(3秒間取得)
_FramesToCapture = (int)icImagingControl1.DeviceFrameRate * 3;
// 3 seconds capture time;
UpdateControls();
}
ライブ開始時にリングバッファに取りためる数を変数_FramesToCapture(フレームレート×3)で計算しておく。
[Prepare]ボタンクリックした時
private void btnPrepareFrameBufferQueue_Click(object sender, EventArgs e)
{
btnPrepareFrameBufferQueue.Enabled = false;
if (_bufferlist != null)
{
_bufferlist = null;
GC.Collect();
}
_sink.AllocAndQueueBuffers(_FramesToCapture);
btnSnapEvent.Enabled = true;
}
_bufferlistにデータが残っていればクリアにしてGC.Collect()メソッドで強制的にガベージコレクションを行います。
シンクは、画像をキャプチャするためのバッファをキューに割り当てます。
AllocAndQueueBuffersを使って_FramesToCapture(フレームレート×3)分のリングバッファを割り当てます。
[Snap]ボタンをクリックしたとき
private void btnSnap_Click(object sender, EventArgs e)
{
btnSnapEvent.Enabled = false;
System.Threading.Thread.Sleep(1000);
_bufferlist = _sink.PopAllOutputQueueBuffers();
if (_bufferlist.Length > 0)
{
tbImageIndex.Maximum = _FramesToCapture - 1;
tbImageIndex.Value = 0;
pictureBox1.Image = _bufferlist[0].CreateBitmapWrap();
btnSaveClip.Enabled = true;
}
btnPrepareFrameBufferQueue.Enabled = true;
}
さらに1待ってから、キャプチャを停止します。
キャプチャされたリングバッファはシンクから_bufferlistにコピーされます。
Sinkからのコピーされたフレームを取得するためにPopAllOutputQueueBuffersを使用します。最初のバッファのイメージをCreateBitmapWrapを使って画像ボックス(pictureBox1)に引き渡します。
スライドバーを動かしたとき
private void tbImageIndex_Scroll(object sender, EventArgs e)
{
if (_bufferlist == null)
return;
if (tbImageIndex.Value < _bufferlist.Length)
{
pictureBox1.Image = _bufferlist[tbImageIndex.Value].CreateBitmapWrap();
pictureBox1.Update();
}
}
_bufferlistに保存された画像を表示するには配列_bufferlistにインデックス番号を割り振ることで簡単にリングバッファにアクセスすることができます。
[save]ボタンをクリックしたとき
private void btnSaveClip_Click(object sender, EventArgs e)
{
if( saveClipDlg.ShowDialog(this) == DialogResult.OK)
{
SaveBufferList(saveClipDlg.FileName);
}
}
private void SaveBufferList(String FileName)
{
MFTestSharp.H264Writer writer;
int BITRATE = 60 * 1000000;
writer = new MFTestSharp.H264Writer(FileName, _sink.OutputFrameType, (int)icImagingControl1.DeviceFrameRate, BITRATE);
writer.Begin();
for (int i = 0; i < _bufferlist.Length; i++)
{
writer.Write(_bufferlist[i]);
}
writer.End();
}
Microsoft Media Foundationを使ってH264コーデックでビデオファイルを保存します。
H264Writerメソッドをラップしたもので下記の4つの変数を引き渡すことでH264コーデックにて保存することが可能です。
- FileName(ファイル名)
- OutputFrameType(解像度)
- DeviceFrameRate(フレームレート)
- BITRATE(ビットレート)
ビットレートをH.264で使用される4,000kbps程度としています。
ビットレートは1秒あたりのデータ量となっておりますので、ビットレートは高ければ高いほど、ブロックノイズが減りますがデータ量が重くなるため注意が必要です。