PVCAM  3.9.x
Programmable Virtual Camera Access Method library
Centroids

This code sample demonstrates how to configure the camera for Centroids acquisition. Centroids are small regions of interest that are selected by the camera based on results from in-camera image analysis. The approach for decoding the regions from the image buffer is exactly the same as with acquisitions where regions are selected by the user (see Multiple Regions example). However, as the Centroids counts and locations may change with every frame, special precautions may be required on the application side to handle this dynamic behavior.

For general information about Centroids acquisition please refer to Centroids chapter.

The code snippet below initializes PVCAM and opens the first available camera. Please, refer to the actual code sample in the SDK installation directory for more details about the common helper functions used in this documentation.

std::vector<CameraContext*> contexts;
if (!InitAndOpenOneCamera(contexts, cSingleCamIndex))
return APP_EXIT_ERROR;
CameraContext* ctx = contexts[cSingleCamIndex];

This code example uses the recommended callbacks acquisition (see Polling versus Callbacks). A part of the code omitted here defines a static callback function and registers the function with PVCAM. This approach is used in other code samples as well. The relevant code part is described in Frame Callback Handler section.

As the Centroids feature is not supported by all camera models, a check for the feature availability is recommended.

if (!IsParamAvailable(ctx->hcam, PARAM_CENTROIDS_ENABLED, "PARAM_CENTROIDS_ENABLED"))
{
CloseAllCamerasAndUninit(contexts);
return APP_EXIT_ERROR;
}

If the feature is available, we can now access the PARAM_CENTROIDS_COUNT to retrieve and configure the maximum number of regions the camera can generate. Another parameter, the PARAM_CENTROIDS_RADIUS, can be used to configure the desired size of the regions.

The centroids feature requires frame metadata feature to be enabled, therefore, before starting the Centroids acquisition, the PARAM_METADATA_ENABLED needs to be set to TRUE.

Prepare the acquisition. The pl_exp_setup_seq function returns the size of the frame buffer required for the acquisition. For Centroids acquisition, the input region defines the bounding rectangle where the camera will search for events. The resulting ROIs will all originate from within this boundary region. For general information about how to set up a sequence acquisition please refer to Acquisition Configuration. Please note that newer cameras also support PARAM_CENTROIDS_BG_COUNT and PARAM_CENTROIDS_THRESHOLD parameters. Depending on the light level and number of frames requested, these parameters may need to be adjusted as well. If these parameters are not adjusted, the camera may not be able to locate any events in the image and may send empty frames with no centroids.

uns32 exposureBytes;
const uns32 exposureTime = 10; // milliseconds
const uns16 framesToAcquire = 5;
if (PV_OK != pl_exp_setup_seq(ctx->hcam, framesToAcquire, 1, &ctx->region,
exposureMode, exposureTime, &exposureBytes))
{
PrintErrorMessage(pl_error_code(), "pl_exp_setup_seq() error");
CloseAllCamerasAndUninit(contexts);
return APP_EXIT_ERROR;
}
Note
For detailed information about error handling, please, refer to Getting Error Messages.
This example uses the extended trigger mode configuration and sets up the camera to use internal trigger with first-row expose out mode. However, legacy cameras do not support the extended modes and the list of supported expose out modes may differ as well. It is important to always discover the supported modes using the PARAM_EXPOSURE_MODE and PARAM_EXPOSE_OUT_MODE parameters when working with multiple camera models.

Allocate a buffer of the size reported by the pl_exp_setup_seq function.

uns8* pSequenceBuffer = new (std::nothrow) uns8[exposureBytes];
if (!pSequenceBuffer)
{
printf("Unable to allocate buffer for camera %d\n", ctx->hcam);
CloseAllCamerasAndUninit(contexts);
return APP_EXIT_ERROR;
}
// Calculate size of each frame (needed later)
const uns32 oneFrameBytes = exposureBytes / framesToAcquire;

Prepare the frame descriptor that will be used to extract the centroids from the image buffer. Since we know how many centroids were requested, we can pre-allocate a single descriptor and reuse it for each frame.

md_frame* pFrameDesc = NULL;
(uns16)desiredCentroidsCount))
{
PrintErrorMessage(pl_error_code(),
"pl_md_create_frame_struct_cont() error");
delete [] pSequenceBuffer;
CloseAllCamerasAndUninit(contexts);
return APP_EXIT_ERROR;
}

Start the acquisition:

if (PV_OK != pl_exp_start_seq(ctx->hcam, pSequenceBuffer))
{
PrintErrorMessage(pl_error_code(), "pl_exp_start_seq() error");
delete [] pSequenceBuffer;
CloseAllCamerasAndUninit(contexts);
return APP_EXIT_ERROR;
}

Acquire the desired number of frames in a loop:

bool errorOccurred = false;
uns32 framesAcquired = 0;
while (framesAcquired < framesToAcquire)
{

Here we need to wait for a frame readout notification signaled by the eofEvent in the CameraContext which is raised in the callback handler we registered. If the frame does not arrive within 5 seconds, or if the user aborts the acquisition with ctrl+c shortcut, the main while loop is interrupted and the acquisition is aborted.

printf("Waiting for EOF event to occur on camera %d\n", ctx->hcam);
if (!WaitForEofEvent(ctx, 5000, errorOccurred))
break;

Once the frame is acquired, use the pl_md_frame_decode to decode the frame and fill in the frame descriptor with pointers to frame metadata and ROI image data. After the frame is decoded, the descriptor can be used to display information about the ROI coordinates. The descriptor can also be used to create a black-filled displayable image (using the pl_md_frame_recompose) or the descriptor can be processed manually by the application to only display the selected ROIs. Please, see the Multiple Regions and pl_md_frame_recompose for more details.

if (PV_OK != pl_md_frame_decode(pFrameDesc, ctx->eofFrame, oneFrameBytes))
{
PrintErrorMessage(pl_error_code(), "pl_md_frame_decode() error");
errorOccurred = true;
break;
}
PrintMetaFrame(ctx, pFrameDesc, false);
framesAcquired++;
} // End of while cycle

Here the pl_exp_abort is not strictly required as correctly acquired sequence does not need to be aborted. However, it is kept here for situations where the acquisition is interrupted by the user or when it unexpectedly times out. Please, refer to the Closing Camera and Uninitializing PVCAM section for more details.

if (PV_OK != pl_exp_abort(ctx->hcam, CCS_HALT))
PrintErrorMessage(pl_error_code(), "pl_exp_abort() error");
if (PV_OK != pl_exp_finish_seq(ctx->hcam, pSequenceBuffer, 0))
PrintErrorMessage(pl_error_code(), "pl_exp_finish_seq() error");
if (PV_OK != pl_md_release_frame_struct(pFrameDesc))
PrintErrorMessage(pl_error_code(), "pl_md_release_frame_struct() error");
delete [] pSequenceBuffer;
CloseAllCamerasAndUninit(contexts);
if (errorOccurred)
return APP_EXIT_ERROR;
return 0;