14.2. Creating an ML Module For Simple Average

In the following chapter, we will create a new ML module that calculates an average over voxel values, in the following steps:

[Tip]Tip

This example is delivered with MeVisLab (.def file in $(InstallDir)Packages/MeVisLab/Examples/Modules/GettingStarted/MLSimpleAverageExample, source files in $(InstallDir)Packages/MeVisLab/Examples/Sources/GettingStarted/MLSimpleAverage). The module can be added via quick search.

14.2.1. Creating the Basic ML Module with the Project Wizard

For the following example, we expect the user package Example/General to be available, see Section 14.1.1, “Creating the Basic ML Module with the Project Wizard”.

  1. Run the Project Wizard and select the link ML Module. This starts the Wizard for C++/ML Modules. Enter the following:

    1. Name: SimpleAverage

    2. Comment: Computes the average voxel value of an image.

    3. Keywords: Statistics Average

    4. See Also: ImageStatistics

    5. Target Package: Example/General

    6. Project: SimpleAverage

    Click Next to proceed.

  2. On the dialog Imaging Module Properties, the inputs and outputs as well as possible sample code can be added to the ML module.

    Select the Module Type Classic ML Module. For information on the differences, see the MeVisLab Reference Manual, chapter ML Wizard.

    Enter the following settings:

    • Inputs: 1

    • Outputs: 1

    • Check Add calculateOutputSubImage() template.

    • Check Add voxel loop to calculateOutputSubImage().

    [Note]Note

    Although we will have no real "output" of the module, it is helpful to create an output here, as this will add some of the ML methods necessary for the module functionality. It is easier to exchange or delete some code than to add new code sections manually.

    Click Next to proceed.

  3. On the dialog Additional Module Properties, check Add MDL window with fields.

    Click Next to proceed.

  4. On the dialog Module Field Interface, create two new fields:

    One field to keep the calculated value:

    • Field Name: voxelValueAverage

    • Field Type: Double

    • Field Value: 0.

    One field that will function as Update button:

    • Field Name: update

    • Field Type: Notify

  5. Click Create to create the module.

    In the default file browser of your system, two folders are opened:

    • folder with the source code: {packagePath}/Sources/ML/MLSimpleAverage

    • folder with the module's GUI definition: {packagePath}/Modules/ML/MLSimpleAverage

    [Note]Note

    For a full list of all created files and their contents, refer to the MeVisLab Reference Manual, chapter ML Module — Created Files.

  6. Reload the module database.

  7. Prepare the project, as described in Section 14.1.2, “Preparing the Project”.

14.2.2. Editing the Header File of SimpleAverage

  1. Open the file mlSimpleAverage.h.

  2. Add the following two lines to the private section

      //! 
      NotifyField* _updateFld;
      
      size_t _numVoxels;
      MLdouble _sumVoxelValues;

    They will be used as follows: All voxel values are added (_sumVoxelValues) and divided by the number of counted voxels (_numVoxels).

  3. Remove the following lines.

      //! Sets properties of the output image at output outIndex.
      virtual void calculateOutputImageProperties(int outputIndex, 
                                                  PagedImage* outputImage);

    The virtual function calling calculateOutputImageProperties has to be removed because there will be no image output. If the line is not removed, a warning will be generated by the compiler. (However, the calculateOutputSubImage template is necessary.)

14.2.3. Editing the CPP File of SimpleAverage

Open the file mlSimpleAverage.cpp.

[Note]Note

In the following code examples, the comment lines already available in the created .cpp file are added for better overview, when necessary.

  1. Change the constructor call of the superclass from Module(1,1) to Module(1,0).

    SimpleAverage::SimpleAverage () : Module(1, 0)

    This leaves our module with one input and no output image.

  2. Add the following code in the method handleNotification(Field* field).

      // Handle changes of module parameters and  input image fields here.
        if (field == _updateFld) 
      {
        _numVoxels = 0;
        _sumVoxelValues = 0;
    
        processAllPages(-1);
    
        MLdouble result = 0;
    
        if (_numVoxels > 0) 
        {
          result = _sumVoxelValues / static_cast<MLdouble>(_numVoxels);
        }
        _voxelValueAverageFld->setDoubleValue(result);
      }
    

    The code includes the important ML Module method processAllPages(). This method can be used in algorithms that only extract information from an image (but do not modify it). As the extraction of information is not driven by demand, the loop over all pages has to be implemented with processAllPages(). The provided parameter '-1' causes the input image to be read-only for optimization reasons. For further information, see the ML Guide.

  3. Remove the following lines, as no image will be output by this module.

    //------------------------------------------------------------------------------
    
    void SimpleAverage::calculateOutputImageProperties(int /*outputIndex*/, 
                                                       PagedImage* outputImage)
    {
      // Change properties of output image outputImage here whose
      // defaults are inherited from the input image 0 (if there is one).
    }
  4. In the method calculateOutputSubImage(...), remove outputSubImage and outputIndex from the method's signature. Result:

    template <typename T>
    void SimpleAverage::calculateOutputSubImage (TSubImage<T>* , 
                                         int , 
                                         TSubImage<T>* inputSubImage0
                                         )

    outIndex would reference an output image of the module which we do not have.

  5. Replace the line:

      const SubImageBox validOutBox = outputSubImage->getValidRegion()

    with the line:

      const SubImageBox inBox = inputSubImage0->getValidRegion();

    Resulting in:

      // Compute subimage of output image outIndex from input subimages.
      const SubImageBox inBox = inputSubImage0->getValidRegion();
  6. Remove the line

      T *outVoxel = outputSubImage->getImagePointer(p);
  7. Replace all occurrences of validOutBox with inBox.

  8. Replace the line

        *outVoxel = *inVoxel0; 

    with the lines:

        _sumVoxelValues += static_cast<MLdouble>(*inVoxel0);
        ++_numVoxels;

    Remove the ++outVoxel, from the inner for-loop over the voxel row.

    Resulting in:

      // Process all row voxels.
      for (; p.x <= rowEnd;  ++p.x, ++in0Voxel) {
        _sumVoxelValues += static_cast<MLdouble>(*in0Voxel);
        ++_numVoxels;
      }
  9. At last, compile the project. Then restart MeVisLab so that the new module is registered and added to the module database.

  10. In MeVisLab, instantiate the new module, right-click it and open the module's .script file.

    In the .script file, enter the following lines before the Window section:

    Description {
      Field voxelValueAverage { editable = No }
    }

    Setting the editable of a field to No (or False or 0) has two consequences: firstly, the field is not editable by the user which makes sense, because the field should be set from the C++ code only, and secondly, the value of the field is not saved with the .mlab file which makes sense, because the value needs to be calculated in a live network.

14.2.4. Testing the Module

Now you can use the new module in MeVisLab.

  1. Add your new module SimpleAverage and a LocalImage module to a new network. Connect them and load an image.

  2. Then double-click SimpleAverage to open its automatic panel and click the Update button on the module panel. The calculated output of SimpleAverage is displayed.

A module with a similar functionality is available in MeVisLab, called ImageStatistics.

Add ImageStatistics via the quick search and compare its mean value with the displayed value of SimpleAverage. You will find that the results are almost the same apart from the rounding error in the display.

[Tip]Tip

This test network is delivered as the example network for SimpleAverageExample.