Advanced Tutorial Image Processing C++

Example 1: Creating a New ML Module for Adding a Value to Each Voxel

Precondition

Make sure to have cmake installed. This example has been created using CMake Legacy Release (3.31.11).

Introduction

In this example, we develop our own C++ ML module, which adds a constant value to each voxel of the given input image.

Steps to Do

Create a New ML Module

Before creating the module, make sure to have your own user package available. See Package creation for details about Packages.

Use the Project Wizard via the menu entry [ File → Run Project Wizard ... ] to create a new ML module. Select ML Module and click Run Wizard.

ML Module Project Wizard

ML Module Project Wizard

Enter properties of your new module and give your module the name SimpleAdd. Make sure to select your user package and name your project SimpleAdd.

ML Module Properties

ML Module Properties

Click Next. The next screen of the Wizard allows you to define the inputs and outputs of your module. Select Module Type as New style ML Module, make sure to have one input and one output and leave the rest of the settings unchanged.

ML Module Properties

ML Module Properties

Click Next. On the next screen, we can define some additional properties of our module. Select Add activateAttachments(), unselect Add configuration hints and select Add MDL window with fields.

ML Module Additional Properties

ML Module Additional Properties

Click Next. The Module Field Interface allows you to define additional fields for the module. More fields can be added later but this is the easiest way to add fields. Click New to create a new field, then enter the following:

  • Field Name: constantValue
  • Field Type: Double
  • Field Comment: This constant value is added to each voxel.
  • Field Value: 0.

ML Module Field Interface

ML Module Field Interface

Click Create. You see a screen showing the results of the module creation process. In the case the Wizard finished succesfully, you can close the window. Additionally, an explorer window opens showing the created folder containing your sources and the CMakeLists.txt.

The foundation of the module has been created with the Wizard. From here on, the programming starts.

After module creation, the module database needs to be reloaded.

Preparing the Project

The Project Wizard creates a CMakeLists.txt file that describes the typical projects settings and used source files. This file can be translated manually with the CMake tool into a project file for your preferred C++ development tool. But most Integrated Development Environments (IDEs) nowadays can open CMake files directly.

Just make sure that the MLAB_ROOT environment variable is set on your system and points to the packages directory of your MeVisLab installation, because this is used to resolve the reference to the ‘MeVisLab’ project.

Open a command line and change to your current module directory (the directory containing your CMakeLists.txt file). Enter cmake . -G “Visual Studio 17”. After execution, a lot of files are generated by CMake.

For further documentation about our use of CMake, see: CMake for MeVisLab - Documentation.

Programming the Functions of the ML Module

Open the file ALL_BUILD.vcxproj in your preferred C++ development environment. Select the file mlSimpleAdd.cpp.

Implementing calculateOutputImageProperties

As we add a constant value to each voxel, we need to adjust the value range of the output image, which results in:

outMin = inMin + constValue
outMax = inMax + constValue

Change the file mlSimpleAdd.cpp as shown below.

mlSimpleAdd.cpp

void SimpleAdd::calculateOutputImageProperties(int /*outputIndex*/, PagedImage* outputImage)
{
  // Set up data types and read-only flags of output image and input subimages.
  SimpleAddOutputImageHandler::setupKnownProperties(outputImage);

  // Change properties of output image outputImage here whose
  // defaults are inherited from the input image 0 (if there is one).
  // Get the constant add value.
  const MLdouble constantValue = _constantValueFld->getDoubleValue();

  // Get the input image's minimum and maximum values.
  const MLdouble inMinValue = getInputImage(0)->getMinVoxelValue();
  const MLdouble inMaxValue = getInputImage(0)->getMaxVoxelValue();

  // Set the output image's minimum and maximum values.
  outputImage->setMinVoxelValue(inMinValue + constantValue);
  outputImage->setMaxVoxelValue(inMaxValue + constantValue);

  // Verify whether the input/output data types are supported by our handler.
  // This will invalidate the output image if the type combination is not supported by the handler.
  SimpleAddOutputImageHandler::verifyProperties(outputImage);
}

Implementing typedCalculateOutputSubImage

Next, we are going to finally change the voxel values of the image. Open the file mlSimpleAddOutputImageHandler.cpp. A loop over all voxels of the output page is generated automatically and we want to add the constant value to each voxel. Add the following line at the start of the method to obtain the current constant value in the correct data type:

mlSimpleAddOutputImageHandler.cpp

  // Compute subimage of output image outIndex from input subimages.
  const OUTTYPE constantValue = static_cast<OUTTYPE>(_parameters.constantValue);

Then, change the inner line of the loop so that the constant value is added to the value of the input voxel:

mlSimpleAddOutputImageHandler.cpp

    // Process all row voxels.
    for (; p.x <= rowEnd;  ++p.x, ++outVoxel, ++inVoxel0)
    {
      *outVoxel = *inVoxel0 + constantValue;
    }

Compile the project in the development environment. Make sure to select a Release build.

Use Your Module in MeVisLab

Your compiled *.dll is available in your project directory under Sources/lib. In order to use it in MeVisLab, it needs to be copied to the lib folder of your user package.

This only works in a post-build step.

If the environment variable MLAB_AUTOMATIC_POSTBUILD_COPY is set, the newly compiled DLLs and .lib files are copied to the correct location when MeVisLab restarts. Otherwise, they must be copied manually.

For testing purposes, you can use a LocalImage module and two View2D modules. Connect the SimpleAdd module to the second View2D and change the Constant Value field.

Testing Network

Testing Network

The output image of the module SimpleAdd is automatically recalculated on changing the field Constant Value. This is already implemented in the generated code of the file below:

mlSimpleAdd.cpp

    void SimpleAdd::handleNotification(Field* field)
    {
      // Handle changes of module parameters and input image fields here.
      bool touchOutputs = false;
      if (isInputImageField(field))
      {
        touchOutputs = true;
      }
      else if (field == _constantValueFld)
      {
        touchOutputs = true;
      }

      if (touchOutputs)
      {
        // Touch all output image fields to notify connected modules.
        touchOutputImageFields();
      }
    }

Summary

  • MeVisLab allows to develop your own C++ modules.
  • The Project Wizard already generates all necessary .cpp and .h files and a loop through all voxels of the input image.
  • Changes of user-defined fields automatically lead to a recalculation of the input image.