Spinnaker C++
2.6.0.0
AcquisitionMultipleCameraRecovery.cpp

AcquisitionMultipleCameraRecovery.cpp shows how to continously acquire images from multiple cameras using image events. It demonstrates the use of User Set Control to save persistent camera configurations, allowing for smooth camera recovery through interface events. This example relies on information provided in the ImageEvents, EnumerationEvents, ImageFormatControl, and Acquisition examples.

This example uses a global map to retain image information, including the number of images grabbed, the number of incomplete images and the number of removals for each camera over the duration of the example. Cameras may be added or removed after the example has started.

The example assumes each camera has a unique serial number and is capable of configuring User Set 1. Note that if a camera was configured and is disconnected before the example ends, it will not be reconfigured to use the default User Set.

//=============================================================================
// Copyright (c) 2001-2021 FLIR Systems, Inc. All Rights Reserved.
//
// This software is the confidential and proprietary information of FLIR
// Integrated Imaging Solutions, Inc. ("Confidential Information"). You
// shall not disclose such Confidential Information and shall use it only in
// accordance with the terms of the license agreement you entered into
// with FLIR Integrated Imaging Solutions, Inc. (FLIR).
//
// FLIR MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
// SOFTWARE, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE, OR NON-INFRINGEMENT. FLIR SHALL NOT BE LIABLE FOR ANY DAMAGES
// SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
// THIS SOFTWARE OR ITS DERIVATIVES.
//=============================================================================
#include "Spinnaker.h"
#include <iostream>
#include <iomanip>
#include <map>
using namespace Spinnaker;
using namespace Spinnaker::GenApi;
using namespace Spinnaker::GenICam;
using namespace std;
// This class defines the properties and methods for image event handling; please see
// ImageEvents example for more in-depth comments on setting up image event handlers.
{
public:
ImageEventHandlerImpl(string deviceSerial)
{
// The device serial number is stored so the grab information can be updated
// on each image event.
m_deviceSerialNumber = deviceSerial;
}
{
}
//
// *** NOTES ***
// The implementation for OnImageEvent is defined later as it requires the implementation
// of the GrabInfo struct below. GrabInfo relies on the implementation of the
// ImageEventHandlerImpl constructor so it is defined first.
//
void OnImageEvent(ImagePtr image);
private:
string m_deviceSerialNumber;
};
// This struct defines grab information for each unique device
// A Grab Info object is created each time a new device is configured and is updated on device
// arrival, device removal, and image events.
struct GrabInfo
{
unsigned int numImagesGrabbed;
unsigned int numIncompleteImages;
unsigned int numRemovals;
std::shared_ptr<ImageEventHandlerImpl> imageEventHandler;
GrabInfo(const string& deviceSerial) : numImagesGrabbed(0), numIncompleteImages(0), numRemovals(0)
{
imageEventHandler = make_shared<ImageEventHandlerImpl>(deviceSerial);
}
};
// This map stores the Grab Info for all cameras.
std::map<std::string, GrabInfo> cameraGrabInfoMap;
//
// This method belongs to the ImageEventHandlerImpl class and defines an image event.
//
// *** NOTES ***
// Since the example has the potential of grabbing a large number of images, printouts are
// limited to every 10th image grabbed. The final grab information is printed on example
// completion. Images are not saved for this exmaple.
//
{
auto cameraGrabInfo = cameraGrabInfoMap.find(m_deviceSerialNumber);
if (cameraGrabInfo == cameraGrabInfoMap.end())
{
cout << "Error OnImageEvent: camera " << m_deviceSerialNumber << " not found in grab info map" << endl;
return;
}
// Check image retrieval status
if (image->IsIncomplete())
{
// Increment the number of incomplete images
cameraGrabInfo->second.numIncompleteImages++;
}
else
{
// Retrieve and increment the total number of images grabbed
unsigned int& numImagesGrabbed = cameraGrabInfo->second.numImagesGrabbed;
numImagesGrabbed++;
if (numImagesGrabbed % 10 == 0)
{
cout << numImagesGrabbed << " Images grabbed for " << m_deviceSerialNumber << endl;
}
}
}
// A global camera list is updated on camera arrival/removals to ensure old cameras can be
// cleaned up when appropriate.
// This helper function allows the example to sleep in both Windows and Linux
// systems. Note that Windows sleep takes milliseconds as a parameter while
// Linux systems take microseconds as a parameter.
void SleepyWrapper(int milliseconds)
{
#if defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64
Sleep(milliseconds);
#else
usleep(1000 * milliseconds);
#endif
}
// This helper function wraps GetCameras in a try catch. This ensures cleanup or setup
// continues even if the camera list failed to update.
{
try
{
globalCamList = system->GetCameras();
}
catch (Exception& e)
{
cout << "Error refreshing camera list: " << e.what() << endl;
}
}
// Provide the function signature for RunSingleCamera so it can be triggered on a
// device arrival.
// This class defines the properties and functions for device arrivals and removals
// on an interface; please see EnumerationEvents example for more in-depth comments
// on setting up interface event handlers.
{
public:
InterfaceEventHandlerImpl(SystemPtr system) : m_system(system){};
// This function defines the arrival event on an interface. It refreshes the
// global camera list and starts a RunSingleCamera thread for the device.
void OnDeviceArrival(uint64_t deviceSerialNumber)
{
const std::string serialNum = to_string(deviceSerialNumber);
// Refresh the global camera list
RefreshCameraList(m_system);
// Configure the camera and begin acquisition
if (cam.IsValid())
{
if (ConfigureCamera(cam))
{
try
{
cam->BeginAcquisition();
}
catch (Exception& e)
{
cout << "Error beginning acquisition: " << e.what() << endl;
}
}
else
{
cout << "Error OnDeviceArrival: configuration for device " << serialNum << " failed.";
}
}
}
// This function defines removal events on an interface.
void OnDeviceRemoval(uint64_t deviceSerialNumber)
{
// Log the device removal
string deviceSerial = to_string(deviceSerialNumber);
cout << endl << deviceSerial << " Device Removal Detected" << endl << endl;
// Refresh the global camera list
RefreshCameraList(m_system);
// Increment the number of removals for disconnected cameras
auto cameraGrabInfo = cameraGrabInfoMap.find(deviceSerial);
if (cameraGrabInfo != cameraGrabInfoMap.end())
{
cameraGrabInfo->second.numRemovals++;
}
else
{
cout << "Error OnDeviceRemoval: camera " << deviceSerial << " not found in grab info map" << endl;
}
}
private:
SystemPtr m_system;
};
// This function configures newly discovered cameras with desired settings, saves
// them to User Set 1 and sets this as the default. This way cameras will not need
// to be reconfigured if they are disconnected during the example. Note that this may
// overwrite current settings for User Set 1; please see ImageFormatControl example
// for more in-depth comments on camera configuration.
{
bool result = true;
try
{
// Get the camera node map
INodeMap& nodeMap = pCam->GetNodeMap();
// Get User Set 1 from the User Set Selector
CEnumerationPtr ptrUserSetSelector = nodeMap.GetNode("UserSetSelector");
if (!IsWritable(ptrUserSetSelector))
{
cout << "Unable to set User Set Selector to User Set 1 (node retrieval). Aborting..." << endl << endl;
return false;
}
CEnumEntryPtr ptrUserSet1 = ptrUserSetSelector->GetEntryByName("UserSet1");
if (!IsReadable(ptrUserSet1))
{
cout << "Unable to set User Set Selector to User Set 1 (enum entry retrieval). Aborting..." << endl << endl;
return false;
}
const int64_t userSet1 = ptrUserSet1->GetValue();
// Set User Set Selector to User Set 1
ptrUserSetSelector->SetIntValue(userSet1);
// Set User Set Default to User Set 1
// This ensures the camera will re-enumerate using User Set 1, instead of the default user set.
CEnumerationPtr ptrUserSetDefault = nodeMap.GetNode("UserSetDefault");
if (!IsWritable(ptrUserSetDefault))
{
cout << "Unable to set User Set Default to User Set 1 (node retrieval). Aborting..." << endl << endl;
return false;
}
ptrUserSetDefault->SetIntValue(userSet1);
// Set acquisition mode to continuous
CEnumerationPtr ptrAcquisitionMode = nodeMap.GetNode("AcquisitionMode");
if (!IsWritable(ptrAcquisitionMode))
{
cout << "Unable to set acquisition mode to continuous (node retrieval). Aborting..." << endl << endl;
return false;
}
CEnumEntryPtr ptrAcquisitionModeContinuous = ptrAcquisitionMode->GetEntryByName("Continuous");
if (!IsReadable(ptrAcquisitionModeContinuous))
{
cout << "Unable to set acquisition mode to continuous (enum entry retrieval). Aborting..." << endl << endl;
return false;
}
const int64_t acquisitionModeContinuous = ptrAcquisitionModeContinuous->GetValue();
ptrAcquisitionMode->SetIntValue(acquisitionModeContinuous);
//
// *** NOTES ***
// Any additional settings should be changed before executing the user set save.
//
// Execute User Set Save to save User Set 1
CCommandPtr ptrUserSetSave = nodeMap.GetNode("UserSetSave");
if (!ptrUserSetSave.IsValid())
{
cout << "Unable to save Settings to User Set 1. Aborting..." << endl << endl;
return false;
}
ptrUserSetSave->Execute();
}
catch (Exception& e)
{
cout << "Error configuring user set 1: " << e.what() << endl;
result = false;
}
return result;
}
// This function resets the cameras default User Set to the Default User Set.
// Note that User Set 1 will retain the settings set during ConfigureUserSet1.
{
try
{
// Get the camera node map
INodeMap& nodeMap = pCam->GetNodeMap();
// Get User Set Default from User Set Selector
CEnumerationPtr ptrUserSetSelector = nodeMap.GetNode("UserSetSelector");
if (!IsWritable(ptrUserSetSelector))
{
cout << "Unable to set User Set Selector to Default (node retrieval). Aborting..." << endl << endl;
return;
}
CEnumEntryPtr ptrUserSetDefaultEntry = ptrUserSetSelector->GetEntryByName("Default");
if (!IsReadable(ptrUserSetDefaultEntry))
{
cout << "Unable to set User Set Selector to Default (enum entry retrieval). Aborting..." << endl << endl;
return;
}
const int64_t userSetDefault = ptrUserSetDefaultEntry->GetValue();
// Set User Set Selector back to User Set Default
ptrUserSetSelector->SetIntValue(userSetDefault);
// Set User Set Default to User Set Default
CEnumerationPtr ptrUserSetDefault = nodeMap.GetNode("UserSetDefault");
if (!IsWritable(ptrUserSetDefault))
{
cout << "Unable to set User Set Default to User Set 1 (node retrieval). Aborting..." << endl << endl;
return;
}
ptrUserSetDefault->SetIntValue(userSetDefault);
// Execute User Set Load to load User Set Default
CCommandPtr ptrUserSetLoad = nodeMap.GetNode("UserSetLoad");
if (!ptrUserSetLoad.IsValid())
{
cout << "Unable to load Settings from User Set Default. Aborting..." << endl << endl;
return;
}
ptrUserSetLoad->Execute();
}
catch (Exception& e)
{
cout << "Error resetting camera to use default user set: " << e.what() << endl;
}
}
// This helper function retrieves the device serial number from the cameras nodemap.
{
INodeMap& nodeMap = pCam->GetTLDeviceNodeMap();
CStringPtr ptrDeviceSerialNumber = nodeMap.GetNode("DeviceSerialNumber");
if (IsReadable(ptrDeviceSerialNumber))
{
return string(ptrDeviceSerialNumber->GetValue());
}
return "";
}
// This function configures new cameras to use camera settings from User Set 1 and
// registers an image event handler on new and rediscovered cameras.
{
bool result = true;
try
{
// Initialize camera
pCam->Init();
// Get the device serial number
const string deviceSerialNumber = GetDeviceSerial(pCam);
// Check if the camera has been connected before
auto camera = cameraGrabInfoMap.find(deviceSerialNumber);
const bool cameraFound = camera != cameraGrabInfoMap.end();
cout << endl
<< endl
<< "*** " << (cameraFound ? "RESUM" : "START") << "ING RECOVERY EXAMPLE FOR " << deviceSerialNumber
<< " ***" << endl
<< endl;
// Configure newly discovered cameras to use User Set 1
if (!cameraFound)
{
// Add a new entry into the camera grab info map using the default GrabInfo constructor
cameraGrabInfoMap.insert(std::make_pair(deviceSerialNumber, GrabInfo(deviceSerialNumber)));
cout << "Configuring device " << deviceSerialNumber << endl;
// Configure the new camera to use User Set 1
// Return if it is unsuccessful
if (!ConfigureUserSet1(pCam))
{
return false;
}
}
//
// Register the imageEvent to the camera
//
// *** NOTES ***
// An image event handler is created in the GrabInfo default constructor for every
// new camera. The event handler can be reused for cameras that have been reconnected.
//
pCam->RegisterEventHandler(*(cameraGrabInfoMap.find(deviceSerialNumber)->second.imageEventHandler));
}
catch (Exception& e)
{
cout << "Error Running single camera: " << e.what() << endl;
result = false;
}
return result;
}
// This helper function prints the complete grab info results for each camera.
{
cout << endl << "Printing Final Statistics " << endl << endl;
const unsigned int printWidth = 20;
cout << setw(printWidth) << left << "Serial Number:" << setw(printWidth) << left
<< "Images Grabbed:" << setw(printWidth) << left << "Incomplete Images:" << setw(printWidth) << left
<< "Camera Removals:" << endl;
for (auto camInfo = cameraGrabInfoMap.begin(); camInfo != cameraGrabInfoMap.end(); camInfo++)
{
cout << setw(printWidth) << left << camInfo->first << setw(printWidth) << left
<< camInfo->second.numImagesGrabbed << setw(printWidth) << left << camInfo->second.numIncompleteImages
<< setw(printWidth) << left << camInfo->second.numRemovals << endl;
}
}
// Example entry point; please see EnumerationEvents example for more in-depth
// comments on preparing and cleaning up the system.
int main(int /*argc*/, char** /*argv*/)
{
// Since this application saves images in the current folder
// we must ensure that we have permission to write to this folder.
// If we do not have permission, fail right away.
FILE* tempFile = fopen("test.txt", "w+");
if (tempFile == nullptr)
{
cout << "Failed to create file in current folder. Please check permissions." << endl;
cout << "Press Enter to exit..." << endl;
getchar();
return -1;
}
fclose(tempFile);
remove("test.txt");
// Print application build information
cout << "Application build date: " << __DATE__ << " " << __TIME__ << endl << endl;
// Retrieve singleton reference to system object
// Print out current library version
const LibraryVersion spinnakerLibraryVersion = system->GetLibraryVersion();
cout << "Spinnaker library version: " << spinnakerLibraryVersion.major << "." << spinnakerLibraryVersion.minor
<< "." << spinnakerLibraryVersion.type << "." << spinnakerLibraryVersion.build << endl
<< endl
<< endl;
// Retrieve list of cameras from the system
globalCamList = system->GetCameras();
cout << "Number of cameras detected: " << globalCamList.GetSize() << endl;
// Configure each connected camera to use User Set 1 and register image events
for (unsigned int camIndex = 0; camIndex < globalCamList.GetSize(); camIndex++)
{
CameraPtr cam = globalCamList[camIndex];
if (!ConfigureCamera(cam))
{
cout << "Camera configuration for device " << GetDeviceSerial(cam) << " unsuccessful, aborting...";
return -1;
}
}
// Create an interface event handler to handle all interface events on the system.
// Please see EnumerationEvents example for more in-depth comments on setting up system and interface event
// handlers.
InterfaceEventHandlerImpl interfaceEventHandler(system);
system->RegisterInterfaceEventHandler(interfaceEventHandler);
cout << endl
<< "Cameras may be unplugged/plugged in after the example has started." << endl
<< "After the example begins, please press any key to end the example..." << endl;
cout << endl << "Press any key to begin..." << endl << endl;
getchar();
// Begin Acquisition on all cameras
for (unsigned int camIndex = 0; camIndex < globalCamList.GetSize(); camIndex++)
{
try
{
globalCamList[camIndex]->BeginAcquisition();
}
catch (Exception& e)
{
cout << "Error starting acquistion on camera at index " << camIndex << ": " << e.what() << endl;
}
}
// Wait for user to stop the example by pressing any key
getchar();
// End Acquisition on all cameras
for (unsigned int camIndex = 0; camIndex < globalCamList.GetSize(); camIndex++)
{
try
{
globalCamList[camIndex]->EndAcquisition();
}
catch (Exception& e)
{
cout << "Error ending acquistion on camera at index " << camIndex << ": " << e.what() << endl;
}
}
// Unregister the interface events from the system
system->UnregisterInterfaceEventHandler(interfaceEventHandler);
// Reset camera user sets and deinitialize all cameras
for (unsigned int camIndex = 0; camIndex < globalCamList.GetSize(); ++camIndex)
{
CameraPtr cam = globalCamList[camIndex];
if (cam.IsValid())
{
cout << "Resetting configuration for device " << GetDeviceSerial(cam) << endl;
cam->DeInit();
}
}
cout << endl;
cout << endl;
// Clear camera list before releasing system
// Release system
system->ReleaseInstance();
cout << endl << "Done! Press any key to exit..." << endl;
getchar();
return 0;
}
Contact Support Spinnaker® SDK Programmer's Guide and API Reference