Basler pylon SDKでソフトウェアトリガー撮影を行う(C# / .NET対応)
Baslerなどの産業用カメラを使うとき、常に撮り続ける「フリーラン撮影」ではなく、
任意のタイミングで撮影したい 場面があります。
例えば──
- 実験装置のボタンを押した瞬間
- 温度や電圧がある値に達した瞬間
- 外部イベントに同期したタイミング
このような場合に便利なのが ソフトウェアトリガー撮影 です。
本記事では、pylon SDKを使ってC#からソフトウェアトリガー撮影を行う方法を紹介し、
フリーラン撮影との違いをストップウォッチで比較してみます。
✅ 環境
項目 | 内容 |
---|---|
カメラ | Basler acA2500-14gm |
SDK | pylon Camera Software Suite |
言語 | C# / .NET 8.0 (Windows) |
今回も前回記事(Baslerカメラで撮影画像に設定値を記録する方法(C# / .NET対応))で紹介したBaslerCameraSample
クラスに機能を追加し、実行例を紹介していきます。
🔧 ソフトウェアトリガーの設定
pylon SDKでは、以下のようにトリガーモードを設定します。
public bool SetSoftwareTrigger(bool enable)
{
if(Camera == null)
return false;
if (enable)
{
// トリガーをソフトウェアトリガーに設定する。
var isTriggerSourceSet = SetPLCameraParameter(PLCamera.TriggerSource, PLCamera.TriggerSource.Software);
// トリガーソースが設定されているか確認
if (!isTriggerSourceSet)
{
Console.WriteLine("Failed to set software trigger source.");
return false;
}
// トリガーを有効化する
return SetPLCameraParameter(PLCamera.TriggerMode, PLCamera.TriggerMode.On);
}
else
{
// トリガーを無効にする
return SetPLCameraParameter(PLCamera.TriggerMode, PLCamera.TriggerMode.Off);
}
}
撮影時は ExecuteSoftwareTrigger()
を呼び出すことで、
その瞬間のフレームを取得できます。
// トリガーを発行して1枚撮影
public void ExecuteSoftwareTrigger(int timeoutMs = 5000)
{
if (Camera?.WaitForFrameTriggerReady(timeoutMs, TimeoutHandling.ThrowException) == true)
{
Camera?.ExecuteSoftwareTrigger();
}
}
ExecuteSoftwareTrigger
を呼ぶ前には、カメラのストリーミングを開始している必要があるため、Camera.StreamGrabber.Start()
をあらかじめ呼ぶ必要があります。
実行例
ソフトウェアトリガーの実行例です。やり方は本当にいろいろありますが、これまでの実装を流用しつつ、以下の流れで実行します。
- トリガーモードを設定
- カメラのストリーミングを開始
- ソフトウェアトリガー作動
- フレーム取得を検知し、ストリーミング停止
ソフトウェアトリガーを作動をさせた後、ImageGrabbed
イベントが発生するまで時間かかるため、確認してからストリーミングを停止します。
[TestMethod()]
public async Task ExecuteSoftwareTriggerTest()
{
if (!_baslerCameraSample.IsConnected)
_baslerCameraSample.Connect();
// トリガーモードを設定
var isEnabled = _baslerCameraSample.SetSoftwareTrigger(true);
Assert.IsTrue(isEnabled);
// ストリーミングを開始
_baslerCameraSample.StartGrabbing();
// 1000ms後に何か起きたと仮定する。
await Task.Delay(1000);
// イベント発生からの経過時間を調べる。
var startTime = DateTime.Now;
// ソフトウェアトリガー作動
_baslerCameraSample.ExecuteSoftwareTrigger();
var elapsedTrigger = (DateTime.Now - startTime).TotalMilliseconds;
// 例:Elapsed time(Trigger): 5.9921 ms
Console.WriteLine($"Elapsed time(Trigger): {elapsedTrigger} ms");
Assert.IsTrue(elapsedTrigger <= 10, "Elapsed time should be less than 10 ms");
// フレームを取得してOnImageGrabedが走るまでに時間がかかるので待つ。
// CancellationTokenを使用して待機をキャンセルできるようにする。
try
{
var cts = new CancellationTokenSource();
var waitTask = Task.Delay(1000, cts.Token);
while (!waitTask.IsCompleted)
{
await Task.Delay(1);
// フレーム取得を検知
if (_baslerCameraSample.BufferedImageQueue.Count > 0)
cts.Cancel();
}
await waitTask;
// キャンセルされなければ、フレームがきていないので失敗とする。
Assert.Fail("OnImageGrabed was not called within the expected time.");
}
catch (TaskCanceledException)
{
// 期待通り、OnImageGrabedでキャンセルされた。
}
// フレーム取得後にストリーミングを停止する。
_baslerCameraSample.StopGrabbing();
// 取得した画像を取り出し、OnImageGrabedが呼ばれた時間を計測したり、画像を保存したりする。
Assert.AreEqual(1, _baslerCameraSample.BufferedImageQueue.Count);
_baslerCameraSample.BufferedImageQueue.TryDequeue(out var item);
var elapsedOnImageGrabed = (item.Item1-startTime).TotalMilliseconds;
// 例:Elapsed time(OnImageGrabed): 112.0971 ms
Console.WriteLine($"Elapsed time(OnImageGrabed): {elapsedOnImageGrabed} ms");
var bitmap = BaslerCameraSample.ConvertGrabResultToBitmap(item.Item2);
var fileName = $"TriggeredCapture_{DateTime.Now:yyyyMMdd_HHmmss}.bmp";
BaslerCameraSample.SaveBitmap(bitmap, fileName);
Assert.IsTrue(File.Exists(fileName), $"File {fileName} should be created.");
}
⏱ 実験:ソフトウェアトリガー vs 単に1枚撮影(GrabOne()
)
「イベント発生してからGrabOne()
」などで画像取得するのと何が違うのか?
と疑問に思いませんか?
そこで、ExecuteSoftwareTriggerTest
に比較用のコードを足して、時間を比較します。
[TestMethod()]
public async Task ExecuteSoftwareTriggerTest()
{
// ~~前略~~
// トリガーを無効にする
var isDisabled = _baslerCameraSample.SetSoftwareTrigger(false);
Assert.IsTrue(isDisabled);
// GrabOneで1枚取得するのにかかる時間を計測して比較する。
var grabSW = new Stopwatch();
grabSW.Start();
var grabbedImage = _baslerCameraSample.Snap();
grabSW.Stop();
var elapesedGrabOne = grabSW.ElapsedMilliseconds;
// 例:Elapsed time(GrabOne): 193 ms
Console.WriteLine($"Elapsed time(GrabOne): {elapesedGrabOne} ms");
// トリガー待ちしていた方が、普通に1枚画像取得するよりも早く画像が取得できる。
// また、GrabOneではいつトリガーが入ったのか分からないが、ソフトウェアトリガーでは確実にトリガー直後に画像が取得できる。
Assert.IsTrue(elapsedOnImageGrabed < elapesedGrabOne);
}
📊 実験結果
「筆者の環境(acA2500-14gm, GigE, AMD Ryzen 9 Windows環境)では、ソフトウェアトリガーの方が早く画像を取得できました。ストリーミングをすでに開始しているため、その分早く画像を取得できたのだと思います。
まとめると以下のとおりです。
モード | 取得時間 | 特徴 |
---|---|---|
ソフトトリガー | 112ms | 画像取得が早い。いつトリガーが入ったのか分かる。 |
GrabOne | 193ms | 実装は簡単。トリガーがいつ入ったのか厳密にはわからない。 |
📝 まとめ
-
ソフトウェアトリガーは「意味のある瞬間を狙って撮影する」のに便利
- 特に研究や検査では 実験データと同期できる自由度の高さ が大きなメリットになる
次回はWPFでカメラ画像を表示させる方法をご紹介します。現在開発中のカメラ設定管理ツールにも採用している設計です。
筆者:@MilleVision
🛠 サンプルコード完全版のご案内
Qiita記事で紹介しているサンプルコードをまとめた C#プロジェクト(単体テスト付き) を
BOOTHにて配布しています。
- 単体テストプロジェクト同梱で動作確認や学習がスムーズ
- 記事更新にあわせてアップデート予定