Writing a Frame Filter: Binarization

This example demonstrates how to create frame filters to apply binarization to live video.

As frame filters need perform quickly and efficiently, only languages that can handle plain pointers (like Managed C++ or C# in unsafe blocks) should be used to implement them. This example uses C#.

It shows, how to:

The example is located in the samples\C#\Binarization directory in your My Documents/IC Imaging Control 3.5 directory.

Implementing a Frame Filter

All frame filter implementations have to be derived from FrameFilterImpl:

public class BinarizationFilter : FrameFilterImpl

Two member variables contain the current settings of the frame filter. m_bEnabled controls whether the filter applies the binarization at all. m_threshold controls the value that determines whether a pixel is transformed to the maximum gray value or zero.

private bool _enabled = false; private int _threshold = 127;

To create a working frame filter, the methods GetSupportedInputTypes, GetTransformOutputTypes and Transform have to be implemented.

In the GetSupportedInputTypes method, an ArrayList is filled with FrameType objects that describe the frame types that our frame filter accepts as inputs. To keep it simple, we are only going to accept the 8-bit formats RGB8 and Y800. When the video format of the video capture device differs from this, the image data is automatically converted before it is presented to the frame filter.

public override void GetSupportedInputTypes( System.Collections.ArrayList frameTypes ) { // This filter works for 8-bit-gray images only frameTypes.Add( new FrameType(MediaSubtypes.Y800 ) ); }

The GetTransformOutputTypes method fills the ArrayList outTypes with frame types that the filter can produce from a specified input frame type. Because the binarization filter does not change the frame type or size, we just insert the input type into the list:

public override bool GetTransformOutputTypes( FrameType inType, System.Collections.ArrayList outTypes ) { // We don't change the image type, output = input outTypes.Add( inType ); return true; }

The Transform method is called for every frame. It is responsible to do the binarization according to the current settings.

Because the Transform method runs in a different thread from the user interface, the access to the member variables m_bEnabled and m_threshold has to be protected from parallel adjustments by the application program.

First, FrameFilterImpl.BeginParamTransfer is called. Internally, this method enters a critical section. When a user of the frame filter wants to set parameters, s/he has to call FrameFilter::BeginParamTransfer, which tries to acquire the same lock. FrameFilterImpl.EndParamTransfer leaves the critical section. The values of the member variables are copied to local stack variables so that they remain constant for the whole transformation.

The binarization itself is a simple operation: Compare each pixel's gray value to the threshold. If the gray value is greater than the threshold, set the pixel to the maximum gray value, otherwise set it to zero.

public override bool Transform( IFrame src, IFrame dest ) { unsafe { // Check whether the destination frame is available if( dest.Ptr == null ) return false; // Copy the member variables to the function's stack, to protect them from being // overwritten by parallel calls to setThreshold() etc. // // beginParamTransfer/endParamTransfer makes sure that the values from various // member variables are consistent, because the user of this filter must enclose // writing parameter access into beginParamTransfer/endParamTransfer, too. BeginParameterTransfer(); int threshold = _threshold; bool enabled = _enabled; EndParameterTransfer(); byte* pIn = src.Ptr; byte* pOut = dest.Ptr; // Check whether binarization is enabled if( enabled ) { // For each byte in the input buffer, check whether it is greater or // equal to the threshold. int bufferSize = src.FrameType.BufferSize; while( bufferSize-- > 0 ) { if( *pIn++ >= threshold ) { *pOut++ = 255; } else { *pOut++ = 0; } } } else { // Binarization is disabled: Copy the image data without modifying it. dest.CopyFrom( src ); } } return true; }

Using the Frame Filter

First of all, we create an instance of BinarizationFilter and pass it to FrameFilter.Create.

There is a special overload of that method that takes a FrameFilterImpl object and returns a FrameFilter object that can be used in ICImagingControl.DeviceFrameFilters, ICImagingControl.DisplayFrameFilters and the sink types.

We save a reference to the FrameFilter object in a member variable of the dialog class so that we can access the filter's parameters later.

When the ICImagingControl object is initialized, ICImagingControl.DisplayFrameFilters.Add is called to set our frame filter:

// Disable overlay bitmap icImagingControl1.OverlayBitmapPosition = TIS.Imaging.PathPositions.None; // Create an instance of the frame filter implementation BinarizationFilter binFilterImpl = new BinarizationFilter(); // Create a FrameFilter object wrapping the implementation _frameFilter = TIS.Imaging.FrameFilter.Create( binFilterImpl ); // Set the FrameFilter as display frame filter. icImagingControl1.DisplayFrameFilters.Add( _frameFilter ); // Start live mode icImagingControl1.LiveStart();

When live mode is started, all images are binarized just before they are displayed by IC Imaging Control.

Implementing the Parameter Interface

The frame filter works with the code above, but the host application can not alter the filter's behavior. To let the application access the filter's settings, the filter can define a set of parameters that can be accessed using the generic functions of FrameFilter, such as SetBoolParameter or GetIntParameter.

To create boolean a parameter to switch the filter on and off, call FrameFilterImpl.AddBoolParam in your filter's constructor. To add a parameter to control the threshold value, call AddIntParam.

public BinarizationFilter() { AddBoolParam( "enable", new SetBoolParam( setEnable ), new GetBoolParam( getEnable ) ); AddIntParam( "threshold", new SetIntParam( setThreshold ), new GetIntParam( getThreshold ) ); }

Those two methods both take three parameters. The first is the name of the parameter, that has to be specified when altering the parameter, such as a method like FrameFilter.SetIntParameter. The second and third parameters are delegates of the type SetBoolParam and GetBoolParameter or SetIntParameter and GetIntParameter. These delegates connect the parameter with the member methods of your filter implementation:

void setEnable( bool enable ) { _enabled = enable; }
bool getEnable() { return _enabled; }
void setThreshold( int threshold ) { _threshold = threshold; }
int getThreshold() { return _threshold; }

Accessing Frame Filter Parameters

In the event handler for the check box that enables or disables the binarization, FrameFilter.SetBoolParameter is called.

All calls to methods modifying filter parameters have to be enclosed by FrameFilter.BeginParameterTransfer and FrameFilter.EndParameterTransfer.

private void chkEnable_CheckedChanged( object sender, EventArgs e ) { _frameFilter.BeginParameterTransfer(); _frameFilter.SetBoolParameter( "enable", chkEnable.Checked ); sldThreshold.Enabled = _frameFilter.GetBoolParameter( "enable" ); lblThreshold.Enabled = _frameFilter.GetBoolParameter( "enable" ); _frameFilter.EndParameterTransfer(); }

In the event handler for the threshold slider, FrameFilter.SetIntParameter is called.

This call also has to be enclosed by FrameFilter.BeginParameterTransfer and FrameFilter.EndParameterTransfer.

private void sldThreshold_Scroll( object sender, EventArgs e ) { _frameFilter.BeginParameterTransfer(); _frameFilter.SetIntParameter( "threshold", sldThreshold.Value ); lblThreshold.Text = _frameFilter.GetIntParameter( "threshold" ).ToString(); _frameFilter.EndParameterTransfer(); }

<< Programmer's Guide