The continuous acquisition is similar to the single capture. The only major difference is that this sample starts a separate thread that continuously requests images from the device.
The sample needs at least two valid devices to run properly and checks this with getValidDevices()
from the included exampleHelper.h
.
The application will allow the user to specify the master and some slave devices. The selected master devices digital output must be connected appropriately to the selected digital inputs of all. An example for the electrical setup can be seen in the following picture:
You could extend the sample and trigger this configuration externally. The electrical setup would be like this:
The user can select the digital output of the master and the digital inputs of all devices (via function setupTriggerInput()
).
The application will start a capture thread for each device and within the thread the capture queue of each device is pre-filled with some requests (using a DeviceData
array with both devices). Once the acquisition threads are running another thread will be started, that will periodically generate trigger signals by flipping the state of the selected digital output with a user defined frequency.
#include <cstdio>
#include <iostream>
#include <memory>
#include <mutex>
#include <thread>
#include <apps/Common/exampleHelper.h>
#include <mvIMPACT_CPP/mvIMPACT_acquire.h>
#ifdef _WIN32
# include <mvDisplay/Include/mvIMPACT_acquire_display.h>
# define USE_DISPLAY
#endif
using namespace std;
static mutex s_mutex;
class ThreadData
{
volatile bool boTerminateThread_;
unique_ptr<thread> pThread_;
public:
explicit ThreadData() : boTerminateThread_( false ), pThread_() {}
virtual ~ThreadData() {}
bool terminated( void ) const
{
return boTerminateThread_;
}
template<class _Fn, class _Arg>
void startThread( _Fn&& _Fx, _Arg&& _Ax )
{
pThread_ = unique_ptr<thread>( new thread( _Fx, _Ax ) );
}
void terminateThread( void )
{
boTerminateThread_ = true;
if( pThread_ )
{
pThread_->join();
}
}
};
class DeviceData : public ThreadData
{
bool isMaster_;
unique_ptr<FunctionInterface> pFI_;
unique_ptr<IOSubSystem> pIOSS_;
unique_ptr<Statistics> pSS_;
#ifdef USE_DISPLAY
unique_ptr<ImageDisplayWindow> pDisplayWindow_;
#endif
int lastRequestNr_;
public:
explicit DeviceData(
Device* p ) : ThreadData(), pDev_( p ), isMaster_( false ), pFI_(), pIOSS_(), pSS_(),
#ifdef USE_DISPLAY
pDisplayWindow_(),
#endif
lastRequestNr_( INVALID_ID ) {}
~DeviceData()
{
if( pFI_->isRequestNrValid( lastRequestNr_ ) )
{
pFI_->imageRequestUnlock( lastRequestNr_ );
}
}
void init( const bool isMaster )
{
isMaster_ = isMaster;
pSS_ = unique_ptr<Statistics>(
new Statistics( pDev_ ) );
{
lock_guard<mutex> lockedScope( s_mutex );
cout << "Please note that there will be just one refresh for the display window, so if it is\n"
<< "hidden under another window the result will not be visible.\n";
}
#ifdef USE_DISPLAY
pDisplayWindow_ = unique_ptr<ImageDisplayWindow>(
new ImageDisplayWindow(
string( isMaster ?
"Master " :
"Slave " ) + pDev_->serial.read() ) );
#endif
}
{
return pDev_;
}
{
return pFI_.get();
}
bool isMaster( void ) const
{
return isMaster_;
}
{
return pIOSS_.get();
}
{
return pSS_.get();
}
#ifdef USE_DISPLAY
{
return pDisplayWindow_.get();
}
#endif
};
class TriggerSignal : public ThreadData
{
unsigned int frequency_Hz_;
public:
explicit TriggerSignal(
DigitalOutput* pTriggerOutput,
unsigned int frequency_Hz ) : ThreadData(), pTriggerOutput_( pTriggerOutput ), frequency_Hz_( frequency_Hz ) {}
{
return pTriggerOutput_;
}
unsigned int frequency_Hz( void ) const
{
return frequency_Hz_;
}
};
void liveThread( DeviceData* pThreadParameter )
{
#ifdef USE_DISPLAY
ImageDisplay& display = pThreadParameter->pDisp()->GetImageDisplay();
#endif
if( result != DEV_NO_FREE_REQUEST_AVAILABLE )
{
lock_guard<mutex> lockedScope( s_mutex );
cout << "'FunctionInterface.imageRequestSingle' returned with an unexpected result: " << result
<< "(" << ImpactAcquireException::getErrorCodeAsString( result ) << ")" << endl;
}
manuallyStartAcquisitionIfNeeded( pThreadParameter->device(), *pFI );
const unsigned int timeout_ms = {200};
unsigned int cnt = {0};
while( !pThreadParameter->terminated() )
{
{
{
++cnt;
if( cnt % 100 == 0 )
{
lock_guard<mutex> lockedScope( s_mutex );
cout << "Info from " << pThreadParameter->device()->serial.read() << "(" << ( pThreadParameter->isMaster() ? "Master" : "Slave" ) << ")"
}
#ifdef USE_DISPLAY
#endif
}
else
{
lock_guard<mutex> lockedScope( s_mutex );
}
{
}
lastRequestNr = requestNr;
}
else
{
}
}
{
lock_guard<mutex> lockedScope( s_mutex );
cout << "Overall good frames captured from device " << pThreadParameter->device()->serial.read() << ": " << cnt << endl;
}
manuallyStopAcquisitionIfNeeded( pThreadParameter->device(), *pFI );
#ifdef USE_DISPLAY
#endif
{
}
}
void triggerThread( TriggerSignal* pSignal )
{
unsigned int cnt = {0};
const unsigned int sleepPeriod_ms = 1000 / ( pSignal->frequency_Hz() * 2 );
while( !pSignal->terminated() )
{
this_thread::sleep_for( chrono::milliseconds( sleepPeriod_ms ) );
pSignal->triggerOutput()->flip();
this_thread::sleep_for( chrono::milliseconds( sleepPeriod_ms ) );
pSignal->triggerOutput()->flip();
++cnt;
if( cnt % 100 == 0 )
{
lock_guard<mutex> lockedScope( s_mutex );
cout << "Trigger signals generated: " << cnt << endl;
}
}
lock_guard<mutex> lockedScope( s_mutex );
cout << "Overall trigger signals generated: " << cnt << endl;
}
unsigned int getNumberFromUser( void )
{
unsigned int nr = {0};
std::cin >> nr;
std::cin.get();
return nr;
}
bool isDeviceSupportedBySample(
const Device*
const pDev )
{
return match( pDev->
product.
read(),
string(
"mvBlueCOUGAR-X*" ),
'*' ) == 0;
}
void setupTriggerInput( DeviceData* pDevData )
{
cout << "Select the digital INPUT of device(" << pDevData->device()->serial.read() << ")(as a string) that shall serve as a trigger input:" << endl;
DisplayPropertyDictionary<mvIMPACT::acquire::PropertyI>( cs.triggerSource );
modifyPropertyValue( cs.triggerSource );
cs.triggerMode.write( ctmOnRisingEdge );
cs.imageRequestTimeout_ms.write( 0 );
}
int main( void )
{
cout << "This sample is meant for mvBlueCOUGAR-X devices only. Other devices might be installed" << endl
<< "but won't be recognized by the application." << endl
<< endl;
std::vector<mvIMPACT::acquire::Device*> validDevices;
if( getValidDevices( devMgr, validDevices, isDeviceSupportedBySample ) < 2 )
{
cout << "This sample needs at least 2 valid devices(one master and one slave). " << validDevices.size() << " device(s) has/have been detected." << endl
<< "Unable to continue! Press [ENTER] to end the application" << endl;
cin.get();
return 1;
}
cout << "Please select the MASTER device(the one that will create the trigger for all devices).\n\n";
Device* pMaster = getDeviceFromUserInput( devMgr, isDeviceSupportedBySample,
true );
if( pMaster == nullptr )
{
cout << "Master device has not been properly selected. Unable to continue!\n"
<< "Press [ENTER] to end the application\n";
cin.get();
return 1;
}
set<Device*> setOfSlaves;
do
{
cout << "\nPlease select a SLAVE device(this one will be triggered by the master).\n\n";
Device* p = getDeviceFromUserInput( devMgr, isDeviceSupportedBySample,
true );
if( p == pMaster )
{
cout << "Master and slave must be different. Skipped!" << endl;
}
else if( p != nullptr )
{
if( setOfSlaves.find( p ) == setOfSlaves.end() )
{
setOfSlaves.insert( p );
}
else
{
cout << "ALL slaves must be different. This one has already been selected. Skipped!" << endl;
}
}
cout << "\nAdd another slave device('y')? ";
}
while( getchar() == 'y' );
cout << endl;
vector<DeviceData*> devices;
devices.push_back( new DeviceData( pMaster ) );
for( auto* const pSlave : setOfSlaves )
{
devices.push_back( new DeviceData( pSlave ) );
}
const vector<DeviceData*>::size_type DEV_COUNT = devices.size();
try
{
for( vector<DeviceData*>::size_type i = 0; i < DEV_COUNT; i++ )
{
cout << "Initialising device " << devices[i]->device()->serial.read() << "..." << endl;
const bool isMaster = i == 0;
devices[i]->init( isMaster );
cout << endl
<< "Setup the " << ( isMaster ? "MASTER" : "SLAVE" ) << " device:" << endl
<< "===========================" << endl
<< endl;
if( isMaster )
{
const unsigned int digoutCount = devices[i]->IOSS()->getOutputCount();
for( unsigned int digOut = 0; digOut < digoutCount; digOut++ )
{
cout <<
" [" << digOut <<
"]: " << devices[i]->IOSS()->output( digOut )->
getDescription() << endl;
}
cout << endl
<< "Select the digital OUTPUT of the MASTER device(" << devices[i]->device()->serial.read() << ") where the trigger pulse shall be generated on: ";
pTriggerOutput = devices[i]->IOSS()->output( getNumberFromUser() );
cout << endl;
}
setupTriggerInput( devices[i] );
}
}
{
cout <<
"An error occurred while opening the devices(error code: " << e.
getErrorCodeAsString() <<
")." << endl
<< "Press [ENTER] to end the application" << endl;
cin.get();
return 1;
}
try
{
unsigned int triggerFrequency_Hz = {0};
bool boRun = {true};
while( boRun )
{
cout << "Please enter the approx. desired trigger frequency in Hz: ";
triggerFrequency_Hz = getNumberFromUser();
if( ( triggerFrequency_Hz >= 1 ) && ( triggerFrequency_Hz <= 100 ) )
{
boRun = false;
continue;
}
cout << "Invalid Selection. This sample will accept values from 1 - 100." << endl;
}
TriggerSignal triggerSignal( pTriggerOutput, triggerFrequency_Hz );
for( auto* pDevData : devices )
{
pDevData->startThread( liveThread, pDevData );
}
this_thread::sleep_for( chrono::milliseconds( 1000 ) );
triggerSignal.startThread( triggerThread, &triggerSignal );
{
lock_guard<mutex> lockedScope( s_mutex );
cout << "Press [ENTER] to end the acquisition" << endl;
}
if( getchar() == EOF )
{
printf( "Calling '_getch()' did return EOF...\n" );
}
{
lock_guard<mutex> lockedScope( s_mutex );
cout << "Terminating threads..." << endl;
}
for( auto* pDevData : devices )
{
pDevData->terminateThread();
}
triggerSignal.terminateThread();
}
{
cout << "Press [ENTER] to end the application" << endl;
cin.get();
return 1;
}
cout << "Press [ENTER] to end the application" << endl;
cin.get();
return 0;
}
mvBlueCOUGAR related camera settings (Device specific interface layout only).
Definition mvIMPACT_acquire.h:20241
std::string name(void) const
Returns the name of the component referenced by this object.
Definition mvIMPACT_acquire.h:1206
Grants access to devices that can be operated by this software interface.
Definition mvIMPACT_acquire.h:7171
This class and its functions represent an actual device detected by this interface in the current sys...
Definition mvIMPACT_acquire.h:6118
PropertyS product
A string property (read-only) containing the product name of this device.
Definition mvIMPACT_acquire.h:6537
void open(void)
Opens a device.
Definition mvIMPACT_acquire.h:6420
A class to represent a digital output pin(Device specific interface layout only).
Definition mvIMPACT_acquire.h:15546
std::string getDescription(void) const
Returns a description for this digital output.
Definition mvIMPACT_acquire.h:15609
The function interface to devices supported by this interface.
Definition mvIMPACT_acquire.h:10758
int imageRequestWaitFor(int timeout_ms, int queueNr=0) const
Waits for a request object to become ready.
Definition mvIMPACT_acquire.h:11616
int imageRequestUnlock(int nr) const
Unlocks the request for the driver again.
Definition mvIMPACT_acquire.h:11584
int imageRequestSingle(ImageRequestControl *pImageRequestControl=0, int *pRequestUsed=0) const
Sends an image request to the mvIMPACT::acquire::Device driver.
Definition mvIMPACT_acquire.h:11503
bool isRequestNrValid(int nr) const
Check if nr specifies a valid mvIMPACT::acquire::Request.
Definition mvIMPACT_acquire.h:11763
int imageRequestReset(int requestCtrlNr, int mode) const
Deletes all requests currently queued for the specified mvIMPACT::acquire::ImageRequestControl.
Definition mvIMPACT_acquire.h:11450
Request * getRequest(int nr) const
Returns a pointer to the desired mvIMPACT::acquire::Request.
Definition mvIMPACT_acquire.h:11189
A base class to handle the digital inputs and outputs for devices (Device specific interface layout o...
Definition mvIMPACT_acquire.h:17150
A base class to handle digital inputs and outputs(Device specific interface layout only).
Definition mvIMPACT_acquire.h:16385
A base class for exceptions generated by Impact Acquire.
Definition mvIMPACT_acquire.h:256
std::string getErrorCodeAsString(void) const
Returns a string representation of the error associated with the exception.
Definition mvIMPACT_acquire.h:288
std::string read(int index=0) const
Reads a value from a property.
Definition mvIMPACT_acquire.h:5323
std::string readS(int index=0, const std::string &format="") const
Reads data from this property as a string.
Definition mvIMPACT_acquire.h:3340
Contains information about a captured buffer.
Definition mvIMPACT_acquire.h:8640
PropertyI64 infoFrameID
A 64 bit integer property (read-only) containing a frame identifier.
Definition mvIMPACT_acquire.h:9849
bool isOK(void) const
Convenience function to check if a request has been processed successfully.
Definition mvIMPACT_acquire.h:9474
PropertyIRequestResult requestResult
An enumerated integer property (read-only) defining the result of this request.
Definition mvIMPACT_acquire.h:9780
PropertyI64 infoFrameNr
A 64 bit integer property (read-only, zero-based) containing the number of image requests processed s...
Definition mvIMPACT_acquire.h:9856
Contains basic statistical information.
Definition mvIMPACT_acquire.h:14509
PropertyF framesPerSecond
A float property (read-only) containing the current number of frames captured per second.
Definition mvIMPACT_acquire.h:14586
PropertyI errorCount
An integer property (read-only) containing the overall count of image requests which returned with an...
Definition mvIMPACT_acquire.h:14568
PropertyI frameCount
An integer property (read-only) containing the overall count of images captured since the mvIMPACT::a...
Definition mvIMPACT_acquire.h:14592
A class that can be used to display images in a window.
Definition mvIMPACT_acquire_display.h:606
A class that can be used for displaying images within existing windows or GUI elements that can provi...
Definition mvIMPACT_acquire_display.h:176
void RemoveImage(void)
Removes the current image from the display.
Definition mvIMPACT_acquire_display.h:395
void SetImage(const void *pData, int width, int height, int bitsPerPixel, int pitch)
Sets the next image to display.
Definition mvIMPACT_acquire_display.h:316
void Update(void) const
Immediately redraws the current image.
Definition mvIMPACT_acquire_display.h:405
TDMR_ERROR
Errors reported by the device manager.
Definition mvDriverBaseEnums.h:2601
const int INVALID_ID
A constant to check for an invalid ID returned from the property handling module.
Definition mvPropHandlingDatatypes.h:62
@ DMR_NO_ERROR
The function call was executed successfully.
Definition mvDriverBaseEnums.h:2603
This namespace contains classes and functions that can be used to display images.
This namespace contains classes and functions belonging to the image acquisition module of this SDK.
Definition mvCommonDataTypes.h:34