PVCAM  3.9.x
Programmable Virtual Camera Access Method library
Multiple Regions

This sample application demonstrates acquisition of frames with multiple defined regions of interest. Callback acquisition is used and 5 frames are acquired in total. The example uses so called 'emulated' software trigger where sequence acquisition for a single frame is configured and the acquisition is then executed multiple times. The number of regions can be configured by the cRoiCount constant, the ROIs are automatically arranged in a diagonal order.

Please note that multiple region acquisition is supported on modern cameras only. The acquisition with multiple regions stores additional metadata in user image buffer and requires this feature to be enabled with PARAM_METADATA_ENABLED parameter.

Initialize PVCAM and open 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.

Ensure that the camera supports multiple region acquisition and obtain the maximum number of supported ROIs per frame.

if (!IsParamAvailable(ctx->hcam, PARAM_ROI_COUNT, "PARAM_ROI_COUNT"))
{
CloseAllCamerasAndUninit(contexts);
return APP_EXIT_ERROR;
}
uns16 roiCount;
// ATTR_COUNT or ATTR_MAX can be checked here, both give the same value
(void*)&roiCount))
{
PrintErrorMessage(pl_error_code(), "pl_get_param(PARAM_ROI_COUNT) error");
CloseAllCamerasAndUninit(contexts);
return APP_EXIT_ERROR;
}

The multiple-ROI feature requires frame metadata feature to be enabled, therefore, before starting the acquisition, the PARAM_METADATA_ENABLED needs to be set to TRUE.

Set regions diagonally from the top-left corner to the bottom-right corner with serial and parallel binning set to 1. Regions must not overlap.

rgn_type regions[cRoiCount];
for (uns16 n = 0; n < cRoiCount; n++)
{
regions[n].s1 = ctx->sensorResX / cRoiCount * n;
regions[n].s2 = (ctx->sensorResX / cRoiCount * (n + 1)) - 1;
regions[n].sbin = 1;
regions[n].p1 = ctx->sensorResY / cRoiCount * n;
regions[n].p2 = (ctx->sensorResY / cRoiCount * (n + 1)) - 1;
regions[n].pbin = 1;
}

Prepare the sequence acquisition. The pl_exp_setup_seq function returns the size of the buffer required to store all images (unlike a size of just one frame as with the pl_exp_setup_cont function). In this case, we only require one image acquisition. For more details, please refer to Acquisition Configuration. section.

uns32 exposureBytes;
const uns32 exposureTime = 40; // milliseconds
if (PV_OK != pl_exp_setup_seq(ctx->hcam, 1, cRoiCount, &regions[0],
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.

Now allocate the buffer memory to the size reported by pl_exp_setup_seq function.

uns8* frameInMemory = new (std::nothrow) uns8[exposureBytes];
if (!frameInMemory)
{
printf("Unable to allocate acq. buffer for camera %d\n", ctx->hcam);
CloseAllCamerasAndUninit(contexts);
return APP_EXIT_ERROR;
}

In this example we will recompose all individual ROIs into full frame image, as they would appear on the sensor, thus we allocate memory for the full frame image. At the moment the pl_md_frame_recompose function works with uns16 type pixels only.

const uns32 fullFrameBytes = ctx->sensorResX * ctx->sensorResY * sizeof(uns16);
uns8 *fullFrame = new (std::nothrow) uns8[fullFrameBytes];
if (!fullFrame)
{
printf("Unable to allocate recompose buffer for camera %d\n", ctx->hcam);
delete [] frameInMemory;
CloseAllCamerasAndUninit(contexts);
return APP_EXIT_ERROR;
}
Warning
Do not forget to fill the whole frame with black pixels, otherwise the recomposed frame will contain random values for pixels outside of the acquired regions:
std::memset(fullFrame, 0, fullFrameBytes);

Allocate memory for the metadata structures. Do not access the structure members before the structure is filled with the pl_md_frame_decode function.

md_frame* mdFrameInMemory;
if (PV_OK != pl_md_create_frame_struct_cont(&mdFrameInMemory, cRoiCount))
{
PrintErrorMessage(pl_error_code(),
"pl_md_create_frame_struct_cont(cRoiCount) error");
delete [] fullFrame;
delete [] frameInMemory;
CloseAllCamerasAndUninit(contexts);
return APP_EXIT_ERROR;
}

Acquire 5 images in a loop:

bool errorOccured = false;
uns32 imageCounter = 0;
while (imageCounter < 5)
{

Start the acquisition. Since the pl_exp_setup_seq was configured to use the internal camera trigger, the acquisition is started immediately. In hardware triggering modes the camera would wait for an external trigger signal.

if (PV_OK != pl_exp_start_seq(ctx->hcam, frameInMemory))
{
PrintErrorMessage(pl_error_code(), "pl_exp_start_seq() error");
errorOccured = true;
break;
}

Here we need to wait for a frame readout notification signaled by eofEvent in 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))
{
if (!errorOccurred)
{
// This happens on ctrl+c, but in this sample we have to
// handle the acquisition abort explicitly
if (PV_OK != pl_exp_abort(ctx->hcam, CCS_HALT))
{
PrintErrorMessage(pl_error_code(), "pl_exp_abort() error");
errorOccurred = true;
}
printf("Processing aborted on camera %d\n", ctx->hcam);
}
break;
}

Decode the frame and display ADU values of first several pixels for each region:

if (PV_OK != pl_md_frame_decode(mdFrameInMemory, frameInMemory, exposureBytes))
{
PrintErrorMessage(pl_error_code(), "pl_md_frame_decode() error");
errorOccurred = true;
break;
}
// mdFrameInMemory->roiCount should be equal to cRoiCount
for (uns16 n = 0; n < mdFrameInMemory->roiCount; n++)
{
std::string title;
title += "ROI " + std::to_string(n);
ShowImage(ctx, mdFrameInMemory->roiArray[n].data,
mdFrameInMemory->roiArray[n].dataSize, title.c_str());
}

Optionally, recompose all ROIs into a full-frame image. The whole frame is already filled with black pixels, here we only overwrite the ROI pixels with the new data because the region positions do not change in between the acquisitions.

if (PV_OK != pl_md_frame_recompose(fullFrame,
mdFrameInMemory->impliedRoi.s1, mdFrameInMemory->impliedRoi.p1,
ctx->sensorResX, ctx->sensorResY, mdFrameInMemory))
{
PrintErrorMessage(pl_error_code(), "pl_md_frame_recompose() error");
errorOccurred = true;
break;
}
printf("Frame #%u has been recomposed to full frame\n", imageCounter + 1);
ShowImage(ctx, fullFrame, fullFrameBytes, "recomposed");

When acquiring single frames with callback notifications, call the pl_exp_finish_seq after every frame before a new acquisition is started with pl_exp_start_seq.

if (PV_OK != pl_exp_finish_seq(ctx->hcam, frameInMemory, 0))
PrintErrorMessage(pl_error_code(), "pl_exp_finish_seq() error");
imageCounter++;
} // End of while loop

Clean up before exiting the program. Please, refer to the Closing Camera and Uninitializing PVCAM section for more details.

if (PV_OK != pl_md_release_frame_struct(mdFrameInMemory))
PrintErrorMessage(pl_error_code(), "pl_md_release_frame_struct() error");
delete [] fullFrame;
delete [] frameInMemory;
CloseAllCamerasAndUninit(contexts);
if (errorOccured)
return APP_EXIT_ERROR;
return 0;