Writing Your Own Vision ToolsCognex VisionPro

VisionPro lets you build your own tools and tool edit controls that will work in QuickBuild.

There are three major steps in writing your own VisionPro tool:

  • Write the tool
  • Write the edit control
  • Integrate the tool with QuickBuild

The VisionPro installer places two examples of user-written tools in C:\Program Files\Cognex\VisionPro\Samples\Programming\UserTools. The first, SimpleTool, copies its input image to an output image. The second, CogColorConversionTool, converts three-plane RGB images to individual R, G, and B planes or to individual H, S, and I planes. The sample tools are written in C# and in Visual Basic.NET.

Note: If you installed VisionPro in a directory other than Program Files, the sample programs will be located within that directory.

Writing the Tool

This walkthrough uses that SimpleTool sample program as an example. When you write your tool, you will, of course, define the properties and methods that are appropriate for it.

Create a new Class Library project. Your project must include references to Cognex.VisionPro and Cognex.VisionPro.Core.

Your tool must be derived from CogToolBase which provides the basic tool functionality: serialization, changed events, run status, and timing information.

public class SimpleTool : Cognex.VisionPro.Implementation.CogToolBase
{
    ....
}

Next fill in your class with constructors and clone methods.

public class SimpleTool : Cognex.VisionPro.Implementation.CogToolBase
{
    public SimpleTool()
    {
    }

    public SimpleTool(SimpleTool otherTool)
    {
        _InputImage = otherTool._InputImage;
        _OutputImage = otherTool._OutputImage;
        _CopyTwice = otherTool._CopyTwice;
    }

    private SimpleTool(SerializationInfo info, StreamingContext context) 
        : base(info, context)
    { 
    }

    protected override object Clone() {return new SimpleTool(this);}
    ...
}

You will need some tool-specific private fields. The simple tool example uses an input image, and output image, and a custom field. Later, you will expose these fields with properties.

public class SimpleTool : Cognex.VisionPro.Implementation.CogToolBase
{
    // constructors and clone methods
    ...
    // private fields
    private Cognex.VisionPro.CogImage8Grey _InputImage;
    private Cognex.VisionPro.CogImage8Grey _OutputImage;
    // CopyTwice is a field that will be modifiable in the edit control.
    private bool _CopyTwice = false;

}

The state flags let changed event handlers know which properties have changed. By convention, the first state flag defined for each class is named Sf0 and is set to the base class' SfNextSf. You typically define a state flag for each property that your class exposes.Each state flag is set to a value bit-shifted from Sf0. The last state flag, by convention, is SfNextSf. Any classes derived from your class can use that value so its state flags don't overlap.

The names of the state flags must be the same as the properties they represent with the Sf prefix. For example, if your tool has a Region property, the corresponding state flag must be named SfRegion.

public class SimpleTool : Cognex.VisionPro.Implementation.CogToolBase
{
    // constructors and clone methods
    ...
    // private fields
    ...
    // state flags
    private const long Sf0 = Cognex.VisionPro.Implementation.CogToolBase.SfNextSf;
    public const long SfInputImage = Sf0 << 0;
    public const long SfOutputImage = Sf0 << 1;
    public const long SfCopyTwice = Sf0 << 2;
    protected new const long SfNextSf = Sf0 << 3;
}

Next, define the properties that access the private fields. To make sure that changed event fires when the value of the fields changes, you must call OnChanged.

public class SimpleTool : Cognex.VisionPro.Implementation.CogToolBase
{
    // constructors and clone methods
    ...
    // private fields
    ...
    // state flags
    ...
    // properties
    public Cognex.VisionPro.CogImage8Grey InputImage
    {
        get {return _InputImage;}
        set 
        {
            if ((_InputImage == null) || (!_InputImage.Equals(value))) 
            {
                _InputImage = value;
                // Note that the state flags also indicate that the current run record
                // has changed.  This is because the input image is part of the current run 
                // record.  SfCreateCurrentRecord is provided by CogToolBase
                OnChanged(SfInputImage | SfCreateCurrentRecord);
            }
        }
    }

    public Cognex.VisionPro.CogImage8Grey OutputImage
    {
        // This property has no setter since it is an output of the tool.
        get {return _OutputImage;}
    }

    public bool CopyTwice
    {
        get {return _CopyTwice;}
        set 
        {
            if (_CopyTwice != value)
            {
                _CopyTwice = value;
                // Fire CopyTwice changed event
                OnChanged(SfCopyTwice);
            }
        }
    }

}

