fglib5#
To use the library, the include file basler_fg.h
should be added to the source code.
#include <basler_fg.h>
Additionally, fglib5.lib
should be added to your Microsoft Visual Studio Project, or libfglib5.so
to your Linux project. If you use CMake, the package name is FgLib5. CMake will store the include directory in the variable ${FgLib5_INCLUDE_DIR}
and the libraries in ${FgLib5_LIBRARIES}
. See Prerequisites for more details on projects and how to use CMake.
Error Handling in the Frame Grabber Library#
const char * Fg_getErrorDescription(
Fg_Struct * fg,
int result);
int Fg_getLastErrorNumber(
Fg_Struct * fg);
Most functions of the API return an int
result code. If the function call was executed successfully, the return value will be FG_OK
. A negative value denotes an error condition in most cases. The error codes are defined in the header file basler_fg.h
.
The function Fg_getErrorDescription()
can be used to get a string representation of a specific result code. The first argument to this function, a handle to a frame grabber, isn't used and is only included for API backwards compatibility. You should always pass nullptr
.
The function Fg_getLastErrorNumber()
will always return the result code of the last function called in the same thread context. This is most useful in cases when a function doesn't return a result code itself. The function accepts a frame grabber handle as an argument, which can be nullptr
in case the last function called did not receive a frame grabber handle. To request the result code of a function that requires a frame grabber handle, the same handle has to be passed to Fg_getLastErrorNumber()
.
Info
Error codes are stored in thread-local storage. This means that by calling Fg_getLastErrorNumber()
from one thread the application can't request error codes which occurred in another thread.
The following code will print the last error which happened in a context not specific to a frame grabber handle, and a description:
int result = Fg_getLastErrorNumber(nullptr);
if (result != FG_OK) {
const char * description = Fg_getErrorDescription(nullptr, result);
std::cout << "Error " << result
<< ": " << description
<< std::endl;
}
The code examples in the rest of this part of the documentation will not include error handling as this is specific to the requirements of the application. As much as is feasible, return codes will be checked however for successful execution of the functions.
Frame Grabber Library Initialization#
int Fg_InitLibraries(
const char *);
void Fg_FreeLibraries();
Before using the library fglib5, it should be initialized by calling the function Fg_InitLibraries()
. The function accepts an argument which is used for internal purposes only and should be set to nullptr.
After the application is no longer using the library, the function Fg_FreeLibraries()
should be called to release any resources allocated by the previous initialization.
int result = Fg_InitLibraries(nullptr);
if (result != FG_OK) {
// handle error ...
}
// use frame grabber ...
Fg_FreeLibraries();
System Information#
Before initializing a frame grabber, information about the version of the Framegrabber API which is used, the number and type of frame grabbers installed in the computer and other information can be requested.
Framegrabber API Version#
const char * Fg_getSWVersion();
A string representation of the version of the Framegrabber API can be requested using Fg_getSWVersion()
.
const char * rtVersion = Fg_getSWVersion();
std::cout << "Runtime SDK version: " << rtVersion
<< std::endl;
General System Information#
int Fg_getIntSystemInformationGlobal(
Fg_Info_Selector information,
FgProperty propertyId,
int * value);
Info
The function Fg_getIntSystemInformationGlobal()
was added in version 5.9 of the Framegrabber API. See section System Information in Plain C for the old interface.
The following information can be requested through the function Fg_getIntSystemInformationGlobal()
using the property PROP_ID_VALUE
:
Information | Description | Type |
---|---|---|
INFO_NR_OF_BOARDS | The number of boards found in the computer | int32_t |
INFO_MAX_NR_OF_BOARDS | Maximum number of boards supported | int32_t |
INFO_SERVICE_ISRUNNING | 0: service isn't running; 1: service is running | int32_t |
For example, the number of frame grabbers installed in the computer can be requested as shown below:
int numBoards = 0;
int result = Fg_getIntSystemInformationGlobal(INFO_NR_OF_BOARDS, PROP_ID_VALUE, &numBoards);
if (result == FG_OK) {
std::cout << "Number of boards: " << numBoards
<< std::endl;
}
Board-Specific System Information#
int Fg_getIntSystemInformationForBoardIndex(
unsigned int board,
Fg_Info_Selector information,
FgProperty propertyId,
int * value);
int Fg_getInt64SystemInformationForBoardIndex(
unsigned int board,
Fg_Info_Selector information,
FgProperty propertyId,
int64_t * value);
int Fg_getStringSystemInformationForBoardIndex(
unsigned int board,
Fg_Info_Selector information,
FgProperty propertyId,
std::string & value,
const std::string & arg = "");
int Fg_getIntSystemInformationForFgHandle(
Fg_Struct * fg,
Fg_Info_Selector information,
FgProperty propertyId,
int * value);
int Fg_getInt64SystemInformationForFgHandle(
Fg_Struct * fg,
Fg_Info_Selector information,
FgProperty propertyId,
int64_t * value);
int Fg_getStringSystemInformationForFgHandle(
Fg_Struct * fg,
Fg_Info_Selector information,
FgProperty propertyId,
std::string & value,
const std::string & arg = "");
Info
The functions documented in this chapter were added in version 5.9 of the Framegrabber API. See section System Information in Plain C for the old interface.
For any given frame grabber, most other information can be requested through the functions Fg_getIntSystemInformationForBoardIndex()
, Fg_getInt64SystemInformationForBoardIndex()
and Fg_getStringSystemInformationForBoardIndex()
by using the board index or frame grabber handle and the property PROP_ID_VALUE
:
Information | Description | Type |
---|---|---|
INFO_TIMESTAMP_FREQUENCY | The timestamp frequency used for the image timestamps | int64_t |
INFO_BOARDNAME | Board name as shown e.g. in microDiagnostics | string |
INFO_BOARDTYPE | Board type as defined in sisoboards.h | int32_t |
INFO_BOARDSERIALNO | Board serial number | int32_t |
INFO_FIRMWAREVERSION | Firmware version of the board | string |
INFO_HARDWAREVERSION | Hardware version of the board | string |
INFO_CAMERA_INTERFACE | Camera interface provided by the board ('CameraLink', or 'CXP') | string |
INFO_DRIVERVERSION | Driver version used for the board | string |
INFO_DRIVERARCH | Driver architecture used for the board | string |
INFO_DRIVERFULLVERSION | Full driver version including architecture used for the board | string |
INFO_DRIVERGROUPAFFINITY | The driver IRQ group affinity (see Support for Non-Uniform Memory Access) | int32_t |
INFO_DRIVERAFFINITYMASK | The driver IRQ processor affinity mask (see Support for Non-Uniform Memory Access) | int64_t |
INFO_LICENSE_GROUP_CODE | Frame grabber license group code (must be a superset of the applet license group codea) | int32_t |
INFO_LICENSE_USER_CODE | Frame grabber license user code (must match the applet license user code) | int32_t |
INFO_IS_POCL | 0: the board doesn't support PoCL; 1: the board supports PoCL | int32_t |
INFO_NR_OF_CXP_PORTS | Number of ports on a board with 'CXP' interface | int32_t |
INFO_NR_OF_CL_PORTS | Number of ports on a board with 'CameraLink' interface | int32_t |
INFO_NR_OF_PORTS | Number of ports on a board with 'CameraLinkHS' interface | int32_t |
INFO_NR_OF_GIGE_PORTS | Number of ports on a board with 'GigE' interface | int32_t |
INFO_APPLET_DESIGN_ID | The HAP id of an applet (the applet path must be passed in the parameter arg) | string |
INFO_APPLET_BITSTREAM_ID | The bit stream id of an applet (the applet path must be passed in the parameter arg) | string |
INFO_STATUS_PCI_LINK_WIDTH | Number of PCIe lanes used by the frame grabber | int32_t |
INFO_STATUS_PCI_EXPECTED_LINK_WIDTH | Number of PCIe lanes supported by the frame grabber | int32_t |
INFO_STATUS_PCI_LINK_SPEED | PCIe generation used by the frame grabber | int32_t |
INFO_STATUS_PCI_EXPECTED_LINK_WIDTH | PCIe generation supported by the frame grabber | int32_t |
INFO_STATUS_PCI_PAYLOAD_SIZE | PCIe payload size used by the frame grabber | int32_t |
The list above isn't complete and contains only the properties for the information useful to the application. See the Framegrabber API r for more use cases of the API.
For example, the following code requests the board type and name.
const int boardIndex = 0;
int boardType = 0;
int result =
Fg_getIntSystemInformationForBoardIndex(boardIndex, INFO_BOARDTYPE,
PROP_ID_VALUE, &boardType);
std::string boardName;
if (result == FG_OK) {
result =
Fg_getStringSystemInformationForBoardIndex(boardIndex, INFO_BOARDNAME,
PROP_ID_VALUE, boardName);
}
if (result == FG_OK) {
std::cout << "Board #" << boardIndex
<< " is a " << boardName
<< " (type " << std::hex << boardType
<< std::dec << ")" << std::endl;
}
Once a frame grabber handle is available after initialization, the functions Fg_getIntSystemInformationForFgHandle()
, Fg_getInt64SystemInformationForFgHandle()
and Fg_getStringSystemInformationForFgHandle()
can be used to get information specific to the board according to the list above. (See chapter Frame Grabber Initialization.) The following information can be requested using the frame grabber handle and the property PROP_ID_VALUE
:
Information | Description | Type |
---|---|---|
INFO_APPLET_CAPABILITY_TAGS | A list of key-value-pairs describing the applet features | string |
INFO_OWN_BOARDINDEX | The board index of the frame grabber | int32_t |
INFO_FPGA_BITSTREAM_ID | The bit stream id for the applet active in the FPGA | string |
INFO_APPLET_FULL_PATH | The full applet path | string |
INFO_APPLET_FILE_NAME | The applet file name | string |
INFO_APPLET_TYPE | 0: HAP file; 1: DLL/SO file (see Applets) | int32_t |
The list above isn't complete and contains only the properties for the information useful to the application. See the Framegrabber API reference for more use cases of the API.
Using the Board Index#
A frame grabber is selected by using the board index. If you know the number of boards present in the system, in the default case, the first board is identified by the index 0, the second board by the index 1 and so on.
The Framegrabber API allows the user to assign a unique index to each board using a configuration file, which can be generated from within microDiagnostics. In that case, the user should know their assignments and according board indices.
Applets#
To work with the frame grabber, an applet is needed. An applet is a collection of several things, most generally:
- A design for the FPGA on the frame grabber which implements the image processing from receiving images from a camera device to sending the processed image into the computer memory
- A description of the design for the software which contains the VisualApplets operators used in the design and the information to map the parameters to FPGA registers
- A collection of software interface libraries which serve as generators for class instances for handling the translation of operator parameter values to and from FPGA register contents
- A top-level library, VAS, which handles instantiating all operator classes, initialization and interfacing with the applet
Applets come in the form of a HAP file if they were designed using VisualApplets, or as a wrapped applet library file if they come pre-installed by the Framegrabber SDK. HAP files are usually stored inside a board-specific subdirectory of the Hardware Applets
directory in the Framegrabber SDK installation, while wrapped applet library files are installed in a board-specific subdirectory of the dll
directory. These subdirectories are referred to as standard locations for the corresponding type of applet throughout this documentation.
Each applet provides a set of parameters to configure the provided features. Consult the documentation for the specific applet to be used for an in-depth description of the features and parameters provided by the applet.
Choosing the Right Applet#
Applets which can be installed with the Framegrabber SDK usually come as a wrapped library file. The naming convention for those files follows some rules:
- The file name usually starts with
Acq_
. (These applets are referred to as Advanced Acquisition Applet, the previous generation was referred to as Standard Applet and had a different naming convention.) - Next, the number of cameras supported by the applet is given in the form of
Single
,Dual
orQuad
. - Then, the camera interface supported is given. For example,
CXP12
indicates the CoaXPress camera interface with up to 12 Gbit/s as specified in the CoaXPress Standard Version 2.0. - On older frame grabber platforms, the maximum number of camera link connections is given in the form of
x1
,x2
orx4
. (This was dropped from the naming convention asx4
includes support forx2
andx1
,x2
supportsx1
and the maximum number of links supported usually should be clear from the number of cameras supported and the number of physical ports the frame grabber provides.) - Then, the sensor type supported is given. This is either
Area
orLine
. For applets supporting both area and line type sensors, this is dropped. - The last part of the name is the data format supported by the applet, like
Gray8
,Bayer16
,RGB24
. For applets supporting multiple data formats, this is dropped.
For example, the applet Acq_SingleCXP12Area
is an Advanced Acquisition Applet which supports one CXP camera with up to 12 Gbit/s and an area sensor. The applet supports multiple data formats. To understand the supported data formats and sensor sizes the applet documentation has to be consulted.
If the application for example requires two CXP cameras with 12 Gbit/s, a line type sensor with 10-bit gray scale output, look for applets called Acq_DualCXP12Line
, Acq_DualCXP12LineGray10
or Acq_DualCXP12LineGray16
and read the accompanying documentation. If the cameras connect using only a single physical link, the Quad
variants of the applets can be considered as well.
Start-up Behavior#
When the Framegrabber SDK starts to initialize the fglib5 library, access to the frame grabber is needed even before an applet is loaded as described in Frame Grabber Initialization. This is required, for example, to allow sorting boards according to their serial number or even to see all applets that are available for loading.
Which applet is used for this initialization depends on the frame grabber you use. You can use the information in the following sections to configure your frame grabber to start with a pre-defined applet. Using a different applet than the pre-defined one when calling Fg_Init
leads to a longer start-up time.
At start-up, the Framegrabber SDK by default loads the last applet which was used by Fg_Init
.
You can deactivate this behavior by setting the system environment variable FGSDK_LOAD_LAST_APPLET_ON_INIT=Off
.
Start-up Behavior of microEnable 5 marathon Frame Grabbers#
On a microEnable 5 marathon frame grabber, you must flash an applet to the board into one of the partitions available. The default behavior of the Framegrabber SDK is to set the applet used in Fg_Init
as the boot partition. At start-up, the applet in the boot partition is used to initialize the board at system power-up. This applet is then also used in the first initialization phase of the Framegrabber SDK.
Start-up Behavior of microEnable 6 Frame Grabbers#
On a CXP-12 Interface Card, imaWorx CXP-12 Quad, imaFlex CXP-12 Quad or imaFlex CXP-12 Penta frame grabber, the Framegrabber SDK tries to open the last applet that was loaded. The last applet that was loaded is defined in the LastApplet
key in the section FG
in the configuration file named me6_<n>_init.config, where <n> is the driver index of the board. The driver index is very similar to the board index described in section Using the Board Index, but is driver-specific and isn't affected by re-sorting boards. On Windows, driver index 0
refers to the first CXP-12 Interface Card, imaWorx CXP-12 or imaFlex CXP-12 frame grabber found by the driver, driver index 1
refers to the second board and so on. On all other operating systems, the driver index is identical to the board index without any re-sorting applied.
On Windows systems, the configuration file is usually located in the directory %APPDATA%\basler
. On all other operating systems, the configuration file is located in the $HOME/.config/basler
directory. If no configuration file is found, then the Framegrabber SDK looks in the root folder of the Framegrabber SDK installation.
If no configuration file was found for the frame grabber or if the initialization failed, the default applet is used. The default applet is usually the applet that supports one area sensor type camera per physical port.
If the initialization fails also for the default applet, the Framegrabber SDK opens any applet which can be used.
Enumerating Applets#
int Fg_getAppletIterator(
int board,
FgAppletIteratorSource source,
Fg_AppletIteratorType * iter,
int flags);
Fg_AppletIteratorItem Fg_getAppletIteratorItem(
Fg_AppletIteratorType iter,
int index);
int64_t Fg_getAppletIntProperty(
Fg_AppletIteratorItem item,
FgAppletIntProperty property);
const char * Fg_getAppletStringProperty(
Fg_AppletIteratorItem item,
FgAppletIntProperty property);
int Fg_freeAppletIterator(
Fg_AppletIteratorType iter);
Usually, an application will use one or maybe a few specific applets and the features will be known through the applet documentation and the following functions will not be useful to most applications. In cases where an application is designed more dynamically, the Framegrabber API provides functions through which the applets can be enumerated and various information about applets can be requested.
To enumerate Applets, the function Fg_getAppletIterator()
can be called. If you want to enumerate the applets which are available on the Framegrabber SDK installation, the source FG_AIS_FILESYSTEM
should be used. To enumerate only applets which can be loaded on the given board, pass FG_AF_IS_LOADABLE
for the flags. This ensures that any applets returned by the API can be used by a subsequent call to initialize the frame grabber. The function returns the number of items in the iterator.
The items of the applet iterator can be retrieved by calling Fg_getAppletIteratorItem()
.
The following information can be requested from an item by calling Fg_getAppletIntProperty()
or Fg_getAppletStringProperty()
:
Property | Description | Type |
---|---|---|
FG_AP_INT_FLAGS | Flags which apply to the applet (FG_AF_… constants) | int32_t |
FG_AP_INT_INFO | Tags which apply to the applet (FG_AI_… constants) | int32_t |
FG_AP_INT_PARTITION | Partition on which the applet is flashed (mE5 only) | int32_t |
FG_AP_INT_NR_OF_DMA | Maximum number of DMA channels provided by the applet | int32_t |
FG_AP_INT_NR_OF_CAMS | Maximum number of cameras accessible when using the applet | int32_t |
FG_AP_INT_GROUP_CODE | Applet license group code (must be a subset of the frame grabber license group codea) | int32_t |
FG_AP_INT_USER_CODE | Applet license user code (must match the frame grabber license user code) | int32_t |
FG_AP_INT_DESIGN_VERSION | Major version of the FPGA design | int32_t |
FG_AP_INT_DESIGN_REVISION | Version revision of the FPGA design | int32_t |
FG_AP_STRING_APPLET_UID | UID identifying the applet file | string |
FG_AP_STRING_BITSTREAM_UID | UID identifying the FPGA design | string |
FG_AP_STRING_DESIGN_NAME | Name of the FPGA design | string |
FG_AP_STRING_APPLET_NAME | Name of the applet | string |
FG_AP_STRING_DESCRIPTION | Description of the applet | string |
FG_AP_STRING_CATEGORY | Category of the applet | string |
FG_AP_STRING_APPLET_PATH | Full path of the applet | string |
FG_AP_STRING_SUPPORTED_PLATFORMS | Comma separated list of supported platforms | string |
FG_AP_STRING_TAGS | Comma separated list of tags | string |
FG_AP_STRING_VERSION | Version of the applet | string |
FG_AP_STRING_APPLET_FILE | File name of the applet | string |
FG_AP_STRING_RUNTIME_VERSION | Required Framegrabber SDK version | string |
The list above isn't complete and contains only the properties for the information useful to the application. See the Framegrabber API reference for more use cases of the API.
After using the applet iterator, it should be released by calling Fg_freeAppletIterator()
.
The applet name should be sufficient for a subsequent call to initialize the frame grabber, as long as the name is unique and the applets are located in one of the standard locations. If multiple applets use the same name, or an applet is located outside of one of the standard locations, then the full path should be used. The following example shows how to extract the applet name from all applets found in the standard locations:
const in boardIndex = 0;
Fg_AppletIteratorType iter = 0;
int numItems =
Fg_getAppletIterator(boardIndex, FG_AIS_FILESYSTEM,
&iter, FG_AF_IS_LOADABLE);
if (numItems >= 0) {
std::cout << "Found " << numItems << " applets";
for (int i = 0; i < numItems; ++i) {
auto item = Fg_getAppletIteratorItem(iter, i);
const char * appletName =
Fg_getAppletStringProperty(item, FG_AP_STRING_APPLET_NAME);
std::cout << " " << appletName;
}
std::cout << std::endl;
Fg_freeAppletIterator(iter);
}
Frame Grabber Initialization#
Fg_Struct * Fg_Init(
const char * applet,
unsigned int board);
int Fg_FreeGrabber(
Fg_Struct * fg);
To work with a frame grabber, it has to be initialized first. This is done by loading an applet using the function Fg_Init()
. The function returns a handle to the frame grabber for the given board index after initializing the frame grabber with the applet specified. The handle is used in most of the API functions described in the following chapters.
After the application is done using the frame grabber, it should be released by calling Fg_FreeGrabber()
.
For example, the following code initializes a CXP-12 frame grabber with the applet Acq_SingleCXP12Area
:
const int boardIndex = 0;
const char * applet = "Acq_SingleCXP12Area";
Fg_Struct * fg = Fg_Init(applet, boardIndex);
if (fg != nullptr) {
// use frame grabber ...
Fg_FreeGrabber(fg);
}
Configuration Files#
Fg_Struct * Fg_InitConfig(
const char * config,
unsigned int board);
int Fg_loadConfig(
Fg_Struct * fg,
const char * config);
int Fg_saveConfig(
Fg_Struct * fg,
const char * config);
If you use microDisplayX to initially configure a frame grabber and test your setup, the configuration can be saved from within the program. The main configuration file with have an extension .mcf
and a second file with an extension .mfs
will be created alongside. With these two files, instead of replicating the configuration through API calls, the application can directly initialize a frame grabber using these two files.
Configuration in this context refers to the applet with which a frame grabber was initialized, and the settings of the parameters of the applet. See chapter Working with Applet Parameters for more information.
Info
Only the main configuration file with the extension .mcf
is used in the parameters to the functions. The file with extension .mfs
will be used automatically as long as the file name up to the extension is the same.
To initialize a frame grabber from configuration files, the function Fg_InitConfig()
can be called instead of Fg_Init()
. The function returns a handle to the frame grabber for the given board index after initializing the frame grabber according to the main configuration file specified.
After the application is done using the frame grabber, it should be released by calling Fg_FreeGrabber()
.
If the application requires different sets of configurations, these can be applied to a frame grabber by calling Fg_loadConfig()
at any point after calling Fg_Init()
or Fg_InitConfig()
.
The current configuration can be written to configuration files by calling Fg_saveConfig()
at any point after calling Fg_Init()
or Fg_InitConfig()
.
The following example shows how to save and restore the configuration of a frame grabber:
const char * config = "SavedState.mcf";
int result = Fg_saveConfig(fg, config);
if (result != FG_OK) {
// handle error ...
}
// change frame grabber configuration ...
result = Fg_loadConfig(fg, config);
if (result != FG_OK) {
// handle error ...
}
Working with Applet Parameters#
Once the frame grabber is initialized using an applet, the parameters of the applet can be read or manipulated. For example, to acquire images from a camera, the applet has to be configured to use the correct image dimensions and image format according to the image data the camera sends. Another example is configuring the trigger module provided by the applet to match the application requirements.
Parameter Identifiers and Parameter Names#
int Fg_getParameterIdByName(
Fg_Struct * fg,
const char * name);
const char * Fg_getParameterNameById(
Fg_Struct * fg,
unsigned int id,
unsigned int dma);
FgParamTypes Fg_getParameterTypeById(
Fg_Struct * fg,
unsigned int id,
unsigned int dma);
Info
The function Fg_getParameterTypeById()
was added in version 5.9 of the Framegrabber SDK.
Each parameter of the applet is identified by its name, but the API uses a numerical identifier to access the parameter value or properties. Many parameters have fixed numerical identifiers as specified in the header file basler_fg.h
, however the use isn't recommended for all but the most common parameters. To get the numerical identifier for a parameter, the function Fg_getParameterIdByName()
is used. The function returns 0 or a negative error code if an error occurred translating the name into an identifier.
To get the name of a parameter for which the numerical identifier is known, the function Fg_getParameterNameById()
can be used.
To get the type of a parameter for which the numerical identifier is known, the function Fg_getParameterTypeById()
can be used.
Parameter Types#
Parameters are typed and to get or set the value of a parameter the type must be known, either from the documentation of the applet, implicitly by applets designed in-house using VisualApplets, or by requesting the type by using the function Fg_getParameterTypeById()
.
Parameter Type | C/C++ Type |
---|---|
FG_PARAM_TYPE_INT32_T | int32_t |
FG_PARAM_TYPE_UINT32_T | uint32_t |
FG_PARAM_TYPE_INT64_T | int64_t |
FG_PARAM_TYPE_UINT64_T | uint64_t |
FG_PARAM_TYPE_DOUBLE | double |
FG_PARAM_TYPE_CHAR_PTR | string |
FG_PARAM_TYPE_SIZE_T | size_t |
FG_PARAM_TYPE_STRUCT_FIELDPARAMINT | FieldParameterAccess |
FG_PARAM_TYPE_STRUCT_FIELDPARAMINT64 | FieldParameterAccess |
FG_PARAM_TYPE_STRUCT_FIELDPARAMDOUBLE | FieldParameterAccess |
Parameters with one of the field parameter types should be handled using the FG_PARAM_TYPE_STRUCT_FIELDPARAMACCESS
API described in subsection Accessing Field Parameters of chapter Considerations When Using Plain C.
Accessing Parameter Values#
int Fg_getParameterWithType(
Fg_Struct * fg,
int id,
int32_t * value,
unsigned int dma);
// Fg_getParameterWithType() is overloaded for:
// int32_t * value
// uint32_t * value
// int64_t * value
// uint64_t * value
// float * value
// double * value
// std::string & value
int Fg_setParameterWithType(
Fg_Struct * fg,
int id,
int32_t value,
unsigned int dma);
// Fg_setParameterWithType() is overloaded for:
// int32_t value
// uint32_t value
// int64_t value
// uint64_t value
// float value
// double value
// const std::string & value
To get or set the value of a parameter, the overloaded functions Fg_getParameterWithType()
and Fg_setParameterWithType()
should be used.
For example, given a frame grabber handle fg
from a previous call to Fg_Init()
, the following code sets the width of the first DMA channel of the applet to 1024.
const int dma = 0;
const int width = 1024;
int result = FG_INVALID_PARAMETER;
int paramId = Fg_getParameterIdByName(fg, "FG_WIDTH");
if (paramId > 0) {
result = Fg_setParameterWithType(fg, paramId, width, dma);
}
The last parameter which is passed to the functions Fg_getParameterWithType()
and Fg_setParameterWithType()
in most cases refers to a DMA channel. Parameters like FG_WIDTH
, FG_HEIGHT
can be different for each DMA channel and each instance of these parameters can be requested or changed by passing the number of the DMA channel to the functions.
In some cases, the last parameter can refer to other logical indices. For example, the parameters FG_NR_OF_DMAS
or FG_NR_OF_CAMS
exist in multiple instances for each process of the applet, which is a scope defined in VisualApplets.
Other parameters are global in the context of an applet and the last parameter in the functions is ignored. One such parameter would be FG_NR_OF_PROCESSES
. But even more prominently, if the application developer designs their own applets using VisualApplets, all parameters which control the applet can be considered global as they are identified by unique names, and the last parameter in the functions is ignored.
Some parameters can be different for every image acquired and require both the DMA channel and the frame number or buffer number. This applies to any image meta data, like the time stamp when an image has been transferred to the computer memory, the length of the actual image data, and similar information. These parameters can't be handled by the functions mentioned above, and this topic will be discussed in more detail in chapter Image Acquisition.
Accessing Parameter Properties#
int Fg_getParameterPropertyWithType(
Fg_Struct * fg,
int id,
FgProperty propertyId,
int32_t * value);
// Fg_getParameterPropertyWithType() is overloaded for:
// int32_t * value
// uint32_t * value
// int64_t * value
// uint64_t * value
// float * value
// double * value
// std::string & value
int Fg_getParameterPropertyWithTypeEx(
Fg_Struct * fg,
int id,
FgProperty propertyId,
int32_t * value,
unsigned int dma);
// Fg_getParameterPropertyWithTypeEx() is overloaded for:
// int32_t * value
// uint32_t * value
// int64_t * value
// uint64_t * value
// float * value
// double * value
// std::string & value
Info
The functions documented in this chapter were added in version 5.9 of the Framegrabber SDK. See section Accessing Parameter Properties in Plain C for the old interface.
Parameters have various properties besides the current value. Using the overloaded functions Fg_getParameterPropertyWithType()
isn't recommended, as the function calls implicitly use DMA channel 0, parameter properties might differ though for different channels. Using the overloaded functions Fg_getParameterPropertyWithTypeEx()
the following properties can be requested:
Property | Description | Type |
---|---|---|
PROP_ID_VALUE | Current parameter value | same as parameter |
PROP_ID_DATATYPE | Parameter type (enum value according to FgParamTypes) | int32_t |
PROP_ID_NAME | Descriptive name | string |
PROP_ID_PARAMETERNAME | Parameter name | string |
PROP_ID_VALUELLEN | Length required to encode the value to a string | int32_t |
PROP_ID_ACCESS | Access flags of the parameter | int32_t |
PROP_ID_MIN | Minimum parameter valueb | same as parameter |
PROP_ID_MAX | Maximum parameter valueb | same as parameter |
PROP_ID_STEP | Step size of the parameterb | same as parameter |
PROP_ID_IS_ENUM | 0: not an enumeration parameter n: size of buffer needed for PROP_ID_ENUM_VALUES | int32_t |
PROP_ID_ENUM_VALUES | Enumeration values (see Accessing the Enum Values Parameter Property) | FgPropertyEnumValues[] |
PROP_ID_FIELD_SIZE | Number of elements in a field parameter | int32_t |
The list above isn't complete and contains only the properties for the information which isn't considered deprecated. See the Framegrabber API reference for more use cases of the API.
The following example shows how to get the minimum value of a parameter, assuming paramId is a parameter of type FG_PARAM_TYPE_INT32_T
:
int minVal = 0;
int result =
Fg_getParameterPropertyWithTypeEx(fg, paramId, PROP_ID_MIN,
&minVal, dma);
if (result == FG_OK) {
// work with the property ...
}
Memory Management#
To acquire images from a camera, memory is needed in the computer to transfer those images to and to access the image data from within a program. The Framegrabber API uses a circular buffer memory model. A memory buffer consists of at least two items, often referred to as frame buffers or sub-buffers, which will be re-used for subsequent frames acquired from the camera. How many frame buffers should be used depends on various factors, the most important one being how many further frames can arrive while a frame is still being processed. This will be discussed in more detail in section Acquisition Models of chapter Image Acquisition.
Each frame buffer must be of the same size, and must be large enough to store the largest possible image for the application requirements, usually defined by the width FG_WIDTH
, height FG_HEIGHT
and pixel format FG_FORMAT
. (For an applet designed in VisualApplets, the width, height and pixel format will usually be more complex to setup.)
The circular buffer can be one large block of memory, contiguous in virtual address space and subdivided into equally sized frame buffers. Or it can consist of frame buffers which were individually allocated in memory and added to the management structure dma_mem
used by the API.
Basic Memory Management (Not Recommended)#
void * Fg_AllocMem(
Fg_Struct * fg,
size_t totalSize,
frameindex_t numFrames,
unsigned int dma);
int Fg_FreeMem(
Fg_Struct * fg,
unsigned int dma);
There are three different sets of functions which can be used for memory management. However, using the functions Fg_AllocMem()
and Fg_FreeMem()
isn't recommended as it is limited to the use of the standard acquisition model (ACQ_STANDARD). The remaining two sets of functions will be discussed here in more detail as they can be used with all three acquisition models (ACQ_STANDARD, ACQ_BLOCK and ACQ_SELECT) and can be applied both to simple use cases as well as to more specific requirements of the application.
Advanced Memory Management#
dma_mem * Fg_AllocMemEx(
Fg_Struct * fg,
size_t totalSize,
frameindex_t numFrames);
int Fg_FreeMemEx(
Fg_Struct * fg,
dma_mem * mem);
The function Fg_AllocMemEx()
can be used to allocate one large contiguous memory buffer of size totalSize
which is subdivided into numFrames
equally sized frame buffers. The function returns a handle to the memory management structure, or nullptr in case of an error.
The pointer returned isn't a pointer to the memory buffer itself and should not be used directly! After the memory is no longer needed it should be released using Fg_FreeMemEx()
.
In the following example, the code will allocate a memory buffer for 16 frame buffers which can hold 24-bit RGB images of a size of 1024 x 1024:
const int width = 1024, height = 1024;
const int bytesPerPixel = 3;
const frameindex_t numFrames = 16;
const size_t frameSize = static_cast<size_t>(width) * height * bytesPerPixel;
const size_t totalSize = frameSize * numFrames;
dma_mem * mem = Fg_AllocMemEx(fg, totalSize, numFrames);
if (mem != nullptr) {
// use memory, acquire and process images ...
Fg_FreeMem(fg, mem);
}
The static_cast<size_t>(width)
in the calculation of the frameSize
. This is necessary to make sure that the size of very large images will be calculated correctly.
Flexible Memory Management#
dma_mem * Fg_AllocMemHead(
Fg_Struct * fg,
size_t totalSize,
frameindex_t numFrames);
int Fg_AddMem(
Fg_Struct * fg,
void * frameBuffer,
size_t size,
frameindex_t index,
dma_mem * mem);
int Fg_DelMem(
Fg_Struct * fg,
dma_mem * mem,
frameindex_t index);
int Fg_FreeMemHead(
Fg_Struct * fg,
dma_mem * mem);
If the application has specific requirements for memory allocation instead of letting the Framegrabber API handle it, the function Fg_AllocMemHead()
can be used to prepare the memory management structure. The function expects the same parameters as Fg_AllocMemEx()
, but it will not allocate any memory. After allocating memory in the application, each frame buffer needs to be added individually using the function Fg_AddMem()
. If the application requires dynamic changes of the frame buffers used for a memory buffer during acquisition runs, the function Fg_DelMem()
can be used to remove frame buffers from the memory buffer. After the memory buffer is no longer needed, the management structure should be released first by calling Fg_FreeMemHead()
before releasing the memory for the frame buffers.
The following code shows how the function 'Fg_AllocMemEx()' is a convenience wrapper for the more flexible memory management API:
int Fg_AllocMemEx(Fg_Struct * fg, size_t totalSize, frameindex_t numFrames)
{
const size_t frameSize = totalSize / numFrames;
char * buf = nullptr;
dma_mem * mem = Fg_AllocMemHead(fg, totalSize, numFrames);
if (mem != nullptr) {
try {
buf = new char[totalSize];
for (frameindex_t frame = 0; frame < numFrames; ++frame) {
const int result =
Fg_AddMem(fg, buf + frameSize*frame,
frameSize, frame, mem);
if (result != FG_OK) {
throw std::runtime_error("Failed to add frame buffer");
}
}
}
catch (...) {
for (frameindex_t frame = 0; frame < numFrames; ++frame) {
Fg_DelMem(fg, mem, frame);
}
Fg_FreeMemHead(fg, mem);
delete[] buf;
return nullptr;
}
}
return mem;
}
Image Acquisition#
The Framegrabber API provides two modes of image data delivery which can be combined with three different acquisition models.
Image data can be delivered in synchronous or asynchronous mode. In the synchronous mode, an acquisition loop has to be provided by the application. In the asynchronous mode, a callback function can be registered which will be called whenever new image data is available. The Framegrabber API will provide the image acquisition loop in a separate thread for asynchronous mode. For some GUI frameworks, synchronizing the Framegrabber API thread context back into the GUI thread context can be complicated, and running an acquisition loop in a thread context provided by the framework might prove more sensible.
Registering a Callback Function for Asynchronous Mode#
typedef int (* Fg_ApcFunc_t)(
frameindex_t frame,
void * data);
int Fg_registerApcHandlerEx(
Fg_Struct * fg,
unsigned int dma,
Fg_ApcFunc_t func,
void * data,
unsigned int timeout,
unsigned int flags);
int Fg_unregisterApcHandler(
Fg_Struct * fg,
unsigned int dma);
Info
In version 5.9 of the Framegrabber API the functions Fg_registerApcHandlerEx()
and Fg_unregisterApcHandler()
were added and the behavior when using the blocking acquisition model ACQ_BLOCK
was changed. See section Registering a Callback Function for Asynchronous Mode in Plain C for the old interface.
The function Fg_registerApcHandlerEx()
can be used to setup the asynchronous mode before starting the acquisition.
Using Fg_registerApcHandlerEx()
, a function of type Fg_ApcFunc_t
can be registered to be called when image data is received. The function will be registered for a given frame grabber and DMA channel and will be passed two parameters when called. The first parameter to the callback function is a frame number (in ACQ_STANDARD
and ACQ_SELECT
) or buffer number (in ACQ_BLOCK
) for the image received. The second parameter is a pointer which was supplied with the call to Fg_registerApcHandlerEx()
and which can be used as a pointer to a context structure or class, for example the this
pointer of a class instance which implements image processing.
Only one callback function can be registered for each DMA channel on any given frame grabber.
The callback function will be called from the acquisition loop provided by the Framegrabber API. This implies that callback function is called in the thread context of the acquisition loop. It also implies that the time the callback function takes until it returns is added to the general management overhead of the acquisition loop, and that during this time multiple images might have been received. If any images were received during that time, the callback function will be called again immediately after it returns.
The handling of the image acquisition can be controlled through the timeout
and flags
parameters in the call to Fg_registerApcHandlerEx()
. The timeout is given in seconds which the acquisition loop should wait for new images to arrive. For the parameter flags
, the following values and combinations thereof combined with the operator |
(binary OR) can be used:
Flag | Description |
---|---|
FG_APC_DEFAULTS | default handling in the acquisition loop |
FG_APC_BATCH_FRAMES | only the newest image may be delivered in a single call to the callback function if multiple images were received |
FG_APC_DELIVER_ERRORS | deliver errors to callback function as negative frameindex_t values |
FG_APC_IGNORE_TIMEOUTS | ignore image timeouts and continue processing |
FG_APC_IGNORE_APCFUNC_RETURN | ignore the callback return value and continue processing |
FG_APC_IGNORE_STOP | ignore when the acquisition is stopped and continue processing |
FG_APC_HIGH_PRIORITY | increase the priority of the thread implementing the acquisition loop |
FG_APC_OLD_ACQ_BLOCK_BEHAVIOR | use Fg_getLastPicNumberBlockingEx() in ACQ_BLOCKc |
The default handling for the acquisition loop is:
- Every single image is delivered to the callback function
- Error values aren't delivered to the callback function
- An image timeout ends the acquisition loop
- A non-zero return value from the callback function ends the acquisition loop
- Stopping the acquisition ends the acquisition loop
- The acquisition loop runs in a thread with the default priority
- The function
Fg_getLastPicNumberBlockingEx()
is called, and the result is delivered to the callback function
Info
Ending the acquisition loop will unregister the callback function automatically. While it may seem logical to always end the acquisition loop when the acquisition is stopped, this isn't necessarily the case. Consider for example, if the application requires to run the acquisition in bursts of a certain number of images by calling Fg_AcquireEx with a finite number of frames to acquire. After the number of frames is acquired, the acquisition is automatically stopped by the driver. It might be convenient to keep the acquisition loop running and start acquisition bursts from a separate thread.
The following example shows how to register a callback function using a simple structure which holds information necessary for handling image acquisition:
struct ApcUserCallbackData
{
Fg_Struct * fg;
dma_mem * mem;
unsigned int dma;
unsigned int timeoutInSeconds;
int mode;
};
int ApcUserCallback(frameindex_t frame, void * data)
{
auto context = reinterpret_cast<ApcUserCallbackData *>(data);
if (frame > 0) {
// process new image ...
} else {
// handle error ...
}
return 0;
}
void SetupApcUserCallback(ApcUserCallbackData * context)
{
// register callback function
int result =
Fg_registerApcHandlerEx(
context->fg,
context->dma,
&ApcUserCallback,
context,
context->timeoutInSeconds,
FG_APC_DELIVER_ERRORS | FG_APC_IGNORE_TIMEOUTS);
if (result != FG_OK) {
throw std::runtime_error("Failed to register callback function");
}
}
The allocation and management of the context structure isn't shown in the example. The pointer has to be valid while the callback function remains registered. One solution can be to keep everything together in a C++ class. To use a callback function in a C++ class context, a static function can be used to register the callback handler, and the this
pointer should be used as context data pointer which can be casted back to a class pointer and used accordingly.
After the callback function is no longer needed, it can be unregistered by calling Fg_unregisterApcHandler()
using the same frame grabber handle and DMA channel:
Fg_unregisterApcHandler(context->fg, context->dma);
Starting the Acquisition#
To enable transferring image data from the frame grabber to the application memory, the acquisition in the applet has to be started. This will inform the frame grabber about the frame buffers into which image data should be transferred and enable sending interrupts to the driver whenever the data for a new image has been successfully transferred. In most cases after starting the acquisition on the side of the frame grabber, the application will need to start the acquisition on the side of the camera and start triggering the camera.
See The Camera Control Library siso_genicam or The CameraLink Serial Interface Library clsersis for further information about controlling a camera. While triggering the camera is beyond the scope of this documentation, the documentation for the applet will give more information about the options the application has for triggering the camera through software or hardware signals to the frame grabber.
Image Acquisition Using Basic Memory Management (Not Recommended)#
int Fg_Acquire(
Fg_Struct * fg,
unsigned int dma,
frameindex_t frames);
int Fg_stopAcquire(
Fg_Struct * fg,
unsigned int dma);
The functions Fg_Acquire()
and Fg_stopAcquire()
can be used only in combination with the memory management functions Fg_AllocMem()
and Fg_FreeMem()
and are limited to using the standard acquisition model ACQ_STANDARD
. Using these functions isn't recommended and will not be documented. Instead the functions Fg_AcquireEx()
and Fg_stopAcquireEx()
should be used.
Image Acquisition Using Advanced or Flexible Memory Management#
int Fg_AcquireEx(
Fg_Struct * fg,
unsigned int dma,
frameindex_t frames,
int flags,
dma_mem * mem);
int Fg_stopAcquireEx(
Fg_Struct * fg,
unsigned int dma,
dma_mem * mem,
int flags);
The function Fg_AcquireEx()
can be called to start the image acquisition on a single DMA channel on a frame grabber. This call will tie a DMA channel dma
to a memory handle mem
and thus will use the memory allocated for receiving image data.
The acquisition will run until the number of frames given through the parameter frames
is reached. If GRAB_INFINITE
is passed to frames
, the acquisition will run indefinitely. Through the parameter flags
, the acquisition model can be selected. See section Acquisition Models for more information.
Model | Description |
---|---|
ACQ_STANDARD | continuous acquisition with no frame buffer protection |
ACQ_BLOCK | continuous acquisition with frame buffer blocking |
ACQ_SELECT | fully application controlled acquisition with manual frame buffer handling |
When specifying a number of frames to acquire in the call to Fg_AcquireEx()
, the acquisition will be stopped automatically when the Framegrabber SDK encounters the expected number of frames. In some cases this can lead to unexpected behavior when processing the last frame. To avoid this, in addition to the acquisition model, ACQ_NO_AUTOSTOP
can be specified in the parameter flags
. When ACQ_NO_AUTOSTOP
is used, the driver will stop acquiring frames when the requested number of frames is reached, but the rest of the Framegrabber SDK and the frame grabber remain in acquisition mode until Fg_stopAcquireEx()
is called.
To stop the acquisition and to reset the running state of the applet used, the function Fg_stopAcquireEx()
should be called. Through the parameter flags
, the stop mode can be selected:
Mode | Description |
---|---|
STOP_ASYNC | stop the acquisition immediately |
STOP_SYNC_TO_APC | synchronize stopping the acquisition in asynchronous mode to the callback function the time to wait for the callback to finish in milliseconds can be set using the parameter FG_APC_STOP_TIMEOUT |
STOP_SYNC | synchronize stopping the acquisition with the driver the time to wait for the next image to finish transferring in seconds can be set using the parameter FG_STOP_TIMEOUT |
STOP_ASYNC_FALLBACK | can be used together with STOP_SYNC if the stop could not be synchronized, fall back to STOP_ASYNC |
Writing an Acquisition Loop for Synchronous Mode#
In synchronous mode, the application has to handle receiving new images. This is usually done in the form of an acquisition loop which calls one of the functions provided by the Framegrabber API which wait for new images to be transferred on a specific DMA channel. To understand which function to use, see section Acquisition Models for more information. For each model, an example acquisition loop is given in the corresponding subsection.
Waiting for Images Using Basic Memory Management (Not Recommended)#
frameindex_t Fg_getLastPicNumberBlocking(
Fg_Struct * fg,
frameindex_t frame,
unsigned int dma,
int timeout);
The function Fg_getLastPicNumberBlocking()
can be used only in combination with the function Fg_Acquire()
and is limited to the standard acquisition model ACQ_STANDARD
. Using this function isn't recommended and will not be documented. Instead the functions Fg_getLastPicNumberBlockingEx()
or Fg_getImageEx()
should be used.
Waiting for Images Using Advanced or Flexible Memory Management#
frameindex_t Fg_getLastPicNumberBlockingEx(
Fg_Struct * fg,
frameindex_t frame,
unsigned int dma,
int timeout,
dma_mem * mem);
frameindex_t Fg_getImageEx(
Fg_Struct * fg,
int strategy,
frameindex_t frame,
unsigned int dma,
unsigned int timeout,
dma_mem * mem);
The function Fg_getLastPicNumberBlockingEx()
waits for the frame requested in the parameter frame
to arrive within the number of seconds given in the parameter timeout
on a given DMA channel as specified in the parameter dma
. The first frame is numbered 1, not 0. The function returns a frame number larger than 0 in case of success, or a negative error code in case of a failure.
Info
The frame number returned can be larger than the frame number which was requested, as the function will always return the newest frame number available. This means that between two calls to the function, more than one frame might have arrived and the application needs to decide if only the newest frame or all frames should be processed.
The following example shows a simple acquisition loop for ACQ_STANDARD
:
const int timeoutInSeconds = 10;
frameindex_t nextFrame = 1;
while (true) {
// get new image
const frameindex_t newestFrame =
Fg_getLastPicNumberBlockingEx(fg, nextFrame, dma,
timeoutInSeconds, mem);
if (newestFrame > 0) {
// process new images ...
nextFrame = (newestFrame < FRAMEINDEX_MAX) ? newestFrame + 1 : 1;
} else {
// handle error ...
}
}
The function Fg_getImageEx()
is much more complex. The behavior of the function and the meaning of the return code in case of success depend on the parameters strategy
and timeout
and on the acquisition model in use. In case of an error, the result will always be a negative error code. A short description of the different strategies will be given below, but generally the use of the function should be limited to the use cases described in section The Blocking Acquisition Model. Any other use of the function is discouraged and will not be discussed in further detail in the context of this document.
SEL_NEW_IMAGE
: The function requires a timeout to be specified and waits for at least one new image to arrive. In ACQ_STANDARD
and ACQ_SELECT
the frame number of the newest image is returned. In ACQ_BLOCK
, the function blocks the frame buffer for the newest image, unblocks any frame buffers of additional images, and returns the buffer number of the blocked image.
SEL_NEXT_IMAGE
: In ACQ_STANDARD
, if no timeout is specified, the function will return the buffer number of the newest image received. If a timeout is specified, the function will wait for the next frame after the last one which was received in a previous call to the function, or frame number 1 if the function was not called previously. The function will return the frame number of the newest image received. In ACQ_BLOCK
, the function blocks the frame buffer of the first image received which has not been blocked or unblocked yet and returns the buffer number of the blocked image. If no more images can be blocked and a timeout was specified, the function waits for at least one new image to arrive before performing the blocking operation.
SEL_ACT_IMAGE
: In ACQ_STANDARD
, the function behaves the same was as when SEL_NEXT_IMAGE
is specified. In ACQ_BLOCK
, the function blocks the frame buffer for the newest image, unblocks any frame buffers of additional images, and returns the buffer number of the blocked image. If no more images can be blocked and a timeout was specified, the function waits for at least one new image to arrive before performing the blocking operation.
SEL_LAST_IMAGE
: The function returns the last value returned by the function, or by Fg_getLastPicNumberBlockingEx()
. This is either the last frame number, or buffer number, depending on how the function was called last time.
SEL_NUMBER
: The function mimics the behavior of Fg_getLastPicNumberBlockingEx()
.
The following example shows a simple acquisition loop for ACQ_BLOCK
using SEL_NEXT_IMAGE
(every image acquired is processed):
const int timeoutInSeconds = 10;
while (true) {
// get new image
const frameindex_t buffer =
Fg_getImageEx(fg, SEL_NEXT_IMAGE, 0, dma, timeoutInSeconds, mem);
if (buffer > 0) {
// process new image ...
} else {
// handle error ...
}
}
The Driver Image Acquisition Timeout FG_TIMEOUT#
In addition to specifying a time to wait for images when calling the functions Fg_getImageEx()
or Fg_getLastPicNumberBlockingEx()
, or when setting up a callback function for the asynchronous mode, the driver also keeps track of the image acquisition in the context of handling image transfer interrupts. The driver will stop the image acquisition if no image data is received during the number of seconds specified in the parameter FG_TIMEOUT
. If FG_TIMEOUT
is set to FG_TIMEOUT_INFINITE
(the value of FG_TIMEOUT_INFINITE
is INT_MAX
- 1) the driver will not stop the acquisition regardless of the time span between receiving two images.
The value of the parameter FG_TIMEOUT
is forwarded to the driver when the acquisition is started. Any change to the parameter after the start of the acquisition will not be taken into account by the driver until the acquisition is stopped and started again.
After the driver has stopped the acquisition, any function call still waiting for images will return FG_TIMEOUT_ERR
and any function called to wait for images after the acquisition was stopped will return FG_TRANSFER_NOT_ACTIVE
.
Info
The default value for the parameter FG_TIMEOUT
is not FG_TIMEOUT_INFINITE
, but in most cases is set to 1000000 seconds (this equals about 277 hours and 46 minutes). It is recommended to explicitly set the parameter FG_TIMEOUT
to FG_TIMEOUT_INFINITE
before starting the acquisition.
The following example shows how to disable the driver image acquisition timeout:
Fg_setParameterWithType(fg, FG_TIMEOUT, FG_TIMEOUT_INFINITE, dma);
Frame and Acquisition Information#
int Fg_getParameterEx(
Fg_Struct * fg,
int param,
void * value,
unsigned int dma,
dma_mem * mem,
frameindex_t frame);
void * Fg_getImagePtrEx(
Fg_Struct * fg,
frameindex_t frame,
unsigned int dma,
dma_mem * mem);
frameindex_t Fg_getStatusEx(
Fg_Struct * fg,
int status,
frameindex_t frame,
unsigned int dma,
dma_mem * mem);
For each frame received, the following information can be requested by calling the function Fg_getParameterEx()
:
Parameter | Description | Type |
---|---|---|
FG_TRANSFER_LEN | The actual number of bytes transferred | size_t |
FG_TIMESTAMP_LONG | High resolution timestamp of the image | uint64_t |
FG_TIMESTAMP_LONG_FREQUENCY | High resolution timestamp frequency | uint64_t |
FG_TIMESTAMP | Timestamp of the image in milliseconds | uint32_t |
FG_IMAGE_TAG | Image tag | uint32_t |
FG_IMAGE_NUMBER | Frame number | uint64_t |
Info
The frame number information was added in version 5.9 of the Framegrabber SDK.
The information for each frame isn't available indefinitely, but is only valid as long as the corresponding buffer has not been queued for subsequent transfers. Depending on the acquisition model used, the function requires a frame number or a buffer number. See section Acquisition Models for more details.
The following example shows how to request the timestamp for a given frame:
// get time stamp frequency
uint64_t frequency = 0;
Fg_getParameterEx(fg, FG_TIMESTAMP_LONG_FREQUENCY, &frequency, 0, nullptr, 0);
// ...
// get frame time stamp
uint64_t timestamp = 0
int result =
Fg_getParameterEx(fg, FG_TIMESTAMP_LONG, ×tamp, dma, mem, frame);
if (result == FG_OK) {
// this will probably be 'seconds since booting the computer' ...
// it makes more sense when you calculate the difference
// between timestamps of two frames
double seconds =
static_cast<double>(timestamp)/static_cast<double>(frequency);
// ...
}
To get a pointer to a frame buffer, the function Fg_getImagePtrEx()
can be called. The pointer for each frame isn't available indefinitely, but is only valid as long as the corresponding buffer has not been queued for subsequent transfers. Depending on the acquisition model used, the function requires a frame number or a buffer number. See section Acquisition Models for more details.
The following example shows how to request the frame buffer pointer for a given frame:
// get a pointer to the frame buffer for the newest image
void * buffer = Fg_getImagePtrEx(fg, frame, dma, mem);
// get the actual number of bytes transferred
size_t length = 0;
int result =
Fg_getParameterEx(fg, FG_TRANSFER_LEN, &length, dma, mem, frame);
if ((buffer != nullptr) && (result == FG_OK) && (length > 0)) {
// process image data ...
}
The function Fg_getStatusEx()
can be called to request the following general status information, the parameter frame
will be ignored for these:
Status | Description |
---|---|
NUMBER_OF_GRABBED_IMAGES | the total number of frames transferred |
NUMBER_OF_LAST_IMAGE | the last frame number reported by Fg_getLastPicNumberBlockingEx() |
NUMBER_OF_NEXT_IMAGE | the frame number of the next frame after the last one reported |
GRAB_ACTIVE | 0: the DMA channel isn't active 1: the DMA channel is active |
The following example shows how to request the acquisition status for a DMA channel:
frameindex_t active =
Fg_getStatusEx(fg, GRAB_ACTIVE, 0, dma, mem);
if (active == 1) {
// the DMA channel is active ...
} else if (active == 0) {
// the DMA channel is inactive ...
} else {
// handle error ...
}
Acquisition Models#
The Framegabber API provides three different acquisition models, which can be selected when calling Fg_AcquireEx()
: standard, blocking and selective. All three acquisition models are based on the memory model explained in chapter Memory Management which uses at least two frame buffers for acquiring images.
For the examples in this section, a memory buffer which consists of four frame buffers contiguous in virtual memory will be used.
Frame Numbers and Buffer Numbers#
Throughout the Framegrabber API, frame numbers and buffer numbers are used, both of which are of type frameindex_t
.
While frame numbers are strictly monotonic increasing natural numbers in the range of [1; FRAMEINDEX_MAX]
, referring to the number of images acquired since the start of the acquisition, the type frameindex_t
used for frame numbers is a signed type and is used to return negative error codes as well as frame numbers every so often in the API. Especially in 32-bit applications, care must be taken to handle the overflow of the frame number correctly: once FRAMEINDEX_MAX
has been reached, image counting will wrap to 1! (In 64-bit applications even with extremely high frame rates, overflow of the image counter will happen only after hundreds of thousands of years.)
The buffer number in contrast refers to the frame buffer in which image data resides, and for N buffers is in the range of [1; N]
. In the examples used here, the buffer number is between 1 … 4 and refers to FB0
… FB3
accordingly.
The Standard Acquisition Model (ACQ_STANDARD)#
frameindex_t Fg_getLastPicNumberBlockingEx(
Fg_Struct * fg,
frameindex_t frame,
unsigned int dma,
int timeout,
dma_mem * mem);
int Fg_getParameterEx(
Fg_Struct * fg,
int param,
void * value,
unsigned int dma,
dma_mem * mem,
frameindex_t frame);
void * Fg_getImagePtrEx(
Fg_Struct * fg,
frameindex_t frame,
unsigned int dma,
dma_mem * mem);
The standard acquisition model is selected by passing ACQ_STANDARD
to flags
in the call to Fg_AcquireEx()
. All frame buffers are always accessible both by the driver for acquiring image data and by the application to process image data.
When acquisition is started, the driver queues at least one frame buffer in the frame grabber for image data transfer, starting with the first frame buffer FB0
. Once an image has been transferred completely to the computer memory, the software is informed of the completed transfer, and the driver will queue one more frame buffer. The frame grabber will continue data transfer for the next image into the next buffer in the queue, in our example FB1
.
(The number of frame buffers queued depends on various factors, like the frame grabber used, the driver version, and settings in the Windows registry or parameters to the Linux driver. However, in standard acquisition mode, queueing will always be in a linear manner and will start with FB0
again after the last frame buffer has been queued. While the number of buffers queued can have an effect on the maximum frame rate which can be achieved in a computer, this effect will only be measurable above ~10.000 frames per second.)
Because the driver uses frame buffers in a consistent circular manner, frame numbers can be naively translated to buffer numbers using the relation bufferNumber = 1 + ((frameNumber - 1) % numberOfBuffers)
. The function Fg_getImagePtrEx()
can be used to get a pointer to the frame buffer for either a frame number, or a buffer number.
In the standard acquisition model the application is fully responsible for ensuring that image acquisition will not "overrun" image processing. This can only be ensured if triggering the image source is controlled from the application in combination with the image processing, or in which the number of buffers are carefully tuned to the frame rate and the performance of the computer system used, or in which images can be skipped.
In a scenario, where the application triggers only a single image while a previous image is still being processed, two buffers are sufficient, one for the image data being processed and one for transferring image data to. (If the application would make sure that triggers are only generated after an image has been completely processed, actually one buffer would be sufficient for processing. This is however not supported by the Framegrabber API, and the application has to allocate two buffers at least in all cases.)
In a scenario, where images are generated continuously without control from the application side, both the image source and the buffer handling in the driver can be considered free running. In the following example, FB0
and FB1
contain valid image data. FB0
is being processed by the application, FB1
is pending processing. The driver has queued at least FB2
and image data is currently being transferred. This situation is still safe, because FB3
isn't in use yet, even though it might be queued already.
Calling the function Fg_getLastPicNumberBlockingEx()
for the next frame number after processing FB0
is finished would return a single new frame (the frame number for the image transferred to FB1
) in this case if the transfer to FB2
has not finished in the meantime.
If image processing is slower than image acquisition, the application might have moved on to process FB1
. However, in the following example in the meantime two more images were transferred, FB2
and FB3
, and the driver has started again with FB0
. When the image transfer completes, the driver will use FB1
, and thus the frame grabber might overwrite data which is currently being processed. This scenario is only safe, if the application can make sure that no image data is being generated by the image source while FB1
is still being processed.
Calling the function Fg_getLastPicNumberBlockingEx()
for the next frame number after processing FB0
is finished would return two new frames (the frame number for the image transferred to FB3
) in this case if the transfer to FB0
has not finished in the meantime.
The following example extends the structure of the acquisition loop given in section Writing an Acquisition Loop to process all frames:
const int timeoutInSeconds = 10;
frameindex_t nextFrame = 1;
while (true) {
// get new image
const frameindex_t newestFrame =
Fg_getLastPicNumberBlockingEx(fg, nextFrame, dma,
timeoutInSeconds, mem);
if (newestFrame > 0) {
for (frameindex_t frame = nextFrame; frame <= newestFrame; ++frame) {
// get a pointer to the frame buffer for the newest image
void * ptr =
Fg_getImagePtrEx(fg, frame, dma, mem);
// get the actual number of bytes transferred
size_t length = 0;
int result =
Fg_getParameterEx(fg, FG_TRANSFER_LEN, &length,
dma, mem, frame);
if ((ptr != nullptr) && (result == FG_OK) && (length > 0)) {
// process image data ...
}
}
nextFrame = (newestFrame < FRAMEINDEX_MAX) ? newestFrame + 1 : 1;
} else {
// handle error ...
}
}
If the application can skip images and only the newest frame received should be processed, the inner for-loop can be dropped from the acquisition loop, instead processing only newestFrame
.
The function Fg_getImageEx()
should not be used in combination with the standard acquisition model.
In asynchronous mode, the Framegrabber API will call Fg_getLastPicNumberBlockingEx()
to wait for a new image, and will call the callback function once for the newest image if the flag FG_APC_BATCH_FRAMES
was set, or multiple times if it was not set, once for each image received. The callback will receive a frame number for the image.
To process frames in asynchronous mode, the content of the inner loop can be used in the callback function (ApcCallbackData is the structure as described in section Registering a Callback Function for Asynchronous Mode):
int ApcUserCallback(frameindex_t frame, void * data)
{
auto context = reinterpret_cast<ApcUserCallbackData *>(data);
if (frame > 0) {
// get a pointer to the frame buffer for the newest image
void * ptr =
Fg_getImagePtrEx(context->fg, frame, context->dma, context->mem);
// get the actual number of bytes transferred
size_t length = 0;
int result =
Fg_getParameterEx(context->fg, FG_TRANSFER_LEN, &length,
context->dma, context->mem, frame);
if ((ptr != nullptr) && (result == FG_OK) && (length > 0)) {
// process image data ...
}
} else {
// handle error ...
}
return 0;
}
This way, the callback function can be used both for processing only the newest image, when FG_APC_BATCH_FRAMES
was passed in flags
to Fg_registerApcHandlerEx()
, as well as processing all images. The callback function will be called multiple times, once for each new image, when FG_APC_BATCH_FRAMES
was not passed.
The Blocking Acquisition Model (ACQ_BLOCK)#
frameindex_t Fg_getImageEx(
Fg_Struct * fg,
int strategy,
frameindex_t frame,
unsigned int dma,
unsigned int timeout,
dma_mem * mem);
int Fg_getParameterEx(
Fg_Struct * fg,
int param,
void * value,
unsigned int dma,
dma_mem * mem,
frameindex_t buffer);
void * Fg_getImagePtrEx(
Fg_Struct * fg,
frameindex_t buffer,
unsigned int dma,
dma_mem * mem);
frameindex_t Fg_getStatusEx(
Fg_Struct * fg,
int status,
frameindex_t buffer,
unsigned int dma,
dma_mem * mem);
int Fg_setStatusEx(
Fg_Struct * fg,
int status,
frameindex_t buffer,
unsigned int dma,
dma_mem * mem);
Info
The behavior when using asynchronous mode in the blocking acquisition model ACQ_BLOCK
was changed in version 5.9 of the Framegrabber API.
The blocking acquisition model is selected by passing ACQ_BLOCK
to flags
in the call to Fg_AcquireEx()
. Each frame buffer is either exclusively accessible by the driver for acquiring image data or queued or blocked for the application to process image data. Using frame buffers is always safe as long as they are blocked, but images may be lost when the driver encounters a situation where no more unblocked buffers are available for image data transfer.
When acquisition is started, the driver queues at least one frame buffer in the frame grabber for image data transfer, starting with the first frame buffer FB0
. Once an image has been transferred completely to the computer memory, the software is informed of the completed transfer, the frame buffer is queued for the application and the driver will not use it until the application has explicitly or implicitly unblocked it. The driver will queue one more frame buffer in the frame grabber as long as one or more unblocked frame buffers are available. If no more unblocked frame buffers are available, a special frame buffer called dummy buffer is queued in the frame grabber to keep image acquisition always running. The frame grabber will continue data transfer for the next image into the next buffer in the queue, in our example this would be FB1
.
(As mentioned in subsection The Standard Acquisition Model, the number of frame buffers queued in the frame grabber depends on various factors.)
The dummy frame buffer can be considered infinitely large and can absorb data of arbitrary size, however not in a meaningful way for processing an image. Because of this, the dummy buffer isn't accessible to the application, and any image transferred to the dummy buffer will be lost. Because queueing the next buffer happens automatically each time a frame is transferred, this means that automatically at least one image will be lost if no unblocked frame buffers are available to the driver for queueing. This is true even if the data source is stopped and started again only after a frame buffer becomes available again!
In synchronous mode, the function Fg_getImageEx()
should be called to request and block one frame buffer for newly acquired images. The function will return a buffer number for the blocked frame buffer. The following strategies are relevant to the blocking acquisition model:
Strategy | Description |
---|---|
SEL_NEXT_IMAGE | returns the buffer number for the next frame this strategy is used to process each single frame, one after the other in the order they were transferred |
SEL_ACT_IMAGE | returns the buffer number for the newest frame this strategy is used to allow skipping frames when image processing is slower than the acquisition frame rate |
The list above isn't complete, and only contains the strategies relevant to the blocking acquisition model.
If SEL_NEXT_IMAGE
is used, only the next frame buffer will be blocked, all others remain queued for the application to be requested at a later point. If SEL_ACT_IMAGE
is used, only the newest frame buffer will be blocked, all other frame buffers which are queued for the application will be removed from the queue and unblocked implicitly.
When calling the function Fg_getImageEx()
using this model, 0 should always be passed to frame
. The function can't be used to wait for a specific frame number or to translate frame numbers into buffer numbers. To get the frame number for a frame buffer, the function Fg_getParameterEx()
can be called passing FG_IMAGE_NUMBER
in the parameter param
, a pointer to a variable of type frameindex_t
for the frame number and the buffer number in the parameter buffer
.
The function Fg_getStatusEx()
can be called to request the following status information relevant for the blocking acquisition model:
Status | Description |
---|---|
NUMBER_OF_LOST_IMAGES | the number of frames lost |
NUMBER_OF_BLOCKED_IMAGES | the number of frames currently blocked |
NUMBER_OF_IMAGES_IN_PROGRESS | the number of frames available through Fg_getImageEx() |
BUFFER_STATUS | 0: the frame buffer isn't blocked 1: the frame buffer is blocked |
The function Fg_setStatusEx()
can be called to unblock frame buffers after they were requested an blocked by a previous call to Fg_getImageEx()
:
Status | Description |
---|---|
FG_UNBLOCK | unblock a single frame buffer |
FG_UNBLOCK_ALL | unblock all currently blocked buffers |
FG_UNBLOCK_ALL
will also remove any buffers still queued for the application which have not yet been requested and blocked.
When using the function Fg_AcquireEx()
to specify a number of images to be grabbed, lost images are considered as well as images delivered successfully. This may lead to unexpected FG_TIMEOUT_ERR
or FG_TRANSFER_NOT_ACTIVE
results when calling Fg_getImageEx()
and only counting delivered images in the acquisition loop.
In a scenario, where the application triggers only a single image while a previous image is still being processed, two buffers are sufficient, one for the image data being processed and one for transferring image data to. (Due to buffer locking taking place, even if the application would make sure that triggers are only generated after an image has been completely processed, two buffers are required at least in all cases.)
In a scenario, where images are generated continuously without control from the application side, the image source can be considered free running. The driver however will only be able to deliver images to the application while there are still unblocked buffers available. In the following example, FB0
and FB1
contain valid image data. FB0
is being processed by the application, FB1
is pending processing. The driver has queued at least FB2
and image data is currently being transferred. This situation is still safe, because FB3
isn't in use yet, even though it might be queued already.
If image processing is slower than image acquisition, the application might have finished processing FB0
, unblocked it and moved on to process FB1
. However, in the following example in the meantime two more images were transferred, FB2
and FB3
, and the driver has started again with FB0
. When the image transfer completes, if the application hasn't finished processing FB1
and unlocked it, the driver will have no more free buffers available for queueing. The dummy buffer will be queued, and at least one image will be lost in the acquisition, but data integrity of the frame buffers not yet unblocked will be kept.
The following example extends the structure of the acquisition loop given in section Writing an Acquisition Loop to process all frames:
const int timeoutInSeconds = 10;
while (true) {
// get new image
frameindex_t buffer =
Fg_getImageEx(fg, SEL_NEXT_IMAGE, 0, dma, timeoutInSeconds, mem);
if (buffer > 0)
// get the frame number (if needed)
uint64_t frame = 0;
Fg_getParameterEx(fg, FG_IMAGE_NUMBER, &frame, dma, mem, buffer);
// get a pointer to the frame buffer for the newest image
void * ptr = Fg_getImagePtrEx(fg, buffer, dma, mem);
// get the actual number of bytes transferred
size_t length = 0;
int result =
Fg_getParameterEx(fg, FG_TRANSFER_LEN, &length, dma, mem, buffer);
if ((ptr != nullptr) && (result == FG_OK) && (length > 0)) {
// process image data ...
}
// unblock frame buffer
Fg_setStatusEx(fg, FG_UNBLOCK, buffer, dma, mem);
} else {
// handle error ...
}
}
The function Fg_getLastPicNumberBlockingEx()
should not be used in combination with the blocking acquisition model.
In asynchronous mode, the Framegrabber API will call Fg_getImageEx()
using SEL_ACT_IMAGE
if the flag FG_APC_BATCH_FRAMES
was set, or using SEL_NEXT_IMAGE
if it was not set. The callback function will receive a buffer number for the image.c
To process frames in asynchronous mode, the if-clause can be used in the callback function (ApcUserCallbackData is the structure as described in section Registering a Callback Function for Asynchronous Mode):
int ApcUserCallback(frameindex_t buffer, void * data)
{
auto context = reinterpret_cast<ApcUserCallbackData *>(data);
if (buffer > 0) {
// get the frame number (if needed)
uint64_t frame = 0;
Fg_getParameterEx(fg, FG_IMAGE_NUMBER, &frame, dma, mem, buffer);
// get a pointer to the frame buffer for the newest image
void * ptr =
Fg_getImagePtrEx(context->fg, buffer, context->dma, context->mem);
// get the actual number of bytes transferred
size_t length = 0;
int result =
Fg_getParameterEx(context->fg, FG_TRANSFER_LEN, &length,
context->dma, context->mem, buffer);
if ((ptr != nullptr) && (result == FG_OK) && (length > 0)) {
// process image data ...
}
// unblock frame buffer
Fg_setStatusEx(fg, FG_UNBLOCK, buffer, dma, mem);
} else {
// handle error ...
}
return 0;
}
This way, the callback function can be used both for processing only the newest image, when FG_APC_BATCH_FRAMES
was passed in flags
to Fg_registerApcHandlerEx()
, as well as processing all images. The callback function will be called multiple times, once for each new image, when FG_APC_BATCH_FRAMES
was not passed.
The Selective Acquisition Model ACQ_SELECT#
frameindex_t Fg_getLastPicNumberBlockingEx(
Fg_Struct * fg,
frameindex_t frame,
unsigned int dma,
int timeout,
dma_mem * mem);
int Fg_getParameterEx(
Fg_Struct * fg,
int param,
void * value,
unsigned int dma,
dma_mem * mem,
frameindex_t frame);
void * Fg_getImagePtrEx(
Fg_Struct * fg,
frameindex_t buffer,
unsigned int dma,
dma_mem * mem);
int Fg_setStatusEx(
Fg_Struct * fg,
int status,
frameindex_t buffer,
unsigned int dma,
dma_mem * mem);
Info
The selective acquisition model ACQ_SELECT
was added in version 5.9 of the Framegrabber SDK.
The selective acquisition model is selected by passing ACQ_SELECT
to flags
in the call to Fg_AcquireEx()
. The application has full control over the use of frame buffers by the driver.
Frame buffers have to be selected explicitly for queueing in the frame grabber, and the driver will queue frame buffers in the exact order they were selected. When acquisition is started, the driver queues frame buffers in the frame grabber for image data transfer only if any frame buffers were selected, starting with the first frame buffer which was selected. Once an image has been transferred completely to the computer memory, the software is informed of the completed transfer, the frame buffer isn't considered selected anymore and the driver will not use it until it is selected explicitly again. The driver will queue one more frame buffers if available. If no more frame buffers are available, no frame buffer will be queued. The frame grabber will continue data transfer for the next image into the next buffer in the queue. If the queue in the frame grabber runs empty this may result in an internal buffer overflow and images might be lost.
(As mentioned in subsection The Standard Acquisition Model, the number of frame buffers queued in the frame grabber depends on various factors.)
Because the application has full control over the use of frame buffers, keeping track of the buffer numbers is also the responsibility of the application. Unless frame buffers are always selected in the same order, there is no naive way to translate frame numbers to buffer numbers. Also there is no API function to perform the translation.
The function Fg_setStatusEx()
can be called to select a frame buffer by using FG_SELECT_BUFFER
and passing the buffer number.
To keep things simple, in the following examples we assume that after starting the acquisition all four buffers FB0
… FB3
are selected in natural order. Further, after an image is transferred it is processed and the buffer selected again in order. This way, the sequence of buffers will always remain the same, and frame numbers can be naively translated to buffer numbers using the relation bufferNumber = 1 + ((frameNumber - 1) % numberOfBuffers)
.
In a scenario, where images are generated continuously without control from the application side, the image source can be considered free running. The driver however will only be able to deliver images to the application while there are still selected buffers available. In the following example, FB0
and FB1
contain valid image data and aren't selected. FB0
is being processed by the application, FB1
is pending processing. The driver has queued at least FB2
and image data is currently being transferred. This situation is still safe, because FB3
isn't in use yet, even though it might be queued already.
Calling the function Fg_getLastPicNumberBlockingEx()
for the next frame number after processing FB0
is finished would return a single new frame (the frame number for the image transferred to FB1
) in this case if the transfer to FB2
has not finished in the meantime.
If image processing is slower than image acquisition, the application might have finished processing FB0
, selected it and moved on to process FB1
. However, in the following example in the meantime two more images were transferred, FB2
and FB3
, and the driver has started again with FB0
. When the image transfer completes, if the application hasn't finished processing FB1
and selected it, the driver will have no more free buffers available for queueing. As long as no further image arrives, this situation doesn't cause the same implicit image loss as would be encountered in ACQ_BLOCK
. But as soon as another image is received, the internal memory buffers in the frame grabber can run into an overflow condition and data loss can occur.
The following example extends the structure of the acquisition loop given in section Writing an Acquisition Loop to process all frames:
const frameindex_t numberOfBuffers = 4;
const int timeoutInSeconds = 10;
frameindex_t nextFrame = 1;
while (true) {
// get new image
const frameindex_t newestFrame =
Fg_getLastPicNumberBlockingEx(fg, nextFrame, dma,
timeoutInSeconds, mem);
if (newestFrame > 0) {
for (frameindex_t frame = nextFrame; frame <= newestFrame; ++frame) {
// get the buffer number
frameindex_t buffer = 1 + ((frame - 1) % numberOfBuffers);
// get a pointer to the frame buffer for the newest image
void * ptr = Fg_getImagePtrEx(fg, buffer, dma, mem);
// get the actual number of bytes transferred
size_t length = 0;
int result =
Fg_getParameterEx(fg, FG_TRANSFER_LEN, &length,
dma, mem, buffer);
if ((ptr != nullptr) && (result == FG_OK) && (length > 0)) {
// process image data ...
}
// select frame buffer
Fg_setStatusEx(fg, FG_SELECT_BUFFER, buffer, dma, mem);
}
nextFrame = (newestFrame < FRAMEINDEX_MAX) ? newestFrame + 1 : 1;
} else {
// handle error ...
}
}
If the application can skip images and only the newest frame received should be processed, Fg_setStatusEx()
has to be used with FG_SELECT_BUFFER
to select buffers which were skipped. Otherwise the application will end up with unused buffers.
The function Fg_getImageEx()
should not be used in combination with the selective acquisition model.
In asynchronous mode, the Framegrabber API will call Fg_getLastPicNumberBlockingEx()
to wait for a new image, and will call the callback function once for the newest image if the flag FG_APC_BATCH_FRAMES
was set, or multiple times if it was not set, once for each image received. The callback will receive a frame number for the image.
To process frames in asynchronous mode, the content of the inner loop can be used in the callback function (ApcUserCallbackData is the structure as described in section Registering a Callback Function for Asynchronous Mode):
int ApcUserCallback(frameindex_t frame, void * data)
{
auto context = reinterpret_cast<ApcUserCallbackData *>(data);
if (frame > 0) {
// get the buffer number
frameindex_t buffer = 1 + ((frame - 1) % numberOfBuffers);
// get a pointer to the frame buffer for the newest image
void * ptr = Fg_getImagePtrEx(fg, buffer, dma, mem);
// get the actual number of bytes transferred
size_t length = 0;
int result =
Fg_getParameterEx(fg, FG_TRANSFER_LEN, &length, dma, mem, buffer);
if ((ptr != nullptr) && (result == FG_OK) && (length > 0)) {
// process image data ...
}
// select frame buffer
Fg_setStatusEx(fg, FG_SELECT_BUFFER, buffer, dma, mem);
} else {
// handle error ...
}
return 0;
}
This way, the callback function can be used both for processing only the newest image, when FG_APC_BATCH_FRAMES
was passed in flags
to Fg_registerApcHandlerEx()
, as well as processing all images. The callback function will be called multiple times, once for each new image, when FG_APC_BATCH_FRAMES
was not passed.
Transferring Data to the Frame Grabber#
Limitations
This section describes a new API for applications using the VisualApplets operator DmaFromPC
. The API has the following limitations:
- It is a preliminary feature preview. This means that function names and functionality may change in future versions of the Framegrabber SDK.
- It only works for applets that contain the VisualApplets operator
DmaFromPC
. Do not use the API for regular DMA channels usingDmaToPC
or the Advanced Acquisition Applets delivered with the Framegrabber SDK. The operatorDmaFromPC
is available in VisualApplets version 3.4.0 or higher. - It is currently only implemented for Windows.
With the VisualApplets operator DmaFromPC
, data can be transferred from the PC to the frame grabber. Applications include, but are not limited to co-processing image data or providing additional parameters for complex image processing on the frame grabber.
Allocating Memory#
The buffers to transfer data must be allocated using the functions documented in Advanced Memory Management, for example:
const size_t bufferSize = 1024 * 1024;
const size_t numBuffers = 16;
const size_t totalSize = bufferSize * numBuffers;
dma_mem * mem = Fg_AllocMemEx(fg, totalSize, numBuffers);
if (mem != nullptr) {
// use memory, send buffers ...
Fg_FreeMem(fg, mem);
}
Or you can use the functions documented in Flexible Memory Management, for example:
dma_mem * mem = Fg_AllocMemHead(fg, totalSize, numBuffers);
for (int i = 0; i < numBuffers; i++) {
auto buffer = new uint8_t[bufferSize];
Fg_AddMem(fg, buffer, bufferSize, i, mem);
}
While buffers are all equally sized, the number of bytes for each single transfer is specified individually. The size of the data transfer must be a multiple of the parallelism of the operator DmaFromPc
, which is 32 and the upper limit is the size of the buffer.
Starting Data Transfer#
int Fg_startBufferQueue(
Fg_Struct * fg,
uint32_t dma,
dma_mem * mem);
int Fg_stopBufferQueue(
Fg_Struct * fg,
uint32_t dma,
int32_t flags);
To start the data transfer to the frame grabber, call the function Fg_startBufferQueue()
. This call will tie a DMA channel dma
to a memory handle mem
and thus will use the memory allocated for data transfers.
The parameter dma
to the function Fg_startBufferQueue()
is determined by the number of regular DMA channels for image transfer to the PC using the VisualApplets operator DmaToPC
present in the VisualApplets design. The DMA channel for the VisualApplets operator DmaFromPC
is always the last DMA channel.
After all data has been transferred, a call to Fg_stopBufferQueue()
stops the buffer queue and cuts the tie between the DMA channel and the memory handle used.
Queueing Buffers for Data Transfer#
int Fg_queueBuffer(
Fg_Struct * fg,
frameindex_t buffer,
uint64_t numBytesToTransfer,
uint32_t dma,
dma_mem * mem);
Once data to be transferred to the frame grabber has been written to a buffer, and the buffer is ready for transfer, you can put the buffer in the buffer queue by calling Fg_queueBuffer()
. For each buffer which you put in the queue, you must specify the number of bytes to be transferred.
You can put up to 16 buffers in the buffer queue. The next buffer in the queue will be transferred to the frame grabber automatically by the driver. This way, a continuous stream of data to the frame grabber can be ensured when necessary.
The parameter mem
is only needed, if buffers are queued before the buffer queue is started. After Fg_startBufferQueue()
is called, the parameter mem
is optional and can be NULL
.
Waiting for Buffer Transfer to Complete#
int Fg_waitForBuffers(
Fg_Struct * fg,
uint32_t dma,
uint64_t timeout,
void * /* reserved */,
size_t /* reserved */);
The function Fg_waitForBuffers()
waits for at least one buffer to be completely transferred within the number of seconds given in the parameter timeout
on a given DMA channel as specified in the parameter dma
. The function returns the number of buffers completely transferred in case of success, or a negative error code in case of a failure.
You can only re-use buffers which were queued using Fg_queueBuffer()
and fill them with new data once the buffer has been completely transferred.
The data transfer model for the VisualApplets operator DmaFromPC
is very similar to The Selective Acquisition Model. This means that because the application has full control over the use of buffers, keeping track of the buffer numbers is also the responsibility of the application. Unless buffers are always selected in the same order, there is no naive way to translate a transfer counter to buffer numbers. Also there is no API function to perform the translation.
Applet Events#
The applets that are included in the Framegrabber SDK installation can notify the application about various events related to image acquisition or processing in the frame grabber. Examples of such events are start of frame events, or end of frame events which are generated whenever the first, or last, pixel of an image is received from the camera, or trigger input rising events, or trigger input falling events which are generated whenever a trigger input signal edge is detected. Users of VisualApplets can use event operators within their design to generate events as needed by their application.
Each event source has a unique name and is identified by a single bit of an unsigned 64-bit integer, called the event mask. This way, multiple event sources can be grouped together by setting multiple bits in an event mask, for example by using the binary or operator |
for multiple event masks for several event sources. However, this also implies that any applet can support only up to 64 event sources.
Events can be delivered in synchronous or asynchronous mode. In the synchronous mode, an event loop has to be provided by the application. In the asynchronous mode, a callback function can be registered which will be called whenever one or more events occur. Both approaches should not be used mixed in an application as this can cause resource conflicts.
Since event sources can generate a high interrupt load on the computer system, each event source must be enabled explicitly, and should only be activated when needed.
For every occurrence of an event, the time when the interrupt for the event source was received is recorded. In addition to the time stamp, some event sources can generate additional data with each event, called the event payload. Events that have a payload can't be grouped together by setting multiple bits for more than one event source. Consult the documentation for any applet you use to learn about whether or not event sources in an applet generate additional data, and how to interpret that data.
Event Information#
uint64_t Fg_getEventMask(
Fg_Struct * fg,
const char * name);
int Fg_getEventPayload(
Fg_Struct * fg,
uint64_t mask);
int Fg_getEventCount(
Fg_Struct * fg);
const char * Fg_getEventName(
Fg_Struct * fg,
uint64_t mask);
If the name for an event source is known, the corresponding bit can be requested by calling the function Fg_getEventMask()
. If the event name isn't recognized, the function will return 0.
The size of the payload for any given event can be requested by calling the function Fg_getEventPayload()
. The function will return a value greater or equal to zero if the event mask corresponds to a single valid event source, or a negative error code otherwise.
To iterate over all event sources, the function Fg_getEventCount()
can be called to request the number of event sources supported by an applet, and the function Fg_getEventName()
can be used to get the name of an event source. This is shown in the following example:
int numEvents = Fg_getEventCount(fg);
for (int event = 0; event < numEvents; ++event) {
// get the event mask for each event
const uint64_t eventMask = (1 << event);
const char * eventName = Fg_getEventName(fg, eventMask);
if (eventName != nullptr) {
std::cout << "Event " << event << " is " << eventName << std::endl;
}
}
Registering a Callback Function for Asynchronous Event Handling#
typedef int (* Fg_EventFunc_t)(
uint64_t events,
void * data,
const struct fg_event_info * info);
int Fg_registerEventCallback(
Fg_Struct * fg,
uint64_t mask,
Fg_EventFunc_t handler,
void * data,
unsigned int flags,
struct fg_event_info * info);
int Fg_unregisterEventCallback(
Fg_Struct * fg,
uint64_t mask);
Info
In version 5.9 of the Framegrabber SDK the type struct fg_event_data
was removed and the function Fg_unregisterEventCallback()
was added. See section Unregistering a Callback Function for Asynchronous Event Handling in Plain C for the old interface.
Using Fg_registerEventCallback()
, a function of type Fg_EventFunc_t
can be registered to be called back when one or more events from a group of event sources is received. The function will be registered for a given frame grabber and an event mask and will be passed three parameters when called. The first parameter to the callback function is an event mask of all event sources for which events were received. The second parameter is a pointer which was supplied with the call to Fg_registerEventCallback()
and which can be used as a pointer to a context structure or class, for example the this
pointer of a class instance which implements image processing. The third parameter is a pointer to a struct fg_event_info
which has to be allocated by the application and passed in the call to Fg_registerEventCallback()
and which is used to store information about the events.
Only one callback function can be registered for the same group of events on any given frame grabber.
The callback function will be called from an event loop provided by the Framegrabber API. This implies that callback function is called in the thread context of the event loop. It also implies that the time the callback function takes until it returns is added to the general management overhead of the event loop, and that during this time multiple events might have been received. If any events were received during that time, the callback function will be called again immediately after it returns.
Through the parameter flags
in the call to Fg_registerEventCallback()
, the application can choose whether only a single event is delivered each time the callback function is called or if all pending events are grouped together. The default behavior when passing FG_EVENT_DEFAULT_FLAGS
to the parameter flags
is to deliver one event every time the callback function is called. By passing FG_EVENT_BATCHED
all pending events will be grouped together.
After the callback function is no longer needed, it can be unregistered by calling Fg_unregisterEventCallback()
.
The following example shows how to register a callback function using a simple structure which holds information necessary for handling image acquisition:
struct EventUserCallbackData
{
Fg_Struct * fg;
fg_event_info info;
uint64_t mask;
};
int EventUserCallback(uint64_t events, void * data,
const struct fg_event_info * info)
{
auto context = reinterpret_cast<EventUserCallbackData *>(data);
// process events
return 0;
}
void SetupEventUserCallback(EventUserCallbackData * context)
{
// register callback function
int result = Fg_registerEventCallback(
context->fg, context->mask, &EventUserCallback, context,
FG_EVENT_DEFAULT_FLAGS, &context->info);
if (result != FG_OK) {
throw std::runtime_error("Failed to register callback function");
}
}
The allocation and management of the context structure isn't shown in the example. The pointer has to be valid while the callback function remains registered. One solution can be to keep everything together in a C++ class. To use a callback function in a C++ class context, a static function can be used to register the callback handler, and the this
pointer should be used as context data pointer which can be casted back to a class pointer and used accordingly.
Waiting for Events Synchronously#
uint64_t Fg_eventWait(
Fg_Struct * fg,
uint64_t mask,
unsigned int timeout,
unsigned int flags,
struct fg_event_info * info);
The application can wait for an event from a group of event sources synchronously by calling Fg_eventWait()
. The parameter flags
allows choosing whether only a single event is returned each time the function is called or if all pending events are grouped together. The default behavior when passing FG_EVENT_DEFAULT_FLAGS
to the parameter flags
is to return at most one event every time the function is called. By passing FG_EVENT_BATCHED
all pending events will be grouped together. The function will wait until either an event is received for any of the event sources specified in the parameter mask
, or the number of seconds specified in the parameter timeout
has elapsed. If no events were received until a timeout occurs the function will return 0, otherwise it will return a mask containing the event or the group of events received.
Activating Events#
int Fg_activateEvents(
Fg_Struct * fg,
uint64_t mask,
int enable);
Before an event source can send events it has to be enabled. By calling Fg_activateEvents()
a group of event sources can be enabled or disabled. If 1 is passed to the parameter enable
, the group of event sources passed in the parameter mask
is enabled. If 0 is passed to the parameter enable
, they are disabled. All event sources should be disabled once they are no longer used by the application.
Since event sources can fire events any time after they were enabled, it is recommended to activate event sources only after either a callback has been registered for them using Fg_registerEventCallback()
, or a separate thread is ready and waiting for events using Fg_eventWait()
.
Support for using a Frame Grabber from Multiple Processes#
Some applications might require access to a single frame grabber from different processes. One simple example is a process which monitors the activity of another process, for example an acquisition process. Another example might be separate acquisition processes using one camera each of a frame grabber with up to four cameras connected.
The Framegrabber API provides limited support for such scenarios with the master/slave mode. When using master/slave processes it is important to understand the limitations. It is also important to understand the general challenges that application developers meet when two or more processes have to be synchronized. While the Framegrabber API provides support for using a frame grabber from multiple processes, it doesn't support inter-process synchronization beyond very basic features to synchronize the startup of processes when initializing a frame grabber. For general purpose inter-process communication and synchronization, other operating system features or support libraries have to be used.
One particular limitation of the master/slave mode is that whenever two processes try to control the same feature on a frame grabber, the last process "wins" and overrides the actions of the first process. The first process might not even notice the override and make wrong assumptions as to the state of the frame grabber. (An experimental feature exists to synchronize the state between processes using inter-process communication, however, this approach has its own implications and will be discussed in section Experimental Support for Parameter and Acquisition Synchronization.)
The general recommendation is to design processes in such a way that they take on unique tasks (the tasks are disjunct with respect to applet parameters and features) and don't have to know about the existence of the other processes. One exception is a monitoring process which only accesses read-only features of an applet or gathers information from other processes through inter-process communication means.
Another limitation is that while only the master process will fully initialize the frame grabber, basic initialization has to be done in each process. This basic initialization can interfere with earlier processes if they already started image acquisition or camera discovery.
Synchronizing Processes During Initialization#
int Fg_InitLibrariesEx(
const char * path,
unsigned int flags,
const char * id,
unsigned int timeout);
void Fg_AbortInitLibraries();
void Fg_InitLibrariesStartNextSlave();
Fg_Struct * Fg_InitEx(
const char * applet,
unsigned int board,
int flags);
Fg_Struct * Fg_InitConfigEx(
const char * config,
unsigned int board,
int flags);
When synchronizing multiple processes, the synchronization point provided by the Framegrabber API is the call to Fg_InitLibrariesEx()
. As with Fg_InitLibraries()
, the first parameter to the call isn't used and the application should always pass nullptr
.
Processes to be synchronized form a group and synchronization will only consider processes within a group. The group of processes which should be synchronized is identified through the parameter id
. This way, a complex application can have multiple independent synchronization groups. The string which identifies the group should not be empty and should consist only of characters that are allowed in file names on the operating system used. Starting an identifier used by an application with siso-
isn't recommended, the prefix siso-
should be considered reserved for use by the Framegrabber API.
The synchronization behavior can be configured through the parameter flags
. The following values and macros can be used in the parameter flags
:
Flag | Description |
---|---|
FG_INIT_LIBRARIES_SINGLE | Default initialization, no synchronization |
FG_INIT_LIBRARIES_MASTER | Master initialization, prepare slave synchronization |
FG_INIT_LIBRARIES_SLAVE | Slave initialization, wait for master |
FG_INIT_LIBRARIES_SEQUENTIAL | Strictly sequential startup |
FG_INIT_LIBRARIES_AUTOSTART_ON_INIT | Start next slave on frame grabber initialization |
FG_INIT_LIBRARIES_SET_SLAVE_PRIORITY(n) | Set slave priority |
FG_INIT_LIBRARIES_SET_NUMBER_OF_SLAVES(n) | Set number of slaves |
Using FG_INIT_LIBRARIES_SINGLE
is the same as calling Fg_InitLibraries()
.
Using FG_INIT_LIBRARIES_SEQUENTIAL
will only start the slave process with the highest priority from the master process. The slave process with the second highest priority has to be started from the slave process with the highest priority, and so on. This ensures a strict order of process initialization. If FG_INIT_LIBRARIES_SEQUENTIAL
is used in a synchronization group, it has to be specified in all processes, including the master process. The macro FG_INIT_LIBRARIES_SET_SLAVE_PRIORITY()
must be used in all slave processes when FG_INIT_LIBRARIES_SEQUENTIAL
is used and defines the priority of the slave process. The highest priority is 1, the lowest priority is 63. Each process in a group must have a unique priority and priorities have to be assigned in a way not to leave gaps. (If process 2 wants to start process 3, but there is no process 3, then nobody will start process 4.) The macro FG_INIT_LIBRARIES_SET_NUMBER_OF_SLAVES()
must be used in the master process when FG_INIT_LIBRARIES_SEQUENTIAL
is used and defines the number of slave processes. The minimum value allowed is 1, the maximum is 63.
The last parameter in the call to Fg_InitLibrariesEx()
specifies the time in milliseconds to wait for the process to be scheduled before an error is reported to the application.
The first process of the group, usually referred to as master process, establishes a group when Fg_InitLibrariesEx()
is called. All following processes, usually referred to as slave processes, can choose to wait in the respective call to Fg_InitLibrariesEx()
to be scheduled. Starting one or more slaves is triggered then by a call to Fg_InitLibrariesStartNextSlave()
in the master process. The function Fg_InitLibrariesStartNextSlave()
can be called automatically when a process initializes the frame grabber through a call to Fg_Init()
, Fg_InitEx()
, Fg_InitConfig()
or Fg_InitConfigEx()
.
While the function Fg_InitLibraries()
should never fail in normal circumstances, the same isn't true for Fg_InitLibrariesEx()
when using synchronization. Specify waiting times sensibly and handle errors accordingly.
In a multi-threaded application, waiting in a call to Fg_InitLibrariesEx()
can be aborted from a different thread by calling Fg_AbortInitLibraries()
. This will result in an error reported by the aborted call.
While in a master process, a call to Fg_Init()
or Fg_InitConfig()
can be used to initialize the frame grabber, all slave processes must call either Fg_InitEx()
or Fg_InitConfigEx()
and pass FG_INIT_FLAG_SLAVE
in the parameter flags
. Calling Fg_InitEx()
or Fg_InitConfigEx()
with FG_INIT_FLAG_DEFAULT
is the same as calling Fg_Init()
or Fg_InitConfig()
, respectively.
The following example shows how a master process can set up a synchronization group for strictly sequential scheduling using two slave processes. The master uses a call to Fg_InitLibrariesStartNextSlave()
to start the first slave process after some further initialization:
const char * syncGroupId = "example";
int result =
Fg_InitLibrariesEx(
nullptr,
FG_INIT_LIBRARIES_MASTER
| FG_INIT_LIBRARIES_SEQUENTIAL
| FG_INIT_LIBRARIES_SET_NUMBER_OF_SLAVES(2),
syncGroupId,
0);
if (result == FG_OK) {
const char * applet = "Acq_SingleCXP12Area";
Fg_Struct * fg = Fg_Init(applet, 0);
if (fg != nullptr) {
// further initialization ...
Fg_InitLibrariesStartNextSlave();
// use frame grabber ...
}
Fg_FreeLibraries();
}
The slave processes in the example are identical save for the priority specified in the call to Fg_InitLibrariesEx()
, only the first slave process is shown here. The slave process uses FG_INIT_LIBRARIES_AUTOSTART_ON_INIT
to start the next slave process implicitly when Fg_InitEx()
is called:
const char * syncGroupId = "example";
int result =
Fg_InitLibrariesEx(
nullptr,
FG_INIT_LIBRARIES_SLAVE
| FG_INIT_LIBRARIES_SEQUENTIAL
| FG_INIT_LIBRARIES_AUTOSTART_ON_INIT
| FG_INIT_LIBRARIES_SET_SLAVE_PRIORITY(1),
syncGroupId,
10000);
if (result == FG_OK) {
const char * applet = "Acq_SingleCXP12Area";
Fg_Struct * fg = Fg_InitEx(applet, 0, FG_INIT_FLAG_SLAVE);
if (fg != nullptr) {
// use frame grabber ...
}
Fg_FreeLibraries();
}
If the slave process is started before the master process, it will wait in the call to Fg_InitLibrariesEx()
for the master process to be started. Once the master process calls Fg_InitLibrariesStartNextSlave()
the slave process is scheduled to run and the call to Fg_InitLibrariesEx()
returns FG_OK
. If the master process isn't started, or doesn't finish initialization before the waiting time of the slave runs out, then the slave can't be scheduled to run, and the call to Fg_InitLibrariesEx()
will return a timeout error.
The tool microDisplayX which is installed with the Framegrabber SDK supports master/slave synchronization and slave processes can synchronize to microDisplayX by using the group id siso-microdisplay-master
. The flag FG_INIT_LIBRARIES_SEQUENTIAL
isn't supported by microDisplayX.
Experimental Support for Parameter and Acquisition Synchronization#
Info
The experimental support for parameter and acquisition synchronization was added in version 5.9 of the Framegrabber SDK.
The Framegrabber API provides experimental support to synchronize all parameter changes and the acquisition state across the process synchronization group. To enable parameter and acquisition synchronization, FG_INIT_FLAG_SLAVE_PARAM_SYNC
has to be added to the parameter flags
in the call to Fg_InitEx()
or Fg_InitConfigEx()
for all processes in the group.
There are a few things to consider when using parameter and acquisition synchronization, however.
First, the synchronization provided by the Framegrabber API uses inter-process communication mechanisms provided by the operating system. Because getting and setting parameters is no longer done locally in the process but has to be synchronized among several processes, both the latency and jitter for these operations increases. For most applications, these drawbacks should not matter. However, if the application uses a real-time operating system and getting or setting one or more parameters is considered real-time critical, parameter and acquisition synchronization should not be enabled. If FG_INIT_FLAG_SLAVE_PARAM_SYNC
isn't used explicitly, parameter and acquisition synchronization isn't enabled and the experimental feature has no impact on the performance compared to earlier versions of the Framegrabber API.
Second, either the master or the slave processes can control acquisition when synchronization is used, never both. Acquisition in the master process can't be mixed with acquisition in one or more slave processes within a synchronization group. In most cases, the acquisition should be done in one or more slave processes, and FG_INIT_FLAG_ACQUISITION_SLAVE
should be added to the parameter flags
in the call to Fg_InitEx()
or Fg_InitConfigEx()
for the master process.
Support for Non-Uniform Memory Access#
int Fg_NumaPinThread(
Fg_Struct * fg);
void * Fg_NumaAllocDmaBuffer(
Fg_Struct * fg,
size_t size);
int Fg_NumaFreeDmaBuffer(
Fg_Struct * fg,
void * ptr);
In multi-processor computers using non-uniform memory access (NUMA), each physical processor has its own memory and peripheral busses. As long as a process accesses only resources on the same physical processor it is running on, the access is almost as fast as on a single-processor computer and considerably faster than on a multi-processor computer using symmetric multiprocessing (SMP). However, if a process running on one physical processor wants to access resources which belong to another physical processor, the data has to be moved between two physical processors, slowing down the access. See the article about non-uniform memory access on Wikipedia for more information.
To support applications running on NUMA computers, all threads accessing a frame grabber should be pinned to the physical processor to which the device is attached locally. This can be done by calling Fg_NumaPinThread()
in the context of each thread that accesses parameters or controls the acquisition on a frame grabber.
Furthermore, memory should be allocated locally on the same physical processor as well. If the application uses the allocation functions provided by the Framegrabber API this is done automatically. However, if the application uses Fg_AllocMemHead()
and Fg_AddMem()
, memory should be allocated using NUMA-aware memory allocation. The Framegrabber API provides the functions Fg_NumaAllocDmaBuffer()
and Fg_NumaFreeDmaBuffer()
to allocate and release memory on the same physical processor a frame grabber is attached to locally.
If the application uses other NUMA aware functions for allocating memory, the function Fg_getIntSystemInformationForBoardIndex()
can be called using INFO_DRIVERGROUPAFFINITY
to provide the driver IRQ group affinity, and the function Fg_getInt64SystemInformationForBoardIndex()
can be called using INFO_DRIVERAFFINITYMASK
to provide the driver IRQ processor affinity mask for the device.
There might be other limitations on some NUMA computers. Some NUMA computers for example are designed in a way to balance access to the peripheral busses and don't allow for a single device to use all or even most of the bandwidth. While this is beneficial to most server applications, for image acquisition and processing applications this strategy might limit the bandwidth available to a frame grabber device.
Considerations When Using Plain C#
When an application is limited to using a plain C compiler, some of the C++ wrappers provided for convenience or type-safety by the Framegrabber API can't be used. This chapter will explain for each topic how to use the underlying functionality used by the C++ wrapper.
System Information in Plain C#
int Fg_getSystemInformation(
Fg_Struct * fg,
enum Fg_Info_Selector info,
enum FgProperty property,
int arg,
void * buffer,
unsigned int * size);
In sections General System Information and Board-Specific System Information of chapter System Information a set of functions is documented to request various information about the system in general or a specific frame grabber. The functions in these two chapters are all C++ wrappers for the function Fg_getSystemInformation()
.
The function Fg_getSystemInformation()
will always return the requested information as a string which is stored in a buffer passed to the function. To allocate a buffer of sufficient size, the size of the buffer can be requested by initializing the variable used for the parameter size
to 0 and passing NULL
to the parameter buffer
in the call. If the information requested is of a numeric type, the string has to be converted after successfully requesting the information.
To request information about the system in general, as documented in section General System Information, the function Fg_getSystemInformation()
should be called with NULL
passed to the parameter fg
and 0 to the parameter arg
.
For example, the number of frame grabbers installed in the computer can be requested as shown below:
int numBoards = 0;
char buffer[256];
unsigned int size = sizeof(buffer);
int result =
Fg_getSystemInformation(NULL, INFO_NR_OF_BOARDS, PROP_ID_VALUE,
0, buffer, &size);
if (result == FG_OK) {
numBoards = atoi(buffer);
std::cout << "Number of boards: " << numBoards << std::endl;
}
To request information about a specific frame grabber, as documented in section Board-Specific System Information, the function Fg_getSystemInformation()
expects either a handle to the frame grabber passed in the parameter fg
, or a board index passed in the parameter arg
.
For example, the following code requests the board type and name.
const int boardIndex = 0;
char buffer[256];
unsigned int size = sizeof(buffer);
int boardType = 0;
std::string boardName;
int result =
Fg_getSystemInformation(NULL, INFO_BOARDTYPE, PROP_ID_VALUE,
boardIndex, buffer, &size);
if (result == FG_OK) {
boardType = atoi(buffer);
size = sizeof(buffer);
result =
Fg_getSystemInformation(NULL, INFO_BOARDNAME, PROP_ID_VALUE,
boardIndex, buffer, &size);
}
if (result == FG_OK) {
boardName = buffer;
std::cout << "Board #" << boardIndex
<< " is a " << boardName
<< " (type " << std::hex << boardType
<< std::dec << ")" << std::endl;
}
To request information which requires an additional argument on input, the buffer works two-ways. The additional argument has to be stored as a string in the buffer before the call. During the call the argument passed in the buffer is used and then overwritten, as the information requested is written to the buffer.
Accessing Parameter Values in Plain C#
int Fg_getParameterWithType(
Fg_Struct * fg,
int id,
void * value,
unsigned int dma,
enum FgParamTypes type);
int Fg_freeParameterStringWithType(
Fg_Struct * fg,
int id,
void * value,
unsigned int dma,
enum FgParamTypes type);
int Fg_setParameterWithType(
Fg_Struct * fg,
int id,
const void * value,
unsigned int dma,
enum FgParamTypes type);
In section Accessing Parameter Values of chapter Working with Applet Parameters, C++ wrappers to request parameters with the most common types are documented. To access parameters from plain C, or to access parameters of a type for which no C++ wrapper exists, the functions Fg_getParameterWithType()
and Fg_setParameterWithType()
can be used. A variable of the matching type is needed for the call, and the corresponding FgParamTypes
value has to be passed in the parameter type
in the call. The type values are documented in section Parameter Types and the applet documentation should be consulted to know the type of a specific parameter.
For example, the following code sets the width of the first DMA channel of the applet to 1024.
const int dma = 0;
const int width = 1024;
int result = FG_INVALID_PARAMETER;
int paramId = Fg_getParameterIdByName(fg, "FG_WIDTH");
if (paramId > 0) {
result =
Fg_setParameterWithType(fg, paramId, &width, dma,
FG_PARAM_TYPE_INT32_T);
}
One special case when calling Fg_getParameterWithType()
is the type FG_PARAM_TYPE_CHAR_PTR
which represents a string parameter. While the length of the string needed to allocate a buffer of sufficient size can be requested by calling Fg_getParameterPropertyEx()
, this isn't the safest way to access dynamic parameters. A better approach is to request parameters with type FG_PARAM_TYPE_CHAR_PTR
using FG_PARAM_TYPE_CHAR_PTR_PTR
instead. This request will allocate a buffer of the right size, which can be released by calling Fg_freeParameterStringWithType()
after the value is no longer needed.
The following example shows how to use FG_PARAM_TYPE_CHAR_PTR_PTR
, assuming paramId
is a parameter of type FG_PARAM_TYPE_CHAR_PTR
:
const char * val = NULL;
int result =
Fg_getParameterWithType(fg, paramId, &val, dma,
FG_PARAM_TYPE_CHAR_PTR_PTR);
if (result == FG_OK) {
// use the string value stored in val ...
Fg_freeParameterStringWithType(fg, paramId, val, dma,
FG_PARAM_TYPE_CHAR_PTR_PTR);
}
The call to Fg_freeParameterStringWithType()
receives a char *
, not actually a char **
.
Accessing Field Parameters#
struct FieldParameterAccess {
enum FgParamTypes vtype;
unsigned int index;
unsigned int count;
union {
int32_t * p_int32_t;
uint32_t * p_uint32_t;
int64_t * p_int64_t;
uint64_t * p_uint64_t;
double * p_double;
};
};
Field parameters are parameters which represent an array of two or more values of the same type. A typical example is a lookup table which can be used to remap pixel values. Field parameters can be of type FG_PARAM_TYPE_STRUCT_FIELDPARAMINT
, FG_PARAM_TYPE_STRUCT_FIELDPARAMINT64
or FG_PARAM_TYPE_STRUCT_FIELDPARAMDOUBLE
.
The size of a field parameter can be determined by requesting the parameter property PROP_ID_FIELD_SIZE
as documented in section Accessing Parameter Properties of chapter Working with Applet Parameters. Also consult section Accessing Parameter Properties in Plain C if needed.
To request or change values in the array of a field parameter, the functions Fg_getParameterWithType()
and Fg_setParameterWithType()
should be called using an instance of struct FieldParameterAccess
and FG_PARAM_TYPE_STRUCT_FIELDPARAMACCESS
should be passed in the parameter type
. The member vtype
of struct FieldParameterAccess
has to be set to the enum FgParamTypes
value corresponding to the type of a single value in the field, not the field parameter type itself. The members index
and count
specify the offset into the array of the field parameter and the number of items requested or provided in the function call. Finally, the pointer in the union corresponding to the type of a single value in the field has to be initialized with a pre-allocated buffer which contains the values to be changed on input if Fg_setParameterWithType()
is called, or the values currently stored in the array after Fg_getParameterWithType()
was called.
The following example requests the first 256 values in a lookup table, assuming paramId
is a parameter of type FG_PARAM_TYPE_STRUCT_FIELDPARAMINT
and the field itself consists of at least 256 values of type FG_PARAM_TYPE_INT32_T
:
int32_t field[256];
struct FieldParameterAccess fpa;
fpa.vtype = FG_PARAM_TYPE_INT32_T;
fpa.index = 0;
fpa.count = 256;
fpa.p_int32_t = &field;
int result =
Fg_getParameterWithType(fg, paramId, &fpa, dma,
FG_PARAM_TYPE_STRUCT_FIELDPARAMACCESS);
if (result == FG_OK) {
// work with the values in field ...
}
Accessing Parameter Properties in Plain C#
int Fg_getParameterProperty(
Fg_Struct * fg,
int id,
enum FgProperty property,
void * buffer,
int * size);
int Fg_getParameterPropertyEx(
Fg_Struct * fg,
int id,
enum FgProperty property,
int dma,
void * buffer,
int * size);
In section Accessing Parameter Properties of chapter Working with Applet Parameters, C++ wrappers to request parameter properties with the most common types are documented. To access parameter properties from plain C, or to access parameter properties of a type for which no C++ wrapper exists, the function Fg_getParameterPropertyEx()
can be used. Using the function Fg_getParameterProperty()
isn't recommended, as the function call implicitly uses DMA channel 0, parameter properties might differ though for different channels.
The function Fg_getParameterPropertyEx()
will in all but one case return the requested information as a string which is stored in a buffer passed to the function. To allocate a buffer of sufficient size, the size of the buffer can be requested by initializing the variable used for the parameter size
to 0 and passing NULL
to the parameter buffer
in the call. If the information requested is of a numeric type, the string has to be converted after successfully requesting the information.
The following example shows how to get the minimum value of a parameter, assuming paramId is a parameter of type FG_PARAM_TYPE_INT32_T
:
char buffer[256];
int size = sizeof(buffer);
int32_t minVal = 0;
int result =
Fg_getParameterPropertyEx(fg, paramId, PROP_ID_MIN, dma, buffer, &size);
if (result == FG_OK) {
minVal = atoi(buffer);
// work with the property ...
}
Accessing the Enum Values Parameter Property#
struct FgPropertyEnumValues {
int32_t value;
char name[1];
};
#define FG_PROP_GET_NEXT_ENUM_VALUE(pev) ...
When requesting the property PROP_ID_ENUM_VALUES
of an enumeration parameter, the call to Fg_getParameterPropertyEx()
will not return the property as a string. Instead, the buffer will be filled using struct FgPropertyEnumValues
. The macro FG_PROP_GET_NEXT_ENUM_VALUE()
can be used to iterate over the elements in the buffer. For convenience, the property PROP_ID_IS_ENUM
will return the size of the buffer needed for an enumeration parameter.
The following example shows how to get and print the enum values property of a parameter, assuming paramId is an enumeration parameter:
const int defBufferSize = 256;
int size = defBufferSize;
char * buffer = malloc(size);
int result =
Fg_getParameterPropertyEx(fg, paramId, PROP_ID_IS_ENUM,
dma, buffer, &size);
if (result == FG_OK) {
int newSize = atoi(buffer);
if (newSize > 0) {
free(buffer);
size = newSize;
buffer = malloc(size);
result =
Fg_getParameterPropertyEx(fg, paramId, PROP_ID_ENUM_VALUES,
dma, buffer, &size);
} else {
result = FG_INVALID_TYPE;
}
}
if (result == FG_OK) {
struct FgPropertyEnumValues * pev;
for (pev = (struct FgPropertyEnumValues *)buffer;
pev != NULL;
pev = FG_PROP_GET_NEXT_ENUM_VALUE(pev)) {
printf("%s is %d\n", pev->name, pev->value);
}
}
free(buffer);
Registering a Callback Function for Asynchronous Mode in Plain C#
struct FgApcControl {
unsigned int version;
Fg_ApcFunc_t func;
void *data;
unsigned int timeout;
unsigned int flags;
};
int Fg_registerApcHandler(
Fg_Struct * fg,
unsigned int dma,
struct FgApcControl * control,
enum FgApcControlFlags flags);
The function Fg_registerApcHandler()
can be used to setup the asynchronous mode before starting the acquisition.
The callback function works in the same way as documented in section Registering a Callback Function for Asynchronous Mode of chapter Image Acquisition. When registering the callback function by calling the function Fg_registerCallbackHandler()
the parameters which control the acquisition loop are passed in an instance of struct FgApcControl
, whereas when calling the C++ wrapper function Fg_registerApcHandlerEx()
these are passed as parameters to the function.
There are two places where flags are passed when registering the callback function. The parameter flags
of the function Fg_registerApcHandler()
doesn't control the acquisition loop and isn't used. The application should always pass 0 to this parameter. Instead, the flags which control the acquisition loop are passed in the member flags
of struct FgApcControl
.
The following example shows how to register a callback function using a simple structure which holds information necessary for handling image acquisition:
struct ApcUserCallbackData
{
Fg_Struct * fg;
dma_mem * mem;
unsigned int dma;
unsigned int timeoutInSeconds;
int mode;
};
int ApcUserCallback(frameindex_t frame, void * data)
{
auto context = reinterpret_cast<ApcUserCallbackData *>(data);
if (frame > 0) {
// process new image ...
} else {
// handle error ...
}
return 0;
}
void SetupApcUserCallback(ApcUserCallbackData * context)
{
// register callback function
FgApcControl control;
control.version = 0;
control.func = &ApcUserCallback;
control.data = context;
control.timeout = context->timeoutInSeconds;
control.flags = FG_APC_DELIVER_ERRORS | FG_APC_IGNORE_TIMEOUTS;
int result = Fg_registerApcHandler(
context->fg, context->dma, &control, 0);
if (result != FG_OK) {
throw std::runtime_error("Failed to register callback function");
}
}
The allocation and management of the context structure isn't shown in the example. The pointer has to be valid while the callback function remains registered. One solution can be to keep everything together in a C++ class. To use a callback function in a C++ class context, a static function can be used to register the callback handler, and the this
pointer should be used as context data pointer which can be casted back to a class pointer and used accordingly.
After the callback function is no longer needed, it can be unregistered by calling Fg_registerApcHandler()
by using the same frame grabber handle and DMA channel, but passing NULL
to the parameter func
:
int result = Fg_registerApcHandler(context->fg, context->dma, NULL, 0);
Unregistering a Callback Function for Asynchronous Event Handling in Plain C#
int Fg_registerEventCallback(
Fg_Struct * fg,
uint64_t mask,
Fg_EventFunc_t handler,
void * data,
unsigned int flags,
struct fg_event_info * info);
Using Fg_registerEventCallback()
, a function of type Fg_EventFunc_t
can be registered to be called back when one or more events from a group of event sources is received as described in section Registering a Callback Function for Asynchronous Event Handling of chapter Image Acquisition.
After the callback function is no longer needed, it can be unregistered by calling Fg_registerEventCallback()
, passing the mask for the same group of events, FG_EVENT_DEFAULT_FLAGS
to the parameter flags
and NULL
to the parameters handler
, data
and info
:
// unregister event handler
Fg_registerEventCallback(fg, mask, NULL, NULL, FG_EVENT_DEFAULT_FLAGS, NULL);
-
The group code is a bit field with each bit corresponding to a single feature of a license. Superset in this context means that for every bit in the applet group code the bit has to be set in the frame grabber group code. Subset in this context means that for every bit in the applet group code the bit has to be set in the frame grabber group code. If the applet uses features which a have not been activated through licensing on a frame grabber, the applet can't be loaded by the Framegrabber SDK. ↩↩
-
The value of a parameter can be set to any value in
[PROP_ID_MIN; PROP_ID_MAX]
in steps ofPROP_ID_STEP
according to the linear relationPROP_ID_VALUE = PROP_ID_MIN + n*PROP_ID_STEP
, where n is in[0; (PROP_ID_MAX-PROP_ID_MIN)/PROP_ID_STEP[
. ↩↩↩ -
Older versions of the Framegrabber API used
Fg_getLastPicNumberBlockingEx()
in the acquisition loop, regardless of the acquisition model used. WhenACQ_BLOCK
is used, the new default behavior is to callFg_getImageEx()
withSEL_ACT_IMAGE
whenFG_APC_BATCH_IMAGES
is set andSEL_NEXT_IMAGE
otherwise. This means that inACQ_BLOCK
, the callback handler receives a buffer number, and not a frame number when the default behavior is used. Since there is no way to safely determine a buffer number for a given frame number when usingACQ_BLOCK
, the use ofFG_APC_OLD_ACQ_BLOCK_BEHAVIOR
and callingFg_getImageEx()
from the callback handler isn't recommended and should only be used when backwards compatibility of application code is absolutely necessary. ↩↩