連続高速キャプチャ
GbE-2(Ethernetモデル)の性能を限界まで引き出し、フレーム落ちを防ぎながら連続して高速画像データを取り込むプログラムの作成手順です。
-
ソースコードの記述
メインファイル(
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, YpCtx->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;} -
ビルドと実行
Ctrl+Shift+Bを押してビルドを行います。Ctrl+F5で実行します。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 の倍数に切り上げたバイト数で確保します。