Finally, you must override the following CogToolBasemethods:

  • InternalRun

    Executes when the tool is invoked programmatically or from the edit control.
  • InternalCreateCurrentRecord

    Sets up the image to display in the edit control before the tool runs; usually the input image. If the tool uses a region, it is usually included as well.
  • InternalCreateLastRunRecord

    Sets up the image to display after the tool runs. Usually includes the output image or images. If the tool provides result graphics, they are included here as well.
public class SimpleTool : Cognex.VisionPro.Implementation.CogToolBase
{
    // constructors and clone methods
    ...
    // private fields
    ...
    // state flags
    ...
    // properties
    ...
    // CogToolBase overrides
    /// This method creates the CurrentRunRecord.  Run records consist of
    /// one or more CogRecords.  If a CogRecord contains an ICogImage, it will be shown
    /// on the display of the Tool's edit control.  It the ICogImage record contains a
    /// graphics subrecord, the graphics will appear on the ICogImage.  For this tool,
    /// the CurrentRunRecord includes the InputImage.

    protected override void InternalCreateCurrentRecord(Cognex.VisionPro.ICogRecord newRecord, 
        int mycurrentRecordEnable)
    {
        Cognex.VisionPro.Implementation.CogRecord InputImageRecord;
            InputImageRecord = new Cognex.VisionPro.Implementation.CogRecord("InputImage", 
                typeof(Cognex.VisionPro.CogImage8Grey), 
                Cognex.VisionPro.CogRecordUsageConstants.Input, false, _InputImage, "InputImage");
            newRecord.SubRecords.Add(InputImageRecord);
    }

    /// This method creates the LastRunRecord.  Run records consist of
    /// one or more CogRecords.  If a CogRecord contains an ICogImage, it will be shown
    /// on the display of the Tool's edit control.  It the ICogImage record contains a
    /// graphics subrecord, the graphics will appear on the ICogImage. For this tool,
    /// the LastRunRecord is the OutputImage 

    protected override void InternalCreateLastRunRecord(Cognex.VisionPro.ICogRecord newRecord, int lastRunRecordEnable, int lastRunRecordDiagEnable)
    {
            newRecord.SubRecords.Add(new Cognex.VisionPro.Implementation.CogRecord("OutputImage", 
                typeof(Cognex.VisionPro.CogImage8Grey), 
                Cognex.VisionPro.CogRecordUsageConstants.Result, false, _OutputImage, "OutputImage"));
    }

    /// This method is the "Run" method for the tool.  It generates changed events for 
    /// the OutputImage and CreateLastRunRecord (since the LastRunRecord contains the
    /// OutputImage).

    protected override Cognex.VisionPro.CogToolResultConstants InternalRun(ref string message)
    {
        if (_InputImage == null)
        {
            throw new CogSimpleToolException("No input image specified");  
        }
        _OutputImage = _InputImage.Copy(Cognex.VisionPro.CogImageCopyModeConstants.CopyPixels);
        if (_CopyTwice)
            _OutputImage = _InputImage.Copy(Cognex.VisionPro.CogImageCopyModeConstants.CopyPixels);
        OnChanged(SfOutputImage|SfCreateLastRunRecord);
        return Cognex.VisionPro.CogToolResultConstants.Accept;
    }

}
Write the Tool Edit Control

Create a new User Control as part of the vision tool project. Your project must include references to Cognex.VisionPro.Controls.

The edit control must derive from CogToolEditControlBaseV2, which provides the general framework for the control including the tool buttons and the tool display. Change the base class of the user control from System.Windows.Forms.UserControl to CogToolEditControlBaseV2.

Add an Editor attribute to the tool class that you wrote to associate it with the tool edit control. You will need to add some using statements as shown.

using System.ComponentModel;  // needed for Editor attribute
using System.Windows.Forms;   // needed for Editor attribute

[Serializable]
[Editor(typeof(SimpleToolEditV2), typeof(Control))]
public class SimpleTool : CogToolBase
{
…
}

In the code of your tool edit control you can use the thread-safe capabilities of the CogToolEditControlBaseV2 class to create a control that provides graphical user access to the tool properties while safely preventing access to those properties when the tool is running in a non-GUI thread. The key notion here revolves around the base class’ SubjectInUse property. This boolean property indicates when the subject tool may safely be accessed – generally that will be any time the tool is not executing its Run method from a non-GUI thread. When you run your tool within a job in QuickBuild, the underlying CogJobManager will set and clear the SubjectInUse property as necessary. If you create a multi-threaded application using your edit control, your application can explicitly set and clear SubjectInUse.

