Skip to content
⚡ Developer documentation for INFINICAM, the real-time high-speed camera (UC-1.1 / GbE-2)

Continuous High-Speed Capture

These are the steps to create a program that pushes the performance of the GbE-2 (Ethernet model) to its limit, continuously capturing high-speed image data while preventing dropped frames.

  1. Write the source code

    Open the main file (e.g. GbE2_ContinuousCapture.cpp) and replace its contents with the following code.

    #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>
    #include <PUCLIB.h> // Load the PUCLIB header
    // Context structure passed to the callback function
    struct CaptureContext {
    UINT32 width;
    UINT32 height;
    UINT32 lineBytes;
    unsigned short qVals[PUC_Q_COUNT];
    unsigned char* pDecodedBuffer;
    LONG frameCount;
    DWORD startTime;
    };
    // Callback function automatically invoked by the SDK internally when an image is received
    void WINAPI ImageCallback(PPUC_XFER_DATA_INFO pXferData, void* pUserParam) {
    CaptureContext* pCtx = (CaptureContext*)pUserParam;
    if (!pXferData || !pXferData->pData || !pCtx) {
    return;
    }
    /*
    * --------------------------------------------------------------------------------
    * [Structure of the received data: pXferData (PPUC_XFER_DATA_INFO)]
    * --------------------------------------------------------------------------------
    * - pXferData->pData
    * A pointer to "one frame's worth of compressed RAW data" transferred over UDP from the camera.
    * The first 16 bytes contain a GbE-2-specific transfer header (frame number, timestamp, etc.),
    * followed by the fixed-length compressed pixel data (in 8x8 block units).
    * - pXferData->nDataSize
    * The total byte size of the compressed data.
    * - pXferData->nSequenceNo
    * A 16-bit sequence number (0-65535), used to detect dropped frames.
    * - pXferData->isDamaged
    * A flag indicating whether the data has been corrupted due to packet loss, etc. (1 = abnormal).
    * --------------------------------------------------------------------------------
    */
    // Check for packet damage / dropped frames
    if (pXferData->isDamaged) {
    printf("Warning: Frame corrupted (packet loss occurred). Sequence No: %u\n", pXferData->nSequenceNo);
    return;
    }
    // Parse the GbE-2-specific transfer header (retrieve frame state information as needed)
    PUC_GBE2_XFER_HEADER gbe2Header = { 0 };
    if (PUC_ExtractGbe2XferHeader(pXferData->pData, &gbe2Header) == PUC_SUCCEEDED) {
    // gbe2Header.epochTime : The camera's timestamp (epoch seconds)
    // gbe2Header.nImageNo : The camera's internal frame sequence number
    // gbe2Header.isPacketDropped : Whether a drop occurred at the network layer
    }
    /*
    * --------------------------------------------------------------------------------
    * [Structure of the decoded data: pCtx->pDecodedBuffer]
    * --------------------------------------------------------------------------------
    * When PUC_DecodeDataMultiThread is executed, the compressed data (pXferData->pData) is decompressed
    * and 8-bit monochrome brightness data is stored in pCtx->pDecodedBuffer with the following layout.
    *
    * [Memory layout]
    * - Data type : unsigned char (1 pixel = 1 byte, 0 = black ... 255 = white)
    * - Array layout: a 1-dimensional array (raster scan order: contiguous from the image's
    * top-left origin (0, 0) to the bottom right)
    * - Row size : lineBytes bytes (aligned to a 4-byte boundary for faster memory access)
    *
    * [Example of accessing an arbitrary coordinate (x, y)]
    * unsigned char pixel = pCtx->pDecodedBuffer[y * pCtx->lineBytes + x];
    * --------------------------------------------------------------------------------
    */
    PUCRESULT ret = PUC_DecodeDataMultiThread(
    pCtx->pDecodedBuffer, // [out] Buffer to which the decoded image data is written
    0, 0, // [in] Decode start coordinates X, Y
    pCtx->width, // [in] Resolution width (pixels)
    pCtx->height, // [in] Resolution height (pixels)
    pCtx->lineBytes, // [in] Bytes per row in memory
    pXferData->pData, // [in] The received compressed data
    pCtx->qVals, // [in] The quantization table
    4 // [in] Number of CPU threads to use
    );
    if (ret == PUC_SUCCEEDED) {
    LONG currentFrame = InterlockedIncrement(&pCtx->frameCount);
    /*
    * --------------------------------------------------------------------------------
    * [Example: saving as a PGM image file]
    * --------------------------------------------------------------------------------
    * An example of saving the decoded data (pCtx->pDecodedBuffer) in PGM (P5 format).
    * Note: Since saving every frame to disk during high-speed continuous capture would make
    * disk I/O the bottleneck, it is recommended to save only specific frames, or
    * to delegate the saving process to a separate thread.
    *
    * char filename[64];
    * sprintf_s(filename, "captured_%05ld.pgm", currentFrame);
    * FILE* fp = NULL;
    * if (fopen_s(&fp, filename, "wb") == 0 && fp != NULL) {
    * // Write the PGM header (P5 / width / height / max gray value 255)
    * fprintf(fp, "P5\n%u %u\n255\n", pCtx->width, pCtx->height);
    *
    * // Write the valid image data (the width in bytes, excluding padding) one row at a time
    * for (UINT32 y = 0; y < pCtx->height; y++) {
    * fwrite(pCtx->pDecodedBuffer + (y * pCtx->lineBytes), sizeof(unsigned char), pCtx->width, fp);
    * }
    * fclose(fp);
    * }
    * --------------------------------------------------------------------------------
    */
    // Display the reception performance (FPS) every 100 frames
    if (currentFrame % 100 == 0) {
    DWORD elapsed = GetTickCount() - pCtx->startTime;
    if (elapsed > 0) {
    double fps = (double)currentFrame * 1000.0 / elapsed;
    printf("Received: %ld frames | Average speed: %.1f FPS (SeqNo: %u)\n",
    currentFrame, fps, pXferData->nSequenceNo);
    }
    }
    }
    }
    int main() {
    printf("Starting INFINICAM GbE-2 continuous high-speed capture...\n");
    // 1. Initialize the SDK
    PUCRESULT ret = PUC_Initialize();
    if (ret != PUC_SUCCEEDED) {
    printf("Error: Failed to initialize the SDK. ErrorCode: %d\n", ret);
    return EXIT_FAILURE;
    }
    // 2. Search for a GbE-2 device
    PUC_DETECT_FILTER filter = { 0 };
    filter.nDeviceTypeFlags = PUC_DETECT_TARGET_GBE2;
    PUC_DETECT_INFO detectInfo = { 0 };
    ret = PUC_DetectDevice2(&filter, &detectInfo);
    if (ret != PUC_SUCCEEDED || detectInfo.nDeviceCount == 0) {
    printf("Error: No GbE-2 camera was found. ErrorCode: %d\n", ret);
    return EXIT_FAILURE;
    }
    // 3. Open the device
    PUC_HANDLE hDevice = NULL;
    ret = PUC_OpenDevice2(0, &hDevice);
    if (ret != PUC_SUCCEEDED) {
    printf("Error: Failed to open the device. ErrorCode: %d\n", ret);
    return EXIT_FAILURE;
    }
    // 4. Get the resolution and quantization table
    UINT32 width = 0, height = 0;
    PUC_GetResolution(hDevice, &width, &height);
    unsigned short qVals[PUC_Q_COUNT] = { 0 };
    PUC_GetQuantization2(hDevice, qVals);
    printf("Capture resolution: %u x %u pixels\n", width, height);
    // 5. Set the ring buffer count for continuous transfer (to prevent dropped frames)
    ret = PUC_SetRingBufferCount(hDevice, 64);
    if (ret != PUC_SUCCEEDED) {
    printf("Warning: Failed to set the ring buffer count.\n");
    }
    // 6. Prepare the decode buffer and context
    // Round up the row byte count (lineBytes) to a multiple of 4 for more efficient memory transfer
    UINT32 lineBytes = (width + 3) & ~3;
    unsigned long decodedSize = (unsigned long)lineBytes * height;
    unsigned char* pDecodedBuffer = (unsigned char*)malloc(decodedSize);
    if (pDecodedBuffer == NULL) {
    printf("Error: Memory allocation failed.\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. Start continuous transfer (register the callback function)
    ret = PUC_BeginXferData(hDevice, ImageCallback, &ctx);
    if (ret != PUC_SUCCEEDED) {
    printf("Error: Failed to start continuous transfer. ErrorCode: %d\n", ret);
    free(pDecodedBuffer);
    PUC_CloseDevice(hDevice);
    return EXIT_FAILURE;
    }
    printf("Continuous transfer started. Press Enter to stop...\n");
    getchar(); // Wait for key input
    // 8. Stop the continuous transfer and close
    printf("Stopping continuous transfer...\n");
    PUC_EndXferData(hDevice);
    printf("Total frames captured: %ld\n", ctx.frameCount);
    // 9. Cleanup
    free(pDecodedBuffer);
    PUC_CloseDevice(hDevice);
    return EXIT_SUCCESS;
    }
  2. Build and run

    1. Press Ctrl + Shift + B to build.
    2. Run with Ctrl + F5.
    3. Press Enter to safely stop the transfer and end the program.

How Image Data Is Stored (Supplementary Notes)

Section titled “How Image Data Is Stored (Supplementary Notes)”

1. Structure of the receive buffer (pXferData->pData)

Section titled “1. Structure of the receive buffer (pXferData->pData)”
  • Data type: One frame’s worth of fixed-length compressed RAW data transferred from the camera.
  • Header structure: The GbE-2-specific transfer header (PUC_GBE2_XFER_HEADER) is written in the first 16 bytes, and passing it through PUC_ExtractGbe2XferHeader allows you to extract the timestamp, the camera’s internal sequence number, and other information.

2. Structure of the decode buffer (pCtx->pDecodedBuffer)

Section titled “2. Structure of the decode buffer (pCtx->pDecodedBuffer)”
  • Data type: The expanded 8-bit monochrome brightness data (0 = pure black, 255 = pure white).
  • Array layout: A 1-dimensional array in raster scan order, from the image’s top-left origin (0, 0) toward the bottom right.
  • Row width in memory (lineBytes): Allocated with the width (in pixels) rounded up to a multiple of 4 bytes, to speed up memory access and GPU/OpenCV integration.