Medical Imaging Interaction Toolkit  2023.04.00
Medical Imaging Interaction Toolkit
MITK Tutorial - Step 6: Use an interactive region-grower

The source is now split among several files:

In this step the program is enhanced by the possibility to start a region-grower at interactively added points. We will see how MITK images can be accessed as ITK images. We now load the image file Pic3D.nrrd only since the surface will be the result of the region-growing.

Add points in the image by pressing SHIFT+left mouse key, then adjust the thresholds and press 'Start region growing'.


The class Step6 inherits from QWidget and provides methods for setting up the widgets. Step6RegionGrowing.cpp contains a method for performing the region-growing. Step6main.cpp contains main. Like in ITK and VTK class member names start with m_ followed by the proper member name starting with a capital letter (e.g. m_Tree). Function names start with capital letters. To learn more about style conventions in MITK read The MITK Style Guide.

The widgets are initialized as in the previous steps but with an additional QVBox for a button to start the segmentation:

// Create controlsParent widget with horizontal layout
QWidget *controlsParent = new QWidget(this);
this->layout()->addWidget(controlsParent);
QHBoxLayout *hlayout = new QHBoxLayout(controlsParent);
hlayout->setSpacing(2);
QLabel *labelThresholdMin = new QLabel("Lower Threshold:", controlsParent);
hlayout->addWidget(labelThresholdMin);
m_LineEditThresholdMin = new QLineEdit("-1000", controlsParent);
hlayout->addWidget(m_LineEditThresholdMin);
QLabel *labelThresholdMax = new QLabel("Upper Threshold:", controlsParent);
hlayout->addWidget(labelThresholdMax);
m_LineEditThresholdMax = new QLineEdit("-400", controlsParent);
hlayout->addWidget(m_LineEditThresholdMax);

This creates a button to start the segmentation and its clicked() signal is connected to the method StartRegionGrowing():

QPushButton *startButton = new QPushButton("start region growing", controlsParent);
connect(startButton, SIGNAL(clicked()), this, SLOT(StartRegionGrowing()));

Access MITK images as ITK images

ITK images are templated whereas mitk::Images are not. To use ITK filters with MITK images, we have to convert from MITK to ITK. To do so, first define an access method, which is templated as an ITK image is:

template<TPixel, VImageDimension>
MyAccessMethod(itk::Image<TPixel, VImageDimension>* itkImage)
{
...
}

If you don't understand this template syntax, you should read any C++ text book. Understanding template syntax is crucial to successfully using ITK.

To call this templated method with an (untemplated) mitk::Image, you can use the AccessByItk macro from mitkImageAccessByItk.h. This macro checks for the actual image type of the mitk::Image and does any neccessary conversions. Look into "Modules / Adaptor classes" for more information.

AccessByItk(mitkImage, MyAccessMethod)

In this step our access method is called RegionGrowing() (defined in Step6RegionGrowing.txx ):