Once you have established the base class for your control, use the designer to add whatever buttons, checkboxes, or similar subcontrols that your control needs. You will typically have a one-to-one correspondence between a particular subcontrol and a specific property of your tool.

Create a constructor for your edit control class.

public SimpleToolEditV2()
: base(false) // base(bool hasFloatingResults)
{
InitializeComponent(); // required by designer
…
}

Create a Subject property, including a getter and a setter. The subject is the instance of your vision tool whose state will be exposed by this control. Note that the base class will effectively queue a call to the setter if it happens to be called while SubjectInUse is true – this will be processed as soon as SubjectInUse becomes false.

[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public SimpleTool Subject
{
get { return base.GetSubject() as SimpleTool; }
set { base.SetSubject(value); }
}

Override the base class’ InitializeFromSubject method. This method is called at the first thread-safe opportunity after the Subject has been replaced. The control should use this method to (re)initialize itself using values from the new subject.

protected override void InitializeFromSubject()
{
base.InitializeFromSubject();

SetupSettingsTab(); // a helper method that we’ll define further on 
}

Override the base class’ SubjectValuesChanged method. This method is called whenever the subject raises a Changed event. This function is always executed on the GUI thread. This is where you can update any subcontrols whose associated tool property value may have changed, as indicated by the state flags of the supplied Changed event args.

Protected override void SubjectValuesChanged(
  object sender,
CogChangedEventArgs e)
{
base.SubjectValuesChanged(sender, e);

if ((e.StateFlags & SimpleTool.SfCopyTwice) != 0)
{
SetupSettingsTab(); // helper method, copies from tool to ctrl
 }
}

Override the base class’ SubjectInUseChanged method. This method is called whenever the SubjectInUse property has changed. This is a good place to enable or disable subcontrols.

protected override void SubjectInUseChanged()
{
base.SubjectInUseChanged();
bool bEnabled = false;
if (Subject != null)
if (! SubjectInUse)
  bEnabled = true;
chkCopyTwice.Enabled = bEnabled;
}

Create a private helper method to set the values of any subcontrols you’ve placed on the control. This is not strictly required, but it is often convenient to implement a private helper method that sets the value and state of any subcontrols based upon the values of your tool properties. This helper method presumes that it is being called when SubjectInUse is false.

private void SetuSettingsTab()
{
AssertSafe(); // ensures SubjectInUse is false

bool bChecked = false;
If (Subject != null)
If (Subject.CopyTwice)
  bChecked = true;
chkCopyTwice.Checked = bChecked;

bool bEnabled = false;
If (Subject != null)
bEnabled = true;
chkCopyTwice.Enabled = bEnabled;
}

Add event handlers to respond to user input from each subcontrol. Note that because we disable input from the subcontrols during unsafe times (when the tool is running from a non-GUI thread), it should be safe to access the tool from within these event handlers.

private void chkCopyTwice_CheckedChanged(
object sender,
EventArgs e)
{
if (Subject == null) // It should be safe, but check anyway …
return;
if (SubjectInUse)
return;
Subject.CopyTwice = chkCopyTwice.Checked;
}
Integrating the New Vision Tool with QuickBuild

Before you can integrate your new vision tool with QuickBuild, you will need to create a 16x16 pixel icon for it. The name of the icon file should be the name of your tool class with an .ICO extension.

Create a simple application that uses your tool, and use it to persist a default-constructed instance of your tool. Give the persistence file the same name as your tool plus the .VTT extension. For example, SimpleTool.vtt.

Note: VTT files are Vision Tool Templates that QuickBuild uses to find available tools.

Copy the tool assembly, any .dll files required for the user tool, and the icon file to \Program Files\Cognex\VisionPro\bin. Copy the VTT file to \Program Files\Cognex\VisionPro\bin\Templates\Tools.

Note: If you installed VisionPro in a directory other than \Program Files, use that directory instead.

Run QuickBuild to see your tool in the toolbox.

To add default terminals to your tool:

  1. Add it to a QuickBuild tool group.
  2. Right-click on the tool and select Add Terminals.
  3. To create a terminal, select a property, then click Add Input or Add Output.
  4. Open the tool's edit control and save the tool as a VTT file.
  5. Copy the new VTT file to \Program Files\Cognex\VisionPro\Templates\Tools to overwrite the one you created earlier.
  6. Close QuickBuild and restart it.

The next time you add the tool to a tool group, the new default terminals should appear.

For More Information

This walkthrough provides the basic information you need to create a vision tool. See the CogColorConversionTool sample to see a complete example of a fully developed tool.