Impact 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 Balluff 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>
#include <mvIMPACT_CPP/mvIMPACT_acquire_GenICam.h>
#ifdef _WIN32
# include <mvDisplay/Include/mvIMPACT_acquire_display.h>
# define USE_DISPLAY
#elif defined(linux) || defined(__linux) || defined(__linux__) || defined(__APPLE__)
# 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
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 (note that some devices will
// only allow to modify this parameter while NOT streaming data!) or the property
// mvIMPACT::acquire::Device::defaultRequestCount BEFORE opening the device.
while( ( result = static_cast<TDMR_ERROR>( pParameter->fi.imageRequestSingle() ) ) == DMR_NO_ERROR ) {};
if( result != DEV_NO_FREE_REQUEST_AVAILABLE )
{
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++ )
{
const string param( argv[i] );
const 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
{
const string key = param.substr( 0, keyEnd );
const string 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;
}
A base class for camera related settings(Device specific interface layout only).
Definition mvIMPACT_acquire.h:18786
Grants access to devices that can be operated by this software interface.
Definition mvIMPACT_acquire.h:7159
Device * getDeviceBySerial(const std::string &serial="", unsigned int devNr=0, char wildcard=' *') const
Tries to locate a device via the serial number.
Definition mvIMPACT_acquire.h:7512
This class and its functions represent an actual device detected by this interface in the current sys...
Definition mvIMPACT_acquire.h:6118
PropertyS serial
A string property (read-only) containing the serial number of this device.
Definition mvIMPACT_acquire.h:6550
void open(void)
Opens a device.
Definition mvIMPACT_acquire.h:6419
ZYX getMaxValue(void) const
Reads the maximum value from a property.
Definition mvIMPACT_acquire.h:4932
ZYX read(int index=0) const
Reads a value from a property.
Definition mvIMPACT_acquire.h:4300
The function interface to devices supported by this interface.
Definition mvIMPACT_acquire.h:10746
Category for device information and control.
Definition mvIMPACT_acquire_GenICam.h:82
Category for Image Format Control features.
Definition mvIMPACT_acquire_GenICam.h:1132
PropertyI64 mvBlockscanBlockCount
An integer property. Blockscan mode only: Number of blocks combined to one frame.
Definition mvIMPACT_acquire_GenICam.h:1539
A wrapper class to handle mvIMPACT::acquire::ImageBuffer structures.
Definition mvIMPACT_acquire.h:8104
ImageBuffer * getBuffer(void) const
Grants access to the underlying mvIMPACT::acquire::ImageBuffer structure managed by this object.
Definition mvIMPACT_acquire.h:8301
A helper class to control the way an image request will be processed.
Definition mvIMPACT_acquire.h:10348
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
void * read(int index=0) const
Reads a value from a property.
Definition mvIMPACT_acquire.h:5176
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:8628
PropertyI imagePixelPitch
An integer property (read-only) containing the offset (in bytes) to the next pixel of the specified c...
Definition mvIMPACT_acquire.h:10213
PropertyI imageHeight
An integer property (read-only) containing the height of the image in pixels.
Definition mvIMPACT_acquire.h:10319
bool isOK(void) const
Convenience function to check if a request has been processed successfully.
Definition mvIMPACT_acquire.h:9462
PropertyI imageBytesPerPixel
An integer property (read-only) containing the number of bytes per pixel in this image.
Definition mvIMPACT_acquire.h:10294
PropertyIRequestResult requestResult
An enumerated integer property (read-only) defining the result of this request.
Definition mvIMPACT_acquire.h:9768
PropertyI imageSize
An integer property (read-only) containing the size (in bytes) of the whole image.
Definition mvIMPACT_acquire.h:10190
PropertyI imageWidth
An integer property (read-only) containing the width of the image in pixels.
Definition mvIMPACT_acquire.h:10308
PropertyIRequestImageMemoryMode imageMemoryMode
An enumerated integer property (read-only) containing the memory mode used for this request.
Definition mvIMPACT_acquire.h:10112
int attachUserBuffer(void *pBuf, int bufSize)
Convenience function to attach a user supplied buffer to a mvIMPACT::acquire::Request object.
Definition mvIMPACT_acquire.h:9653
PropertyS imageChannelDesc
A string property (read-only) containing the string descriptors of each channel belonging to the curr...
Definition mvIMPACT_acquire.h:10292
PropertyI imageChannelOffset
An integer property (read-only) containing the offset (in bytes) to each channel belonging to the cur...
Definition mvIMPACT_acquire.h:10234
PropertyIImageBufferPixelFormat imagePixelFormat
An enumerated integer property (read-only) containing the pixel format of this image.
Definition mvIMPACT_acquire.h:10120
PropertyPtr imageData
A pointer property (read-only) containing the start address of the image data.
Definition mvIMPACT_acquire.h:10175
int detachUserBuffer(void)
Convenience function to detach a user supplied buffer from a mvIMPACT::acquire::Request object.
Definition mvIMPACT_acquire.h:9745
int unlock(void)
Unlocks the request for the driver again.
Definition mvIMPACT_acquire.h:9602
int getNumber(void) const
Returns the number associated with this request.
Definition mvIMPACT_acquire.h:9048
PropertyI imageLinePitch
An integer property (read-only) containing the offset (in bytes) to the next line of each channel bel...
Definition mvIMPACT_acquire.h:10250
Contains basic statistical information.
Definition mvIMPACT_acquire.h:14497
A base class for accessing settings that control the overall behaviour of a device driver.
Definition mvIMPACT_acquire.h:14706
TDMR_ERROR
Errors reported by the device manager.
Definition mvDriverBaseEnums.h:2601
@ DMR_NO_ERROR
The function call was executed successfully.
Definition mvDriverBaseEnums.h:2603
Fully describes a captured image.
Definition mvImageBuffer.h:94
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