template <typename TPixel, unsigned int VImageDimension>
void RegionGrowing(itk::Image<TPixel, VImageDimension> *itkImage, Step6 *step6)
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
typedef float InternalPixelType;
typedef itk::Image<InternalPixelType, VImageDimension> InternalImageType;
mitk::BaseGeometry *geometry = step6->m_FirstImage->GetGeometry();
// create itk::CurvatureFlowImageFilter for smoothing and set itkImage as input
typedef itk::CurvatureFlowImageFilter<ImageType, InternalImageType> CurvatureFlowFilter;
typename CurvatureFlowFilter::Pointer smoothingFilter = CurvatureFlowFilter::New();
smoothingFilter->SetInput(itkImage);
smoothingFilter->SetNumberOfIterations(4);
smoothingFilter->SetTimeStep(0.0625);
// create itk::ConnectedThresholdImageFilter and set filtered image as input
typedef itk::ConnectedThresholdImageFilter<InternalImageType, ImageType> RegionGrowingFilterType;
typedef typename RegionGrowingFilterType::IndexType IndexType;
typename RegionGrowingFilterType::Pointer regGrowFilter = RegionGrowingFilterType::New();
regGrowFilter->SetInput(smoothingFilter->GetOutput());
regGrowFilter->SetLower(step6->GetThresholdMin());
regGrowFilter->SetUpper(step6->GetThresholdMax());
// convert the points in the PointSet m_Seeds (in world-coordinates) to
// "index" values, i.e. points in pixel coordinates, and add these as seeds
// to the RegionGrower
mitk::PointSet::PointsConstIterator pit, pend = step6->m_Seeds->GetPointSet()->GetPoints()->End();
IndexType seedIndex;
for (pit = step6->m_Seeds->GetPointSet()->GetPoints()->Begin(); pit != pend; ++pit)
{
geometry->WorldToIndex(pit.Value(), seedIndex);
regGrowFilter->AddSeed(seedIndex);
}
regGrowFilter->GetOutput()->Update();
mitk::Image::Pointer mitkImage = mitk::Image::New();
mitk::CastToMitkImage(regGrowFilter->GetOutput(), mitkImage);
if (step6->m_ResultNode.IsNull())
{
step6->m_ResultNode = mitk::DataNode::New();
step6->m_DataStorage->Add(step6->m_ResultNode);
}
step6->m_ResultNode->SetData(mitkImage);
// set some additional properties
step6->m_ResultNode->SetProperty("name", mitk::StringProperty::New("segmentation"));
step6->m_ResultNode->SetProperty("binary", mitk::BoolProperty::New(true));
step6->m_ResultNode->SetProperty("color", mitk::ColorProperty::New(1.0, 0.0, 0.0));
step6->m_ResultNode->SetProperty("volumerendering", mitk::BoolProperty::New(true));
step6->m_ResultNode->SetProperty("layer", mitk::IntProperty::New(1));
mitk::LevelWindowProperty::Pointer levWinProp = mitk::LevelWindowProperty::New();
mitk::LevelWindow levelwindow;
levelwindow.SetAuto(mitkImage);
levWinProp->SetLevelWindow(levelwindow);
step6->m_ResultNode->SetProperty("levelwindow", levWinProp);
step6->m_ResultImage = static_cast<mitk::Image *>(step6->m_ResultNode->GetData());
} //RegionGrowing()

Additionally the access function has to be instantiated for all datatypes and two/three dimensions as some compilers have memory problems without this explicit instantiation, some even need instantiations in separate files for 2D/3D:
For 2D in Step6RegionGrowing1.cpp :

... and for 3D in Step6RegionGrowing2.cpp :

The method StartRegionGrowing() finally calls our access method RegionGrowing():

Converting ITK images to MITK images and vice versa

In some cases it is useful to simply convert between ITK and MITK images. The direction ITK to MITK is easy, since mitk::Image can handle most data types. The direction MITK to ITK is more critical, since ITK images have to be instantiated with a fixed pixel type and fixed dimension at compile time.

Connecting MITK images to VTK

Images are not converted or copied: The data array is just accessed via an encapsulating VTK object.

MITK Surfaces to VTK and vice versa

Again: not a conversion, just accessing.

[Previous step] [Next step] [Main tutorial page]

mitk::ImportItkImage
Image::Pointer ImportItkImage(const itk::SmartPointer< ItkOutputImageType > &itkimage, const BaseGeometry *geometry=nullptr, bool update=true)
Imports an itk::Image (with a specific type) as an mitk::Image.
mitk::RenderingManager::GetInstance
static RenderingManager * GetInstance()
mitk::Image
Image class for storing images.
Definition: mitkImage.h:69
mitk::RenderingManager::RequestUpdateAll
void RequestUpdateAll(RequestType type=REQUEST_UPDATE_ALL)
Step6::StartRegionGrowing
virtual void StartRegionGrowing()
AccessByItk_1
#define AccessByItk_1(mitkImage, itkImageTypeFunction, arg1)
Definition: mitkImageAccessByItk.h:565
mitk::CastToItkImage
void MITKCORE_EXPORT CastToItkImage(const mitk::Image *mitkImage, itk::SmartPointer< ItkOutputImageType > &itkOutputImage)
Cast an mitk::Image to an itk::Image with a specific type.
mitk::Surface::SetVtkPolyData
virtual void SetVtkPolyData(vtkPolyData *polydata, unsigned int t=0)
InstantiateAccessFunctionForFixedDimension
#define InstantiateAccessFunctionForFixedDimension(itkImgFunc, dim)
Instantiate access function for all datatypes and a specific dimension.
Definition: mitkInstantiateAccessFunctions.h:108
mitk::Image::GetVtkImageData
virtual vtkImageData * GetVtkImageData(int t=0, int n=0)
Get a volume at a specific time t of channel n as a vtkImageData.
mitk::Surface::GetVtkPolyData
virtual vtkPolyData * GetVtkPolyData(unsigned int t=0) const
AccessByItk
#define AccessByItk(mitkImage, itkImageTypeFunction)
Access a MITK image by an ITK image.
Definition: mitkImageAccessByItk.h:188