mvIMPACT Acquire SDK C++
CaptureToMegaBuffer.cpp

The CaptureToMegaBuffer program is based on the CaptureToUserMemory.cpp example. It is meant to provide an example on how to make optimal use of the mvBlockscan mode allowing to combine multiple images or AOIs into a single capture buffer thus reducing the overall overhead of the transmission of lots of small images. A similar way of allocating memory inside the user application and then passing this memory down to the driver to capture data into as shown in the CaptureToUserMemory.cpp is used. However here the concept of a buffer larger than needed to transfer on block of data from the device is introduced here(referred to as a MegaBuffer within this example). Each request object then is associated with one part of this larger buffer and data is captured into each buffer part IN ORDER then to form a much bigger, virtual mega frame.

Program location
The source file CaptureToMegaBuffer.cpp is only part of this document so far, so in order to play around with it must be copied from here!
CaptureToMegaBuffer example:
  1. Opens a MATRIX VISION device.
  2. Captures images continuously.
Console Output
No command line parameters specified.
Available parameters:
  'serial' or 's' to specify the serial number of the device to use(if not specified the user will be asked to select a device)
  'pixelFormat' or 'pf' to specify the pixel format to use(default: The current value used by the device)
  'width' or 'w' to specify the width(default: The current value used by the device)
  'blockHeight' or 'bh' to specify the height of a block within the mega-buffer(default: 200)
  'blocksPerMegaBuffer' or 'bpmb' to specify the number of blocks that will define one mega-buffer(default: 3)
  'megaBufferCount' or 'mbc' to specify the number of mega-buffers to allocate(default: 2)

USAGE EXAMPLE:
  CaptureToMegaBuffer s=VD* pf=RGB888Packed w=1024 bh=200 bpmb=5

[0]: VD000001 (VirtualDevice, mvVirtualDevice, interface layout: DeviceSpecific, acquisition start/stop behaviour: Default)
[1]: VD000002 (VirtualDevice, mvVirtualDevice, interface layout: DeviceSpecific, acquisition start/stop behaviour: Default)

Please enter the number in front of the listed device followed by [ENTER] to open it: 0
Using device number 0.
Initialising the device. This might take some time...
width*blockHeight does not result in a block size that is properly aligned! Will use buffer alignment(4096) for the width instead!
Press [ENTER] to end the application...
Displaying mega-buffer 0(address 0000020A30D34000, 1638400 bytes, request number 0)
Displaying mega-buffer 0(address 0000020A30EC4000, 1638400 bytes, request number 1)
Displaying mega-buffer 0(address 0000020A31054000, 1638400 bytes, request number 2)
Displaying mega-buffer 1(address 0000020A311FF000, 1638400 bytes, request number 3)
Displaying mega-buffer 1(address 0000020A3138F000, 1638400 bytes, request number 4)
Displaying mega-buffer 1(address 0000020A3151F000, 1638400 bytes, request number 5)
Displaying mega-buffer 0(address 0000020A30D34000, 1638400 bytes, request number 0)
Displaying mega-buffer 0(address 0000020A30EC4000, 1638400 bytes, request number 1)
Displaying mega-buffer 0(address 0000020A31054000, 1638400 bytes, request number 2)
Displaying mega-buffer 1(address 0000020A311FF000, 1638400 bytes, request number 3)
Displaying mega-buffer 1(address 0000020A3138F000, 1638400 bytes, request number 4)
How it works
For the continuous acquisition and memory allocation, this sample uses CaptureToUserMemory.cpp as a basis. So this example is recommended as a starting point before moving on here!

If no command line parameters are specified a list of supported parameters will be sent to the standard output. These will prove useful for testing and playing around with the source code.

The class MegaBuffer represents a large virtual image that is meant to contain a couple of captured image in one contiguous piece of memory. Assuming e.g. that an application wants to capture 5 frames of 300 by 200 pixels into a single buffer of 300 by 1000 pixels (5*200 lines) the MegaBuffer would be allocated accordingly using as many request objects as possible. If an application would require several frames being combined together in a contiguous piece of memory even without the mvBlockscan feature this example can show how to achieve this without the need to copy each smaller image into the large block of memory by allocating one big chunk of memory (the mega buffer) and then attaching small pieces of this buffer to individual request objects. If these are then queued in the correct order the additional copy operation can be eliminated.

