Skip to content

連続高速キャプチャ

This content is not available in your language yet.

GbE-2(Ethernetモデル)の性能を限界まで引き出し、フレーム落ちを防ぎながら連続して高速画像データを取り込むプログラムの作成手順です。

  1. ソースコードの記述

    メインファイル(GbE2_ContinuousCapture.cpp など)を開き、以下のコードに書き換えます。

    #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>
    #include <PUCLIB.h> // PUCLIB ヘッダーの読み込み
    // コールバック関数へ引き渡すコンテキスト構造体
    struct CaptureContext {
    UINT32 width;
    UINT32 height;
    UINT32 lineBytes;
    unsigned short qVals[PUC_Q_COUNT];
    unsigned char* pDecodedBuffer;
    LONG frameCount;
    DWORD startTime;
    };
    // 画像受信時に SDK 内部から自動呼び出しされるコールバック関数
    void WINAPI ImageCallback(PPUC_XFER_DATA_INFO pXferData, void* pUserParam) {
    CaptureContext* pCtx = (CaptureContext*)pUserParam;
    if (!pXferData || !pXferData->pData || !pCtx) {
    return;
    }
    /*
    * --------------------------------------------------------------------------------
    * 【受信データの格納構造:pXferData (PPUC_XFER_DATA_INFO)】
    * --------------------------------------------------------------------------------
    * ・pXferData->pData
    * カメラからUDP転送されてきた「1フレーム分の圧縮RAWデータ」が格納されたポインタ。
    * 先頭16バイトには GbE-2 専用転送ヘッダー(フレーム番号や時刻等)が含まれ、
    * それに続いて固定長圧縮されたピクセルデータ(8x8ブロック単位)が並んでいます。
    * ・pXferData->nDataSize
    * 圧縮データ全体のバイトサイズ。
    * ・pXferData->nSequenceNo
    * 16bit のシーケンス番号(0〜65535)。フレーム欠損検出用。
    * ・pXferData->isDamaged
    * パケットロス等により、データに損壊が発生しているかを示すフラグ(1=異常あり)。
    * --------------------------------------------------------------------------------
    */
    // パケット損傷・フレーム落ちの確認
    if (pXferData->isDamaged) {
    printf("警告: フレーム損壊(パケットロス発生) シーケンスNo: %u\n", pXferData->nSequenceNo);
    return;
    }
    // GbE-2 専用転送ヘッダーの解析(必要に応じてフレーム状態を取得)
    PUC_HAWK_XFER_HEADER hawkHeader = { 0 };
    if (PUC_ExtractHawkXferHeader(pXferData->pData, &hawkHeader) == PUC_SUCCEEDED) {
    // hawkHeader.epochTime : カメラのタイムスタンプ(エポック秒)
    // hawkHeader.nImageNo : カメラ内部のフレーム連番
    // hawkHeader.isPacketDropped : ネットワーク層でのドロップ有無
    }
    /*
    * --------------------------------------------------------------------------------
    * 【復号データの格納構造:pCtx->pDecodedBuffer】
    * --------------------------------------------------------------------------------
    * PUC_DecodeDataMultiThread を実行すると、圧縮データ (pXferData->pData) が解凍され、
    * pCtx->pDecodedBuffer に以下のレイアウトで 8bit モノクロ輝度データが格納されます。
    *
    * [メモリレイアウト]
    * ・データ型 : unsigned char (1ピクセル = 1バイト、0=黒 〜 255=白)
    * ・配列構造 : 1次元配列(ラスタースキャン順:画像の左上原点(0,0)から右下へ連続)
    * ・行サイズ : lineBytes バイト(メモリアクセス高速化のため4バイト境界にアラインメント)
    *
    * [任意の座標 (x, y) へのアクセス例]
    * unsigned char pixel = pCtx->pDecodedBuffer[y * pCtx->lineBytes + x];
    * --------------------------------------------------------------------------------
    */
    PUCRESULT ret = PUC_DecodeDataMultiThread(
    pCtx->pDecodedBuffer, // [out] 復号画像データが書き込まれるバッファ
    0, 0, // [in] 復号開始座標 X, Y
    pCtx->width, // [in] 解像度 幅 (ピクセル)
    pCtx->height, // [in] 解像度 高さ (ピクセル)
    pCtx->lineBytes, // [in] 1行あたりのメモリバイト数
    pXferData->pData, // [in] 受信した圧縮データ
    pCtx->qVals, // [in] 量子化テーブル
    4 // [in] 使用するCPUスレッド数
    );
    if (ret == PUC_SUCCEEDED) {
    LONG currentFrame = InterlockedIncrement(&pCtx->frameCount);
    /*
    * --------------------------------------------------------------------------------
    * 【PGM画像ファイルとして保存する場合の処理例】
    * --------------------------------------------------------------------------------
    * 復号されたデータ (pCtx->pDecodedBuffer) を PGM (P5形式) で保存する例です。
    * ※高速連写中に全フレームをファイル保存するとディスクI/Oがボトルネックになるため、
    * 特定のフレームのみ保存するか、別スレッドに保存処理を委譲することを推奨します。
    *
    * char filename[64];
    * sprintf_s(filename, "captured_%05ld.pgm", currentFrame);
    * FILE* fp = NULL;
    * if (fopen_s(&fp, filename, "wb") == 0 && fp != NULL) {
    * // PGM ヘッダーの書き込み (P5 / 幅 / 高さ / 最大階調値 255)
    * fprintf(fp, "P5\n%u %u\n255\n", pCtx->width, pCtx->height);
    *
    * // パディングを除いた有効画像データ(幅バイト分)を 1 行ずつ書き込み
    * for (UINT32 y = 0; y < pCtx->height; y++) {
    * fwrite(pCtx->pDecodedBuffer + (y * pCtx->lineBytes), sizeof(unsigned char), pCtx->width, fp);
    * }
    * fclose(fp);
    * }
    * --------------------------------------------------------------------------------
    */
    // 100 フレームごとに受信性能(FPS)を表示
    if (currentFrame % 100 == 0) {
    DWORD elapsed = GetTickCount() - pCtx->startTime;
    if (elapsed > 0) {
    double fps = (double)currentFrame * 1000.0 / elapsed;
    printf("受信済み: %ld フレーム | 平均速度: %.1f FPS (SeqNo: %u)\n",
    currentFrame, fps, pXferData->nSequenceNo);
    }
    }
    }
    }
    int main() {
    printf("INFINICAM GbE-2 連続高速キャプチャ開始...\n");
    // 1. SDKの初期化
    PUCRESULT ret = PUC_Initialize();
    if (ret != PUC_SUCCEEDED) {
    printf("エラー: SDKの初期化に失敗しました。ErrorCode: %d\n", ret);
    return EXIT_FAILURE;
    }
    // 2. GbE-2 デバイスの検索
    PUC_DETECT_FILTER filter = { 0 };
    filter.nDeviceTypeFlags = PUC_DETECT_TARGET_HAWK;
    PUC_DETECT_INFO detectInfo = { 0 };
    ret = PUC_DetectDevice2(&filter, &detectInfo);
    if (ret != PUC_SUCCEEDED || detectInfo.nDeviceCount == 0) {
    printf("エラー: GbE-2 カメラが見つかりませんでした。ErrorCode: %d\n", ret);
    return EXIT_FAILURE;
    }
    // 3. デバイスのオープン
    PUC_HANDLE hDevice = NULL;
    ret = PUC_OpenDevice2(0, &hDevice);
    if (ret != PUC_SUCCEEDED) {
    printf("エラー: デバイスのオープンに失敗しました。ErrorCode: %d\n", ret);
    return EXIT_FAILURE;
    }
    // 4. 解像度および量子化テーブルの取得
    UINT32 width = 0, height = 0;
    PUC_GetResolution(hDevice, &width, &height);
    unsigned short qVals[PUC_Q_COUNT] = { 0 };
    PUC_GetQuantization2(hDevice, qVals);
    printf("撮影解像度: %u x %u ピクセル\n", width, height);
    // 5. 連続転送用のリングバッファ数の設定 (コマ落ち防止)
    ret = PUC_SetRingBufferCount(hDevice, 64);
    if (ret != PUC_SUCCEEDED) {
    printf("警告: リングバッファ数の設定に失敗しました。\n");
    }
    // 6. デコードバッファおよびコンテキストの準備
    // メモリ転送処理を効率化するため、横幅バイト数(lineBytes)を4の倍数に切上げます
    UINT32 lineBytes = (width + 3) & ~3;
    unsigned long decodedSize = (unsigned long)lineBytes * height;
    unsigned char* pDecodedBuffer = (unsigned char*)malloc(decodedSize);
    if (pDecodedBuffer == NULL) {
    printf("エラー: メモリ割り当てに失敗しました。\n");
    PUC_CloseDevice(hDevice);
    return EXIT_FAILURE;
    }
    CaptureContext ctx = { 0 };
    ctx.width = width;
    ctx.height = height;
    ctx.lineBytes = lineBytes;
    ctx.pDecodedBuffer = pDecodedBuffer;
    memcpy(ctx.qVals, qVals, sizeof(qVals));
    ctx.frameCount = 0;
    ctx.startTime = GetTickCount();
    // 7. 連続転送の開始(コールバック関数の登録)
    ret = PUC_BeginXferData(hDevice, ImageCallback, &ctx);
    if (ret != PUC_SUCCEEDED) {
    printf("エラー: 連続転送の開始に失敗しました。ErrorCode: %d\n", ret);
    free(pDecodedBuffer);
    PUC_CloseDevice(hDevice);
    return EXIT_FAILURE;
    }
    printf("連続転送を開始しました。Enterキーを押すと停止します...\n");
    getchar(); // キー入力待ち
    // 8. 連続転送の停止とクローズ
    printf("連続転送を停止中...\n");
    PUC_EndXferData(hDevice);
    printf("合計取得フレーム数: %ld\n", ctx.frameCount);
    // 9. 後処理
    free(pDecodedBuffer);
    PUC_CloseDevice(hDevice);
    return EXIT_SUCCESS;
    }
  2. ビルドと実行

    1. Ctrl + Shift + B を押してビルドを行います。
    2. Ctrl + F5 で実行します。
    3. Enter キーを押すと転送が安全に停止し、プログラムが終了します。

画像データの格納方式(補足解説)

Section titled “画像データの格納方式(補足解説)”

1. 受信バッファ (pXferData->pData) の構造

Section titled “1. 受信バッファ (pXferData->pData) の構造”
  • データ種別: カメラから転送された 1フレーム分の固定長圧縮RAWデータ。
  • ヘッダー構造: 先頭 16 バイトに GbE-2 専用転送ヘッダー(PUC_HAWK_XFER_HEADER)が書き込まれており、PUC_ExtractHawkXferHeader を通すことでタイムスタンプやカメラ内連番などを抽出できます。

2. 復号バッファ (pCtx->pDecodedBuffer) の構造

Section titled “2. 復号バッファ (pCtx->pDecodedBuffer) の構造”
  • データ種別: 展開後の 8bit モノクロ輝度データ(0 = 完全な黒、255 = 完全な白)。
  • 配列の並び順: 画像の左上原点 (0, 0) から右下へ向けた ラスタースキャン順の1次元配列。
  • メモリの行幅 (lineBytes): メモリアクセスやGPU/OpenCV連携の高速化のため、横幅(幅ピクセル数)を 4 の倍数に切り上げたバイト数で確保します。