Impact Acquire SDK Java
Typical Usage Scenarios And Example Applications

Interesting Topics To Start Integrating Impact Acquire

Since Impact Acquire SDK and the Balluff devices provide a huge variety of functionality, it will be helpful to use this page as a starting point which leads through a number of common image acquisition and configuration tasks.

The summarized code samples will show the most common cases and can be used as a base for an own image acquisition using the Impact Acquire SDK.

Note
Apart from those usage scenarios a large number of use cases can be found in the corresponding device manuals. Usually they are located at the 'Use Case'-chapter of each device's manual which is

Image Acquisition

Continuous Image Acquisition

A very common task is to acquire various images continuously to use them e.g. for video streams, continuous analysis or displaying purpose. This purpose is a bit more complex than acquiring just a single frame, but usually not that complex.

Note
It will be very useful to get familiar with the image capturing process of Impact Acquire to work create continuous image acquisitions. All important details are explained at the chapter The Capture Process.

The following samples will illustrate how simple continuous acquisitions can be realized.

Displaying Acquired Images And Capturing Data Into User Controlled Memory

In some cases simple continuous acquisitions are not sufficient since the image data should be processed or stored within the application. The complexity of such an application depends on the task.

Special Device Functionalities

Beginning with the release of 2.49.0 of Impact Acquire firmware updates for all devices still active can be applied in a rather convenient way by using a high level firmware update API. This API is available for every supported programming language. For object orientated languages a FirmwareUpdater class will be part of the SDK and code snippets can be found in the corresponding documentation of the class.

BVS CA-BN, mvBlueCOUGAR-X, mvBlueFOX3

mvBlockscan Mode

Beginning with release 2.35 of the GenICam™ device firmware a growing number of device models now support the mvBlockscan mode allowing to transmit multiple images as one thus using a single transfer block for transporting several consecutive frames from the same AOI reducing the overall overhead needed for transporting and reporting a frame thus in total reducing the CPU load on the host for certain scenarios.

The properties to control this feature are

  • DeviceScanType of the GenICam/DeviceControl category
  • mvBlockscanLinesPerBlock of the GenICam/ImageFormatControl category
  • mvBlockscanBlockCount of the GenICam/ImageFormatControl category

The maximum number of frames that can be combined that way by the device might be limited for various reasons but depending on the use-case the performance benefit can be significant.

mvBlueFOX

PowerMode

mvBlueFOX2 USB devices allow to put themselves into a mode where less power is consumed. This can be used when no image acquisition is currently active and helps to reduce the overall power consumption of the system in idle mode.

mvVirtualDevice

Apart from allowing to generate arbitrary test images in every pixel format supported by Impact Acquire the mvVirtualDevice package can also be used to capture images from a defined folder on the hard disk drive. This can be extremely useful when prototyping without real hardware or to feed defined test images into an algorithm in way the capture engine seems to provide these images. To do that just a couple of properties must be set appropriately:

The images are captured from the directory specified by the application in a Round-Robin fashion so once the last image from the folder has been captured the next image will be the first again. Whenever for some reason an image cannot be imported from the hard disk an artificial test image will be generated instead.

General SDK Functionalities

Callbacks

To get an idea how callbacks are implemented, the following sample will be helpful:

This API will allow you to register a callback that gets executed whenever a certain feature of interest did change in any way (e.g. its visibility, value, etc.). To do that you first need to derive from the class mvIMPACT.acquire.ComponentCallback like shown in the following example:

