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

Creating the OpenCV LiveView Project

These are the steps to create a program that decodes compressed data continuously captured from an INFINICAM GbE-2 (monochrome model) in real time and streams it to a window (live view) using OpenCV.

  1. Write the source code

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

    #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>
    #include <PUCLIB.h> // PUCLIB header
    #include <opencv2/opencv.hpp> // OpenCV header
    // Context structure passed to the callback function
    struct LiveViewContext {
    UINT32 width;
    UINT32 height;
    UINT32 lineBytes;
    unsigned short qVals[PUC_Q_COUNT];
    unsigned char* pDecodedBuffer;
    bool isUpdated;
    CRITICAL_SECTION cs;
    };
    // Callback function automatically invoked by the SDK internally when an image is received
    void WINAPI ImageCallback(PPUC_XFER_DATA_INFO pXferData, void* pUserParam) {
    LiveViewContext* pCtx = (LiveViewContext*)pUserParam;
    if (!pXferData || !pXferData->pData || !pCtx || pXferData->isDamaged) {
    return;
    }
    EnterCriticalSection(&pCtx->cs);
    // High-speed multi-threaded decoding (decoded as 8-bit monochrome data)
    PUCRESULT ret = PUC_DecodeDataMultiThread(
    pCtx->pDecodedBuffer,
    0, 0,
    pCtx->width, pCtx->height,
    pCtx->lineBytes,
    pXferData->pData,
    pCtx->qVals,
    4 // Number of threads to use
    );
    if (ret == PUC_SUCCEEDED) {
    pCtx->isUpdated = true;
    }
    LeaveCriticalSection(&pCtx->cs);
    }
    int main() {
    printf("Starting INFINICAM GbE-2 OpenCV LiveView (monochrome)...\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 on the network
    PUC_DETECT_FILTER filter = { 0 };
    filter.nDeviceTypeFlags = PUC_DETECT_TARGET_GBE2; // Specify GbE-2
    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 first detected 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 (monochrome)\n", width, height);
    // 5. Set the ring buffer count for continuous transfer
    PUC_SetRingBufferCount(hDevice, 32);
    // 6. Prepare the decode buffer and context
    UINT32 lineBytes = (width + 3) & ~3; // Align to a 4-byte boundary
    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;
    }
    LiveViewContext ctx = { 0 };
    ctx.width = width;
    ctx.height = height;
    ctx.lineBytes = lineBytes;
    ctx.pDecodedBuffer = pDecodedBuffer;
    memcpy(ctx.qVals, qVals, sizeof(qVals));
    ctx.isUpdated = false;
    InitializeCriticalSection(&ctx.cs);
    // 7. Create an OpenCV Mat object that directly references the decode buffer (CV_8UC1: 8-bit monochrome, 1 channel)
    cv::Mat displayImage(height, width, CV_8UC1, pDecodedBuffer, lineBytes);
    const char* windowName = "INFINICAM GbE-2 LiveView (Press ESC or Q to exit)";
    cv::namedWindow(windowName, cv::WINDOW_AUTOSIZE);
    // 8. Start continuous transfer
    ret = PUC_BeginXferData(hDevice, ImageCallback, &ctx);
    if (ret != PUC_SUCCEEDED) {
    printf("Error: Failed to start continuous transfer. ErrorCode: %d\n", ret);
    DeleteCriticalSection(&ctx.cs);
    free(pDecodedBuffer);
    PUC_CloseDevice(hDevice);
    return EXIT_FAILURE;
    }
    printf("Displaying live view... Press 'ESC' or 'Q' on the window to exit.\n");
    // 9. Main loop: draw to the OpenCV window and watch for key input
    while (true) {
    EnterCriticalSection(&ctx.cs);
    bool updated = ctx.isUpdated;
    if (updated) {
    ctx.isUpdated = false;
    }
    LeaveCriticalSection(&ctx.cs);
    if (updated) {
    // Display the latest decoded buffer
    cv::imshow(windowName, displayImage);
    }
    // Wait 1ms and check for key input
    int key = cv::waitKey(1);
    if (key == 27 || key == 'q' || key == 'Q') { // ESC (27) or 'q' to exit
    printf("Exiting due to user action.\n");
    break;
    }
    }
    // 10. Cleanup
    PUC_EndXferData(hDevice);
    cv::destroyAllWindows();
    DeleteCriticalSection(&ctx.cs);
    free(pDecodedBuffer);
    PUC_CloseDevice(hDevice);
    return EXIT_SUCCESS;
    }
  2. Build and run

    1. Press Ctrl + Shift + B to build.
    2. With the GbE-2 connected to the network, run with Ctrl + F5.
    3. Confirm that the OpenCV display window opens and the monochrome video is displayed in real time. Pressing ESC or q on the keyboard exits normally.

In the code, the following section passes the pointer to the decode buffer (pDecodedBuffer) allocated with malloc directly to the OpenCV cv::Mat constructor.

cv::Mat displayImage(height, width, CV_8UC1, pDecodedBuffer, lineBytes);

By doing this, the moment data is decompressed by PUC_DecodeDataMultiThread, it can be drawn immediately as OpenCV image data without any intervening memory copy. Since no memory access overhead occurs, low-latency display is possible without any drop in frame rate.