Source code
#include <iostream>
#include <memory>
#include <thread>
#include <apps/Common/exampleHelper.h>
#include <common/minmax.h>
#ifdef _WIN32
# define USE_DISPLAY
#elif defined(linux) || defined(__linux) || defined(__linux__)
# if defined(__x86_64__) || defined(__aarch64__) || defined(__powerpc64__) // -m64 makes GCC define __powerpc64__
using UINT_PTR = uint64_t;
# elif defined(__i386__) || defined(__arm__) || defined(__powerpc__) // and -m32 __powerpc__
using UINT_PTR = uint32_t;
# endif
#endif // #ifdef _WIN32
using namespace std;
using namespace mvIMPACT::acquire;
static bool s_boTerminated = false;
//-----------------------------------------------------------------------------
// the buffer we pass to the device driver must be aligned according to its requirements
// As we can't allocate aligned heap memory we will align it 'by hand'
class UserSuppliedHeapBuffer
//-----------------------------------------------------------------------------
{
unique_ptr<char[]> pBuf_;
int bufSize_;
int alignment_;
public:
explicit UserSuppliedHeapBuffer( int bufSize, int alignment ) : pBuf_(), bufSize_( bufSize ), alignment_( alignment )
{
if( bufSize_ > 0 )
{
pBuf_ = unique_ptr<char[]>( new char[bufSize_ + alignment_] );
}
}
char* getPtr( void ) const
{
if( alignment_ <= 1 )
{
return pBuf_.get();
}
return reinterpret_cast<char*>( align( reinterpret_cast<UINT_PTR>( pBuf_.get() ), static_cast<UINT_PTR>( alignment_ ) ) );
}
int getSize( void ) const
{
return bufSize_;
}
};
using RequestContainer = std::vector<Request*>;
//-----------------------------------------------------------------------------
class MegaBuffer
//-----------------------------------------------------------------------------
{
ImageBufferDesc bufferDesc_;
UserSuppliedHeapBuffer buffer_;
RequestContainer requests_;
public:
explicit MegaBuffer( Request* pCurrentRequestLayout, const RequestContainer& requests, const int bufferAlignment ) : bufferDesc_( pCurrentRequestLayout->imageChannelCount.read() ), buffer_( pCurrentRequestLayout->imageSize.read() * static_cast<int>( requests.size() ), bufferAlignment ), requests_( requests )
{
const auto blockHeight = pCurrentRequestLayout->imageHeight.read();
const auto blockSize = pCurrentRequestLayout->imageLinePitch.read() * blockHeight;
assert( ( ( bufferAlignment == 0 ) || ( ( ( blockSize / bufferAlignment ) * bufferAlignment ) == blockSize ) ) && "Since we are trying to capture into a contiguous block of memory each block boundary which is also represented by a request object must also fulfill the alignment restrictions!" );
const auto requestCount = requests_.size();
// setup the image buffer descriptor associated with multiple requests
ImageBuffer* pIB( bufferDesc_.getBuffer() );
pIB->iBytesPerPixel = pCurrentRequestLayout->imageBytesPerPixel.read();
pIB->iHeight = blockHeight * static_cast<int>( requestCount );
pIB->iWidth = pCurrentRequestLayout->imageWidth.read();
pIB->pixelFormat = pCurrentRequestLayout->imagePixelFormat.read();
pIB->vpData = buffer_.getPtr();
pIB->iSize = buffer_.getSize();
for( auto i = 0; i < pIB->iChannelCount; i++ )
{
pIB->pChannels[i].iChannelOffset = pCurrentRequestLayout->imageChannelOffset.read( i );
assert( ( pIB->pChannels[i].iChannelOffset == 0 ) && "We cannot deal with planar formats here!" );
pIB->pChannels[i].iLinePitch = pCurrentRequestLayout->imageLinePitch.read( i );
if( i > 0 )
{
assert( ( pIB->pChannels[i - 1].iLinePitch == pIB->pChannels[i].iLinePitch ) && "We cannot deal with formats that use a different line pitch per channel right now!" );
}
pIB->pChannels[i].iPixelPitch = pCurrentRequestLayout->imagePixelPitch.read( i );
const string channelDesc( pCurrentRequestLayout->imageChannelDesc.read() );
memcpy( pIB->pChannels[i].szChannelDesc, channelDesc.c_str(), channelDesc.length() ); /// \todo This can be done nicer!!!
}
// setup the individual request objects
assert( ( buffer_.getSize() >= static_cast<int>( blockSize * requestCount ) ) && "It seem like the contiguous block of allocated memory is not big enough to store all the data for one of the BIG buffers!" );
for( size_t i = 0; i < requestCount; i++ )
{
int functionResult = DMR_NO_ERROR;
Request* pRequest = requests[i];
assert( ( pRequest->imageMemoryMode.read() == rimmAuto ) && "Seems like we are trying to use the same request at least twice for attaching a user buffer!" );
if( ( functionResult = pRequest->attachUserBuffer( buffer_.getPtr() + i * blockSize, blockSize ) ) != DMR_NO_ERROR )
{
cout << "An error occurred while attaching a buffer to request number " << requests[i]->getNumber() << ": " << ImpactAcquireException::getErrorCodeAsString( functionResult ) << "." << endl;
exit( 1 );
}
}
}
~MegaBuffer()
{
for( auto pRequest : requests_ )
{
try
{
if( pRequest->imageMemoryMode.read() == rimmUser )
{
const int functionResult = pRequest->detachUserBuffer();
if( functionResult != DMR_NO_ERROR )
{
cout << "An error occurred while detaching a buffer from request number " << pRequest->getNumber() << " : " << ImpactAcquireException::getErrorCodeAsString( functionResult ) << "." << endl;
}
}
}
catch( const ImpactAcquireException& e )
{
cout << "An error occurred while changing the mode of request number " << pRequest->getNumber() << ": " << e.getErrorCodeAsString() << "." << endl;
}
}
}
bool isAddressWithinBuffer( const char* const pAddress ) const
{
return ( ( pAddress >= buffer_.getPtr() ) && ( pAddress < ( buffer_.getPtr() + buffer_.getSize() ) ) );
}
char* getStartAddress( void ) const
{
return buffer_.getPtr();
}
size_t getSize( void ) const
{
return buffer_.getSize();
}
const ImageBufferDesc& getBufferDescriptor( void )const
{
return bufferDesc_;
}
};
using CaptureBufferContainer = std::vector<shared_ptr<MegaBuffer>>;
//-----------------------------------------------------------------------------
struct CaptureParameter
//-----------------------------------------------------------------------------
{
enum
{
DEFAULT_MEGA_BUFFER_COUNT = 2,
DEFAULT_BLOCKS_PER_MEGA_BUFFER = 3,
DEFAULT_BLOCK_HEIGHT = 200
};
Device* pDev;
#ifdef USE_DISPLAY
shared_ptr<ImageDisplayWindow> pDisplayWindow;
#endif // #ifdef USE_DISPLAY
FunctionInterface fi;
Statistics statistics;
int bufferSize;
int bufferAlignment;
int bufferPitch;
CaptureBufferContainer buffers;
explicit CaptureParameter( Device* p ) : pDev{p}, fi{p}, statistics{p},
bufferSize{0}, bufferAlignment{0}, bufferPitch{0}, buffers()
{
#ifdef USE_DISPLAY
// IMPORTANT: It's NOT safe to create multiple display windows in multiple threads!!!
pDisplayWindow = make_shared<ImageDisplayWindow>( "mvIMPACT_acquire sample, Device " + pDev->serial.read() );
#endif // #ifdef USE_DISPLAY
}
CaptureParameter( const CaptureParameter& src ) = delete;
CaptureParameter& operator=( const CaptureParameter& rhs ) = delete;
};
void checkCaptureBufferAddress( const Request* const pRequest, const CaptureBufferContainer& buffers );
void createCaptureBuffers( CaptureParameter& captureParams, const string& pixelFormat, const int width, const int blockHeight, const int blocksPerMegaBuffer, const int megaBufferCount );
void displayCommandLineOptions( void );
void displayImage( CaptureParameter* pCaptureParameter, Request* pRequest );
void freeCaptureBuffers( CaptureParameter& captureParams );
int getOptimalBlockCount( GenICam::ImageFormatControl& ifc, const int blocksPerMegaBuffer );
void runLiveLoop( CaptureParameter& captureParams );
//-----------------------------------------------------------------------------
/// \brief This function checks whether a buffer returned from an acquisition into a
/// request that has been assigned a user supplied buffer really contains a buffer
/// pointer that has been assigned by the user.
void checkCaptureBufferAddress( const Request* const pRequest, const CaptureBufferContainer& buffers )
//-----------------------------------------------------------------------------
{
if( pRequest->imageMemoryMode.read() != rimmUser )
{
cout << "ERROR: Request number " << pRequest->getNumber() << " is supposed to contain user supplied memory, but claims that it doesn't." << endl;
return;
}
const char* const pAddr = reinterpret_cast<char*>( pRequest->imageData.read() );
for( const auto& buffer : buffers )
{
if( buffer->isAddressWithinBuffer( pAddr ) )
{
// found the buffer that has been assigned by the user
return;
}
}
cout << "HELP!!! We did not find a matching mega buffer for request number " << pRequest->getNumber() << " pointing to memory location " << reinterpret_cast<const void*>( pAddr ) << endl
<< "The following memory windows are allocated for mega-buffer structures:" << endl;
for( auto pMegaBuffer : buffers )
{
cout << " " << reinterpret_cast<const void*>( pMegaBuffer->getStartAddress() ) << " - " << reinterpret_cast<const void*>( pMegaBuffer->getStartAddress() + pMegaBuffer->getSize() ) << endl;
}
}
//-----------------------------------------------------------------------------
void createCaptureBuffers( CaptureParameter& captureParams, const string& pixelFormat, const int width, int blockHeight, const int blocksPerMegaBuffer, const int megaBufferCount )
//-----------------------------------------------------------------------------
{
freeCaptureBuffers( captureParams );
auto requestsNeededPerMegaBuffer = blocksPerMegaBuffer;
auto requestsNeeded = megaBufferCount * blocksPerMegaBuffer;
ImageRequestControl irc( captureParams.pDev );
int bufferAlignment = {0};
Request* pCurrentRequestLayout = nullptr;
int result = DMR_NO_ERROR;
if( captureParams.pDev->interfaceLayout.isValid() && ( captureParams.pDev->interfaceLayout.read() == dilGenICam ) )
{
GenICam::DeviceControl dc( captureParams.pDev );
GenICam::ImageFormatControl ifc( captureParams.pDev );
if( dc.deviceScanType.isValid() && supportsEnumStringValue( dc.deviceScanType, "mvBlockscan" ) && ifc.mvBlockscanBlockCount.isValid() && ifc.mvBlockscanLinesPerBlock.isValid() )
{
conditionalSetEnumPropertyByString( dc.deviceScanType, "mvBlockscan" );
ifc.mvBlockscanLinesPerBlock.write( blockHeight );
const int maxBlocksSupportedByTheDeviceForThisBlockHeight = static_cast<int>( ifc.mvBlockscanBlockCount.getMaxValue() );
if( maxBlocksSupportedByTheDeviceForThisBlockHeight >= blocksPerMegaBuffer )
{
ifc.mvBlockscanBlockCount.write( blocksPerMegaBuffer );
requestsNeeded = megaBufferCount;
requestsNeededPerMegaBuffer = 1;
cout << "The device can fully process the block mode setup! The requested buffer layout(" << blocksPerMegaBuffer << " blocks with " << blockHeight << " lines each) can be transmitted as a single image!" << endl;
}
else
{
const int optimalBlockCountOnDevice = getOptimalBlockCount( ifc, blocksPerMegaBuffer );
if( optimalBlockCountOnDevice == 1 )
{
cout << "The device cannot help for this MegaBuffer configuration! The requested buffer layout(" << blocksPerMegaBuffer << " blocks with " << blockHeight << " lines each) is too large to handled by the device alone and there is no common divisor so that the device can transfer several equal sized buffers larger than a single block!" << endl;
conditionalSetEnumPropertyByString( dc.deviceScanType, "Areascan" );
}
else
{
ifc.mvBlockscanBlockCount.write( optimalBlockCountOnDevice );
requestsNeeded /= optimalBlockCountOnDevice;
requestsNeededPerMegaBuffer /= optimalBlockCountOnDevice;
cout << "The device can help processing process the block mode setup! The requested buffer layout(" << blocksPerMegaBuffer << " blocks with " << blockHeight << " lines each) can be transmitted as " << requestsNeededPerMegaBuffer << " images per MegaBuffer!" << endl;
}
}
}
else
{
cout << "Even though this device is operated in the GenICam interface layout it doesn't seem to support the 'mvBlockscan' mode thus it cannot help to reduce the overall CPU load. A firmware update might help but this cannot be guaranteed but must be tested!" << endl;
}
}
else
{
if( captureParams.pDev->interfaceLayout.isValid() && supportsValue( captureParams.pDev->interfaceLayout, dilGenICam ) )
{
cout << "This device supports the 'GenICam' interface layout where it might be possible to benefit from the 'mvBlockScan' mode which could combine multiple images into a single block transfer. It is recommended to work with the 'GenICam' interface layout instead!" << endl;
}
else
{
cout << "The device cannot help to process any MegaBuffer configuration as it doesn't support the 'GenICam' interface layout and therefore also not the 'mvBlockscan' mode!" << endl;
}
CameraSettingsBase cs( captureParams.pDev );
try
{
cs.aoiHeight.write( blockHeight );
// deal with increment related rounding! A device might e.g. not support odd heights.
blockHeight = cs.aoiHeight.read();
}
catch( const ImpactAcquireException& e )
{
cout << "An error occurred while trying to set the height to '" << blockHeight << "': " << e.getErrorCodeAsString() << ". Will use '" << cs.aoiHeight.read() << "' instead." << endl;
}
try
{
if( ( result = captureParams.fi.getCurrentCaptureBufferLayout( irc, &pCurrentRequestLayout, &bufferAlignment ) ) != DMR_NO_ERROR )
{
cout << "An error occurred while querying the current capture buffer layout for device " << captureParams.pDev->serial.read()
<< "(error code: " << ImpactAcquireException::getErrorCodeAsString( result ) << ")." << endl;
exit( 1 );
}
if( ( bufferAlignment == 0 ) || isAligned( width * blockHeight, bufferAlignment ) )
{
cs.aoiWidth.write( width );
}
else
{
cout << "width*blockHeight does not result in a block size that is properly aligned! Will use buffer alignment(" << bufferAlignment << ") for the width instead!" << endl;
cs.aoiWidth.write( bufferAlignment );
}
}
catch( const ImpactAcquireException& e )
{
cout << "An error occurred while trying to set the width to '" << width << "': " << e.getErrorCodeAsString() << ". Will use '" << cs.aoiWidth.read() << "' instead." << endl;
}
if( !pixelFormat.empty() )
{
conditionalSetEnumPropertyByString( cs.pixelFormat, pixelFormat );
}
}
SystemSettings ss( captureParams.pDev );
ss.requestCount.write( requestsNeeded );
if( ( result = captureParams.fi.getCurrentCaptureBufferLayout( irc, &pCurrentRequestLayout, &bufferAlignment ) ) != DMR_NO_ERROR )
{
cout << "An error occurred while querying the current capture buffer layout for device " << captureParams.pDev->serial.read()
<< "(error code: " << ImpactAcquireException::getErrorCodeAsString( result ) << ")." << endl;
exit( 1 );
}
for( int i = 0; i < megaBufferCount; i++ )
{
RequestContainer requests;
for( int j = 0; j < requestsNeededPerMegaBuffer; j++ )
{
requests.push_back( captureParams.fi.getRequest( i * requestsNeededPerMegaBuffer + j ) );
}
captureParams.buffers.push_back( make_shared<MegaBuffer>( pCurrentRequestLayout, requests, bufferAlignment ) );
}
}
//-----------------------------------------------------------------------------
void displayCommandLineOptions( void )
//-----------------------------------------------------------------------------
{
cout << "Available parameters:" << endl
<< " 'serial' or 's' to specify the serial number of the device to use(if not specified the user will be asked to select a device)" << endl
<< " 'pixelFormat' or 'pf' to specify the pixel format to use(default: The current value used by the device)" << endl
<< " 'width' or 'w' to specify the width(default: The current value used by the device)" << endl
<< " 'blockHeight' or 'bh' to specify the height of a block within the mega-buffer(default: " << CaptureParameter::DEFAULT_BLOCK_HEIGHT << ")" << endl
<< " 'blocksPerMegaBuffer' or 'bpmb' to specify the number of blocks that will define one mega-buffer(default: " << CaptureParameter::DEFAULT_BLOCKS_PER_MEGA_BUFFER << ")" << endl
<< " 'megaBufferCount' or 'mbc' to specify the number of mega-buffers to allocate(default: " << CaptureParameter::DEFAULT_MEGA_BUFFER_COUNT << ")" << endl
<< endl
<< "USAGE EXAMPLE:" << endl
<< " CaptureToMegaBuffer s=VD* pf=RGB888Packed w=1024 bh=200 bpmb=5" << endl << endl;
}
//-----------------------------------------------------------------------------
void displayImage( CaptureParameter* pCaptureParameter, Request* pRequest )
//-----------------------------------------------------------------------------
{
const char* const pAddr = reinterpret_cast<char*>( pRequest->imageData.read() );
const auto megaBufferCount = pCaptureParameter->buffers.size();
for( size_t i = 0; i < megaBufferCount; i++ )
{
shared_ptr<MegaBuffer> pMegaBuffer( pCaptureParameter->buffers[i] );
if( pMegaBuffer->isAddressWithinBuffer( pAddr ) )
{
cout << "Displaying mega-buffer " << i << "(" << pMegaBuffer->getBufferDescriptor().getBuffer()->iWidth << "x" << pMegaBuffer->getBufferDescriptor().getBuffer()->iHeight << ", address " << reinterpret_cast<const void*>( pAddr ) << ", " << pRequest->imageSize.read() << " bytes, request number " << pRequest->getNumber() << ")" << endl;
#ifdef USE_DISPLAY
pCaptureParameter->pDisplayWindow->GetImageDisplay().SetImage( pMegaBuffer->getBufferDescriptor().getBuffer() );
pCaptureParameter->pDisplayWindow->GetImageDisplay().Update();
#else
// suppress compiler warnings
( void )pRequest;
( void )pCaptureParameter;
#endif // #ifdef USE_DISPLAY
return;
}
}
}
//-----------------------------------------------------------------------------
void freeCaptureBuffers( CaptureParameter& captureParams )
//-----------------------------------------------------------------------------
{
captureParams.buffers.clear();
const int requestCount = captureParams.fi.requestCount();
for( int i = 0; i < requestCount; i++ )
{
assert( ( captureParams.fi.getRequest( i )->imageMemoryMode.read() == rimmAuto ) && "Something seems to be incorrect with cleaning up the user allocated memory blocks!" );
}
}
//-----------------------------------------------------------------------------
// Helper function to return a suitable block count for the mvBlockscan mode that is
// supported by the device AND is a divisor of the requested block count
int getOptimalBlockCount( GenICam::ImageFormatControl& ifc, const int blocksPerMegaBuffer )
//-----------------------------------------------------------------------------
{
int optimalBlockCount = static_cast<int>( ifc.mvBlockscanBlockCount.getMaxValue() );
while( ( blocksPerMegaBuffer % optimalBlockCount ) != 0 )
{
optimalBlockCount--;
}
return optimalBlockCount;
}
//-----------------------------------------------------------------------------
void liveLoop( CaptureParameter* pParameter )
//-----------------------------------------------------------------------------
{
// Send all requests to the capture queue. There can be more than 1 queue for some devices, but for this sample
// we will work with the default capture queue. If a device supports more than one capture or result
// queue, this will be stated in the manual. If nothing is mentioned about it, the device supports one
// queue only. This loop will send all requests currently available to the driver. To modify the number of requests
// use the property mvIMPACT::acquire::SystemSettings::requestCount at runtime or the property
// mvIMPACT::acquire::Device::defaultRequestCount BEFORE opening the device.
while( ( result = static_cast<TDMR_ERROR>( pParameter->fi.imageRequestSingle() ) ) == DMR_NO_ERROR ) {};
{
cout << "'FunctionInterface.imageRequestSingle' returned with an unexpected result: " << result
<< "(" << ImpactAcquireException::getErrorCodeAsString( result ) << ")" << endl;
}
manuallyStartAcquisitionIfNeeded( pParameter->pDev, pParameter->fi );
// run thread loop
unsigned int cnt = {0};
const unsigned int timeout_ms = {500};
Request* pRequest = nullptr;
while( !s_boTerminated )
{
// wait for results from the default capture queue
const int requestNr = pParameter->fi.imageRequestWaitFor( timeout_ms );
pRequest = pParameter->fi.isRequestNrValid( requestNr ) ? pParameter->fi.getRequest( requestNr ) : 0;
if( pRequest != nullptr )
{
if( pRequest->isOK() )
{
++cnt;
// here we can display some statistical information every 100th image
if( cnt % 100 == 0 )
{
cout << "Info from " << pParameter->pDev->serial.read()
<< ": " << pParameter->statistics.framesPerSecond.name() << ": " << pParameter->statistics.framesPerSecond.readS()
<< ", " << pParameter->statistics.errorCount.name() << ": " << pParameter->statistics.errorCount.readS()
<< ", " << pParameter->statistics.captureTime_s.name() << ": " << pParameter->statistics.captureTime_s.readS()
<< ", CaptureDimension: " << pRequest->imageWidth.read() << "x" << pRequest->imageHeight.read() << "(" << pRequest->imagePixelFormat.readS() << ")" << endl;
}
displayImage( pParameter, pRequest );
checkCaptureBufferAddress( pRequest, pParameter->buffers );
}
else
{
cout << "Error: " << pRequest->requestResult.readS() << endl;
}
// since we capture to memory managed by the application we can immediately unlock and re-queue this buffer
pRequest->unlock();
pParameter->fi.imageRequestSingle();
}
//else
//{
// Please note that slow systems or interface technologies in combination with high resolution sensors
// might need more time to transmit an image than the timeout value which has been passed to imageRequestWaitFor().
// If this is the case simply wait multiple times OR increase the timeout(not recommended as usually not necessary
// and potentially makes the capture thread less responsive) and rebuild this application.
// Once the device is configured for triggered image acquisition and the timeout elapsed before
// the device has been triggered this might happen as well.
// The return code would be -2119(DEV_WAIT_FOR_REQUEST_FAILED) in that case, the documentation will provide
// additional information under TDMR_ERROR in the interface reference.
// If waiting with an infinite timeout(-1) it will be necessary to call 'imageRequestReset' from another thread
// to force 'imageRequestWaitFor' to return when no data is coming from the device/can be captured.
// cout << "imageRequestWaitFor failed (" << requestNr << ", " << ImpactAcquireException::getErrorCodeAsString( requestNr ) << ")"
// << ", timeout value too small?" << endl;
//}
}
manuallyStopAcquisitionIfNeeded( pParameter->pDev, pParameter->fi );
#ifdef USE_DISPLAY
// stop the display from showing freed memory
pParameter->pDisplayWindow->GetImageDisplay().RemoveImage();
#endif // #ifdef USE_DISPLAY
// In this sample the next line is redundant as the device driver will be
// closed now, but in a real world application a thread like this might be started
// several times an then it becomes crucial to clean up correctly.
pParameter->fi.imageRequestReset( 0, 0 );
}
//-----------------------------------------------------------------------------
void runLiveLoop( CaptureParameter& parameter )
//-----------------------------------------------------------------------------
{
s_boTerminated = false;
thread myThread( liveLoop, &parameter );
cout << "Press [ENTER] to end the application..." << endl;
cin.get();
s_boTerminated = true;
myThread.join();
}
//-----------------------------------------------------------------------------
int main( int argc, char* argv[] )
//-----------------------------------------------------------------------------
{
DeviceManager devMgr;
Device* pDev = nullptr;
string pixelFormat;
int width = {-1};
int blockHeight = CaptureParameter::DEFAULT_BLOCK_HEIGHT;
int blocksPerMegaBuffer = CaptureParameter::DEFAULT_BLOCKS_PER_MEGA_BUFFER;
int megaBufferCount = CaptureParameter::DEFAULT_MEGA_BUFFER_COUNT;
// scan command line
if( argc > 1 )
{
bool boInvalidCommandLineParameterDetected = false;
for( int i = 1; i < argc; i++ )
{
string param( argv[i] ), key, value;
string::size_type keyEnd = param.find_first_of( "=" );
if( ( keyEnd == string::npos ) || ( keyEnd == param.length() - 1 ) )
{
cout << "Invalid command line parameter: '" << param << "' (ignored)." << endl;
boInvalidCommandLineParameterDetected = true;
}
else
{
key = param.substr( 0, keyEnd );
value = param.substr( keyEnd + 1 );
if( ( key == "serial" ) || ( key == "s" ) )
{
pDev = devMgr.getDeviceBySerial( value );
}
else if( ( key == "pixelFormat" ) || ( key == "pf" ) )
{
pixelFormat = value;
}
else if( ( key == "width" ) || ( key == "w" ) )
{
width = atoi( value.c_str() );
}
else if( ( key == "blockHeight" ) || ( key == "bh" ) )
{
blockHeight = atoi( value.c_str() );
}
else if( ( key == "blocksPerMegaBuffer" ) || ( key == "bpmb" ) )
{
blocksPerMegaBuffer = atoi( value.c_str() );
}
else if( ( key == "megaBufferCount" ) || ( key == "mbc" ) )
{
megaBufferCount = atoi( value.c_str() );
}
else
{
cout << "Invalid command line parameter: '" << param << "' (ignored)." << endl;
boInvalidCommandLineParameterDetected = true;
}
}
}
if( boInvalidCommandLineParameterDetected )
{
displayCommandLineOptions();
}
}
else
{
cout << "No command line parameters specified." << endl;
displayCommandLineOptions();
}
if( pDev == nullptr )
{
pDev = getDeviceFromUserInput( devMgr );
}
if( pDev == nullptr )
{
cout << "Unable to continue! Press [ENTER] to end the application" << endl;
cin.get();
return 1;
}
cout << "Initialising the device. This might take some time..." << endl;
try
{
pDev->open();
}
catch( const ImpactAcquireException& e )
{
// this e.g. might happen if the same device is already opened in another process...
cout << "An error occurred while opening device " << pDev->serial.read()
<< "(error code: " << e.getErrorCodeAsString() << ")." << endl
<< "Press [ENTER] to end the application..." << endl;
cin.get();
return 1;
}
CaptureParameter captureParams( pDev );
createCaptureBuffers( captureParams, pixelFormat, width, blockHeight, blocksPerMegaBuffer, megaBufferCount );
runLiveLoop( captureParams );
return 0;
}
std::string getErrorCodeAsString(void) const
Returns a string representation of the error associated with the exception.
Definition: mvIMPACT_acquire.h:280
TDMR_ERROR
Errors reported by the device manager.
Definition: mvDriverBaseEnums.h:2351
@ DEV_NO_FREE_REQUEST_AVAILABLE
The user requested a new image, but no free mvIMPACT::acquire::Request object is available to process...
Definition: mvDriverBaseEnums.h:2509
@ DMR_NO_ERROR
The function call was executed successfully.
Definition: mvDriverBaseEnums.h:2356
@ rimmUser
User supplied memory mode.
Definition: mvDriverBaseEnums.h:4809
@ rimmAuto
Automatic mode.
Definition: mvDriverBaseEnums.h:4797
@ dilGenICam
A GenICamâ„¢ like interface layout shall be used.
Definition: mvDriverBaseEnums.h:2004
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.