import mvIMPACT.acquire.*;
//-----------------------------------------------------------------------------
class MyCallback extends ComponentCallback
//-----------------------------------------------------------------------------
{
//=============================================================================
//=== Private static member variables =========================================
//=============================================================================
private static int executeHitCount_ = 0;
//=============================================================================
//=== Private member variables ================================================
//=============================================================================
private Object pUserData_;
//=============================================================================
//=== Public constructors =====================================================
//=============================================================================
public MyCallback( final String name, Object pUserData )
{
super();
pUserData_ = pUserData;
}
@Override
public void execute( Component c, SWIGTYPE_p_void pUserData )
{
++executeHitCount_;
// here you would actually do what is important for you! Please IGNORE the 'pUserData' parameter
// coming into this function! Use the one you did provide in you constructor call instead for
// whatever is necessary (e.g. cast it to an instance of a class you might want to call). Counting
// the number of times this function got called is a silly example only! Remove this in a real world
// application!
}
public static int getExecuteHitCount()
{
return executeHitCount_;
}
public Object getUserData()
{
return pUserData_;
}
}

The next thing that needs to be done now is to create an instance of that callback object somewhere appropriate in the code:

MyCallback cb = new MyCallback( this ); // Here we pass the instance of the class we are currently in to the callback. You may have different ideas!

Now the only thing left to do is to register a feature that you want to get callbacks on changes for:

cb.registerComponent( pDev.getState() );

Now for an open GEV/U3V device or a mvBlueFOX2 device whenever you unplug the device or plug it back in afterwards you should get a callback!

Setting Up The Framework For Third Party GenTL Producer Usage

How to set up the way Impact Acquire connects to and enumerates third party GenTL producers is described here: Setting Up The Framework For Third Party GenTL Producer Usage

Reading/Writing Binary Data To A Property

Some string properties (mvIMPACT.acquire.PropertyS ) instances can store binary data. In order to e.g. allow serializing of all data to an XML file binary data internally is stored encoded in Base64. So when using the standard read/write functions expecting a string parameter it is the responsibility of the application to encode the binary data into a Base64 encoded string before writing the value and decoding the string into a byte array again when reading.

Apart from that the class mvIMPACT.acquire.PropertyS also offers functions that accept a Java ByteBuffer object in order to directly read/write binary data into/from a property. The following example shows how code using these function could look:

// ... more code
PropertyS pseudoBinaryProp = getAccessToPropertyFromSomewhere();
// define some arbitrary binary data. In real life this of course will be something meaningful!
final int BUF_SIZE = 4;
byte[] data_bytes = new byte[BUF_SIZE];
data_bytes[0] = 0x00;
data_bytes[1] = -1;
data_bytes[2] = -128;
data_bytes[3] = 0x33;
ByteBuffer data = ByteBuffer.allocateDirect( BUF_SIZE );
data.put( data_bytes );
// write the data to the property
pseudoBinaryProp.writeBinary( data );
// in a JUnit test case we could now validate if the property actually stores the right amount of data
assertEquals( BUF_SIZE, pseudoBinaryProp.binaryDataBufferSize() );
// read back the data from the property again
ByteBuffer dataReturned = ByteBuffer.allocateDirect( BUF_SIZE );
pseudoBinaryProp.readBinary( dataReturned );
byte[] dataReturned_bytes = new byte[BUF_SIZE];
dataReturned.get( dataReturned_bytes );
for( int i = 0; i < BUF_SIZE; i++ )
{
// in a JUnit test case we could now validate if the property actually returns exactly the same binary blob of data here
assertEquals( data_bytes[i], dataReturned_bytes[i] );
}
// ... even more code

Making use of the 'AutoCloseable' interface

Sometimes there will be the need to destroy certain objects provided by this API at a given time independent from the Java garbage collector. As Java has nothing like a delete statement Java introduced the AutoCloseable interface in Version 8. Some classes (e.g. mvIMPACT.acquire.DeviceManager or mvIMPACT.acquire.ImageDisplayWindow ) implement this interface allowing something like a RAII way of using these objects with Javas try-with-resources statement:

