You are here: Start » FIL.NET » Code Migration Guide to version 4.11
Code Migration Guide to version 4.11
- Introduction
out
Modifier in Output Parameters- Nullable References
- Arrays
- View Properties
- FIL.NET Dialogs dependency
- FIL Function Cancellation
Introduction
FabImage Library 4.11 brings many optimizations and API changes that may make user code invalid after upgrade from older FabImage Library versions. The same applies to the Macrofilter .Net Interface assemblies generated in the FabImage Studio. This article highlights the most important changes and shows how to update code in the applications that reference the FIL.NET assemblies and Macrofilter .Net Interface assemblies.
out
Modifier in Output Parameters
Almost all FIL functions have some arguments that present some calculation results, e.g. AddImages has an outImage parameter which holds a sum of two images when the function exits. Until FabImage Library 4.11 all output arguments had out
modifier which resulted in creating new objects (Image in case of AddImages.outImage) each time the function was called. From FabImage Library 4.11 the out
modifier is gone for the majority of reference types. This has several implications:
- output variables need to be assigned before function call,
- output variables need to be non-null values,
- output variables must not be disposed
In all cases the default constructors are enough to initialize such variables, even if the size of the output object is unknown since the real data will be filled in the function, e.g.:
//... Image sumImage; // just declaration try { FIL.AddImages(image1, image2, 1.0f, out sumImage); //... } finally { if (sumImage != null) sumImage.Dispose(); }
using (Image sumImage = new Image()) { //... FIL.AddImages(image1, image2, 1.0f, sumImage); //... }
This allows significantly reduce the memory consumption in scenarios when such an objects may be reused, e.g. a size of the frame being acquired from the camera rarely change over the application lifetime and can be filled into to the same image buffer each time the new frame comes, instead of creating the whole new image for every single frame.
out
modifier is still present in case of value types and several lightweight reference types, e.g. Object2D, RegionOfInterest, etc.
Nullable References
C# language has built-in support for nullable references so variables of reference types, apart from type-specific values, may be assigned with the special value of null
. This is often confusing especially for FIL methods that accept optional/conditional values, e.g. Regions. There was no way other than referring to the documentation to know if method accepts null
for a particular argument or not. That is why FabImage Library 4.11 introduces explicit types that reflect if a variable may or may not accept null
value: INullable<T> generic interface and NullableRef<T> and SafeNullableRef<T> generic types. API of those types is almost the same as the API of the System.Nullable<T> (value types-restricted nullable wrapper), i.e. all have public Value
and HasValue
properties.
Inputs
The most noticeable change in the API is for ROI parameter which in almost all FIL functions is optional. Now it is clearly visible from method signature what values may be assigned, e.g. ThresholdImage now accepts a NullableRef<Region> as a ROI instead of Region. In case of input parameters however, it is still possible to pass both the null
value (auto conversion to an empty NullableRef<Region>) and an instance of Region (auto conversion to non-empty NullableRef<Region>). Possible calls to the ThresholdImage are as follows:
using (var roi = new Region()) { // threshold in whole image FIL.ThresholdImage(..., null, ...); FIL.ThresholdImage(..., new NullableRef<Region>(), ...); // threshold in roi FIL.ThresholdImage(..., roi, ...); FIL.ThresholdImage(..., new NullableRef<Region>(roi), ...); FIL.ThresholdImage(..., FilNet.Nullable.Create(roi), ...); } //... using (var safeRoi = new SafeNullableRef<Region>()) { //... FIL.ThresholdImage(..., safeRoi, ...); }
Outputs
Breaking change is in case of optional and conditional outputs where appropriate NullableRef<T> instantiation must be passed:
string text; FIL.DecodeQRCode(matrix, out text); if (text != null) { // do something with text }
var text = new NullableRef<string>(); FIL.DecodeQRCode(matrix, text); if (text.HasValue) { // do something with text.Value }
In case of disposable values it is suggested to use SafeNullableRef<T> that takes the responsibility of disposing the underlying object (if exists) when either the object is Reset inside the function or the whole object is disposed, e.g.:
Path npath = null; try { FIL.FitPathToEdges(..., out npath); if (npath != null) { //do something with npath } } finally { if (npath != null) npath.Dispose(); }
using (var npath = new SafeNullableRef<Path>()) { FIL.FitPathToEdges(..., npath); if (npath.HasValue) { //do something with npath.Value } }
It is extremely important when passing a nullable object which already has a value to the function, where the nullable may be reset. Let's imagine, that there are two consecutive calls to the FitPathToEdges, in which second fails in the fitting (results in an empty path):
Path npath = null; try { // first call to the FitPathToEdges // detects a npath... FIL.FitPathToEdges(..., out npath); if (npath != null) { // do something with npath // dispose previous path npath.Dispose(); // ... but the second one does not. FIL.FitPathToEdges(..., out npath); if (npath != null) { //do something with npath } } } finally { if (npath != null) npath.Dispose(); }
using (var npath = new SafeNullableRef<Path>()) { // first call to the FitPathToEdges // detects a npath... FIL.FitPathToEdges(..., npath); if (npath.HasValue) { // ... but the second one does not. // The npath will be reset to an empty // value with automatic disposal of the // Path object in the npath.Value property. FIL.FitPathToEdges(..., npath); if (npath.HasValue) { //do something with npath.Value } } }
If instead of the SafeNullableRef<Path>, the NullableRef<Path> was passed, the Path object in the path.Value
property was not be disposed when reset, thus could be a subject to a memory leak.
Arrays
Until FabImage Library 4.11 the only collection type used in the API was an array (System.Array with its syntactic sugar of T[]), e.g. Image[] in JoinImages_OfArray. While usage in the function inputs was not so problematic, usage in the function outputs could be nonoptimal and forced callers to manually manage disposable element lifetimes. Therefore in FabImage Library 4.11 explicit arrays in the API are replaced with a System.Collections.Generic.IList<T> interface and new collection type SafeList<T> is introduced to help easily manage element lifetime.
Inputs
Since System.Array implements a generic System.Collections.Generic.IList<T> interface, previous call implementation will still compile:
Image[] images; // ... FIL.JoinImages_OfArray(images, ...); // ...
Image[] images; images = new Image[0]; // ... FIL.JoinImages_OfArray(images, ...); // ...
FabImage Library 4.11 in this case just enables one to use not only arrays but any collection that implements the System.Collections.Generic.IList<T> interface, e.g. System.Collections.Generic.List<T>, SafeList<T>, etc.
Outputs
Since there is no out
modifier any longer in the function output of any collection type, objects being passed to the function need to meet the same rules as in case of any FIL function output. In particular, one may pass just an empty list and expect the function change it's size accordingly:
Point2D[] points; Box box; // ... Point2D[] croppedPoints; FIL.CropPointArray(points, box, out croppedPoints); foreach (var point in croppedPoints) { // do something with point }
Point2D[] points; Box box; // ... var croppedPoints = new List<Point2D>(); FIL.CropPointArray(points, box, croppedPoints); foreach (var point in croppedPoints) { // do something with point }
As calling function may change the collection size, great care needs to be taken when passing collection of disposable objects, e.g. Image, Region, Path, etc. In those cases it is suggested to use SafeList<T>, especially if the collection is the only place where the references to those objects are kept, e.g.:
Region[] blobs = null; try { FIL.SplitRegionIntoBlobs(..., out blobs); foreach (var blob in blobs) { // do something with blob } } finally { if (blobs != null) { foreach (var blob in blobs) blob.Dispose(); } }
using (var blobs = new SafeList<Region>()) { FIL.SplitRegionIntoBlobs(..., blobs); foreach (var blob in blobs) { // do something with blob } }
View Properties
There were types that contained get methods for component objects, e.g. RegionOfInterest's GetPolygon()
. In FabImage Library 4.11, whenever possible, such types expose getter-only properties that refers to the original component objects, that may be modified in-place.
RegionOfInterest roi; Point2D[] points; // ... if (roi.Tag == RegionOfInterestType.Polygon) { // update the polygon shape using (var poly = roi.GetPolygon()) { poly.Clear(); poly.AddRange(points); roi.SetPolygon(poly); } // ... // draw the polygon using (var poly = roi.GetPolygon()) FIL.DrawPath(..., poly, ...); }
RegionOfInterest roi; Point2D[] points; // ... if (roi.Tag == RegionOfInterestType.Polygon) { // update the polygon shape roi.Polygon.Clear(); roi.Polygon.AddRange(points); // ... // draw the polygon FIL.DrawPath(..., roi.Polygon, ...); }
FIL.NET Designers dependency
Fil.Net.Designers.dll requires another assemblies to be referenced in the project - Fil.Net.Amr.dll
and Fil.Net.Kit.dll
. Those assemblies can be found in the same directory as the other Fil.Net.* assemblies.
FIL Function Cancellation
The function cancellation logic has been unified with the one known from the native FIL. From now on, user needs to manually call the function cancellation off, to enable consecutive functions to be executed. This solves an issue of unexpected function execution even if explicit cancellation has been called.
In the following example the CreateModels()
method runs in a separate thread, in which some long-lasting FIL functions are executed. The Cancel()
method is responsible for interrupting those methods. Before FabImage Library 4.11, in the worst case scenario, the Cancel()
method should have been called twice to actually interrupt the pending FIL functions. From FabImage Library 4.11 it is enough to call the Cancel()
method once. However, before any other FIL function is called, it is required to call the cancellation off, to prevent those methods to be discarded at startup.
public void CreateModels() { var image = new Image(); var model = new SafeNullableRef<EdgeModel>(); // Create two edge models FIL.CreateEdgeModel(...); FIL.CreateEdgeModel(...); //... } public void Cancel() { // cancel all FIL functions that // are currently running ProcessHelper.CancelCurrentWork(); }
public void CreateModels() { var image = new Image(); var model = new SafeNullableRef<EdgeModel>(); // Call the last cancellation off ProcessHelper.ResetIsCurrentWorkCancelled(); // Create two edge models FIL.CreateEdgeModel(...); FIL.CreateEdgeModel(...); //... } public void Cancel() { // Cancel all FIL functions that are // currently running and will run, // unless ProcessHelper.ResetIsCurrentWorkCancelled() // will be called ProcessHelper.CancelCurrentWork(); }
Previous: FIL.NET Performance Tips | Next: Usage Examples |