PVCAM  3.9.x
Programmable Virtual Camera Access Method library
Live Image Change Exposure

This sample is similar to Live Image Callbacks code with a difference that it demonstrates how to change the camera settings while running the image acquisition. The main difference is that the whole image acquisition runs on a background thread (similarly as it would run in a usual GUI application) and the main thread is used for interaction with the user.

This example utilizes different helper function for user input than in all other samples: the GetCh function. The user can change the exposure time up and down by pressing plus and minus keys without a need to confirm the input by pressing the <Enter> key.

Currently, in order to change the exposure time, the acquisition needs to be stopped and started again.

On the main thread:

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];

Start the acquisition thread:

ctx->threadAbortFlag = false;
ctx->thread = new (std::nothrow) std::thread(ThreadFunc, ctx);
if (!ctx->thread)
{
printf("Failed to start acquisition thread for camera %d\n", ctx->hcam);
CloseAllCamerasAndUninit(contexts);
return APP_EXIT_ERROR;
}

Enter the loop capturing the user input. The loop is interrupted when a key other than plus or minus is pressed. Keyboard shortcuts such as ctrl+c are captured as well and fall into the default case. For this reason, we do not need to register any termination handlers as with other examples.

while (!ctx->threadAbortFlag)
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
const int key = GetCh();
switch (key)
{
case '+':
exposureTime += 5;
adjustmentNeeded = true;
printf("Exposure time increased to %ums\n", exposureTime);
break;
case '-':
if (exposureTime >= 5)
{
exposureTime -= 5;
adjustmentNeeded = true;
printf("Exposure time decreased to %ums\n", exposureTime);
}
break;
default:
printf("Exiting application...\n");
{ // Unblock acquisition thread
std::lock_guard<std::mutex> lock(ctx->eofEvent.mutex);
ctx->threadAbortFlag = true;
}
ctx->eofEvent.cond.notify_all();
if (ctx->thread->joinable())
ctx->thread->join();
delete ctx->thread;
ctx->thread = nullptr;
break;
}
}

Please note that in order to keep the example short and more readable the background thread does not propagate errors to the main thread. During the uninitialization phase initiated by the CloseAllCamerasAndUninit function, the background thread simply errors out and exits. Please, refer to the Closing Camera and Uninitializing PVCAM section for more details.

On the acquisition thread:

The background thread generally does everything that is usually done in other code examples on the main thread.

To use callbacks, we need to create a callback function and register it with PVCAM. PVCAM will then call this function when a new frame arrives. This approach is used in most of the code samples, therefore a generic code block is shared among the samples. Please, refer to Frame Callback Handler section for details.

Prepare the continuous acquisition with circular buffer mode. The pl_exp_setup_cont function returns the size of one frame in bytes (unlike a size of the entire buffer as with pl_exp_setup_seq function). Please see more details in Acquisition Configuration section.

uns32 exposureBytes;
// exposureTime variable is a global variable here
const uns16 circBufferFrames = 20;
const int16 bufferMode = CIRC_OVERWRITE;
if (PV_OK != pl_exp_setup_cont(ctx->hcam, 1, &ctx->region, exposureMode,
exposureTime, &exposureBytes, bufferMode))
{
PrintErrorMessage(pl_error_code(), "pl_exp_setup_cont() error");
return;
}
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. The application is in control of the circular buffer and should allocate memory of appropriate size.

const uns32 circBufferBytes = circBufferFrames * exposureBytes;
uns8* circBufferInMemory = new (std::nothrow) uns8[circBufferBytes];
if (!circBufferInMemory)
{
printf("Unable to allocate buffer for camera %d\n", ctx->hcam);
return;
}

Start the continuous acquisition. By passing the entire size of the buffer to pl_exp_start_cont, PVCAM can calculate the capacity of the circular buffer.

if (PV_OK != pl_exp_start_cont(ctx->hcam, circBufferInMemory, circBufferBytes))
{
PrintErrorMessage(pl_error_code(), "pl_exp_start_cont() error");
delete [] circBufferInMemory;
return;
}

After the acquisition is started, enter a wait loop.

Here we need to wait for a frame readout notification signaled by eofEvent in CameraContext which is raised in the callback handler we registered.

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

If the frame does not arrive within 5 seconds, the loop is interrupted, the acquisition is aborted and the thread exits. If the frame arrives successfully, values of the first several pixels are printed out. The printout is limited to one per second in order to reduce the amount of text printed to console.

The crucial part of this code sample is located at the beginning of the infinite loop. The global flag adjustmentNeeded is checked periodically and once set, the loop knows that an exposure change was requested. Currently, to change any camera settings, the acquisition must be idle. For this reason the pl_exp_abort is called to abort the ongoing acquisition. After that, any parameter can be changed. New settings need to be applied using the pl_exp_setup_cont and finally the acquisition is started by calling the pl_exp_start_cont function.

if (adjustmentNeeded)
{
adjustmentNeeded = false;
if (PV_OK != pl_exp_abort(ctx->hcam, CCS_HALT))
{
PrintErrorMessage(pl_error_code(), "pl_exp_abort() error");
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
if (PV_OK != pl_exp_setup_cont(ctx->hcam, 1, &ctx->region, TIMED_MODE,
exposureTime, &exposureBytes, bufferMode))
{
PrintErrorMessage(pl_error_code(), "pl_exp_setup_cont() error");
break;
}
if (PV_OK != pl_exp_start_cont(ctx->hcam, circBufferInMemory,
circBufferFrames * exposureBytes))
{
PrintErrorMessage(pl_error_code(), "pl_exp_start_seq() error");
break;
}
}

The code contains several 'sleeps' in various places. The reason is to slow down the user input processing in situations where, for example, the user presses and holds the plus or minus key to rapidly adjust the exposure time.