Impact Acquire SDK .NET
CaptureToUserMemory.cs

The CaptureToUserMemory program is based on the ContinuousCapture example. It consists of 4 steps which demonstrate image acquisition and display using different acquisition memory mechanisms. Impact Acquire can either automatically allocate and manage capture memory or if needed can directly acquire into user supplied memory.

Program location
The source file CaptureToUserMemory.cs can be found under:
%INSTALLDIR%\apps\CSharp\CaptureToUserMemory\
Note
If you have installed the package without example applications, this file will not be available. On Windows® the sample application can be installed or removed from the target system at any time by simply restarting the installation package.
CaptureToUserMemory example:
  1. Opens a Balluff device.
  2. Snaps images continuously.
Console Output
[0]: BF000306 (mvBlueFOX-202C, Family: mvBlueFOX, interface layout: DeviceSpecific)

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...
The device will try to capture continuously into memory automatically allocated by the device driver.
This is the default behaviour.
Press [ENTER] to end the continuous acquisition.
Info from BF000306: FramesPerSecond: 28.655669, ErrorCount: 0, CaptureTime_s: 0.104219

The device will now try to capture continuously into user supplied memory.
Press [ENTER] to end the continuous acquisition.
Info from BF000306: FramesPerSecond: 28.652999, ErrorCount: 0, CaptureTime_s: 0.104366

The device will try to capture continuously into memory automatically allocated by the device driver again.
This is the default behaviour.
Press [ENTER] to end the continuous acquisition.
Info from BF000306: FramesPerSecond: 28.653142, ErrorCount: 0, CaptureTime_s: 0.104201

Now the device will try to capture one frame into a specific user supplied buffer
Capture into specific user supplied buffer done.
Press [ENTER] to end the application...
How it works

The continuous acquisition part is similar to the ExampleApplications_section_Continuous sample. After you have entered the number of the device and pressed [ENTER], the device will be initialized and a window to display the captured images is created:

public ImageDisplayWindow window;
public ThreadParameters(Device d, ImageDisplayWindow w)
{
pDev = d;
fi = new FunctionInterface(pDev);
irc = new ImageRequestControl(pDev);
window = w;
}
Note
In this user memory managed step, waitFor will return as fast as possible. No "real" image will be taken but a request object that contains a dummy image with the format. Dimensions and other information will be returned. This is (apart from the pixel data) similar to any "real" image that would be captured with the current settings.

When capturing into a certain buffer a request number "REQUEST_TO_USE" (zero based) for this acquisition is used, thus we haves to make sure that there are at least 'REQUEST_TO_USE + 1' requests:

SystemSettings ss = new SystemSettings(device);
ss.requestCount.write(REQUEST_TO_USE + 1);

To associate a user supplied buffer with this request, you have to do the following:

Request pRequest = captureParams.fi.getRequest(REQUEST_TO_USE);

Next, you have to define that "REQUEST_TO_USE" is used for the next acquisition:

captureParams.irc.requestToUse.write(REQUEST_TO_USE);

And then capture the image:

result = DMR_ImageRequestSingle( captureParams.hDrv, 0, &requestUsed );

Now, you have to wait for the result:

result = DMR_ImageRequestWaitFor( captureParams.hDrv, -1, 0, &requestNr );

You can check, if the request contains a valid image and display it:

int requestUsed = Device.INVALID_ID;
functionResult = captureParams.fi.imageRequestSingle(captureParams.irc, out requestUsed);
if (functionResult != (int)TDMR_ERROR.DMR_NO_ERROR)
{
// Handle request error
}
if (requestUsed != REQUEST_TO_USE)
{
// Handle buffer error
}
int requestNr = captureParams.fi.imageRequestWaitFor(-1);
pRequest = captureParams.fi.getRequest(requestNr);
if (!pRequest.isOK)
{
// Handle request error
}
captureParams.window.imageDisplay.SetImage(pRequest.imageData.read(), pRequest.imageWidth.read(), pRequest.imageHeight.read(), pRequest.imagePixelPitch.read() * 8, pRequest.imageLinePitch.read());
captureParams.window.imageDisplay.Update();

Finally, if you press [ENTER], the image window will be destroyed and the device will be closed.

