コンテンツにスキップ

連続高速キャプチャ(GbE-2 / Python)

HelloWorld で作成した共通モジュール puclib.py と スレッド間キュー (queue.Queue) を使用して、GbE-2 から連続送信されるフレームデータをコマ落ち(フレームドロップ)させることなく高速に取り込み・復号するスクリプトの作成手順です。

  1. スクリプトの作成

    puclib.py と同じフォルダーに gbe2_continuous_capture.py を作成し、以下のコードを記述します。

    gbe2_continuous_capture.py
    import ctypes
    from ctypes import c_uint32, c_ushort, c_ubyte, c_void_p, POINTER, WINFUNCTYPE, byref
    import threading
    import queue
    import time
    import numpy as np
    # 共通モジュール puclib.py から読み込み
    from puclib import (
    load_sdk, PUC_SUCCEEDED, PUC_DETECT_TARGET_HAWK,
    PUC_Q_COUNT, PUC_DETECT_FILTER, PUC_DETECT_INFO, PUC_XFER_DATA_INFO
    )
    def main():
    print("INFINICAM GbE-2 連続高速キャプチャ開始...")
    # 1. SDKのロードと初期化
    puclib = load_sdk()
    if puclib.PUC_Initialize() != PUC_SUCCEEDED:
    print("エラー: SDKの初期化に失敗しました。")
    return
    # 2. 連続転送制御用 API の追加型定義
    CALLBACK_FUNC = WINFUNCTYPE(None, POINTER(PUC_XFER_DATA_INFO), c_void_p)
    puclib.PUC_SetRingBufferCount.argtypes = [c_void_p, c_uint32]
    puclib.PUC_SetRingBufferCount.restype = c_uint32
    puclib.PUC_BeginXferData.argtypes = [c_void_p, CALLBACK_FUNC, c_void_p]
    puclib.PUC_BeginXferData.restype = c_uint32
    puclib.PUC_EndXferData.argtypes = [c_void_p]
    puclib.PUC_EndXferData.restype = c_uint32
    # 3. デバイスの検索とオープン
    detect_filter = PUC_DETECT_FILTER(nDeviceTypeFlags=PUC_DETECT_TARGET_HAWK)
    detect_info = PUC_DETECT_INFO()
    if puclib.PUC_DetectDevice2(byref(detect_filter), byref(detect_info)) != PUC_SUCCEEDED or detect_info.nDeviceCount == 0:
    print("エラー: GbE-2 カメラが見つかりませんでした。")
    return
    h_device = c_void_p()
    if puclib.PUC_OpenDevice2(0, byref(h_device)) != PUC_SUCCEEDED:
    print("エラー: デバイスのオープンに失敗しました。")
    return
    # 4. カメラパラメータの取得
    width, height = c_uint32(), c_uint32()
    puclib.PUC_GetResolution(h_device, byref(width), byref(height))
    w, h = width.value, height.value
    q_vals = (c_ushort * PUC_Q_COUNT)()
    puclib.PUC_GetQuantization2(h_device, q_vals)
    print(f"解像度: {w} x {h} ピクセル")
    # 5. データキューと処理用の準備
    line_bytes = (w + 3) & ~3
    frame_queue = queue.Queue(maxsize=100) # 受信バッファキュー
    captured_count = 0
    target_frames = 100 # キャプチャ目標フレーム数
    stop_event = threading.Event()
    # 6. 画像受信コールバック関数 (SDK内部スレッドから呼び出されます)
    @CALLBACK_FUNC
    def image_callback(p_xfer_data, p_user_param):
    nonlocal captured_count
    if not p_xfer_data or p_xfer_data.contents.isDamaged:
    return
    # コールバック内では生の圧縮データを高速コピーしてキューに退避
    raw_data = bytes(
    ctypes.cast(p_xfer_data.contents.pData, POINTER(c_ubyte * p_xfer_data.contents.nDataSize)).contents
    )
    seq_no = p_xfer_data.contents.nSequenceNo
    try:
    frame_queue.put_nowait((seq_no, raw_data))
    captured_count += 1
    except queue.Full:
    print(f"警告: キューが満杯のためフレーム {seq_no} をドロップしました。")
    # 7. バックグラウンド復号・処理スレッド
    processed_count = 0
    def processing_worker():
    nonlocal processed_count
    # 復号用バッファ
    decoded_buf = (c_ubyte * (line_bytes * h))()
    while not stop_event.is_set() or not frame_queue.empty():
    try:
    seq_no, raw_bytes = frame_queue.get(timeout=0.1)
    except queue.Empty:
    continue
    # 圧縮データを ctypes バッファへ変換して復号
    comp_buf = (c_ubyte * len(raw_bytes)).from_buffer_copy(raw_bytes)
    ret = puclib.PUC_DecodeData(
    decoded_buf, 0, 0, w, h, line_bytes, comp_buf, q_vals
    )
    if ret == PUC_SUCCEEDED:
    processed_count += 1
    # 必要に応じてここで NumPy 化や解析・ファイル保存を実施
    if processed_count % 20 == 0:
    print(f"処理進捗: {processed_count}/{target_frames} フレーム完了 (SequenceNo: {seq_no})")
    frame_queue.task_done()
    # 8. ワーカースレッドの開始
    worker_thread = threading.Thread(target=processing_worker, daemon=True)
    worker_thread.start()
    # 9. 連続転送の開始
    puclib.PUC_SetRingBufferCount(h_device, 64)
    ret = puclib.PUC_BeginXferData(h_device, image_callback, None)
    if ret != PUC_SUCCEEDED:
    print(f"エラー: 連続転送の開始に失敗しました。ErrorCode: {ret}")
    puclib.PUC_CloseDevice(h_device)
    return
    start_time = time.time()
    print(f"連続キャプチャ中 ({target_frames} フレームの到達を待ちます)...")
    # 10. 目標フレーム数に達するまで待機
    while captured_count < target_frames:
    time.sleep(0.01)
    elapsed_time = time.time() - start_time
    # 11. 転送停止と後処理
    puclib.PUC_EndXferData(h_device)
    stop_event.set()
    worker_thread.join()
    fps = target_frames / elapsed_time if elapsed_time > 0 else 0
    print(f"\n--- キャプチャ完了 ---")
    print(f"取得フレーム数: {captured_count} フレーム")
    print(f"復号処理数 : {processed_count} フレーム")
    print(f"所要時間 : {elapsed_time:.2f} 秒")
    print(f"平均スループット: {fps:.1f} fps")
    # 12. クローズ
    puclib.PUC_CloseDevice(h_device)
    if __name__ == "__main__":
    main()
  2. スクリプトの実行

    Terminal window
    python gbe2_continuous_capture.py

    実行するとカメラから目標フレーム数(100フレーム)がキュー経由で高速取り込み・復号され、処理完了後にキャプチャ時間と実効 fps が表示されます。


高速キャプチャ実装のポイント

Section titled “高速キャプチャ実装のポイント”

1. コールバック内の処理最小化(データ退避のみ)

Section titled “1. コールバック内の処理最小化(データ退避のみ)”

コールバック関数 image_callback 内で重い画像復号(PUC_DecodeData)やディスク保存を行うと、SDK 内部の受信処理が遅延しフレームドロップの原因となります。コールバック内では圧縮データ(バイト配列)の退避とキューへの追加のみを最短時間で完了させます。

2. プロデューサー・コンシューマー構成(queue.Queue)

Section titled “2. プロデューサー・コンシューマー構成(queue.Queue)”
  • データ受信(プロデューサー): コールバック関数が受信した生データをキューへ投入。
  • データ復号(コンシューマー): 別スレッド(processing_worker)がキューからデータを取り出して非同期に復号・解析。

このマルチスレッド構造を採用することで、CPU のコアを効率的に利用し、コマ落ちのない安定した高速連続撮影が可能になります。