1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Baslerカメラ画像をOpenCVで表示・保存する【pylon SDK × OpenCvSharp / C#】

Posted at

Baslerカメラ画像をOpenCVで表示・保存する【pylon SDK × OpenCvSharp / C#】

Baslerカメラの画像を取得して処理したいとき、
OpenCVの Mat 形式に変換できると画像処理の幅が一気に広がります。

この記事では、pylon SDKで得た GrabResult から
OpenCVの Mat に変換し、リアルタイム表示 (Cv2.ImShow) と保存 (Cv2.ImWrite) を行う方法を紹介します。


✅ 使用環境

項目 内容
カメラ Basler acA2500-14gm
SDK pylon Camera Software Suite
画像形式 Mono8(8bitグレースケール)
OpenCVラッパー OpenCvSharp4.Windows(NuGet)

🎯 目的:GrabResult → Mat に変換して表示・保存

イメージ図:

[GrabResult] → [byte[]] → [Mat] → [ImShow / ImWrite]

🔧 GrabResult から Mat を作るコード

PixelDataConverterクラスのConvert()メソッドを用いて、IGrabResultbyte[]に変換します。
その後、byte[]を引数にMatをインスタンス化します。

実装する際は以下のようなIGrabResultの拡張メソッドを作っておくと使いやすいです。

csharp IGrabResultExtensions.cs

using Basler.Pylon;
using OpenCvSharp;

namespace BaslerSamples
{
    public static class IGrabResultExtensions
    {
        public static Mat ToMat(this IGrabResult result)
        {
            if (!result.GrabSucceeded)
                throw new InvalidOperationException("Grab failed");

            var converter = new PixelDataConverter
            {
                OutputPixelFormat = PixelType.Mono8
            };

            int width = result.Width;
            int height = result.Height;
            byte[] buffer = new byte[width * height];

            converter.Convert(buffer, result);

            // OpenCVのMatに変換(Gray8 → CV_8UC1)
            var handle = System.Runtime.InteropServices.GCHandle.Alloc(buffer, System.Runtime.InteropServices.GCHandleType.Pinned);
            try
            {
                IntPtr ptr = handle.AddrOfPinnedObject();
                // ※ Mat.FromPixelData() は OpenCvSharp の拡張メソッドで、バイト配列から Mat を生成できます。
                return Mat.FromPixelData(height, width, MatType.CV_8UC1, ptr);
            }
            finally
            {
                handle.Free();
            }
        }
    }
}


🖼️ ImShowでリアルタイム表示

今回も前回記事(Baslerカメラの画像取得をイベント駆動+非同期保存で安定化する)で紹介したBaslerCameraSampleクラスに、機能を追加していきます。
前回はバッファに貯めた画像を非同期で保存しましたが、今回は表示させてみます。

using OpenCvSharp;

public async Task ShowBufferedImages(CancellationTokenSource cts)
{
    // キューが空になるまで、またはキャンセル要求があるまでループします。
    // 撮影中はキューに追加される可能性があるため、ループを続けます。
    while (!cts.IsCancellationRequested && (IsGrabbing || _bufferedImageQueue.Count > 0))
    {
        if (_bufferedImageQueue.TryDequeue(out var item))
        {
            using (Mat mat = item.Item2.ToMat())
            {
                Cv2.ImShow("Camera", mat);
                Cv2.WaitKey(1); // 0だとブロックするので1ミリ秒待機
            }
        }
        else
        {
            await Task.Delay(1); // 空ループ対策
        }
    }
    // キューをクリア(次のStartGrabbingで実行する実装でもよい)
    _bufferedImageQueue.Clear();
}

実行例

テストコードは以下のようになります。前回とほとんど同じですね。

[TestMethod()]
public async Task ShowBufferedImagesTest()
{
    if (!_baslerCameraSample.IsConnected)
        _baslerCameraSample.Connect();


    Task? showTask = null;
    try
    {
        _baslerCameraSample.StartGrabbing();
        Assert.IsTrue(_baslerCameraSample.IsGrabbing, "Camera should be grabbing after StartGrabbing is called.");
        // 撮影開始後に画像を表示するタスクを開始する。
        // 保存をキャンセルしたい場合は、cts.Cancel()を呼び出します。
        var cts = new CancellationTokenSource();
        showTask = _baslerCameraSample.ShowBufferedImages(cts);
        await Task.Delay(10000); // 10秒間連続撮影し、画像を表示する
    }
    catch (InvalidOperationException ex)
    {
        Assert.Fail($"StartGrabbing failed: {ex.Message}");
    }
    finally
    {
        // StopGrabbingを呼び出して、グラブを停止する。
        _baslerCameraSample.StopGrabbing();
        Assert.IsFalse(_baslerCameraSample.IsGrabbing, "Camera should not be grabbing after StopGrabbing is called.");

        if (showTask is not null)
            await showTask; // 保存タスクが完了するのを待つ
    }
}

実行すると以下の画像のようにCameraというウィンドウにカメラのライブ画像が表示されます。

ImShowの結果


💾 ImWriteでファイル保存 ― Matを画像として保存するには?

Matの画像を保存するにはImWrite()メソッドを使用します。
以下はSnap()で1枚画像を取得してMatに変換し、画像処理を加えて保存するサンプルコードです。

[TestMethod]
public void SnapAndSaveMatTest()
{
    if (!_baslerCameraSample.IsConnected)
        _baslerCameraSample.Connect();

    var result = _baslerCameraSample.Snap(1000);
    using (var mat = result.ToMat())
    {
        Assert.IsNotNull(mat);
        // 画像を保存
        mat.ImWrite("SnapAndSaveMatTest_Mat.bmp");
        // 試しに2値化処理する。
        Cv2.Threshold(mat, mat, 128, 255, ThresholdTypes.Binary);
        mat.ImWrite("SnapAndSaveMatTest_Mat_Thresholded.bmp");
        Assert.IsTrue(System.IO.File.Exists("SnapAndSaveMatTest_Mat.bmp"));
    }

    // 比較のため、BitmapSourceでも保存してみる。
    var bitmap = _baslerCameraSample.ConvertGrabResultToBitmap(result);
    BaslerCameraSample.SaveBitmap(bitmap, "SnapAndSaveMatTest_Bitmap.bmp");

    Assert.IsNotNull(result);
}

サンプルを実行すると以下の3枚画像が保存されます。

  • BitmapSourceに変換した後で保存した画像
  • ImWrite()で保存した画像
  • Cv2.Threshold()で生成した2値化画像

比較してみると、BitmapSourceMatで同じ画像が保存できていることがわかります。

BitmapSource Mat Mat(2値化)
BitmapSource Mat Mat(2値化)

📌 注意点とハマりポイント

カメラの種類に応じて、MatTypeを変える必要があります。また、ImShow()で動作確認する場合はWaitKey()を入れ忘れないようにしてください。

注意点 内容
PixelFormatがRGB/BGRの場合 MatTypeを CV_8UC3 にする必要あり
カラー画像で表示がずれる RGB/BGR順の変換が必要な場合あり
WaitKey() を忘れると表示されない 必ず呼ぶ必要あり
NuGetでOpenCvSharpを導入 OpenCvSharp4, OpenCvSharp4.runtime.windows を併用

✅ まとめ

  • GrabResult から byte[] を取得し、OpenCVの Mat に変換できる
  • Cv2.ImShow でリアルタイム表示、Cv2.ImWrite で保存可能
  • 今後、OpenCVによるフィルタ処理や輪郭抽出などにも展開しやすい

筆者:@MilleVision

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?