// Assume that
// - 'pDev' points to a valid mvIMPACT.acquire.Device instance
// - 'fi' points to a valid mvIMPACT.acquire.FunctionInterface instance of that device
try ( ImageDisplayWindow displayWindow = new ImageDisplayWindow( "TestWindow" ) )
{
while( fi.imageRequestSingle() == TDMR_ERROR.DMR_NO_ERROR ) {};
manuallyStartAcquisitionIfNeeded( pDev, fi );
for( int j = 0; j < 50; j++ )
{
final int result = fi.imageRequestWaitFor( 1000 );
if( fi.isRequestNrValid( result ) )
{
Request pRequest = fi.getRequest( result );
displayWindow.GetImageDisplay().SetImage( pRequest );
displayWindow.GetImageDisplay().Update();
if( pLastRequest != null )
{
pLastRequest.unlock();
fi.imageRequestSingle();
}
pLastRequest = pRequest;
}
}
} // The display window will close automatically when reaching this line even when an exception is generated internally!
manuallyStopAcquisitionIfNeeded( pDev, fi );
pLastRequest.unlock();
fi.imageRequestReset( 0, 0 );

Deriving From mvIMPACT.acquire.RequestFactory And mvIMPACT.acquire.Request

Deriving from mvIMPACT.acquire.Request can be useful when a certain device driver or device offers a custom feature that is returned as part of the request object that can not be accessed using the mvIMPACT.acquire.Request class offered by this interface.

In C++ deriving from the class mvIMPACT.acquire.RequestFactory allows to customize the type of mvIMPACT.acquire.Request objects that will be created by the class mvIMPACT.acquire.FunctionInterface. The same feature is available in Java as well however compared to the C++ implementation there is one important difference. The following example code will explain how to work with the mvIMPACT.acquire.RequestFactory:

Deriving From mvIMPACT.acquire.Request

import mvIMPACT.acquire.*;
//-----------------------------------------------------------------------------
// Example for a derived request object. It doesn't introduce new functionality
// but rebinds an existing property. Custom properties could bound in a similar
// way.
class MyRequest extends Request
//-----------------------------------------------------------------------------
{
private PropertyI myRequestResult_ = new PropertyI();
private void init()
{
DeviceComponentLocator locator = new DeviceComponentLocator( getComponentLocator().hObj() );
locator.bindComponent( myRequestResult_, "Result" );
}
protected MyRequest( Device pDev, int requestNr )
{
super( pDev, requestNr );
init();
}
// This additional function is NOT needed in C++ but is important here! Without it the
// request objects later returned by the FunctionInterface will use the type of the base class!
//
// Maybe there is a better way to do this! Suggestions welcome!
public MyRequest( Request pRequest )
{
super( Request.getCPtr( pRequest ), false );
init();
}
public PropertyI getMyRequestResult()
{
return myRequestResult_;
}
}

Deriving From mvIMPACT.acquire.RequestFactory

import mvIMPACT.acquire.*;
//-----------------------------------------------------------------------------
class MyRequestFactory extends RequestFactory
//-----------------------------------------------------------------------------
{
@Override
public Request createRequest( Device pDev, int requestNr )
{
return new MyRequest( pDev, requestNr );
}
}

Using The Derived Classes

Now the request factory must be passed to the function interface:

//-----------------------------------------------------------------------------
void fn( Device pDev )
//-----------------------------------------------------------------------------
{
// ... some code ...
MyRequestFactory mrf = new MyRequestFactory();
FunctionInterface fi = new FunctionInterface( pDev, mrf );
// ... more additional code
// assuming we got back a request from the driver at this point:
// IMPORTANT: Please note the explicit constructor call for the 'MyRequest' instance!
// This is the only way this will work and also the reason for the need of the additional
// constructor mentioned above!
//
// Maybe there is a better way to do this! Suggestions welcome!
MyRequest pRequest = new MyRequest(fi.getRequest( getRequestNrFromSomewhere() ));
if( pRequest->isOK() )
{
System.out.println( pRequest.getMyRequestResult().name() + ": " + pRequest.getMyRequestResult().readS() );
}
// ... probably even more additional code
}