The library fglib5 manages initialization and control of frame grabbers in the computer.
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.
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:
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.
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.
intresult=Fg_InitLibraries(nullptr);if(result!=FG_OK){// handle error ...}// use frame grabber ...Fg_FreeLibraries();
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.
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:
intnumBoards=0;intresult=Fg_getIntSystemInformationGlobal(INFO_NR_OF_BOARDS,PROP_ID_VALUE,&numBoards);if(result==FG_OK){std::cout<<"Number of boards: "<<numBoards<<std::endl;}
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
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.
constintboardIndex=0;intboardType=0;intresult=Fg_getIntSystemInformationForBoardIndex(boardIndex,INFO_BOARDTYPE,PROP_ID_VALUE,&boardType);std::stringboardName;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
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.
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.
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.
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 or Quad.
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 or x4. (This was dropped from the naming convention as x4 includes support for x2 and x1, x2 supports x1 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 or Line. 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.
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.
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:
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:
constintboardIndex=0;constchar*applet="Acq_SingleCXP12Area";Fg_Struct*fg=Fg_Init(applet,boardIndex);if(fg!=nullptr){// use frame grabber ...Fg_FreeGrabber(fg);}
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:
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.
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.
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().
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.
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.
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)
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:
intminVal=0;intresult=Fg_getParameterPropertyWithTypeEx(fg,paramId,PROP_ID_MIN,&minVal,dma);if(result==FG_OK){// work with the property ...}
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.
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.
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:
constintwidth=1024,height=1024;constintbytesPerPixel=3;constframeindex_tnumFrames=16;constsize_tframeSize=static_cast<size_t>(width)*height*bytesPerPixel;constsize_ttotalSize=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.
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:
intFg_AllocMemEx(Fg_Struct*fg,size_ttotalSize,frameindex_tnumFrames){constsize_tframeSize=totalSize/numFrames;char*buf=nullptr;dma_mem*mem=Fg_AllocMemHead(fg,totalSize,numFrames);if(mem!=nullptr){try{buf=newchar[totalSize];for(frameindex_tframe=0;frame<numFrames;++frame){constintresult=Fg_AddMem(fg,buf+frameSize*frame,frameSize,frame,mem);if(result!=FG_OK){throwstd::runtime_error("Failed to add frame buffer");}}}catch(...){for(frameindex_tframe=0;frame<numFrames;++frame){Fg_DelMem(fg,mem,frame);}Fg_FreeMemHead(fg,mem);delete[]buf;returnnullptr;}}returnmem;}
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#
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
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:
structApcUserCallbackData{Fg_Struct*fg;dma_mem*mem;unsignedintdma;unsignedinttimeoutInSeconds;intmode;};intApcUserCallback(frameindex_tframe,void*data){autocontext=reinterpret_cast<ApcUserCallbackData*>(data);if(frame>0){// process new image ...}else{// handle error ...}return0;}voidSetupApcUserCallback(ApcUserCallbackData*context){// register callback functionintresult=Fg_registerApcHandlerEx(context->fg,context->dma,&ApcUserCallback,context,context->timeoutInSeconds,FG_APC_DELIVER_ERRORS|FG_APC_IGNORE_TIMEOUTS);if(result!=FG_OK){throwstd::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:
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)#
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#
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
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)#
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#
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:
constinttimeoutInSeconds=10;frameindex_tnextFrame=1;while(true){// get new imageconstframeindex_tnewestFrame=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):
constinttimeoutInSeconds=10;while(true){// get new imageconstframeindex_tbuffer=Fg_getImageEx(fg,SEL_NEXT_IMAGE,0,dma,timeoutInSeconds,mem);if(buffer>0){// process new image ...}else{// handle error ...}}
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 notFG_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:
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 frequencyuint64_tfrequency=0;Fg_getParameterEx(fg,FG_TIMESTAMP_LONG_FREQUENCY,&frequency,0,nullptr,0);// ...// get frame time stampuint64_ttimestamp=0intresult=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 framesdoubleseconds=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 imagevoid*buffer=Fg_getImagePtrEx(fg,frame,dma,mem);// get the actual number of bytes transferredsize_tlength=0;intresult=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_tactive=Fg_getStatusEx(fg,GRAB_ACTIVE,0,dma,mem);if(active==1){// the DMA channel is active ...}elseif(active==0){// the DMA channel is inactive ...}else{// handle error ...}
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.
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 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:
constinttimeoutInSeconds=10;frameindex_tnextFrame=1;while(true){// get new imageconstframeindex_tnewestFrame=Fg_getLastPicNumberBlockingEx(fg,nextFrame,dma,timeoutInSeconds,mem);if(newestFrame>0){for(frameindex_tframe=nextFrame;frame<=newestFrame;++frame){// get a pointer to the frame buffer for the newest imagevoid*ptr=Fg_getImagePtrEx(fg,frame,dma,mem);// get the actual number of bytes transferredsize_tlength=0;intresult=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):
intApcUserCallback(frameindex_tframe,void*data){autocontext=reinterpret_cast<ApcUserCallbackData*>(data);if(frame>0){// get a pointer to the frame buffer for the newest imagevoid*ptr=Fg_getImagePtrEx(context->fg,frame,context->dma,context->mem);// get the actual number of bytes transferredsize_tlength=0;intresult=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 ...}return0;}
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 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:
constinttimeoutInSeconds=10;while(true){// get new imageframeindex_tbuffer=Fg_getImageEx(fg,SEL_NEXT_IMAGE,0,dma,timeoutInSeconds,mem);if(buffer>0)// get the frame number (if needed)uint64_tframe=0;Fg_getParameterEx(fg,FG_IMAGE_NUMBER,&frame,dma,mem,buffer);// get a pointer to the frame buffer for the newest imagevoid*ptr=Fg_getImagePtrEx(fg,buffer,dma,mem);// get the actual number of bytes transferredsize_tlength=0;intresult=Fg_getParameterEx(fg,FG_TRANSFER_LEN,&length,dma,mem,buffer);if((ptr!=nullptr)&&(result==FG_OK)&&(length>0)){// process image data ...}// unblock frame bufferFg_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
intApcUserCallback(frameindex_tbuffer,void*data){autocontext=reinterpret_cast<ApcUserCallbackData*>(data);if(buffer>0){// get the frame number (if needed)uint64_tframe=0;Fg_getParameterEx(fg,FG_IMAGE_NUMBER,&frame,dma,mem,buffer);// get a pointer to the frame buffer for the newest imagevoid*ptr=Fg_getImagePtrEx(context->fg,buffer,context->dma,context->mem);// get the actual number of bytes transferredsize_tlength=0;intresult=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 bufferFg_setStatusEx(fg,FG_UNBLOCK,buffer,dma,mem);}else{// handle error ...}return0;}
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 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:
constframeindex_tnumberOfBuffers=4;constinttimeoutInSeconds=10;frameindex_tnextFrame=1;while(true){// get new imageconstframeindex_tnewestFrame=Fg_getLastPicNumberBlockingEx(fg,nextFrame,dma,timeoutInSeconds,mem);if(newestFrame>0){for(frameindex_tframe=nextFrame;frame<=newestFrame;++frame){// get the buffer numberframeindex_tbuffer=1+((frame-1)%numberOfBuffers);// get a pointer to the frame buffer for the newest imagevoid*ptr=Fg_getImagePtrEx(fg,buffer,dma,mem);// get the actual number of bytes transferredsize_tlength=0;intresult=Fg_getParameterEx(fg,FG_TRANSFER_LEN,&length,dma,mem,buffer);if((ptr!=nullptr)&&(result==FG_OK)&&(length>0)){// process image data ...}// select frame bufferFg_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):
intApcUserCallback(frameindex_tframe,void*data){autocontext=reinterpret_cast<ApcUserCallbackData*>(data);if(frame>0){// get the buffer numberframeindex_tbuffer=1+((frame-1)%numberOfBuffers);// get a pointer to the frame buffer for the newest imagevoid*ptr=Fg_getImagePtrEx(fg,buffer,dma,mem);// get the actual number of bytes transferredsize_tlength=0;intresult=Fg_getParameterEx(fg,FG_TRANSFER_LEN,&length,dma,mem,buffer);if((ptr!=nullptr)&&(result==FG_OK)&&(length>0)){// process image data ...}// select frame bufferFg_setStatusEx(fg,FG_SELECT_BUFFER,buffer,dma,mem);}else{// handle error ...}return0;}
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.
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 using DmaToPC or the Advanced Acquisition Applets delivered with the Framegrabber SDK. The operator DmaFromPC 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.
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.
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.
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.
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.
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.
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:
intnumEvents=Fg_getEventCount(fg);for(intevent=0;event<numEvents;++event){// get the event mask for each eventconstuint64_teventMask=(1<<event);constchar*eventName=Fg_getEventName(fg,eventMask);if(eventName!=nullptr){std::cout<<"Event "<<event<<" is "<<eventName<<std::endl;}}
Registering a Callback Function for Asynchronous Event Handling#
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:
structEventUserCallbackData{Fg_Struct*fg;fg_event_infoinfo;uint64_tmask;};intEventUserCallback(uint64_tevents,void*data,conststructfg_event_info*info){autocontext=reinterpret_cast<EventUserCallbackData*>(data);// process eventsreturn0;}voidSetupEventUserCallback(EventUserCallbackData*context){// register callback functionintresult=Fg_registerEventCallback(context->fg,context->mask,&EventUserCallback,context,FG_EVENT_DEFAULT_FLAGS,&context->info);if(result!=FG_OK){throwstd::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.
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.
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.
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:
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:
constchar*syncGroupId="example";intresult=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){constchar*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:
constchar*syncGroupId="example";intresult=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){constchar*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.
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.
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.
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:
intnumBoards=0;charbuffer[256];unsignedintsize=sizeof(buffer);intresult=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.
constintboardIndex=0;charbuffer[256];unsignedintsize=sizeof(buffer);intboardType=0;std::stringboardName;intresult=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.
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.
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:
constchar*val=NULL;intresult=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 **.
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.
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_tfield[256];structFieldParameterAccessfpa;fpa.vtype=FG_PARAM_TYPE_INT32_T;fpa.index=0;fpa.count=256;fpa.p_int32_t=&field;intresult=Fg_getParameterWithType(fg,paramId,&fpa,dma,FG_PARAM_TYPE_STRUCT_FIELDPARAMACCESS);if(result==FG_OK){// work with the values in field ...}
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:
charbuffer[256];intsize=sizeof(buffer);int32_tminVal=0;intresult=Fg_getParameterPropertyEx(fg,paramId,PROP_ID_MIN,dma,buffer,&size);if(result==FG_OK){minVal=atoi(buffer);// work with the property ...}
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:
constintdefBufferSize=256;intsize=defBufferSize;char*buffer=malloc(size);intresult=Fg_getParameterPropertyEx(fg,paramId,PROP_ID_IS_ENUM,dma,buffer,&size);if(result==FG_OK){intnewSize=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){structFgPropertyEnumValues*pev;for(pev=(structFgPropertyEnumValues*)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#
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:
structApcUserCallbackData{Fg_Struct*fg;dma_mem*mem;unsignedintdma;unsignedinttimeoutInSeconds;intmode;};intApcUserCallback(frameindex_tframe,void*data){autocontext=reinterpret_cast<ApcUserCallbackData*>(data);if(frame>0){// process new image ...}else{// handle error ...}return0;}voidSetupApcUserCallback(ApcUserCallbackData*context){// register callback functionFgApcControlcontrol;control.version=0;control.func=&ApcUserCallback;control.data=context;control.timeout=context->timeoutInSeconds;control.flags=FG_APC_DELIVER_ERRORS|FG_APC_IGNORE_TIMEOUTS;intresult=Fg_registerApcHandler(context->fg,context->dma,&control,0);if(result!=FG_OK){throwstd::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:
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:
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 of PROP_ID_STEP according to the linear relation PROP_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. When ACQ_BLOCK is used, the new default behavior is to call Fg_getImageEx() with SEL_ACT_IMAGE when FG_APC_BATCH_IMAGES is set and SEL_NEXT_IMAGE otherwise. This means that in ACQ_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 using ACQ_BLOCK, the use of FG_APC_OLD_ACQ_BLOCK_BEHAVIOR and calling Fg_getImageEx() from the callback handler isn't recommended and should only be used when backwards compatibility of application code is absolutely necessary. ↩↩