Source code
using System;
using System.Collections.Generic;
using System.Threading;
#if USE_DISPLAY
#endif // #if USE_DISPLAY
using mv.impact.acquire.examples.helper;
namespace mv.impact.acquire.examples
{
class CaptureToUserMemory
{
public class ThreadParameters
{
public Device pDev;
public FunctionInterface fi;
public ImageRequestControl irc;
#if USE_DISPLAY
public ImageDisplayWindow window;
#endif // #if USE_DISPLAY
public bool terminated = false;
public bool userSuppliedMemoryUsed = false;
public List<UserSuppliedHeapBuffer> buffers = new List<UserSuppliedHeapBuffer>();
public ThreadParameters(Device d)
{
pDev = d;
fi = new FunctionInterface(pDev);
irc = new ImageRequestControl(pDev);
}
}
static void CaptureThread(object data)
{
ThreadParameters parameters = (ThreadParameters)data;
// establish access to the statistic properties
Statistics statistics = new Statistics(parameters.pDev);
// 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 mv.impact.acquire.SystemSettings.requestCount at runtime (note that some devices will
// only allow to modify this parameter while NOT streaming data!) or the property
// mv.impact.acquire.Device.defaultRequestCount BEFORE opening the device.
TDMR_ERROR result = TDMR_ERROR.DMR_NO_ERROR;
while ((result = (TDMR_ERROR)parameters.fi.imageRequestSingle()) == TDMR_ERROR.DMR_NO_ERROR) { };
if (result != TDMR_ERROR.DEV_NO_FREE_REQUEST_AVAILABLE)
{
Console.WriteLine("'FunctionInterface.imageRequestSingle' returned with an unexpected result: {0}({1})", result, ImpactAcquireException.getErrorCodeAsString(result));
}
DeviceAccess.manuallyStartAcquisitionIfNeeded(parameters.pDev, parameters.fi);
// run thread loop
Request pRequest = null;
// we always have to keep at least 2 images as the display module might want to repaint the image, thus we
// cannot free it unless we have a assigned the display to a new buffer.
Request pPreviousRequest = null;
int timeout_ms = 500;
int cnt = 0;
int requestNr = Device.INVALID_ID;
while (!parameters.terminated)
{
// wait for results from the default capture queue
requestNr = parameters.fi.imageRequestWaitFor(timeout_ms);
pRequest = parameters.fi.isRequestNrValid(requestNr) ? parameters.fi.getRequest(requestNr) : null;
if (pRequest != null)
{
if (pRequest.isOK)
{
++cnt;
// here we can display some statistical information every 100th image
if (cnt % 100 == 0)
{
Console.WriteLine("Info from {0}: {1}: {2}, {3}: {4}, {5}: {6}", parameters.pDev.serial.read(),
statistics.framesPerSecond.name, statistics.framesPerSecond.readS(),
statistics.errorCount.name, statistics.errorCount.readS(),
statistics.captureTime_s.name, statistics.captureTime_s.readS());
}
#if USE_DISPLAY
# if CLR_AT_LEAST_3_DOT_5
// Extension methods are not supported by CLR versions smaller than 3.5 and this next function
// is therefore not available then.
parameters.window.imageDisplay.SetImage(pRequest);
# else
// If extension methods are not available, the following function can be used instead. It is
// not as convenient and will only work for some pixel formats. For more complex pixel formats
// use other overloads of this method
parameters.window.imageDisplay.SetImage(pRequest.imageData.read(), pRequest.imageWidth.read(), pRequest.imageHeight.read(), pRequest.imageBytesPerPixel.read() * 8, pRequest.imageLinePitch.read());
# endif
parameters.window.imageDisplay.Update();
#endif // #if USE_DISPLAY
UserSuppliedMemory.checkCaptureBufferAddress(pRequest, parameters.userSuppliedMemoryUsed, parameters.buffers);
}
else
{
Console.WriteLine("Error: {0}", pRequest.requestResult.readS());
}
if (pPreviousRequest != null)
{
// this image has been displayed thus the buffer is no longer needed...
pPreviousRequest.unlock();
}
pPreviousRequest = pRequest;
// send a new image request into the capture queue
parameters.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.
// Console.WriteLine("imageRequestWaitFor failed ({0}, {1}), timeout value too small?", requestNr, ImpactAcquireException.getErrorCodeAsString(requestNr));
//}
}
DeviceAccess.manuallyStopAcquisitionIfNeeded(parameters.pDev, parameters.fi);
#if USE_DISPLAY
// stop the display from showing freed memory
parameters.window.imageDisplay.RemoveImage();
#endif // #if USE_DISPLAY
// In this sample all the next lines are 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.
// free the last potentially locked request
if (pRequest != null)
{
pRequest.unlock();
}
// clear all queues
parameters.fi.imageRequestReset(0, 0);
}
public static void runThread(ThreadParameters captureParams)
{
captureParams.terminated = false;
Thread thread = new Thread(CaptureToUserMemory.CaptureThread);
thread.Start(captureParams);
Console.ReadLine();
captureParams.terminated = true;
thread.Join();
}
public static void exitOnError(String message)
{
Console.WriteLine(message);
Console.WriteLine("Press [ENTER] to exit the application.");
Console.ReadLine();
System.Environment.Exit(1);
}
static void Main(string[] args)
{
mv.impact.acquire.LibraryPath.init(); // this will add the folders containing unmanaged libraries to the PATH variable.
Device pDev = DeviceAccess.getDeviceFromUserInput();
if (pDev == null)
{
exitOnError("Unable to continue!");
}
Console.WriteLine("Initialising the device. This might take some time...");
try
{
pDev.open();
}
{
// this e.g. might happen if the same device is already opened in another process...
exitOnError(String.Format("An error occurred while opening device " + pDev.serial +
"(error code: " + e.Message + ")."));
}
ThreadParameters captureParams = new ThreadParameters(pDev);
#if USE_DISPLAY
captureParams.window = new ImageDisplayWindow(String.Format("mvIMPACT_acquire sample, Device {0}", pDev.serial.read()));
#endif // #if USE_DISPLAY
//=============================================================================
//========= Capture loop into memory managed by the driver (default) ==========
//=============================================================================
Console.WriteLine("The device will try to capture continuously into memory automatically allocated be the device driver.");
Console.WriteLine("This is the default behaviour.");
Console.WriteLine("Press [ENTER] to end the continuous acquisition.");
runThread(captureParams);
//=============================================================================
//========= Capture loop into memory managed by the user (advanced) ===========
//=============================================================================
Console.WriteLine("The device will now try to capture continuously into user supplied memory.");
int bufferSize = 0;
int bufferAlignment = 0;
TDMR_ERROR result = (TDMR_ERROR)captureParams.fi.getCurrentCaptureBufferLayout(captureParams.irc, out bufferSize, out bufferAlignment);
if (result != TDMR_ERROR.DMR_NO_ERROR)
{
exitOnError(String.Format("An error occurred while querying the current capture buffer layout for device {0}(error code: {1}). Press [ENTER] to end the application...", captureParams.pDev.serial.read(), ImpactAcquireException.getErrorCodeAsString(result)));
}
result = UserSuppliedMemory.createCaptureBuffers(captureParams.fi, captureParams.buffers, bufferSize, bufferAlignment);
if (result != TDMR_ERROR.DMR_NO_ERROR)
{
exitOnError(String.Format("An error occurred while setting up the user supplied buffers for device {0}(error code: {1}). Press [ENTER] to end the application...", captureParams.pDev.serial.read(), ImpactAcquireException.getErrorCodeAsString(result)));
}
captureParams.userSuppliedMemoryUsed = true;
runThread(captureParams);
//=============================================================================
//========= unregister user supplied buffers again ============================
//=============================================================================
UserSuppliedMemory.freeCaptureBuffers(captureParams.fi, captureParams.buffers);
//=============================================================================
//========= Capture loop into memory managed by the driver again (default) ====
//=============================================================================
captureParams.userSuppliedMemoryUsed = false;
Console.WriteLine("The device will try to capture continuously into memory automatically allocated be the device driver again.");
Console.WriteLine("This is the default behaviour.");
runThread(captureParams);
//=============================================================================
//========= Capture into a specific buffer managed by the user (advanced) =====
//=============================================================================
// by default the driver will decide which request will be used for an acquisition
// requested by the user. However sometimes it can be necessary to make sure that a
// certain request object will be used...
Console.WriteLine("Now the device will try to capture one frame into a specific user supplied buffer");
UserSuppliedHeapBuffer buffer = new UserSuppliedHeapBuffer(bufferSize, bufferAlignment);
const int REQUEST_TO_USE = 2;
// we want to use request number 'REQUEST_TO_USE' (zero based) for this acquisition thus we have to make sure
// that there are at least 'REQUEST_TO_USE + 1' requests
ss.requestCount.write(REQUEST_TO_USE + 1);
// associate a user supplied buffer with this request
Request pRequest = captureParams.fi.getRequest(REQUEST_TO_USE);
try
{
result = (TDMR_ERROR)pRequest.attachUserBuffer(buffer.pointer, buffer.size);
if (result != TDMR_ERROR.DMR_NO_ERROR)
{
exitOnError(String.Format("An error occurred while attaching a user buffer to request number {0}: {1}.", REQUEST_TO_USE, ImpactAcquireException.getErrorCodeAsString(result)));
}
}
{
exitOnError(String.Format("An error occurred while attaching a user buffer to request number {0}: {1}.", REQUEST_TO_USE, e.errorCodeAsString));
}
// define that 'REQUEST_TO_USE' is used for the next acquisition
captureParams.irc.requestToUse.write(REQUEST_TO_USE);
// and capture the image
int requestUsed = Device.INVALID_ID;
result = (TDMR_ERROR)captureParams.fi.imageRequestSingle(captureParams.irc, out requestUsed);
if (result != TDMR_ERROR.DMR_NO_ERROR)
{
exitOnError(String.Format("An error occurred while requesting an image for request number {0}: {1}.", REQUEST_TO_USE, ImpactAcquireException.getErrorCodeAsString(result)));
}
if (requestUsed != REQUEST_TO_USE)
{
Console.WriteLine("ERROR! An acquisition into buffer {0} was requested, but the driver did use {1} for this acquisition.", REQUEST_TO_USE, requestUsed);
}
// Start the acquisition manually if this was requested(this is to prepare the driver for data capture and tell the device to start streaming data)
{
if ((result = (TDMR_ERROR)captureParams.fi.acquisitionStart()) != TDMR_ERROR.DMR_NO_ERROR)
{
Console.WriteLine("'FunctionInterface.acquisitionStart' returned with an unexpected result: {0}({1})", result, ImpactAcquireException.getErrorCodeAsString(result));
}
}
// Wait for the buffer to get filled
int requestNr = captureParams.fi.imageRequestWaitFor(-1);
// Stop the acquisition manually if this was requested
{
if ((result = (TDMR_ERROR)captureParams.fi.acquisitionStop()) != TDMR_ERROR.DMR_NO_ERROR)
{
Console.WriteLine("'FunctionInterface.acquisitionStop' returned with an unexpected result: {0}({1})", result, ImpactAcquireException.getErrorCodeAsString(result));
}
}
pRequest = captureParams.fi.getRequest(requestNr);
if (!pRequest.isOK)
{
exitOnError(String.Format("Error: {0}", pRequest.requestResult.readS()));
}
Console.WriteLine("Capture into specific user supplied buffer done.");
#if USE_DISPLAY
# if CLR_AT_LEAST_3_DOT_5
// Extension methods are not supported by CLR versions smaller than 3.5 and this next function
// is therefore not available then.
captureParams.window.imageDisplay.SetImage(pRequest);
# else
// If extension methods are not available, the following function can be used instead. It is
// not as convenient and will only work for some pixel formats. For more complex pixel formats
// use other overloads of this method
captureParams.window.imageDisplay.SetImage(pRequest.imageData.read(), pRequest.imageWidth.read(), pRequest.imageHeight.read(), pRequest.imageBytesPerPixel.read() * 8, pRequest.imageLinePitch.read());
# endif
captureParams.window.imageDisplay.Update();
#endif // #if USE_DISPLAY
// and end the application
Console.WriteLine("Press [ENTER] to end the application.");
Console.ReadLine();
#if USE_DISPLAY
captureParams.window.Dispose();
#endif // #if USE_DISPLAY
captureParams.fi.imageRequestUnlock(requestNr);
captureParams.fi.imageRequestReset(0, 0);
}
}
}
This class and its functions represent an actual device detected by this interface in the current sys...
Definition Device.cs:91
const int INVALID_ID
A symbolic constant to define an invalid handle.
Definition Device.cs:177
readonly EnumPropertyI< TAcquisitionStartStopBehaviour > acquisitionStartStopBehaviour
An enumerated integer property defining the start/stop behaviour during acquisition of this driver in...
Definition Device.cs:719
void open()
Opens a device.
Definition Device.cs:208
readonly PropertyS serial
A string property (read-only) containing the serial number of this device.
Definition Device.cs:515
EnumPropertyI< T > write(T value)
Writes one value to the property.
Definition EnumPropertyI.cs:449
T read()
Reads a value from a property.
Definition EnumPropertyI.cs:342
The function interface to devices supported by this interface.
Definition FunctionInterface.cs:21
A helper class to control the way an image request will be processed.
Definition ImageRequestControl.cs:8
An base class for exceptions generated by Impact Acquire.
Definition Exceptions.cs:9
static String getErrorCodeAsString(int errorCode)
Returns a string representation of a error.
Definition Exceptions.cs:48
String errorCodeAsString
Returns a string representation of the error associated with the exception.
Definition Exceptions.cs:118
A small helper class to administer various library search path related variables and paths.
Definition LibraryPath.cs:14
static void init()
Calling this method will add the folders containing unmanaged libraries to the systems library search...
Definition LibraryPath.cs:251
IntPtr read()
Reads a value from a property.
Definition PropertyPtr.cs:49
String read()
Reads a value from a property.
Definition PropertyS.cs:144
Contains information about a captured buffer.
Definition Request.cs:77
readonly PropertyPtr imageData
A pointer property (read-only) containing the start address of the image data.
Definition Request.cs:1579
readonly PropertyI imageLinePitch
An integer property (read-only) containing the offset (in bytes) to the next line of each channel bel...
Definition Request.cs:1655
int attachUserBuffer(IntPtr pBuf, int bufSize)
Convenience function to attach a user supplied buffer to a mv.impact.acquire.Request object.
Definition Request.cs:674
readonly EnumPropertyI< TRequestResult > requestResult
An enumerated integer property (read-only) defining the result of this request.
Definition Request.cs:1211
readonly PropertyI imageWidth
An integer property (read-only) containing the width of the image in pixels.
Definition Request.cs:1693
readonly PropertyI imageBytesPerPixel
An integer property (read-only) containing the number of bytes per pixel in this image.
Definition Request.cs:1679
bool isOK
Convenience function to check if a request has been processed successfully.
Definition Request.cs:1173
readonly PropertyI imageHeight
An integer property (read-only) containing the height of the image in pixels.
Definition Request.cs:1704
int unlock()
Unlocks the request for the driver again.
Definition Request.cs:619
Contains statistical information.
Definition Statistics.cs:10
readonly PropertyI errorCount
An integer property (read-only) containing the overall count of image requests which returned with an...
Definition Statistics.cs:82
readonly PropertyF framesPerSecond
A float property (read-only) containing the current number of frames captured per second.
Definition Statistics.cs:88
readonly PropertyF captureTime_s
A float property (read-only) containing the average time needed to capture an image.
Definition Statistics.cs:75
A class for accessing general settings that control the overall behaviour of a device driver.
Definition SystemSettings.cs:6
readonly PropertyI requestCount
An integer property defining the number of requests allocated by the driver.
Definition SystemSettings.cs:60
A class that can be used to display images in a window.
Definition ImageDisplayWindow.cs:15
A small helper class to create a user supplied buffer on the heap.
Definition UserSuppliedHeapBuffer.cs:13
int size
Returns the size in bytes of the user buffer.
Definition UserSuppliedHeapBuffer.cs:53
IntPtr pointer
Returns an System.IntPtr instance pointing to the aligned memory location.
Definition UserSuppliedHeapBuffer.cs:51
TDMR_ERROR
Errors reported by the device manager.
Definition mvDriverBaseEnums.cs:2374
TAcquisitionStartStopBehaviour
Defines valid modes for acquisition start/stop behaviour.
Definition mvDriverBaseEnums.cs:76
This namespace contains classes and functions that can be used to display images.
Definition Enumerations.cs:2
This namespace contains some small helper classes and convenience functions belonging to the image ac...
This namespace contains classes and functions belonging to the image acquisition module of this SDK.
Definition Enumerations.cs:2
Definition Enumerations.cs:2
Definition Enumerations.cs:2