diff --git a/Modules/ContourModel/Algorithms/mitkContourModelUtils.cpp b/Modules/ContourModel/Algorithms/mitkContourModelUtils.cpp
index 25c916e624..e53d577736 100755
--- a/Modules/ContourModel/Algorithms/mitkContourModelUtils.cpp
+++ b/Modules/ContourModel/Algorithms/mitkContourModelUtils.cpp
@@ -1,294 +1,292 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include <mitkContourModelUtils.h>
 
 #include <mitkContourModelToSurfaceFilter.h>
 #include <mitkLabelSetImage.h>
 #include <mitkSurface.h>
 #include <vtkImageStencil.h>
 #include <vtkPointData.h>
 #include <vtkPolyData.h>
 #include <vtkPolyDataToImageStencil.h>
 
 mitk::ContourModelUtils::ContourModelUtils()
 {
 }
 
 mitk::ContourModelUtils::~ContourModelUtils()
 {
 }
 
 mitk::ContourModel::Pointer mitk::ContourModelUtils::ProjectContourTo2DSlice(
   const Image *slice, const ContourModel *contourIn3D)
 {
   if (nullptr == slice || nullptr == contourIn3D)
     return nullptr;
 
   auto projectedContour = ContourModel::New();
   projectedContour->Initialize(*contourIn3D);
 
   auto sliceGeometry = slice->GetGeometry();
   const auto numberOfTimesteps = static_cast<TimeStepType>(contourIn3D->GetTimeSteps());
 
   for (std::remove_const_t<decltype(numberOfTimesteps)> t = 0; t < numberOfTimesteps; ++t)
   {
     auto iter = contourIn3D->Begin(t);
     auto end = contourIn3D->End(t);
 
     while (iter != end)
     {
       const auto &currentPointIn3D = (*iter)->Coordinates;
 
       Point3D projectedPointIn2D;
       projectedPointIn2D.Fill(0.0);
 
       sliceGeometry->WorldToIndex(currentPointIn3D, projectedPointIn2D);
 
       projectedContour->AddVertex(projectedPointIn2D, t);
       ++iter;
     }
   }
 
   return projectedContour;
 }
 
 mitk::ContourModel::Pointer mitk::ContourModelUtils::BackProjectContourFrom2DSlice(
   const BaseGeometry *sliceGeometry, const ContourModel *contourIn2D)
 {
   if (nullptr == sliceGeometry || nullptr == contourIn2D)
     return nullptr;
 
   auto worldContour = ContourModel::New();
   worldContour->Initialize(*contourIn2D);
 
   const auto numberOfTimesteps = static_cast<TimeStepType>(contourIn2D->GetTimeSteps());
 
   for (std::remove_const_t<decltype(numberOfTimesteps)> t = 0; t < numberOfTimesteps; ++t)
   {
     auto iter = contourIn2D->Begin(t);
     auto end = contourIn2D->End(t);
 
     while (iter != end)
     {
       const auto &currentPointIn2D = (*iter)->Coordinates;
 
       Point3D worldPointIn3D;
       worldPointIn3D.Fill(0.0);
 
       sliceGeometry->IndexToWorld(currentPointIn2D, worldPointIn3D);
 
       worldContour->AddVertex(worldPointIn3D, t);
       ++iter;
     }
   }
 
   return worldContour;
 }
 
 void mitk::ContourModelUtils::FillContourInSlice2(
   const ContourModel* projectedContour, Image* sliceImage, int paintingPixelValue)
 {
   FillContourInSlice2(projectedContour, 0, sliceImage, paintingPixelValue);
 }
 
 void mitk::ContourModelUtils::FillContourInSlice2(
   const ContourModel* projectedContour, TimeStepType contourTimeStep, Image* sliceImage, int paintingPixelValue)
 {
   if (nullptr == projectedContour)
   {
     mitkThrow() << "Cannot fill contour in slice. Passed contour is invalid";
   }
 
   if (nullptr == sliceImage)
   {
     mitkThrow() << "Cannot fill contour in slice. Passed slice is invalid";
   }
 
   auto contourModelFilter = mitk::ContourModelToSurfaceFilter::New();
   contourModelFilter->SetInput(projectedContour);
   contourModelFilter->Update();
 
   auto surface = mitk::Surface::New();
   surface = contourModelFilter->GetOutput();
 
   if (nullptr == surface->GetVtkPolyData(contourTimeStep))
   {
     MITK_WARN << "Could not create surface from contour model.";
     return;
   }
 
   auto surface2D = vtkSmartPointer<vtkPolyData>::New();
   surface2D->SetPoints(surface->GetVtkPolyData(contourTimeStep)->GetPoints());
   surface2D->SetLines(surface->GetVtkPolyData(contourTimeStep)->GetLines());
 
   auto polyDataToImageStencil = vtkSmartPointer<vtkPolyDataToImageStencil>::New();
 
   // Set a minimal tolerance, so that clipped pixels will be added to contour as well.
   polyDataToImageStencil->SetTolerance(mitk::eps);
   polyDataToImageStencil->SetInputData(surface2D);
   polyDataToImageStencil->Update();
 
   auto imageStencil = vtkSmartPointer<vtkImageStencil>::New();
 
   imageStencil->SetInputData(sliceImage->GetVtkImageData());
   imageStencil->SetStencilConnection(polyDataToImageStencil->GetOutputPort());
   imageStencil->ReverseStencilOn();
   imageStencil->SetBackgroundValue(paintingPixelValue);
   imageStencil->Update();
 
   vtkSmartPointer<vtkImageData> filledImage = imageStencil->GetOutput();
 
   sliceImage->SetVolume(filledImage->GetScalarPointer());
 }
 
 void mitk::ContourModelUtils::FillContourInSlice(
   const ContourModel *projectedContour, Image *sliceImage, const Image* workingImage, int paintingPixelValue)
 {
   FillContourInSlice(projectedContour, 0, sliceImage, workingImage, paintingPixelValue);
 }
 
 void mitk::ContourModelUtils::FillContourInSlice(
   const ContourModel *projectedContour, TimeStepType contourTimeStep, Image *sliceImage, const Image* workingImage, int paintingPixelValue)
 {
   if (nullptr == projectedContour)
   {
     mitkThrow() << "Cannot fill contour in slice. Passed contour is invalid";
   }
 
   if (nullptr == sliceImage)
   {
     mitkThrow() << "Cannot fill contour in slice. Passed slice is invalid";
   }
 
   auto contourModelFilter = mitk::ContourModelToSurfaceFilter::New();
   contourModelFilter->SetInput(projectedContour);
   contourModelFilter->Update();
 
   auto surface = mitk::Surface::New();
   surface = contourModelFilter->GetOutput();
 
   if (nullptr == surface->GetVtkPolyData(contourTimeStep))
   {
     MITK_WARN << "Could not create surface from contour model.";
     return;
   }
 
   auto surface2D = vtkSmartPointer<vtkPolyData>::New();
   surface2D->SetPoints(surface->GetVtkPolyData(contourTimeStep)->GetPoints());
   surface2D->SetLines(surface->GetVtkPolyData(contourTimeStep)->GetLines());
 
   auto image = vtkSmartPointer<vtkImageData>::New();
   image->DeepCopy(sliceImage->GetVtkImageData());
 
   const double FOREGROUND_VALUE = 255.0;
   const double BACKGROUND_VALUE = 0.0;
 
   const vtkIdType count = image->GetNumberOfPoints();
   for (std::remove_const_t<decltype(count)> i = 0; i < count; ++i)
     image->GetPointData()->GetScalars()->SetTuple1(i, FOREGROUND_VALUE);
 
   auto polyDataToImageStencil = vtkSmartPointer<vtkPolyDataToImageStencil>::New();
 
   // Set a minimal tolerance, so that clipped pixels will be added to contour as well.
   polyDataToImageStencil->SetTolerance(mitk::eps);
   polyDataToImageStencil->SetInputData(surface2D);
   polyDataToImageStencil->Update();
 
   auto imageStencil = vtkSmartPointer<vtkImageStencil>::New();
 
   imageStencil->SetInputData(image);
   imageStencil->SetStencilConnection(polyDataToImageStencil->GetOutputPort());
   imageStencil->ReverseStencilOff();
   imageStencil->SetBackgroundValue(BACKGROUND_VALUE);
   imageStencil->Update();
 
   vtkSmartPointer<vtkImageData> filledImage = imageStencil->GetOutput();
   vtkSmartPointer<vtkImageData> resultImage = sliceImage->GetVtkImageData();
   FillSliceInSlice(filledImage, resultImage, workingImage, paintingPixelValue);
 
   sliceImage->SetVolume(resultImage->GetScalarPointer());
 }
 
 void mitk::ContourModelUtils::FillSliceInSlice(
   vtkSmartPointer<vtkImageData> filledImage, vtkSmartPointer<vtkImageData> resultImage, const Image* image, int paintingPixelValue, double fillForegroundThreshold)
 {
   auto labelImage = dynamic_cast<const LabelSetImage *>(image);
   const auto numberOfPoints = filledImage->GetNumberOfPoints();
 
   if (nullptr == labelImage)
   {
     for (std::remove_const_t<decltype(numberOfPoints)> i = 0; i < numberOfPoints; ++i)
     {
       if (fillForegroundThreshold <= filledImage->GetPointData()->GetScalars()->GetTuple1(i))
         resultImage->GetPointData()->GetScalars()->SetTuple1(i, paintingPixelValue);
     }
   }
   else
   {
-    const auto backgroundValue = labelImage->GetExteriorLabel()->GetValue();
-
-    if (paintingPixelValue != backgroundValue)
+    if (paintingPixelValue != LabelSetImage::UnlabeledValue)
     {
       for (std::remove_const_t<decltype(numberOfPoints)> i = 0; i < numberOfPoints; ++i)
       {
         const auto filledValue = filledImage->GetPointData()->GetScalars()->GetTuple1(i);
         if (fillForegroundThreshold <= filledValue)
         {
           const auto existingValue = resultImage->GetPointData()->GetScalars()->GetTuple1(i);
 
-          if (!labelImage->GetLabel(existingValue, labelImage->GetActiveLayer())->GetLocked())
+          if (!labelImage->IsLabelLocked(existingValue))
             resultImage->GetPointData()->GetScalars()->SetTuple1(i, paintingPixelValue);
         }
       }
     }
     else
     {
       const auto activePixelValue = labelImage->GetActiveLabel(labelImage->GetActiveLayer())->GetValue();
       for (std::remove_const_t<decltype(numberOfPoints)> i = 0; i < numberOfPoints; ++i)
       {
         if (fillForegroundThreshold <= filledImage->GetPointData()->GetScalars()->GetTuple1(i))
         {
           if (resultImage->GetPointData()->GetScalars()->GetTuple1(i) == activePixelValue)
             resultImage->GetPointData()->GetScalars()->SetTuple1(i, paintingPixelValue);
         }
       }
     }
   }
 }
 
 mitk::ContourModel::Pointer mitk::ContourModelUtils::MoveZerothContourTimeStep(const ContourModel *contour, TimeStepType t)
 {
   if (nullptr == contour)
     return nullptr;
 
   auto resultContour = ContourModel::New();
   resultContour->Expand(t + 1);
 
   std::for_each(contour->Begin(), contour->End(), [&resultContour, t](ContourElement::VertexType *vertex) {
     resultContour->AddVertex(*vertex, t);
   });
 
   return resultContour;
 }
 
 int mitk::ContourModelUtils::GetActivePixelValue(const Image* workingImage)
 {
   auto labelSetImage = dynamic_cast<const LabelSetImage*>(workingImage);
   int activePixelValue = 1;
   if (nullptr != labelSetImage)
   {
     activePixelValue = labelSetImage->GetActiveLabel(labelSetImage->GetActiveLayer())->GetValue();
   }
 
   return activePixelValue;
 }
diff --git a/Modules/ContourModel/Algorithms/mitkContourModelUtils.h b/Modules/ContourModel/Algorithms/mitkContourModelUtils.h
index e97d315291..769634ae1d 100644
--- a/Modules/ContourModel/Algorithms/mitkContourModelUtils.h
+++ b/Modules/ContourModel/Algorithms/mitkContourModelUtils.h
@@ -1,155 +1,155 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #ifndef mitkContourModelUtils_h
 #define mitkContourModelUtils_h
 
 #include <mitkContourModel.h>
 #include <mitkImage.h>
 #include <vtkSmartPointer.h>
 
 #include <MitkContourModelExports.h>
 
 namespace mitk
 {
   /**
    * \brief Helpful methods for working with contours and images
    *
    *
    */
   class MITKCONTOURMODEL_EXPORT ContourModelUtils : public itk::Object
   {
   public:
     mitkClassMacroItkParent(ContourModelUtils, itk::Object);
 
     /**
       \brief Projects a contour onto an image point by point. Converts from world to index coordinates.
 
       \param slice
       \param contourIn3D
     */
     static ContourModel::Pointer ProjectContourTo2DSlice(const Image *slice,
                                                          const ContourModel *contourIn3D);
 
     /**
       \brief Projects a slice index coordinates of a contour back into world coordinates.
 
       \param sliceGeometry
       \param contourIn2D
     */
     static ContourModel::Pointer BackProjectContourFrom2DSlice(const BaseGeometry *sliceGeometry,
                                                                const ContourModel *contourIn2D);
 
     /**
     \brief Fill a contour in a 2D slice with a specified pixel value.
     This version always uses the contour of time step 0 and fills the image.
     \deprecated Ths function is deprecated. Use FillContourInSlice2() (in
     conjunction e.g. with TransferLabelContent()) instead.
     \pre sliceImage points to a valid instance
     \pre projectedContour points to a valid instance
     */
-    [[deprecated]]
-    static void FillContourInSlice(const ContourModel *projectedContour,
+    //[[deprecated]]
+    DEPRECATED(static void FillContourInSlice(const ContourModel *projectedContour,
                                    Image *sliceImage,
                                    const Image* workingImage,
-                                   int paintingPixelValue = 1);
+                                   int paintingPixelValue = 1));
 
     /**
     \brief Fill a contour in a 2D slice with a specified pixel value.
     This overloaded version uses the contour at the passed contourTimeStep
     to fill the passed image slice.
     \deprecated Ths function is deprecated. Use FillContourInSlice2() (in
-    conjunction e.g. with TransferLabelContent()) instead.
+    conjunction e.g. with TransferLabelContentAtTimeStep()) instead.
     \pre sliceImage points to a valid instance
     \pre projectedContour points to a valid instance
     */
-    [[deprecated]]
-    static void FillContourInSlice(const ContourModel *projectedContour,
+    //[[deprecated]]
+    DEPRECATED(static void FillContourInSlice(const ContourModel *projectedContour,
                                    TimeStepType contourTimeStep,
                                    Image *sliceImage,
                                    const Image* workingImage,
-                                   int paintingPixelValue = 1);
+                                   int paintingPixelValue = 1));
 
     /**
     \brief Fill a contour in a 2D slice with a specified pixel value.
     This version always uses the contour of time step 0 and fills the image.
     \param projectedContour Pointer to the contour that should be projected.
     \param sliceImage Pointer to the image which content should be altered by
     adding the contour with the specified paintingPixelValue.
     \param paintingPixelValue
     \pre sliceImage points to a valid instance
     \pre projectedContour points to a valid instance
     */
     static void FillContourInSlice2(const ContourModel* projectedContour,
       Image* sliceImage,
       int paintingPixelValue = 1);
 
     /**
     \brief Fill a contour in a 2D slice with a specified pixel value.
     This overloaded version uses the contour at the passed contourTimeStep
     to fill the passed image slice.
     \param projectedContour Pointer to the contour that should be projected.
     \param contourTimeStep
     \param sliceImage Pointer to the image which content should be altered by
     \param paintingPixelValue
     adding the contour with the specified paintingPixelValue.
     \pre sliceImage points to a valid instance
     \pre projectedContour points to a valid instance
     */
     static void FillContourInSlice2(const ContourModel* projectedContour,
       TimeStepType contourTimeStep,
       Image* sliceImage,
       int paintingPixelValue = 1);
 
     /**
     \brief Fills the paintingPixelValue into every pixel of resultImage as indicated by filledImage.
     If a LableSet image is specified it also by incorporating the rules of LabelSet images when filling the content.
     \param filledImage Pointer to the image content that should be checked to decied of a pixel in resultImage should
     be filled with paintingPixelValue or not.
     \param resultImage Pointer to the image content that should be overwritten guided by the content of filledImage.
     \param image Pointer to an mitk image that allows to define the LabelSet image which states steer the filling process.
     If an LabelSet instance is passed its states (e.g. locked labels etc...) will be used. If nullptr or an normal image
     is passed, then simply any pixel position indicated by filledImage will be overwritten.
     \param paintingPixelValue the pixelvalue/label that should be used in the result image when filling.
     \param fillForegroundThreshold The threshold value that decides if a pixel in the filled image counts
     as foreground (>=fillForegroundThreshold) or not.
     \deprecated Ths function is deprecated. Use TransferLabelContent() instead.
     */
     [[deprecated]]
     static void FillSliceInSlice(vtkSmartPointer<vtkImageData> filledImage,
                                  vtkSmartPointer<vtkImageData> resultImage,
                                  const Image* image,
                                  int paintingPixelValue,
                                  double fillForegroundThreshold = 1.0);
 
     /**
     \brief Move the contour in time step 0 to to a new contour model at the given time step.
     */
     static ContourModel::Pointer MoveZerothContourTimeStep(const ContourModel *contour, TimeStepType timeStep);
 
     /**
     \brief Retrieves the active pixel value of a (labelset) image.
            If the image is basic image, the pixel value 1 (one) will be returned.
            If the image is actually a labelset image, the pixel value of the active label of the active layer will be
            returned.
 
     \param workingImage   The (labelset) image to retrieve the active pixel value of.
     */
     static int GetActivePixelValue(const Image* workingImage);
 
   protected:
     ContourModelUtils();
     ~ContourModelUtils() override;
   };
 }
 
 #endif
diff --git a/Modules/Core/include/mitkITKImageImport.h b/Modules/Core/include/mitkITKImageImport.h
index 76286d4770..6716a467f7 100644
--- a/Modules/Core/include/mitkITKImageImport.h
+++ b/Modules/Core/include/mitkITKImageImport.h
@@ -1,217 +1,243 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #ifndef mitkITKImageImport_h
 #define mitkITKImageImport_h
 
 #include "itkImageToImageFilterDetail.h"
 #include "mitkImageSource.h"
 #include <MitkCoreExports.h>
 
 namespace mitk
 {
   /**
   * @brief Pipelined import of itk::Image
   *
   * The image data contained in the itk::Image is referenced,
   * not copied.
   * The easiest way of use is by the function
   * mitk::ImportItkImage
   * \code
   * mitkImage = mitk::ImportItkImage(itkImage);
   * \endcode
   * \sa ImportItkImage
   * @ingroup Adaptor
   */
   template <class TInputImage>
   class MITK_EXPORT ITKImageImport : public ImageSource
   {
   public:
     mitkClassMacro(ITKImageImport, ImageSource);
     itkFactorylessNewMacro(Self);
     itkCloneMacro(Self);
 
       ///  \brief The type of the input image.
       typedef TInputImage InputImageType;
     typedef typename InputImageType::Pointer InputImagePointer;
     typedef typename InputImageType::ConstPointer InputImageConstPointer;
     typedef typename InputImageType::RegionType InputImageRegionType;
     typedef typename InputImageType::PixelType InputImagePixelType;
 
     /** ImageDimension constants */
     itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension);
     itkStaticConstMacro(RegionDimension, unsigned int, mitk::SlicedData::RegionDimension);
 
     /** \brief Set the input itk::Image of this image importer.  */
     InputImageType *GetInput(void);
 
     /** \brief Set the input itk::Image of this image importer. */
     void SetInput(const InputImageType *);
     using itk::ProcessObject::SetInput;
 
     /**
     * \brief Set the Geometry of the result image (optional)
     *
     * The Geometry has to fit the dimension and size of
     * the input image. The Geometry will be cloned, not
     * referenced!
     *
     * Providing the Geometry is optional.
     * The default behavior is to set the geometry by
     * the itk::Image::GetDirection() information.
     */
     void SetGeometry(const BaseGeometry *geometry);
 
   protected:
     ITKImageImport();
 
     ~ITKImageImport() override;
 
     void GenerateOutputInformation() override;
 
     void GenerateInputRequestedRegion() override;
 
     void GenerateData() override;
 
     void SetNthOutput(DataObjectPointerArraySizeType num, itk::DataObject *output) override;
 
     /** Typedef for the region copier function object that converts an
      * output region to an input region. */
     typedef itk::ImageToImageFilterDetail::ImageRegionCopier<itkGetStaticConstMacro(InputImageDimension),
                                                              itkGetStaticConstMacro(RegionDimension)>
       OutputToInputRegionCopierType;
 
     BaseGeometry::Pointer m_Geometry;
   };
 
   /**
   * @brief Imports an itk::Image (with a specific type) as an mitk::Image.
   * @ingroup Adaptor
   *
   * Instantiates instance of ITKImageImport.
   * mitk::ITKImageImport does not cast pixel types etc., it just imports
   * image data. If you get a compile error, try image.GetPointer().
   *
   * \param itkimage
   * \param geometry
   * 
   * \param update if \a true, fill mitk::Image, which will execute the
   * up-stream pipeline connected to the input itk::Image. Otherwise you
   * need to make sure that Update() is called on the mitk::Image before
   * its data is being used, e.g., by connecting it to an mitk-pipeline
   * and call Update of a downstream filter at some time.
   * \sa itk::Image::CastToMitkImage
   */
   template <typename ItkOutputImageType>
   Image::Pointer ImportItkImage(const itk::SmartPointer<ItkOutputImageType> &itkimage,
                                 const BaseGeometry *geometry = nullptr,
                                 bool update = true);
 
   /**
   * @brief Imports an itk::Image (with a specific type) as an mitk::Image.
   * @ingroup Adaptor
   *
   * Instantiates instance of ITKImageImport
   * mitk::ITKImageImport does not cast pixel types etc., it just imports
   * image data. If you get a compile error, try image.GetPointer().
   * 
   * \param itkimage
   * \param geometry
   *
   * \param update if \a true, fill mitk::Image, which will execute the
   * up-stream pipeline connected to the input itk::Image. Otherwise you
   * need to make sure that Update() is called on the mitk::Image before
   * its data is being used, e.g., by connecting it to an mitk-pipeline
   * and call Update of a downstream filter at some time.
   *
   *
   * \note If the source (itk image) and the target (mitk image) do not share the same scope, the
   * mitk::GrabItkImageMemory
   * function
   *  has to be used instead. Otherwise the image memory managed by the itk image is lost at a scope level change. This
   * affects especially the
   * usage in combination with AccessByItk macros as in following example code
   *
   * \snippet test/mitkGrabItkImageMemoryTest.cpp OutOfScopeCall
   *
   * which calls an ITK-like filter
   *
   * \snippet test/mitkGrabItkImageMemoryTest.cpp ItkThresholdFilter
   *
   *
   * \sa itk::Image::CastToMitkImage
   * \sa GrabItkImageMemory
   */
   template <typename ItkOutputImageType>
   Image::Pointer ImportItkImage(const ItkOutputImageType *itkimage,
                                 const BaseGeometry *geometry = nullptr,
                                 bool update = true);
 
   /**
   * @brief Grabs the memory of an itk::Image (with a specific type)
   * and puts it into an mitk::Image.
   * @ingroup Adaptor
   *
   * The memory is managed by the mitk::Image after calling this
   * function. The itk::Image remains valid until the mitk::Image
   * decides to free the memory.
   * 
   * \param itkimage
   * \param mitkImage
   * \param geometry
   * 
   * \param update if \a true, fill mitk::Image, which will execute the
   * up-stream pipeline connected to the input itk::Image. Otherwise you
   * need to make sure that Update() is called on the mitk::Image before
   * its data is being used, e.g., by connecting it to an mitk-pipeline
   * and call Update of a downstream filter at some time.
   * \sa ImportItkImage
   */
   template <typename ItkOutputImageType>
   Image::Pointer GrabItkImageMemory(itk::SmartPointer<ItkOutputImageType> &itkimage,
                                     mitk::Image *mitkImage = nullptr,
                                     const BaseGeometry *geometry = nullptr,
                                     bool update = true);
 
   /**
   * @brief Grabs the memory of an itk::Image (with a specific type)
   * and puts it into an mitk::Image.
   * @ingroup Adaptor
   *
   * The memory is managed by the mitk::Image after calling this
   * function. The itk::Image remains valid until the mitk::Image
   * decides to free the memory.
   * 
   * \param itkimage
   * \param mitkImage
   * \param geometry
   * 
   * \param update if \a true, fill mitk::Image, which will execute the
   * up-stream pipeline connected to the input itk::Image. Otherwise you
   * need to make sure that Update() is called on the mitk::Image before
   * its data is being used, e.g., by connecting it to an mitk-pipeline
   * and call Update of a downstream filter at some time.
   * \sa ImportItkImage
   */
   template <typename ItkOutputImageType>
   Image::Pointer GrabItkImageMemory(ItkOutputImageType *itkimage,
                                     mitk::Image *mitkImage = nullptr,
                                     const BaseGeometry *geometry = nullptr,
                                     bool update = true);
 
+  /**
+  * @brief Grabs the memory of an itk::Image (with a specific type)
+  * and puts it into an mitk::Image.
+  * @ingroup Adaptor
+  *
+  * The memory is managed by the mitk::Image after calling this
+  * function. The itk::Image remains valid until the mitk::Image
+  * decides to free the memory.
+  *
+  * \param itkimage
+  * \param mitkImage
+  * \param geometry
+  *
+  * \param update if \a true, fill mitk::Image, which will execute the
+  * up-stream pipeline connected to the input itk::Image. Otherwise you
+  * need to make sure that Update() is called on the mitk::Image before
+  * its data is being used, e.g., by connecting it to an mitk-pipeline
+  * and call Update of a downstream filter at some time.
+  * \sa ImportItkImage
+  */
+  template <typename ItkOutputImageType>
+  Image::Pointer GrabItkImageMemoryChannel(ItkOutputImageType* itkimage,
+    const TimeGeometry* geometry = nullptr,
+    mitk::Image* mitkImage = nullptr,
+    bool update = true);
+
 } // namespace mitk
 
 #ifndef MITK_MANUAL_INSTANTIATION
 #include "mitkITKImageImport.txx"
 #endif
 
 #endif
diff --git a/Modules/Core/include/mitkITKImageImport.txx b/Modules/Core/include/mitkITKImageImport.txx
index 046082da2a..76ad0dcc45 100644
--- a/Modules/Core/include/mitkITKImageImport.txx
+++ b/Modules/Core/include/mitkITKImageImport.txx
@@ -1,187 +1,225 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #ifndef __mitkITKImageImport_txx
 #define __mitkITKImageImport_txx
 #include "mitkITKImageImport.h"
 #include "mitkImageReadAccessor.h"
 
 template <class TInputImage>
 mitk::ITKImageImport<TInputImage>::ITKImageImport()
 {
 }
 
 template <class TInputImage>
 mitk::ITKImageImport<TInputImage>::~ITKImageImport()
 {
 }
 
 template <class TInputImage>
 typename mitk::ITKImageImport<TInputImage>::InputImageType *mitk::ITKImageImport<TInputImage>::GetInput(void)
 {
   return static_cast<TInputImage *>(this->ProcessObject::GetInput(0));
 }
 
 template <class TInputImage>
 void mitk::ITKImageImport<TInputImage>::SetInput(const InputImageType *input)
 {
   this->ProcessObject::SetNthInput(0, const_cast<TInputImage *>(input));
 }
 
 template <class TInputImage>
 void mitk::ITKImageImport<TInputImage>::SetGeometry(const BaseGeometry *geometry)
 {
   if (geometry != nullptr)
   {
     m_Geometry = static_cast<mitk::BaseGeometry *>(geometry->Clone().GetPointer());
   }
   else
   {
     m_Geometry = nullptr;
   }
   Modified();
 }
 
 template <class TInputImage>
 void mitk::ITKImageImport<TInputImage>::GenerateOutputInformation()
 {
   InputImageConstPointer input = this->GetInput();
   mitk::Image::Pointer output = this->GetOutput();
 
   itkDebugMacro(<< "GenerateOutputInformation()");
 
   output->InitializeByItk(input.GetPointer());
 
   if (m_Geometry.IsNotNull())
   {
     output->SetGeometry(m_Geometry);
   }
 }
 
 template <class TInputImage>
 void mitk::ITKImageImport<TInputImage>::GenerateData()
 {
   InputImageConstPointer input = this->GetInput();
   mitk::Image::Pointer output = this->GetOutput();
 
   output->SetImportChannel((void *)input->GetBufferPointer(), 0, mitk::Image::ReferenceMemory);
 }
 
 template <class TInputImage>
 void mitk::ITKImageImport<TInputImage>::GenerateInputRequestedRegion()
 {
   Superclass::GenerateInputRequestedRegion();
 
   // Input is an image, cast away the constness so we can set
   // the requested region.
   InputImagePointer input = const_cast<TInputImage *>(this->GetInput());
 
   // Use the function object RegionCopier to copy the output region
   // to the input.  The default region copier has default implementations
   // to handle the cases where the input and output are the same
   // dimension, the input a higher dimension than the output, and the
   // input a lower dimension than the output.
   InputImageRegionType inputRegion;
   OutputToInputRegionCopierType regionCopier;
   regionCopier(inputRegion, this->GetOutput()->GetRequestedRegion());
   input->SetRequestedRegion(inputRegion);
 }
 
 template <class TInputImage>
 void mitk::ITKImageImport<TInputImage>::SetNthOutput(DataObjectPointerArraySizeType idx, itk::DataObject *output)
 {
   if ((output == nullptr) && (idx == 0))
   {
     // we are disconnected from our output:
     // copy buffer of input to output, because we
     // cannot guarantee that the input (to which our
     // output is referring) will stay alive.
     InputImageConstPointer input = this->GetInput();
     mitk::Image::Pointer currentOutput = this->GetOutput();
     if (input.IsNotNull() && currentOutput.IsNotNull())
       currentOutput->SetChannel(input->GetBufferPointer());
   }
   Superclass::SetNthOutput(idx, output);
 }
 
 template <typename ItkOutputImageType>
 mitk::Image::Pointer mitk::ImportItkImage(const itk::SmartPointer<ItkOutputImageType> &itkimage,
                                           const BaseGeometry *geometry,
                                           bool update)
 {
   typename mitk::ITKImageImport<ItkOutputImageType>::Pointer importer = mitk::ITKImageImport<ItkOutputImageType>::New();
   importer->SetInput(itkimage);
   importer->SetGeometry(geometry);
   if (update)
     importer->Update();
   return importer->GetOutput();
 }
 
 template <typename ItkOutputImageType>
 mitk::Image::Pointer mitk::ImportItkImage(const ItkOutputImageType *itkimage, const BaseGeometry *geometry, bool update)
 {
   typename mitk::ITKImageImport<ItkOutputImageType>::Pointer importer = mitk::ITKImageImport<ItkOutputImageType>::New();
   importer->SetInput(itkimage);
   importer->SetGeometry(geometry);
   if (update)
     importer->Update();
   return importer->GetOutput();
 }
 
 template <typename ItkOutputImageType>
 mitk::Image::Pointer mitk::GrabItkImageMemory(itk::SmartPointer<ItkOutputImageType> &itkimage,
                                               mitk::Image *mitkImage,
                                               const BaseGeometry *geometry,
                                               bool update)
 {
   return GrabItkImageMemory(itkimage.GetPointer(), mitkImage, geometry, update);
 }
 
 template <typename ItkOutputImageType>
 mitk::Image::Pointer mitk::GrabItkImageMemory(ItkOutputImageType *itkimage,
                                               mitk::Image *mitkImage,
                                               const BaseGeometry *geometry,
                                               bool update)
 {
   if (update)
     itkimage->Update();
 
   mitk::Image::Pointer resultImage;
   if (mitkImage != nullptr)
   {
     resultImage = mitkImage;
 
     // test the pointer equality with read accessor only if mitk Image is initialized, otherwise an Exception is thrown
     // by the ReadAccessor
     if (mitkImage->IsInitialized())
     {
       // check the data pointer, for that, we need to ignore the lock of the mitkImage
       mitk::ImageReadAccessor read_probe(mitk::Image::Pointer(mitkImage), nullptr, mitk::ImageAccessorBase::IgnoreLock);
       if (itkimage->GetBufferPointer() == read_probe.GetData())
         return resultImage;
     }
   }
   else
   {
     resultImage = mitk::Image::New();
   }
   resultImage->InitializeByItk(itkimage);
   resultImage->SetImportVolume(itkimage->GetBufferPointer(), 0, 0, Image::ManageMemory);
   itkimage->GetPixelContainer()->ContainerManageMemoryOff();
 
   if (geometry != nullptr)
     resultImage->SetGeometry(static_cast<mitk::BaseGeometry *>(geometry->Clone().GetPointer()));
 
   return resultImage;
 }
 
+template <typename ItkOutputImageType>
+mitk::Image::Pointer mitk::GrabItkImageMemoryChannel(ItkOutputImageType* itkimage,
+  const TimeGeometry* geometry,
+  mitk::Image* mitkImage,
+  bool update)
+{
+  if (update)
+    itkimage->Update();
+
+  mitk::Image::Pointer resultImage;
+  if (mitkImage != nullptr)
+  {
+    resultImage = mitkImage;
+
+    // test the pointer equality with read accessor only if mitk Image is initialized, otherwise an Exception is thrown
+    // by the ReadAccessor
+    if (mitkImage->IsInitialized())
+    {
+      // check the data pointer, for that, we need to ignore the lock of the mitkImage
+      mitk::ImageReadAccessor read_probe(mitk::Image::Pointer(mitkImage), nullptr, mitk::ImageAccessorBase::IgnoreLock);
+      if (itkimage->GetBufferPointer() == read_probe.GetData())
+        return resultImage;
+    }
+  }
+  else
+  {
+    resultImage = mitk::Image::New();
+  }
+  resultImage->InitializeByItk(itkimage);
+  resultImage->SetImportChannel(itkimage->GetBufferPointer(), 0, Image::ManageMemory);
+  itkimage->GetPixelContainer()->ContainerManageMemoryOff();
+
+  if (geometry != nullptr)
+    resultImage->SetTimeGeometry(geometry->Clone().GetPointer());
+
+  return resultImage;
+}
+
 #endif //__mitkITKImageImport_txx
diff --git a/Modules/Core/include/mitkItkImageIO.h b/Modules/Core/include/mitkItkImageIO.h
index 2b39c41e63..f64356551f 100644
--- a/Modules/Core/include/mitkItkImageIO.h
+++ b/Modules/Core/include/mitkItkImageIO.h
@@ -1,82 +1,102 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #ifndef mitkItkImageIO_h
 #define mitkItkImageIO_h
 
 #include "mitkAbstractFileIO.h"
-
+#include <mitkImage.h>
 #include <itkImageIOBase.h>
 
 namespace mitk
 {
   /**
    * This class wraps ITK image IO objects as mitk::IFileReader and
    * mitk::IFileWriter objects.
    *
    * Instantiating this class with a given itk::ImageIOBase instance
    * will register corresponding MITK reader/writer services for that
    * ITK ImageIO object.
    * For all ITK ImageIOs that support the serialization of MetaData
    * (e.g. nrrd or mhd) the ItkImageIO ensures the serialization
    * of Identification UID.
    */
   class MITKCORE_EXPORT ItkImageIO : public AbstractFileIO
   {
   public:
     ItkImageIO(itk::ImageIOBase::Pointer imageIO);
     ItkImageIO(const CustomMimeType &mimeType, itk::ImageIOBase::Pointer imageIO, int rank);
 
     // -------------- AbstractFileReader -------------
 
     using AbstractFileReader::Read;
 
     ConfidenceLevel GetReaderConfidenceLevel() const override;
 
     // -------------- AbstractFileWriter -------------
 
     void Write() override;
     ConfidenceLevel GetWriterConfidenceLevel() const override;
 
+    /**Helper function that can be used to convert a MetaDataDictionary into a PropertyList for a certain mimeType.
+    The function uses the Property serialization service for that.
+    @param mimeTypeName Mime type that should be assumed for the meta data deserialization.
+    @param defaultMetaDataKeys Vector of keys that should be assumed as defaults. For defaults no PropertyInfo will be registered
+    at the PropertyPersistence service, as they are assumed to be handled anyways. For all other keys an info will be registered
+    to ensure that they will be serialized again, even if unkown.
+    @param dictionary Reference to the meta data dictionary that contains the information that should be extracted.*/
+    static PropertyList::Pointer ExtractMetaDataAsPropertyList(const itk::MetaDataDictionary& dictionary, const std::string& mimeTypeName, const std::vector<std::string>& defaultMetaDataKeys);
+
+    /** Helper function that van be used to extract a raw mitk image for the passed path using the also passed ImageIOBase instance.
+    Raw means, that only the pixel data and geometry information is loaded. But e.g. no properties etc...*/
+    static Image::Pointer LoadRawMitkImageFromImageIO(itk::ImageIOBase* imageIO, const std::string& path);
+
+    /** Helper function that van be used to extract a raw mitk image for the passed path using the also passed ImageIOBase instance.
+    Raw means, that only the pixel data and geometry information is loaded. But e.g. no properties etc...*/
+    static void PreparImageIOToWriteImage(itk::ImageIOBase* imageIO, const Image* image);
+
+    static void SavePropertyListAsMetaData(itk::MetaDataDictionary& dictionary, const PropertyList* properties, const std::string& mimeTypeName);
+
+
   protected:
     virtual std::vector<std::string> FixUpImageIOExtensions(const std::string &imageIOName);
     virtual void FixUpCustomMimeTypeName(const std::string &imageIOName, CustomMimeType &customMimeType);
 
     // Fills the m_DefaultMetaDataKeys vector with default values
     virtual void InitializeDefaultMetaDataKeys();
 
     // -------------- AbstractFileReader -------------
     std::vector<itk::SmartPointer<BaseData>> DoRead() override;
 
   private:
     ItkImageIO(const ItkImageIO &other);
 
     ItkImageIO *IOClone() const override;
 
     itk::ImageIOBase::Pointer m_ImageIO;
 
     std::vector<std::string> m_DefaultMetaDataKeys;
   };
 
   /**Helper function that converts the content of a meta data into a time point vector.
    * If MetaData is not valid or cannot be converted an empty vector is returned.*/
   MITKCORE_EXPORT std::vector<TimePointType> ConvertMetaDataObjectToTimePointList(const itk::MetaDataObjectBase* data);
 
 
   /**Helper function that converts the time points of a passed time geometry to a time point list
    and stores it in a itk::MetaDataObject. Use ConvertMetaDataObjectToTimePointList() to convert it back
    to a time point list.*/
   MITKCORE_EXPORT itk::MetaDataObjectBase::Pointer ConvertTimePointListToMetaDataObject(const mitk::TimeGeometry* timeGeometry);
 
 } // namespace mitk
 
 #endif
diff --git a/Modules/Core/src/IO/mitkItkImageIO.cpp b/Modules/Core/src/IO/mitkItkImageIO.cpp
index 2ee7b9a45f..bc52ba2ea0 100644
--- a/Modules/Core/src/IO/mitkItkImageIO.cpp
+++ b/Modules/Core/src/IO/mitkItkImageIO.cpp
@@ -1,764 +1,793 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "mitkItkImageIO.h"
 
 #include <mitkArbitraryTimeGeometry.h>
 #include <mitkCoreServices.h>
 #include <mitkCustomMimeType.h>
 #include <mitkIOMimeTypes.h>
 #include <mitkIPropertyPersistence.h>
 #include <mitkImage.h>
 #include <mitkImageReadAccessor.h>
 #include <mitkLocaleSwitch.h>
 #include <mitkUIDManipulator.h>
 
 #include <itkImage.h>
 #include <itkImageFileReader.h>
 #include <itkImageIOFactory.h>
 #include <itkImageIORegion.h>
 #include <itkMetaDataObject.h>
 
 #include <algorithm>
 
 namespace mitk
 {
   const char *const PROPERTY_NAME_TIMEGEOMETRY_TYPE = "org.mitk.timegeometry.type";
   const char *const PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS = "org.mitk.timegeometry.timepoints";
   const char *const PROPERTY_KEY_TIMEGEOMETRY_TYPE = "org_mitk_timegeometry_type";
   const char *const PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS = "org_mitk_timegeometry_timepoints";
   const char* const PROPERTY_KEY_UID = "org_mitk_uid";
 
   ItkImageIO::ItkImageIO(const ItkImageIO &other)
     : AbstractFileIO(other), m_ImageIO(dynamic_cast<itk::ImageIOBase *>(other.m_ImageIO->Clone().GetPointer()))
   {
     this->InitializeDefaultMetaDataKeys();
   }
 
   std::vector<std::string> ItkImageIO::FixUpImageIOExtensions(const std::string &imageIOName)
   {
     std::vector<std::string> extensions;
     // Try to fix-up some known ITK image IO classes
     if (imageIOName == "GiplImageIO")
     {
       extensions.push_back("gipl");
       extensions.push_back("gipl.gz");
     }
     else if (imageIOName == "GDCMImageIO")
     {
       extensions.push_back("gdcm");
       extensions.push_back("dcm");
       extensions.push_back("DCM");
       extensions.push_back("dc3");
       extensions.push_back("DC3");
       extensions.push_back("ima");
       extensions.push_back("img");
     }
     else if (imageIOName == "PNGImageIO")
     {
       extensions.push_back("png");
       extensions.push_back("PNG");
     }
     else if (imageIOName == "StimulateImageIO")
     {
       extensions.push_back("spr");
     }
     else if (imageIOName == "HDF5ImageIO")
     {
       extensions.push_back("hdf");
       extensions.push_back("h4");
       extensions.push_back("hdf4");
       extensions.push_back("h5");
       extensions.push_back("hdf5");
       extensions.push_back("he4");
       extensions.push_back("he5");
       extensions.push_back("hd5");
     }
     else if ("GE4ImageIO" == imageIOName || "GE5ImageIO" == imageIOName || "Bruker2dseqImageIO" == imageIOName)
     {
       extensions.push_back("");
     }
 
     if (!extensions.empty())
     {
       MITK_DEBUG << "Fixing up known extensions for " << imageIOName;
     }
 
     return extensions;
   }
 
   void ItkImageIO::FixUpCustomMimeTypeName(const std::string &imageIOName, CustomMimeType &customMimeType)
   {
     if ("GE4ImageIO" == imageIOName)
     {
       customMimeType.SetName(this->AbstractFileReader::GetMimeTypePrefix() + "ge4");
     }
     else if ("GE5ImageIO" == imageIOName)
     {
       customMimeType.SetName(this->AbstractFileReader::GetMimeTypePrefix() + "ge5");
     }
     else if ("Bruker2dseqImageIO" == imageIOName)
     {
       customMimeType.SetName(this->AbstractFileReader::GetMimeTypePrefix() + "bruker2dseq");
     }
   }
 
   ItkImageIO::ItkImageIO(itk::ImageIOBase::Pointer imageIO)
     : AbstractFileIO(Image::GetStaticNameOfClass()), m_ImageIO(imageIO)
   {
     if (m_ImageIO.IsNull())
     {
       mitkThrow() << "ITK ImageIOBase argument must not be nullptr";
     }
 
     this->AbstractFileReader::SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".image.");
     this->InitializeDefaultMetaDataKeys();
 
     std::vector<std::string> readExtensions = m_ImageIO->GetSupportedReadExtensions();
 
     if (readExtensions.empty())
     {
       std::string imageIOName = m_ImageIO->GetNameOfClass();
       MITK_DEBUG << "ITK ImageIOBase " << imageIOName << " does not provide read extensions";
       readExtensions = FixUpImageIOExtensions(imageIOName);
     }
 
     CustomMimeType customReaderMimeType;
     customReaderMimeType.SetCategory("Images");
     for (std::vector<std::string>::const_iterator iter = readExtensions.begin(), endIter = readExtensions.end();
          iter != endIter;
          ++iter)
     {
       std::string extension = *iter;
       if (!extension.empty() && extension[0] == '.')
       {
         extension.assign(iter->begin() + 1, iter->end());
       }
       customReaderMimeType.AddExtension(extension);
     }
 
     auto extensions = customReaderMimeType.GetExtensions();
     if (extensions.empty() || (extensions.size() == 1 && extensions[0].empty()))
     {
       std::string imageIOName = m_ImageIO->GetNameOfClass();
       FixUpCustomMimeTypeName(imageIOName, customReaderMimeType);
     }
 
     this->AbstractFileReader::SetMimeType(customReaderMimeType);
 
     std::vector<std::string> writeExtensions = imageIO->GetSupportedWriteExtensions();
     if (writeExtensions.empty())
     {
       std::string imageIOName = imageIO->GetNameOfClass();
       MITK_DEBUG << "ITK ImageIOBase " << imageIOName << " does not provide write extensions";
       writeExtensions = FixUpImageIOExtensions(imageIOName);
     }
 
     if (writeExtensions != readExtensions)
     {
       CustomMimeType customWriterMimeType;
       customWriterMimeType.SetCategory("Images");
       for (std::vector<std::string>::const_iterator iter = writeExtensions.begin(), endIter = writeExtensions.end();
            iter != endIter;
            ++iter)
       {
         std::string extension = *iter;
         if (!extension.empty() && extension[0] == '.')
         {
           extension.assign(iter->begin() + 1, iter->end());
         }
         customWriterMimeType.AddExtension(extension);
       }
 
       auto extensions = customWriterMimeType.GetExtensions();
       if (extensions.empty() || (extensions.size() == 1 && extensions[0].empty()))
       {
         std::string imageIOName = m_ImageIO->GetNameOfClass();
         FixUpCustomMimeTypeName(imageIOName, customWriterMimeType);
       }
 
       this->AbstractFileWriter::SetMimeType(customWriterMimeType);
     }
 
     std::string description = std::string("ITK ") + imageIO->GetNameOfClass();
     this->SetReaderDescription(description);
     this->SetWriterDescription(description);
 
     this->RegisterService();
   }
 
   ItkImageIO::ItkImageIO(const CustomMimeType &mimeType, itk::ImageIOBase::Pointer imageIO, int rank)
     : AbstractFileIO(Image::GetStaticNameOfClass(), mimeType, std::string("ITK ") + imageIO->GetNameOfClass()),
       m_ImageIO(imageIO)
   {
     if (m_ImageIO.IsNull())
     {
       mitkThrow() << "ITK ImageIOBase argument must not be nullptr";
     }
 
     this->AbstractFileReader::SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".image.");
     this->InitializeDefaultMetaDataKeys();
 
     if (rank)
     {
       this->AbstractFileReader::SetRanking(rank);
       this->AbstractFileWriter::SetRanking(rank);
     }
 
     this->RegisterService();
   }
 
   std::vector<TimePointType> ConvertMetaDataObjectToTimePointList(const itk::MetaDataObjectBase* data)
   {
     const auto* timeGeometryTimeData =
       dynamic_cast<const itk::MetaDataObject<std::string>*>(data);
     std::vector<TimePointType> result;
 
     if (timeGeometryTimeData)
     {
       std::string dataStr = timeGeometryTimeData->GetMetaDataObjectValue();
       std::stringstream stream(dataStr);
       TimePointType tp;
       while (stream >> tp)
       {
         result.push_back(tp);
       }
     }
 
     return result;
   };
 
-  itk::MetaDataObjectBase::Pointer ConvertTimePointListToMetaDataObject(const mitk::TimeGeometry* timeGeometry)
+  Image::Pointer ItkImageIO::LoadRawMitkImageFromImageIO(itk::ImageIOBase* imageIO, const std::string& path)
   {
-    std::stringstream stream;
-    stream << timeGeometry->GetTimeBounds(0)[0];
-    const auto maxTimePoints = timeGeometry->CountTimeSteps();
-    for (TimeStepType pos = 0; pos < maxTimePoints; ++pos)
-    {
-      auto timeBounds = timeGeometry->GetTimeBounds(pos);
-
-      ///////////////////////////////////////
-      // Workaround T27883. See https://phabricator.mitk.org/T27883#219473 for more details.
-      // This workaround should be removed as soon as T28262 is solved!
-      if (pos + 1 == maxTimePoints && timeBounds[0]==timeBounds[1])
-      {
-        timeBounds[1] = timeBounds[0] + 1.;
-      }
-      // End of workaround for T27883
-      //////////////////////////////////////
-
-      stream << " " << timeBounds[1];
-    }
-    auto result = itk::MetaDataObject<std::string>::New();
-    result->SetMetaDataObjectValue(stream.str());
-    return result.GetPointer();
-  };
-
-  std::vector<BaseData::Pointer> ItkImageIO::DoRead()
-  {
-    std::vector<BaseData::Pointer> result;
-    mitk::LocaleSwitch localeSwitch("C");
+    LocaleSwitch localeSwitch("C");
 
     Image::Pointer image = Image::New();
 
     const unsigned int MINDIM = 2;
     const unsigned int MAXDIM = 4;
 
-    const std::string path = this->GetLocalFileName();
-
     MITK_INFO << "loading " << path << " via itk::ImageIOFactory... " << std::endl;
 
     // Check to see if we can read the file given the name or prefix
     if (path.empty())
     {
       mitkThrow() << "Empty filename in mitk::ItkImageIO ";
     }
 
     // Got to allocate space for the image. Determine the characteristics of
     // the image.
-    m_ImageIO->SetFileName(path);
-    m_ImageIO->ReadImageInformation();
+    imageIO->SetFileName(path);
+    imageIO->ReadImageInformation();
 
-    unsigned int ndim = m_ImageIO->GetNumberOfDimensions();
+    unsigned int ndim = imageIO->GetNumberOfDimensions();
     if (ndim < MINDIM || ndim > MAXDIM)
     {
       MITK_WARN << "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim
-                << " dimensions! Reading as 4D.";
+        << " dimensions! Reading as 4D.";
       ndim = MAXDIM;
     }
 
     itk::ImageIORegion ioRegion(ndim);
     itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize();
     itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex();
 
     unsigned int dimensions[MAXDIM];
     dimensions[0] = 0;
     dimensions[1] = 0;
     dimensions[2] = 0;
     dimensions[3] = 0;
 
     ScalarType spacing[MAXDIM];
     spacing[0] = 1.0f;
     spacing[1] = 1.0f;
     spacing[2] = 1.0f;
     spacing[3] = 1.0f;
 
     Point3D origin;
     origin.Fill(0);
 
     unsigned int i;
     for (i = 0; i < ndim; ++i)
     {
       ioStart[i] = 0;
-      ioSize[i] = m_ImageIO->GetDimensions(i);
+      ioSize[i] = imageIO->GetDimensions(i);
       if (i < MAXDIM)
       {
-        dimensions[i] = m_ImageIO->GetDimensions(i);
-        spacing[i] = m_ImageIO->GetSpacing(i);
+        dimensions[i] = imageIO->GetDimensions(i);
+        spacing[i] = imageIO->GetSpacing(i);
         if (spacing[i] <= 0)
           spacing[i] = 1.0f;
       }
       if (i < 3)
       {
-        origin[i] = m_ImageIO->GetOrigin(i);
+        origin[i] = imageIO->GetOrigin(i);
       }
     }
 
     ioRegion.SetSize(ioSize);
     ioRegion.SetIndex(ioStart);
 
     MITK_INFO << "ioRegion: " << ioRegion << std::endl;
-    m_ImageIO->SetIORegion(ioRegion);
-    void *buffer = new unsigned char[m_ImageIO->GetImageSizeInBytes()];
-    m_ImageIO->Read(buffer);
+    imageIO->SetIORegion(ioRegion);
+    void* buffer = new unsigned char[imageIO->GetImageSizeInBytes()];
+    imageIO->Read(buffer);
 
-    image->Initialize(MakePixelType(m_ImageIO), ndim, dimensions);
+    image->Initialize(MakePixelType(imageIO), ndim, dimensions);
     image->SetImportChannel(buffer, 0, Image::ManageMemory);
 
-    const itk::MetaDataDictionary &dictionary = m_ImageIO->GetMetaDataDictionary();
+    const itk::MetaDataDictionary& dictionary = imageIO->GetMetaDataDictionary();
 
     // access direction of itk::Image and include spacing
     mitk::Matrix3D matrix;
     matrix.SetIdentity();
     unsigned int j, itkDimMax3 = (ndim >= 3 ? 3 : ndim);
     for (i = 0; i < itkDimMax3; ++i)
       for (j = 0; j < itkDimMax3; ++j)
-        matrix[i][j] = m_ImageIO->GetDirection(j)[i];
+        matrix[i][j] = imageIO->GetDirection(j)[i];
 
     // re-initialize PlaneGeometry with origin and direction
-    PlaneGeometry *planeGeometry = image->GetSlicedGeometry(0)->GetPlaneGeometry(0);
+    PlaneGeometry* planeGeometry = image->GetSlicedGeometry(0)->GetPlaneGeometry(0);
     planeGeometry->SetOrigin(origin);
     planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix);
 
     // re-initialize SlicedGeometry3D
-    SlicedGeometry3D *slicedGeometry = image->GetSlicedGeometry(0);
+    SlicedGeometry3D* slicedGeometry = image->GetSlicedGeometry(0);
     slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2));
     slicedGeometry->SetSpacing(spacing);
 
     MITK_INFO << slicedGeometry->GetCornerPoint(false, false, false);
     MITK_INFO << slicedGeometry->GetCornerPoint(true, true, true);
 
     // re-initialize TimeGeometry
     TimeGeometry::Pointer timeGeometry;
 
     if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE) || dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TYPE))
     { // also check for the name because of backwards compatibility. Past code version stored with the name and not with
       // the key
       itk::MetaDataObject<std::string>::ConstPointer timeGeometryTypeData = nullptr;
       if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE))
       {
         timeGeometryTypeData =
           dynamic_cast<const itk::MetaDataObject<std::string> *>(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TYPE));
       }
       else
       {
         timeGeometryTypeData =
           dynamic_cast<const itk::MetaDataObject<std::string> *>(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TYPE));
       }
 
       if (timeGeometryTypeData->GetMetaDataObjectValue() == ArbitraryTimeGeometry::GetStaticNameOfClass())
       {
         MITK_INFO << "used time geometry: " << ArbitraryTimeGeometry::GetStaticNameOfClass();
         typedef std::vector<TimePointType> TimePointVector;
         TimePointVector timePoints;
 
         if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS))
         {
           timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS));
         }
         else if (dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS))
         {
           timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS));
         }
 
         if (timePoints.empty())
         {
           MITK_ERROR << "Stored timepoints are empty. Meta information seems to bee invalid. Switch to ProportionalTimeGeometry fallback";
         }
         else if (timePoints.size() - 1 != image->GetDimension(3))
         {
           MITK_ERROR << "Stored timepoints (" << timePoints.size() - 1 << ") and size of image time dimension ("
-                     << image->GetDimension(3) << ") do not match. Switch to ProportionalTimeGeometry fallback";
+            << image->GetDimension(3) << ") do not match. Switch to ProportionalTimeGeometry fallback";
         }
         else
         {
           ArbitraryTimeGeometry::Pointer arbitraryTimeGeometry = ArbitraryTimeGeometry::New();
           TimePointVector::const_iterator pos = timePoints.begin();
           auto prePos = pos++;
 
           for (; pos != timePoints.end(); ++prePos, ++pos)
           {
             arbitraryTimeGeometry->AppendNewTimeStepClone(slicedGeometry, *prePos, *pos);
           }
 
           timeGeometry = arbitraryTimeGeometry;
         }
       }
     }
 
     if (timeGeometry.IsNull())
     { // Fallback. If no other valid time geometry has been created, create a ProportionalTimeGeometry
       MITK_INFO << "used time geometry: " << ProportionalTimeGeometry::GetStaticNameOfClass();
       ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New();
       propTimeGeometry->Initialize(slicedGeometry, image->GetDimension(3));
       timeGeometry = propTimeGeometry;
     }
 
     image->SetTimeGeometry(timeGeometry);
 
     buffer = nullptr;
     MITK_INFO << "number of image components: " << image->GetPixelType().GetNumberOfComponents();
+    return image;
+  }
+
+  itk::MetaDataObjectBase::Pointer ConvertTimePointListToMetaDataObject(const mitk::TimeGeometry* timeGeometry)
+  {
+    std::stringstream stream;
+    stream << timeGeometry->GetTimeBounds(0)[0];
+    const auto maxTimePoints = timeGeometry->CountTimeSteps();
+    for (TimeStepType pos = 0; pos < maxTimePoints; ++pos)
+    {
+      auto timeBounds = timeGeometry->GetTimeBounds(pos);
+
+      ///////////////////////////////////////
+      // Workaround T27883. See https://phabricator.mitk.org/T27883#219473 for more details.
+      // This workaround should be removed as soon as T28262 is solved!
+      if (pos + 1 == maxTimePoints && timeBounds[0]==timeBounds[1])
+      {
+        timeBounds[1] = timeBounds[0] + 1.;
+      }
+      // End of workaround for T27883
+      //////////////////////////////////////
+
+      stream << " " << timeBounds[1];
+    }
+    auto result = itk::MetaDataObject<std::string>::New();
+    result->SetMetaDataObjectValue(stream.str());
+    return result.GetPointer();
+  };
+
+  PropertyList::Pointer ItkImageIO::ExtractMetaDataAsPropertyList(const itk::MetaDataDictionary& dictionary, const std::string& mimeTypeName, const std::vector<std::string>& defaultMetaDataKeys)
+  {
+    LocaleSwitch localeSwitch("C");
+    PropertyList::Pointer result = PropertyList::New();
 
     for (auto iter = dictionary.Begin(), iterEnd = dictionary.End(); iter != iterEnd;
-         ++iter)
+      ++iter)
     {
       if (iter->second->GetMetaDataObjectTypeInfo() == typeid(std::string))
       {
-        const std::string &key = iter->first;
+        const std::string& key = iter->first;
         std::string assumedPropertyName = key;
         std::replace(assumedPropertyName.begin(), assumedPropertyName.end(), '_', '.');
 
-        std::string mimeTypeName = GetMimeType()->GetName();
-
         // Check if there is already a info for the key and our mime type.
-        mitk::CoreServicePointer<IPropertyPersistence> propPersistenceService(mitk::CoreServices::GetPropertyPersistence());
+        CoreServicePointer<IPropertyPersistence> propPersistenceService(CoreServices::GetPropertyPersistence());
         IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfoByKey(key);
 
-        auto predicate = [&mimeTypeName](const PropertyPersistenceInfo::ConstPointer &x) {
+        auto predicate = [&mimeTypeName](const PropertyPersistenceInfo::ConstPointer& x) {
           return x.IsNotNull() && x->GetMimeTypeName() == mimeTypeName;
         };
         auto finding = std::find_if(infoList.begin(), infoList.end(), predicate);
 
         if (finding == infoList.end())
         {
-          auto predicateWild = [](const PropertyPersistenceInfo::ConstPointer &x) {
+          auto predicateWild = [](const PropertyPersistenceInfo::ConstPointer& x) {
             return x.IsNotNull() && x->GetMimeTypeName() == PropertyPersistenceInfo::ANY_MIMETYPE_NAME();
           };
           finding = std::find_if(infoList.begin(), infoList.end(), predicateWild);
         }
 
         PropertyPersistenceInfo::ConstPointer info;
 
         if (finding != infoList.end())
         {
           assumedPropertyName = (*finding)->GetName();
           info = *finding;
         }
         else
         { // we have not found anything suitable so we generate our own info
           auto newInfo = PropertyPersistenceInfo::New();
           newInfo->SetNameAndKey(assumedPropertyName, key);
           newInfo->SetMimeTypeName(PropertyPersistenceInfo::ANY_MIMETYPE_NAME());
           info = newInfo;
         }
 
         std::string value =
           dynamic_cast<itk::MetaDataObject<std::string> *>(iter->second.GetPointer())->GetMetaDataObjectValue();
 
         mitk::BaseProperty::Pointer loadedProp = info->GetDeserializationFunction()(value);
 
         if (loadedProp.IsNull())
         {
           MITK_ERROR << "Property cannot be correctly deserialized and is skipped. Check if data format is valid. Problematic property value string: \"" << value << "\"; Property info used to deserialized: " << info;
           break;
         }
 
-        image->SetProperty(assumedPropertyName.c_str(), loadedProp);
+        result->SetProperty(assumedPropertyName.c_str(), loadedProp);
 
         // Read properties should be persisted unless they are default properties
         // which are written anyway
         bool isDefaultKey(false);
 
-        for (const auto &defaultKey : m_DefaultMetaDataKeys)
+        for (const auto& defaultKey : defaultMetaDataKeys)
         {
           if (defaultKey.length() <= assumedPropertyName.length())
           {
             // does the start match the default key
             if (assumedPropertyName.substr(0, defaultKey.length()).find(defaultKey) != std::string::npos)
             {
               isDefaultKey = true;
               break;
             }
           }
         }
 
         if (!isDefaultKey)
         {
           propPersistenceService->AddInfo(info);
         }
       }
     }
+    return result;
+  }
+
+  std::vector<BaseData::Pointer> ItkImageIO::DoRead()
+  {
+    std::vector<BaseData::Pointer> result;
+
+    auto image = LoadRawMitkImageFromImageIO(this->m_ImageIO, this->GetLocalFileName());
+
+    const itk::MetaDataDictionary& dictionary = this->m_ImageIO->GetMetaDataDictionary();
+
+    //meta data handling
+    auto props = ExtractMetaDataAsPropertyList(this->m_ImageIO->GetMetaDataDictionary(), this->GetMimeType()->GetName(), this->m_DefaultMetaDataKeys);
+    for (auto& [name, prop] : *(props->GetMap()))
+    {
+      image->SetProperty(name, prop);
+    }
 
     // Handle UID
     if (dictionary.HasKey(PROPERTY_KEY_UID))
     {
       itk::MetaDataObject<std::string>::ConstPointer uidData = dynamic_cast<const itk::MetaDataObject<std::string>*>(dictionary.Get(PROPERTY_KEY_UID));
       if (uidData.IsNotNull())
       {
         mitk::UIDManipulator uidManipulator(image);
         uidManipulator.SetUID(uidData->GetMetaDataObjectValue());
       }
     }
 
     MITK_INFO << "...finished!";
 
     result.push_back(image.GetPointer());
     return result;
   }
 
   AbstractFileIO::ConfidenceLevel ItkImageIO::GetReaderConfidenceLevel() const
   {
     return m_ImageIO->CanReadFile(GetLocalFileName().c_str()) ? IFileReader::Supported : IFileReader::Unsupported;
   }
 
-  void ItkImageIO::Write()
+  void ItkImageIO::PreparImageIOToWriteImage(itk::ImageIOBase* imageIO, const Image* image)
   {
-    const auto *image = dynamic_cast<const mitk::Image *>(this->GetInput());
-
-    if (image == nullptr)
-    {
-      mitkThrow() << "Cannot write non-image data";
-    }
-
     // Switch the current locale to "C"
     LocaleSwitch localeSwitch("C");
 
     // Clone the image geometry, because we might have to change it
     // for writing purposes
     BaseGeometry::Pointer geometry = image->GetGeometry()->Clone();
 
     // Check if geometry information will be lost
     if (image->GetDimension() == 2 && !geometry->Is2DConvertable())
     {
       MITK_WARN << "Saving a 2D image with 3D geometry information. Geometry information will be lost! You might "
-                   "consider using Convert2Dto3DImageFilter before saving.";
+        "consider using Convert2Dto3DImageFilter before saving.";
 
       // set matrix to identity
       mitk::AffineTransform3D::Pointer affTrans = mitk::AffineTransform3D::New();
       affTrans->SetIdentity();
       mitk::Vector3D spacing = geometry->GetSpacing();
       mitk::Point3D origin = geometry->GetOrigin();
       geometry->SetIndexToWorldTransform(affTrans);
       geometry->SetSpacing(spacing);
       geometry->SetOrigin(origin);
     }
 
-    LocalFile localFile(this);
-    const std::string path = localFile.GetFileName();
+    // Implementation of writer using itkImageIO directly. This skips the use
+    // of templated itkImageFileWriter, which saves the multiplexing on MITK side.
+
+    const unsigned int dimension = image->GetDimension();
+    const unsigned int* const dimensions = image->GetDimensions();
+    const mitk::PixelType pixelType = image->GetPixelType();
+    const mitk::Vector3D mitkSpacing = geometry->GetSpacing();
+    const mitk::Point3D mitkOrigin = geometry->GetOrigin();
+
+    // Due to templating in itk, we are forced to save a 4D spacing and 4D Origin,
+    // though they are not supported in MITK
+    itk::Vector<double, 4u> spacing4D;
+    spacing4D[0] = mitkSpacing[0];
+    spacing4D[1] = mitkSpacing[1];
+    spacing4D[2] = mitkSpacing[2];
+    spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have a valid value here
+
+    itk::Vector<double, 4u> origin4D;
+    origin4D[0] = mitkOrigin[0];
+    origin4D[1] = mitkOrigin[1];
+    origin4D[2] = mitkOrigin[2];
+    origin4D[3] = 0; // There is no support for a 4D origin. However, we should have a valid value here
+
+    // Set the necessary information for imageIO
+    imageIO->SetNumberOfDimensions(dimension);
+    imageIO->SetPixelType(pixelType.GetPixelType());
+    imageIO->SetComponentType(static_cast<int>(pixelType.GetComponentType()) < PixelComponentUserType
+      ? pixelType.GetComponentType()
+      : itk::IOComponentEnum::UNKNOWNCOMPONENTTYPE);
+    imageIO->SetNumberOfComponents(pixelType.GetNumberOfComponents());
+
+    itk::ImageIORegion ioRegion(dimension);
+
+    for (unsigned int i = 0; i < dimension; i++)
+    {
+      imageIO->SetDimensions(i, dimensions[i]);
+      imageIO->SetSpacing(i, spacing4D[i]);
+      imageIO->SetOrigin(i, origin4D[i]);
+
+      mitk::Vector3D mitkDirection(0.0);
+      mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i).as_ref());
+      itk::Vector<double, 4u> direction4D;
+      direction4D[0] = mitkDirection[0];
+      direction4D[1] = mitkDirection[1];
+      direction4D[2] = mitkDirection[2];
+
+      // MITK only supports a 3x3 direction matrix. Due to templating in itk, however, we must
+      // save a 4x4 matrix for 4D images. in this case, add an homogneous component to the matrix.
+      if (i == 3)
+      {
+        direction4D[3] = 1; // homogenous component
+      }
+      else
+      {
+        direction4D[3] = 0;
+      }
+      vnl_vector<double> axisDirection(dimension);
+      for (unsigned int j = 0; j < dimension; j++)
+      {
+        axisDirection[j] = direction4D[j] / spacing4D[i];
+      }
+      imageIO->SetDirection(i, axisDirection);
 
-    MITK_INFO << "Writing image: " << path << std::endl;
+      ioRegion.SetSize(i, image->GetLargestPossibleRegion().GetSize(i));
+      ioRegion.SetIndex(i, image->GetLargestPossibleRegion().GetIndex(i));
+    }
 
-    try
+    imageIO->SetIORegion(ioRegion);
+
+    // Handle time geometry
+    const auto* arbitraryTG = dynamic_cast<const ArbitraryTimeGeometry*>(image->GetTimeGeometry());
+    if (arbitraryTG)
     {
-      // Implementation of writer using itkImageIO directly. This skips the use
-      // of templated itkImageFileWriter, which saves the multiplexing on MITK side.
-
-      const unsigned int dimension = image->GetDimension();
-      const unsigned int *const dimensions = image->GetDimensions();
-      const mitk::PixelType pixelType = image->GetPixelType();
-      const mitk::Vector3D mitkSpacing = geometry->GetSpacing();
-      const mitk::Point3D mitkOrigin = geometry->GetOrigin();
-
-      // Due to templating in itk, we are forced to save a 4D spacing and 4D Origin,
-      // though they are not supported in MITK
-      itk::Vector<double, 4u> spacing4D;
-      spacing4D[0] = mitkSpacing[0];
-      spacing4D[1] = mitkSpacing[1];
-      spacing4D[2] = mitkSpacing[2];
-      spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have a valid value here
-
-      itk::Vector<double, 4u> origin4D;
-      origin4D[0] = mitkOrigin[0];
-      origin4D[1] = mitkOrigin[1];
-      origin4D[2] = mitkOrigin[2];
-      origin4D[3] = 0; // There is no support for a 4D origin. However, we should have a valid value here
-
-      // Set the necessary information for imageIO
-      m_ImageIO->SetNumberOfDimensions(dimension);
-      m_ImageIO->SetPixelType(pixelType.GetPixelType());
-      m_ImageIO->SetComponentType(static_cast<int>(pixelType.GetComponentType()) < PixelComponentUserType
-                                    ? pixelType.GetComponentType()
-                                    : itk::IOComponentEnum::UNKNOWNCOMPONENTTYPE);
-      m_ImageIO->SetNumberOfComponents(pixelType.GetNumberOfComponents());
-
-      itk::ImageIORegion ioRegion(dimension);
-
-      for (unsigned int i = 0; i < dimension; i++)
-      {
-        m_ImageIO->SetDimensions(i, dimensions[i]);
-        m_ImageIO->SetSpacing(i, spacing4D[i]);
-        m_ImageIO->SetOrigin(i, origin4D[i]);
-
-        mitk::Vector3D mitkDirection(0.0);
-        mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i).as_ref());
-        itk::Vector<double, 4u> direction4D;
-        direction4D[0] = mitkDirection[0];
-        direction4D[1] = mitkDirection[1];
-        direction4D[2] = mitkDirection[2];
-
-        // MITK only supports a 3x3 direction matrix. Due to templating in itk, however, we must
-        // save a 4x4 matrix for 4D images. in this case, add an homogneous component to the matrix.
-        if (i == 3)
-        {
-          direction4D[3] = 1; // homogenous component
-        }
-        else
-        {
-          direction4D[3] = 0;
-        }
-        vnl_vector<double> axisDirection(dimension);
-        for (unsigned int j = 0; j < dimension; j++)
-        {
-          axisDirection[j] = direction4D[j] / spacing4D[i];
-        }
-        m_ImageIO->SetDirection(i, axisDirection);
+      itk::EncapsulateMetaData<std::string>(imageIO->GetMetaDataDictionary(),
+        PROPERTY_KEY_TIMEGEOMETRY_TYPE,
+        ArbitraryTimeGeometry::GetStaticNameOfClass());
 
-        ioRegion.SetSize(i, image->GetLargestPossibleRegion().GetSize(i));
-        ioRegion.SetIndex(i, image->GetLargestPossibleRegion().GetIndex(i));
-      }
+      auto metaTimePoints = ConvertTimePointListToMetaDataObject(arbitraryTG);
+      imageIO->GetMetaDataDictionary().Set(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS, metaTimePoints);
+    }
+  }
 
-      // use compression if available
-      m_ImageIO->UseCompressionOn();
+  void ItkImageIO::SavePropertyListAsMetaData(itk::MetaDataDictionary& dictionary, const PropertyList* properties, const std::string& mimeTypeName)
+  {
+    // Switch the current locale to "C"
+    LocaleSwitch localeSwitch("C");
 
-      m_ImageIO->SetIORegion(ioRegion);
-      m_ImageIO->SetFileName(path);
+    for (const auto& property : *properties->GetMap())
+    {
+      mitk::CoreServicePointer<IPropertyPersistence> propPersistenceService(mitk::CoreServices::GetPropertyPersistence());
+      IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfo(property.first, mimeTypeName, true);
 
-      // Handle time geometry
-      const auto *arbitraryTG = dynamic_cast<const ArbitraryTimeGeometry *>(image->GetTimeGeometry());
-      if (arbitraryTG)
+      if (infoList.empty())
       {
-        itk::EncapsulateMetaData<std::string>(m_ImageIO->GetMetaDataDictionary(),
-                                              PROPERTY_KEY_TIMEGEOMETRY_TYPE,
-                                              ArbitraryTimeGeometry::GetStaticNameOfClass());
-
-        auto metaTimePoints = ConvertTimePointListToMetaDataObject(arbitraryTG);
-        m_ImageIO->GetMetaDataDictionary().Set(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS, metaTimePoints);
+        continue;
       }
 
-      // Handle properties
-      mitk::PropertyList::Pointer imagePropertyList = image->GetPropertyList();
+      std::string value = mitk::BaseProperty::VALUE_CANNOT_BE_CONVERTED_TO_STRING;
+      try
+      {
+        value = infoList.front()->GetSerializationFunction()(property.second);
+      }
+      catch (const std::exception& e)
+      {
+        MITK_ERROR << "Error when serializing content of property. This often indicates the use of an out dated reader. Property will not be stored. Skipped property: " << property.first << ". Reason: " << e.what();
+      }
+      catch (...)
+      {
+        MITK_ERROR << "Unknown error when serializing content of property. This often indicates the use of an out dated reader. Property will not be stored. Skipped property: " << property.first;
+      }
 
-      for (const auto &property : *imagePropertyList->GetMap())
+      if (value == mitk::BaseProperty::VALUE_CANNOT_BE_CONVERTED_TO_STRING)
       {
-        mitk::CoreServicePointer<IPropertyPersistence> propPersistenceService(mitk::CoreServices::GetPropertyPersistence());
-        IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfo(property.first, GetMimeType()->GetName(), true);
+        continue;
+      }
 
-        if (infoList.empty())
-        {
-          continue;
-        }
+      std::string key = infoList.front()->GetKey();
 
-        std::string value = mitk::BaseProperty::VALUE_CANNOT_BE_CONVERTED_TO_STRING;
-        try
-        {
-          value = infoList.front()->GetSerializationFunction()(property.second);
-        }
-        catch (const std::exception& e)
-        {
-          MITK_ERROR << "Error when serializing content of property. This often indicates the use of an out dated reader. Property will not be stored. Skipped property: " << property.first << ". Reason: " << e.what();
-        }
-        catch (...)
-        {
-          MITK_ERROR << "Unknown error when serializing content of property. This often indicates the use of an out dated reader. Property will not be stored. Skipped property: " << property.first;
-        }
+      itk::EncapsulateMetaData<std::string>(dictionary, key, value);
+    }
+  }
 
-        if (value == mitk::BaseProperty::VALUE_CANNOT_BE_CONVERTED_TO_STRING)
-        {
-          continue;
-        }
+  void ItkImageIO::Write()
+  {
+    const auto *image = dynamic_cast<const mitk::Image *>(this->GetInput());
+
+    if (image == nullptr)
+    {
+      mitkThrow() << "Cannot write non-image data";
+    }
 
-        std::string key = infoList.front()->GetKey();
+    PreparImageIOToWriteImage(m_ImageIO, image);
 
-        itk::EncapsulateMetaData<std::string>(m_ImageIO->GetMetaDataDictionary(), key, value);
-      }
+    LocalFile localFile(this);
+    const std::string path = localFile.GetFileName();
 
+    MITK_INFO << "Writing image: " << path << std::endl;
+
+    try
+    {
+      // Handle properties
+      SavePropertyListAsMetaData(m_ImageIO->GetMetaDataDictionary(), image->GetPropertyList(), this->GetMimeType()->GetName());
       // Handle UID
       itk::EncapsulateMetaData<std::string>(m_ImageIO->GetMetaDataDictionary(), PROPERTY_KEY_UID, image->GetUID());
 
+      // use compression if available
+      m_ImageIO->UseCompressionOn();
+      m_ImageIO->SetFileName(path);
+
       ImageReadAccessor imageAccess(image);
       LocaleSwitch localeSwitch2("C");
       m_ImageIO->Write(imageAccess.GetData());
     }
     catch (const std::exception &e)
     {
       mitkThrow() << e.what();
     }
   }
 
   AbstractFileIO::ConfidenceLevel ItkImageIO::GetWriterConfidenceLevel() const
   {
     // Check if the image dimension is supported
     const auto *image = dynamic_cast<const Image *>(this->GetInput());
     if (image == nullptr)
     {
       // We cannot write a null object, DUH!
       return IFileWriter::Unsupported;
     }
 
     //Fix to ensure T29391. Can be removed as soon as T28524 is solved
     //and the new MultiLabelSegmentation class is in place, as
     //segmentations won't be confused with simple images anymore.
     std::string className = this->GetInput()->GetNameOfClass();
     if (className == "LabelSetImage")
     {
       // We cannot write a null object, DUH!
       return IFileWriter::Unsupported;
     }
 
     if (!m_ImageIO->SupportsDimension(image->GetDimension()))
     {
       // okay, dimension is not supported. We have to look at a special case:
       // 3D-Image with one slice. We can treat that as a 2D image.
       if ((image->GetDimension() == 3) && (image->GetSlicedGeometry()->GetSlices() == 1))
         return IFileWriter::Supported;
       else
         return IFileWriter::Unsupported;
     }
 
     // Check if geometry information will be lost
     if (image->GetDimension() == 2 && !image->GetGeometry()->Is2DConvertable())
     {
       return IFileWriter::PartiallySupported;
     }
     return IFileWriter::Supported;
   }
 
   ItkImageIO *ItkImageIO::IOClone() const { return new ItkImageIO(*this); }
   void ItkImageIO::InitializeDefaultMetaDataKeys()
   {
     this->m_DefaultMetaDataKeys.push_back("NRRD.space");
     this->m_DefaultMetaDataKeys.push_back("NRRD.kinds");
     this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TYPE);
     this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS);
     this->m_DefaultMetaDataKeys.push_back("ITK.InputFilterName");
   }
 }
diff --git a/Modules/Multilabel/Testing/files.cmake b/Modules/Multilabel/Testing/files.cmake
index bf283fc5a7..0a03695261 100644
--- a/Modules/Multilabel/Testing/files.cmake
+++ b/Modules/Multilabel/Testing/files.cmake
@@ -1,9 +1,9 @@
 set(MODULE_TESTS
     mitkLabelTest.cpp
     mitkLabelSetTest.cpp
     mitkLabelSetImageTest.cpp
-    mitkLabelSetImageIOTest.cpp
+    mitkLegacyLabelSetImageIOTest.cpp
     mitkLabelSetImageSurfaceStampFilterTest.cpp
     mitkTransferLabelTest.cpp
 )
 
diff --git a/Modules/Multilabel/Testing/mitkLabelSetImageIOTest.cpp b/Modules/Multilabel/Testing/mitkLabelSetImageIOTest.cpp
deleted file mode 100644
index e5bc2b557d..0000000000
--- a/Modules/Multilabel/Testing/mitkLabelSetImageIOTest.cpp
+++ /dev/null
@@ -1,235 +0,0 @@
-/*============================================================================
-
-The Medical Imaging Interaction Toolkit (MITK)
-
-Copyright (c) German Cancer Research Center (DKFZ)
-All rights reserved.
-
-Use of this source code is governed by a 3-clause BSD license that can be
-found in the LICENSE file.
-
-============================================================================*/
-
-#include <mitkIOUtil.h>
-#include <mitkLabelSetImage.h>
-
-#include <mitkTestFixture.h>
-#include <mitkTestingMacros.h>
-#include <mitkArbitraryTimeGeometry.h>
-#include <mitkCoreServices.h>
-#include <mitkPropertyPersistenceInfo.h>
-#include <mitkIPropertyPersistence.h>
-
-std::string pathToImage;
-
-class mitkLabelSetImageIOTestSuite : public mitk::TestFixture
-{
-  CPPUNIT_TEST_SUITE(mitkLabelSetImageIOTestSuite);
-  MITK_TEST(TestReadWrite3DLabelSetImage);
-  MITK_TEST(TestReadWrite3DplusTLabelSetImage);
-  MITK_TEST(TestReadWrite3DplusTLabelSetImageWithArbitraryGeometry);
-  MITK_TEST(TestReadWriteProperties);
-  CPPUNIT_TEST_SUITE_END();
-
-private:
-  mitk::Image::Pointer regularImage;
-  mitk::LabelSetImage::Pointer multilabelImage;
-
-public:
-  void setUp() override
-  {
-    regularImage = mitk::Image::New();
-  }
-
-  void tearDown() override
-  {
-    regularImage = nullptr;
-    multilabelImage = nullptr;
-  }
-  
-  void TestReadWrite3DLabelSetImage()
-  {
-    unsigned int dimensions[3] = {30, 20, 10};
-    regularImage->Initialize(mitk::MakeScalarPixelType<int>(), 3, dimensions);
-
-    multilabelImage = mitk::LabelSetImage::New();
-    multilabelImage->Initialize(regularImage);
-    mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New();
-    newlayer->SetLayer(1);
-    mitk::Label::Pointer label0 = mitk::Label::New();
-    label0->SetName("Background");
-    label0->SetValue(0);
-
-    mitk::Label::Pointer label1 = mitk::Label::New();
-    label1->SetName("Label1");
-    label1->SetValue(1);
-
-    mitk::Label::Pointer label2 = mitk::Label::New();
-    label2->SetName("Label2");
-    label2->SetValue(200);
-
-    newlayer->AddLabel(label0);
-    newlayer->AddLabel(label1);
-    newlayer->AddLabel(label2);
-    newlayer->SetActiveLabel(200);
-
-    multilabelImage->AddLayer(newlayer);
-
-    pathToImage = mitk::IOUtil::CreateTemporaryDirectory();
-    pathToImage.append("/LabelSetTestImage3D.nrrd");
-
-    mitk::IOUtil::Save(multilabelImage, pathToImage);
-
-    auto loadedImage =
-      mitk::IOUtil::Load<mitk::LabelSetImage >(pathToImage);
-
-    // This information is currently not serialized but also checked within the Equals function
-    loadedImage->SetActiveLayer(multilabelImage->GetActiveLayer());
-
-    CPPUNIT_ASSERT_MESSAGE("Error reading label set image", loadedImage.IsNotNull());
-    CPPUNIT_ASSERT_MESSAGE("Error reading label set image", mitk::Equal(*multilabelImage, *loadedImage, 0.0001, true));
-    CPPUNIT_ASSERT_EQUAL_MESSAGE("Error, read image has different UID", multilabelImage->GetUID(), loadedImage->GetUID());
-
-    itksys::SystemTools::RemoveFile(pathToImage);
-  }
-
-  void TestReadWrite3DplusTLabelSetImage()
-  {
-    unsigned int dimensions[4] = {30, 20, 15, 10};
-    regularImage->Initialize(mitk::MakeScalarPixelType<int>(), 4, dimensions);
-
-    multilabelImage = mitk::LabelSetImage::New();
-    multilabelImage->Initialize(regularImage);
-    mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New();
-    newlayer->SetLayer(1);
-    mitk::Label::Pointer label0 = mitk::Label::New();
-    label0->SetName("Background");
-    label0->SetValue(0);
-
-    mitk::Label::Pointer label1 = mitk::Label::New();
-    label1->SetName("Label1");
-    label1->SetValue(1);
-
-    mitk::Label::Pointer label2 = mitk::Label::New();
-    label2->SetName("Label2");
-    label2->SetValue(200);
-
-    newlayer->AddLabel(label0);
-    newlayer->AddLabel(label1);
-    newlayer->AddLabel(label2);
-    newlayer->SetActiveLabel(200);
-
-    multilabelImage->AddLayer(newlayer);
-
-    pathToImage = mitk::IOUtil::CreateTemporaryDirectory();
-    pathToImage.append("/LabelSetTestImage3DplusT.nrrd");
-
-    mitk::IOUtil::Save(multilabelImage, pathToImage);
-
-    auto loadedImage =
-      mitk::IOUtil::Load<mitk::LabelSetImage >(pathToImage);
-
-    // This information is currently not serialized but also checked within the Equals function
-    loadedImage->SetActiveLayer(multilabelImage->GetActiveLayer());
-
-    CPPUNIT_ASSERT_MESSAGE("Error reading label set image", loadedImage.IsNotNull());
-    CPPUNIT_ASSERT_MESSAGE("Error reading label set image", mitk::Equal(*multilabelImage, *loadedImage, 0.0001, true));
-    CPPUNIT_ASSERT_MESSAGE("Error reading time geometry of label set image", mitk::Equal(*(multilabelImage->GetTimeGeometry()), *(loadedImage->GetTimeGeometry()), 0.000000001, true));
-
-    itksys::SystemTools::RemoveFile(pathToImage);
-  }
-
-  void TestReadWrite3DplusTLabelSetImageWithArbitraryGeometry()
-  {
-    unsigned int dimensions[4] = { 30, 20, 10, 4 };
-    regularImage->Initialize(mitk::MakeScalarPixelType<int>(), 4, dimensions);
-
-    multilabelImage = mitk::LabelSetImage::New();
-    multilabelImage->Initialize(regularImage);
-    mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New();
-    newlayer->SetLayer(1);
-    mitk::Label::Pointer label0 = mitk::Label::New();
-    label0->SetName("Background");
-    label0->SetValue(0);
-
-    mitk::Label::Pointer label1 = mitk::Label::New();
-    label1->SetName("Label1");
-    label1->SetValue(1);
-
-    mitk::Label::Pointer label2 = mitk::Label::New();
-    label2->SetName("Label2");
-    label2->SetValue(200);
-
-    newlayer->AddLabel(label0);
-    newlayer->AddLabel(label1);
-    newlayer->AddLabel(label2);
-    newlayer->SetActiveLabel(200);
-
-    multilabelImage->AddLayer(newlayer);
-
-    auto geometry = multilabelImage->GetGeometry()->Clone();
-
-    auto refTimeGeometry = mitk::ArbitraryTimeGeometry::New();
-    refTimeGeometry->AppendNewTimeStep(geometry, 0., 0.5);
-    refTimeGeometry->AppendNewTimeStep(geometry, 0.5, 1.);
-    refTimeGeometry->AppendNewTimeStep(geometry, 1., 2.);
-    refTimeGeometry->AppendNewTimeStep(geometry, 2., 5.5);
-    multilabelImage->SetTimeGeometry(refTimeGeometry);
-
-    pathToImage = mitk::IOUtil::CreateTemporaryDirectory();
-    pathToImage.append("/LabelSetTestImage3DplusTWithArbitraryTimeGeometry.nrrd");
-
-    mitk::IOUtil::Save(multilabelImage, pathToImage);
-
-    auto loadedImage =
-      mitk::IOUtil::Load<mitk::LabelSetImage >(pathToImage);
-
-    // This information is currently not serialized but also checked within the Equals function
-    loadedImage->SetActiveLayer(multilabelImage->GetActiveLayer());
-
-    CPPUNIT_ASSERT_MESSAGE("Error reading label set image", loadedImage.IsNotNull());
-    CPPUNIT_ASSERT_MESSAGE("Error reading label set image", mitk::Equal(*multilabelImage, *loadedImage, 0.0001, true));
-    CPPUNIT_ASSERT_MESSAGE("Error reading time geometry of label set image", mitk::Equal(*refTimeGeometry, *(loadedImage->GetTimeGeometry()), 0.000000001, true));
-    itksys::SystemTools::RemoveFile(pathToImage);
-  }
-
-  void TestReadWriteProperties()
-  {
-    unsigned int dimensions[3] = { 30, 20, 10 };
-    regularImage->Initialize(mitk::MakeScalarPixelType<int>(), 3, dimensions);
-
-    multilabelImage = mitk::LabelSetImage::New();
-    multilabelImage->Initialize(regularImage);
-    mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New();
-    newlayer->SetLayer(1);
-    mitk::Label::Pointer label0 = mitk::Label::New();
-    label0->SetName("Background");
-    label0->SetValue(0);
-    newlayer->AddLabel(label0);
-    multilabelImage->AddLayer(newlayer);
-
-    auto propPersistenceInfo = mitk::PropertyPersistenceInfo::New();
-    propPersistenceInfo->SetNameAndKey("my.cool.test.property", "my_cool_test_property");
-    mitk::CoreServicePointer<mitk::IPropertyPersistence> propPersService(mitk::CoreServices::GetPropertyPersistence());
-    propPersService->AddInfo(propPersistenceInfo);
-
-    multilabelImage->SetProperty("my.cool.test.property", mitk::StringProperty::New("test_content"));
-
-    pathToImage = mitk::IOUtil::CreateTemporaryDirectory();
-    pathToImage.append("/LabelSetPropertiesTestImage.nrrd");
-
-    mitk::IOUtil::Save(multilabelImage, pathToImage);
-
-    auto loadedImage =
-      mitk::IOUtil::Load<mitk::LabelSetImage >(pathToImage);
-
-    auto loadedProp = loadedImage->GetProperty("my.cool.test.property");
-    CPPUNIT_ASSERT_MESSAGE("Error reading properties of label set image", loadedProp.IsNotNull());
-    CPPUNIT_ASSERT_MESSAGE("Error reading properties of label set image", loadedProp->GetValueAsString() == "test_content");
-    itksys::SystemTools::RemoveFile(pathToImage);
-  }
-
-
-};
-
-MITK_TEST_SUITE_REGISTRATION(mitkLabelSetImageIO)
diff --git a/Modules/Multilabel/Testing/mitkLabelSetImageTest.cpp b/Modules/Multilabel/Testing/mitkLabelSetImageTest.cpp
index 612295804f..700825fdaa 100644
--- a/Modules/Multilabel/Testing/mitkLabelSetImageTest.cpp
+++ b/Modules/Multilabel/Testing/mitkLabelSetImageTest.cpp
@@ -1,519 +1,514 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include <mitkIOUtil.h>
 #include <mitkImageStatisticsHolder.h>
 #include <mitkLabelSetImage.h>
 #include <mitkTestFixture.h>
 #include <mitkTestingMacros.h>
 
 #include <mitkAutoCropImageFilter.h>
 
 class mitkLabelSetImageTestSuite : public mitk::TestFixture
 {
   CPPUNIT_TEST_SUITE(mitkLabelSetImageTestSuite);
   MITK_TEST(TestInitialize);
   MITK_TEST(TestAddLayer);
   MITK_TEST(TestGetActiveLabelSet);
   MITK_TEST(TestGetActiveLabel);
   MITK_TEST(TestInitializeByLabeledImage);
   MITK_TEST(TestGetLabelSet);
   MITK_TEST(TestGetLabel);
-  MITK_TEST(TestSetExteriorLabel);
+  MITK_TEST(TestSetUnlabeledLabelLock);
   MITK_TEST(TestGetTotalNumberOfLabels);
   MITK_TEST(TestExistsLabel);
   MITK_TEST(TestExistsLabelSet);
   MITK_TEST(TestSetActiveLayer);
   MITK_TEST(TestRemoveLayer);
   MITK_TEST(TestRemoveLabels);
   MITK_TEST(TestEraseLabels);
   MITK_TEST(TestMergeLabels);
   MITK_TEST(TestCreateLabelMask);
   CPPUNIT_TEST_SUITE_END();
 
 private:
   mitk::LabelSetImage::Pointer m_LabelSetImage;
 
 public:
   void setUp() override
   {
     // Create a new labelset image
     m_LabelSetImage = mitk::LabelSetImage::New();
     mitk::Image::Pointer regularImage = mitk::Image::New();
     unsigned int dimensions[3] = { 96, 128, 52 };
     regularImage->Initialize(mitk::MakeScalarPixelType<char>(), 3, dimensions);
     m_LabelSetImage->Initialize(regularImage);
   }
 
   void tearDown() override
   {
     // Delete LabelSetImage
     m_LabelSetImage = nullptr;
   }
 
   void TestInitialize()
   {
     // LabelSet image should always has the pixel type mitk::Label::PixelType
     CPPUNIT_ASSERT_MESSAGE("LabelSetImage has wrong pixel type",
                            m_LabelSetImage->GetPixelType() == mitk::MakeScalarPixelType<mitk::Label::PixelType>());
 
     mitk::Image::Pointer regularImage = mitk::Image::New();
     unsigned int dimensions[3] = { 96, 128, 52 };
     regularImage->Initialize(mitk::MakeScalarPixelType<char>(), 3, dimensions);
 
     mitk::BaseGeometry::Pointer regularImageGeo = regularImage->GetGeometry();
     mitk::BaseGeometry::Pointer labelImageGeo = m_LabelSetImage->GetGeometry();
     MITK_ASSERT_EQUAL(labelImageGeo, regularImageGeo, "LabelSetImage has wrong geometry");
 
-    // By default one layer containing the exterior label should be added
+    // By default one layer should be added
     CPPUNIT_ASSERT_MESSAGE("Image was not correctly initialized - number of layers is not one",
                            m_LabelSetImage->GetNumberOfLayers() == 1);
     CPPUNIT_ASSERT_MESSAGE("Image was not correctly initialized - active layer has wrong ID",
                            m_LabelSetImage->GetActiveLayer() == 0);
 
-    CPPUNIT_ASSERT_MESSAGE("Image was not correctly initialized - active label is not the exterior label",
-                           m_LabelSetImage->GetActiveLabel()->GetValue() == 0);
+    CPPUNIT_ASSERT_MESSAGE("Image was not correctly initialized - no active label should be selected",
+                           m_LabelSetImage->GetActiveLabel() == nullptr);
   }
 
   void TestAddLayer()
   {
     CPPUNIT_ASSERT_MESSAGE("Number of layers is not zero", m_LabelSetImage->GetNumberOfLayers() == 1);
 
     m_LabelSetImage->AddLayer();
     CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - number of layers is not one",
                            m_LabelSetImage->GetNumberOfLayers() == 2);
     CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - active layer has wrong ID",
                            m_LabelSetImage->GetActiveLayer() == 1);
 
-    CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - active label is not the exterior label",
-                           m_LabelSetImage->GetActiveLabel()->GetValue() == 0);
+    CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - no active label should be selected",
+                           m_LabelSetImage->GetActiveLabel() == nullptr);
 
     mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New();
     mitk::Label::Pointer label1 = mitk::Label::New();
     label1->SetName("Label1");
     label1->SetValue(1);
 
     mitk::Label::Pointer label2 = mitk::Label::New();
     label2->SetName("Label2");
     label2->SetValue(200);
 
     newlayer->AddLabel(label1);
     newlayer->AddLabel(label2);
     newlayer->SetActiveLabel(200);
 
     unsigned int layerID = m_LabelSetImage->AddLayer(newlayer);
     CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - number of layers is not two",
                            m_LabelSetImage->GetNumberOfLayers() == 3);
     CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - active layer has wrong ID",
                            m_LabelSetImage->GetActiveLayer() == layerID);
     CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - active label is wrong",
                            m_LabelSetImage->GetActiveLabel(layerID)->GetValue() == 200);
   }
 
   void TestGetActiveLabelSet()
   {
     mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New();
     mitk::Label::Pointer label1 = mitk::Label::New();
     label1->SetName("Label1");
     label1->SetValue(1);
 
     mitk::Label::Pointer label2 = mitk::Label::New();
     label2->SetName("Label2");
     label2->SetValue(200);
 
     newlayer->AddLabel(label1);
     newlayer->AddLabel(label2);
     newlayer->SetActiveLabel(200);
 
     unsigned int layerID = m_LabelSetImage->AddLayer(newlayer);
 
     mitk::LabelSet::Pointer activeLayer = m_LabelSetImage->GetActiveLabelSet();
 
     CPPUNIT_ASSERT_MESSAGE("Wrong layer ID was returned", layerID == 1);
     CPPUNIT_ASSERT_MESSAGE("Wrong active labelset returned", mitk::Equal(*newlayer, *activeLayer, 0.00001, true));
 
     mitk::LabelSet::ConstPointer constActiveLayer = const_cast<const mitk::LabelSetImage*>(m_LabelSetImage.GetPointer())->GetActiveLabelSet();
     CPPUNIT_ASSERT_MESSAGE("Wrong active labelset returned", mitk::Equal(*newlayer, *constActiveLayer, 0.00001, true));
   }
 
   void TestGetActiveLabel()
   {
     mitk::Label::Pointer label1 = mitk::Label::New();
     label1->SetName("Label1");
     mitk::Label::PixelType value1 = 1;
     label1->SetValue(value1);
 
     mitk::Label::Pointer label2 = mitk::Label::New();
     label2->SetName("Label2");
     mitk::Label::PixelType value2 = 200;
     label2->SetValue(value2);
 
     m_LabelSetImage->GetActiveLabelSet()->AddLabel(label1);
     m_LabelSetImage->GetActiveLabelSet()->AddLabel(label2);
     m_LabelSetImage->GetActiveLabelSet()->SetActiveLabel(1);
     CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - active label is wrong",
                            m_LabelSetImage->GetActiveLabel()->GetValue() == value1);
     m_LabelSetImage->GetActiveLabelSet()->SetActiveLabel(value2);
     CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - active label is wrong",
                            m_LabelSetImage->GetActiveLabel()->GetValue() == value2);
 
     CPPUNIT_ASSERT_MESSAGE("Active Label was not correctly retreived with const getter",
       const_cast<const mitk::LabelSetImage*>(m_LabelSetImage.GetPointer())->GetActiveLabel()->GetValue() == value2);
 
   }
 
   void TestInitializeByLabeledImage()
   {
     mitk::Image::Pointer image =
       mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("Multilabel/LabelSetTestInitializeImage.nrrd"));
     m_LabelSetImage->InitializeByLabeledImage(image);
-    CPPUNIT_ASSERT_MESSAGE("Image - number of labels is not 6", m_LabelSetImage->GetNumberOfLabels() == 6);
+    CPPUNIT_ASSERT_MESSAGE("Image - number of labels is not 5", m_LabelSetImage->GetNumberOfLabels() == 5);
   }
 
   void TestGetLabelSet()
   {
     // Test get non existing lset
     mitk::LabelSet::ConstPointer lset = m_LabelSetImage->GetLabelSet(10000);
     CPPUNIT_ASSERT_MESSAGE("Non existing labelset is not nullptr", lset.IsNull());
 
     lset = m_LabelSetImage->GetLabelSet(0);
     CPPUNIT_ASSERT_MESSAGE("Existing labelset is nullptr", lset.IsNotNull());
   }
 
   void TestGetLabel()
   {
     mitk::Label::Pointer label1 = mitk::Label::New();
     label1->SetName("Label1");
     mitk::Label::PixelType value1 = 1;
     label1->SetValue(value1);
 
     mitk::Label::Pointer label2 = mitk::Label::New();
     label2->SetName("Label2");
     mitk::Label::PixelType value2 = 200;
     label2->SetValue(value2);
 
     m_LabelSetImage->GetActiveLabelSet()->AddLabel(label1);
     m_LabelSetImage->AddLayer();
     m_LabelSetImage->GetLabelSet(1)->AddLabel(label2);
 
     CPPUNIT_ASSERT_MESSAGE("Wrong label retrieved for active layer",
                            mitk::Equal(*m_LabelSetImage->GetLabel(1), *label1, 0.0001, true));
     CPPUNIT_ASSERT_MESSAGE("Wrong label retrieved for layer 1",
                            mitk::Equal(*m_LabelSetImage->GetLabel(200, 1), *label2, 0.0001, true));
 
     // Try to get a non existing label
     mitk::Label *label3 = m_LabelSetImage->GetLabel(1000);
     CPPUNIT_ASSERT_MESSAGE("Non existing label should be nullptr", label3 == nullptr);
 
     // Try to get a label from a non existing layer
     label3 = m_LabelSetImage->GetLabel(200, 1000);
     CPPUNIT_ASSERT_MESSAGE("Label from non existing layer should be nullptr", label3 == nullptr);
   }
 
-  void TestSetExteriorLabel()
+  void TestSetUnlabeledLabelLock()
   {
-    mitk::Label::Pointer exteriorLabel = mitk::Label::New();
-    exteriorLabel->SetName("MyExteriorSpecialLabel");
-    mitk::Label::PixelType value1 = 10000;
-    exteriorLabel->SetValue(value1);
-
-    m_LabelSetImage->SetExteriorLabel(exteriorLabel);
-    CPPUNIT_ASSERT_MESSAGE("Wrong label retrieved for layer 1",
-                           mitk::Equal(*m_LabelSetImage->GetExteriorLabel(), *exteriorLabel, 0.0001, true));
-
-    // Exterior label should be set automatically for each new layer
-    m_LabelSetImage->AddLayer();
-    CPPUNIT_ASSERT_MESSAGE("Wrong label retrieved for layer 1",
-                           mitk::Equal(*m_LabelSetImage->GetLabel(10000, 1), *exteriorLabel, 0.0001, true));
+    auto locked = m_LabelSetImage->GetUnlabeledLabelLock();
+    CPPUNIT_ASSERT_MESSAGE("Wrong UnlabeledLabelLock default state",
+      locked == false);
+
+    m_LabelSetImage->SetUnlabeledLabelLock(true);
+    locked = m_LabelSetImage->GetUnlabeledLabelLock();
+    CPPUNIT_ASSERT_MESSAGE("Wrong UnlabeledLabelLock state",
+      locked == true);
   }
 
   void TestGetTotalNumberOfLabels()
   {
     mitk::Label::Pointer label1 = mitk::Label::New();
     label1->SetName("Label1");
     mitk::Label::PixelType value1 = 1;
     label1->SetValue(value1);
 
     mitk::Label::Pointer label2 = mitk::Label::New();
     label2->SetName("Label2");
     mitk::Label::PixelType value2 = 200;
     label2->SetValue(value2);
 
     m_LabelSetImage->GetActiveLabelSet()->AddLabel(label1);
     m_LabelSetImage->AddLayer();
     m_LabelSetImage->GetLabelSet(1)->AddLabel(label2);
     CPPUNIT_ASSERT_MESSAGE(
       "Wrong total number of labels",
-      m_LabelSetImage->GetTotalNumberOfLabels() == 4); // added 2 labels + 2 exterior default labels
+      m_LabelSetImage->GetTotalNumberOfLabels() == 2);
   }
 
   void TestExistsLabel()
   {
     mitk::Label::Pointer label = mitk::Label::New();
     label->SetName("Label2");
     mitk::Label::PixelType value = 200;
     label->SetValue(value);
 
     m_LabelSetImage->AddLayer();
     m_LabelSetImage->GetLabelSet(1)->AddLabel(label);
     m_LabelSetImage->SetActiveLayer(0);
     CPPUNIT_ASSERT_MESSAGE("Existing label was not found", m_LabelSetImage->ExistLabel(value) == true);
 
     CPPUNIT_ASSERT_MESSAGE("Non existing label was found", m_LabelSetImage->ExistLabel(10000) == false);
   }
 
   void TestExistsLabelSet()
   {
     // Cache active layer
     mitk::LabelSet::ConstPointer activeLayer = m_LabelSetImage->GetActiveLabelSet();
 
     // Add new layer
     mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New();
     mitk::Label::Pointer label1 = mitk::Label::New();
     label1->SetName("Label1");
     label1->SetValue(1);
 
     mitk::Label::Pointer label2 = mitk::Label::New();
     label2->SetName("Label2");
     label2->SetValue(200);
 
     newlayer->AddLabel(label1);
     newlayer->AddLabel(label2);
     newlayer->SetActiveLabel(200);
 
     m_LabelSetImage->AddLayer(newlayer);
 
     CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistLabelSet(0) == true);
     CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistLabelSet(1) == true);
     CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistLabelSet(20) == false);
   }
 
   void TestSetActiveLayer()
   {
     // Cache active layer
     mitk::LabelSet::ConstPointer activeLayer = m_LabelSetImage->GetActiveLabelSet();
 
     // Add new layer
     mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New();
     mitk::Label::Pointer label1 = mitk::Label::New();
     label1->SetName("Label1");
     label1->SetValue(1);
 
     mitk::Label::Pointer label2 = mitk::Label::New();
     label2->SetName("Label2");
     label2->SetValue(200);
 
     newlayer->AddLabel(label1);
     newlayer->AddLabel(label2);
     newlayer->SetActiveLabel(200);
 
     unsigned int layerID = m_LabelSetImage->AddLayer(newlayer);
 
     // Set initial layer as active layer
     m_LabelSetImage->SetActiveLayer(0);
     CPPUNIT_ASSERT_MESSAGE("Wrong active labelset returned",
                            mitk::Equal(*activeLayer, *m_LabelSetImage->GetActiveLabelSet(), 0.00001, true));
 
     // Set previously added layer as active layer
     m_LabelSetImage->SetActiveLayer(layerID);
     CPPUNIT_ASSERT_MESSAGE("Wrong active labelset returned",
                            mitk::Equal(*newlayer, *m_LabelSetImage->GetActiveLabelSet(), 0.00001, true));
 
     // Set a non existing layer as active layer - nothing should change
     m_LabelSetImage->SetActiveLayer(10000);
     CPPUNIT_ASSERT_MESSAGE("Wrong active labelset returned",
                            mitk::Equal(*newlayer, *m_LabelSetImage->GetActiveLabelSet(), 0.00001, true));
   }
 
   void TestRemoveLayer()
   {
     // Cache active layer
     mitk::LabelSet::ConstPointer activeLayer = m_LabelSetImage->GetActiveLabelSet();
 
     // Add new layers
     m_LabelSetImage->AddLayer();
 
     mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New();
     mitk::Label::Pointer label1 = mitk::Label::New();
     label1->SetName("Label1");
     label1->SetValue(1);
 
     mitk::Label::Pointer label2 = mitk::Label::New();
     label2->SetName("Label2");
     label2->SetValue(200);
 
     newlayer->AddLabel(label1);
     newlayer->AddLabel(label2);
     newlayer->SetActiveLabel(200);
 
     m_LabelSetImage->AddLayer(newlayer);
 
     CPPUNIT_ASSERT_MESSAGE("Wrong active labelset returned",
                            mitk::Equal(*newlayer, *m_LabelSetImage->GetActiveLabelSet(), 0.00001, true));
 
     m_LabelSetImage->RemoveLayer();
     CPPUNIT_ASSERT_MESSAGE("Wrong number of layers, after a layer was removed",
                            m_LabelSetImage->GetNumberOfLayers() == 2);
     CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistLabelSet(2) == false);
     CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistLabelSet(1) == true);
     CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistLabelSet(0) == true);
 
     m_LabelSetImage->RemoveLayer();
     CPPUNIT_ASSERT_MESSAGE("Wrong number of layers, after a layer was removed",
                            m_LabelSetImage->GetNumberOfLayers() == 1);
     CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistLabelSet(1) == false);
     CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistLabelSet(0) == true);
     CPPUNIT_ASSERT_MESSAGE("Wrong active layer",
                            mitk::Equal(*activeLayer, *m_LabelSetImage->GetActiveLabelSet(), 0.00001, true));
 
     m_LabelSetImage->RemoveLayer();
     CPPUNIT_ASSERT_MESSAGE("Wrong number of layers, after a layer was removed",
                            m_LabelSetImage->GetNumberOfLayers() == 0);
     CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistLabelSet(0) == false);
     CPPUNIT_ASSERT_MESSAGE("Active layers is not nullptr although all layer have been removed",
                            m_LabelSetImage->GetActiveLabelSet() == nullptr);
   }
 
   void TestRemoveLabels()
   {
     mitk::Image::Pointer image =
       mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("Multilabel/LabelSetTestInitializeImage.nrrd"));
 
     m_LabelSetImage = nullptr;
     m_LabelSetImage = mitk::LabelSetImage::New();
     m_LabelSetImage->InitializeByLabeledImage(image);
 
-    CPPUNIT_ASSERT_MESSAGE("Image - number of labels is not 6", m_LabelSetImage->GetNumberOfLabels() == 6);
-    // 2ndMin because of the exterior label = 0
+    CPPUNIT_ASSERT_MESSAGE("Image - number of labels is not 6", m_LabelSetImage->GetNumberOfLabels() == 5);
+    // 2ndMin because of unlabeled pixels = 0
     CPPUNIT_ASSERT_MESSAGE("Wrong MIN value", m_LabelSetImage->GetStatistics()->GetScalarValue2ndMin() == 1);
     CPPUNIT_ASSERT_MESSAGE("Wrong MAX value", m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 7);
 
     CPPUNIT_ASSERT_MESSAGE("Label with ID 3 does not exist after initialization",
                            m_LabelSetImage->ExistLabel(3) == true);
 
     m_LabelSetImage->RemoveLabel(1);
     std::vector<mitk::Label::PixelType> labelsToBeRemoved;
     labelsToBeRemoved.push_back(3);
     labelsToBeRemoved.push_back(7);
     m_LabelSetImage->RemoveLabels(labelsToBeRemoved);
 
     CPPUNIT_ASSERT_MESSAGE("Wrong number of labels after some have been removed",
-                           m_LabelSetImage->GetNumberOfLabels() == 3);
+                           m_LabelSetImage->GetNumberOfLabels() == 2);
     // Values within the image are 0, 1, 3, 5, 6, 7 - New Min / Max value should be 5 / 6
-    // 2ndMin because of the exterior label = 0
+    // 2ndMin because of unlabeled pixels = 0
     CPPUNIT_ASSERT_MESSAGE("Labels with value 1 and 3 were not removed from the image",
                            m_LabelSetImage->GetStatistics()->GetScalarValue2ndMin() == 5);
     CPPUNIT_ASSERT_MESSAGE("Label with value 7 was not removed from the image",
                            m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 6);
   }
 
   void TestEraseLabels()
   {
     mitk::Image::Pointer image =
       mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("Multilabel/LabelSetTestInitializeImage.nrrd"));
 
     m_LabelSetImage = nullptr;
     m_LabelSetImage = mitk::LabelSetImage::New();
     m_LabelSetImage->InitializeByLabeledImage(image);
 
-    CPPUNIT_ASSERT_MESSAGE("Image - number of labels is not 6", m_LabelSetImage->GetNumberOfLabels() == 6);
-    // 2ndMin because of the exterior label = 0
+    CPPUNIT_ASSERT_MESSAGE("Image - number of labels is not 6", m_LabelSetImage->GetNumberOfLabels() == 5);
+    // 2ndMin because of unlabeled pixels = 0
     CPPUNIT_ASSERT_MESSAGE("Wrong MIN value", m_LabelSetImage->GetStatistics()->GetScalarValue2ndMin() == 1);
     CPPUNIT_ASSERT_MESSAGE("Wrong MAX value", m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 7);
 
     CPPUNIT_ASSERT_MESSAGE("Label with ID 3 does not exist after initialization",
       m_LabelSetImage->ExistLabel(3) == true);
 
     m_LabelSetImage->EraseLabel(1);
     std::vector<mitk::Label::PixelType> labelsToBeErased;
     labelsToBeErased.push_back(3);
     labelsToBeErased.push_back(7);
     m_LabelSetImage->EraseLabels(labelsToBeErased);
 
     CPPUNIT_ASSERT_MESSAGE("Wrong number of labels since none have been removed",
-      m_LabelSetImage->GetNumberOfLabels() == 6);
+      m_LabelSetImage->GetNumberOfLabels() == 5);
     // Values within the image are 0, 1, 3, 5, 6, 7 - New Min / Max value should be 5 / 6
-    // 2ndMin because of the exterior label = 0
+    // 2ndMin because of unlabeled pixels = 0
     CPPUNIT_ASSERT_MESSAGE("Labels with value 1 and 3 were not erased from the image",
       m_LabelSetImage->GetStatistics()->GetScalarValue2ndMin() == 5);
     CPPUNIT_ASSERT_MESSAGE("Label with value 7 was not erased from the image",
       m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 6);
   }
 
   void TestMergeLabels()
   {
     mitk::Image::Pointer image =
       mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("Multilabel/LabelSetTestInitializeImage.nrrd"));
 
     m_LabelSetImage = nullptr;
     m_LabelSetImage = mitk::LabelSetImage::New();
     m_LabelSetImage->InitializeByLabeledImage(image);
 
-    CPPUNIT_ASSERT_MESSAGE("Image - number of labels is not 6", m_LabelSetImage->GetNumberOfLabels() == 6);
-    // 2ndMin because of the exterior label = 0
+    CPPUNIT_ASSERT_MESSAGE("Image - number of labels is not 6", m_LabelSetImage->GetNumberOfLabels() == 5);
+    // 2ndMin because of unlabeled pixels = 0
     CPPUNIT_ASSERT_MESSAGE("Wrong MIN value", m_LabelSetImage->GetStatistics()->GetScalarValue2ndMin() == 1);
     CPPUNIT_ASSERT_MESSAGE("Wrong MAX value", m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 7);
 
     CPPUNIT_ASSERT_MESSAGE("Label with ID 6 does not exist after initialization",
       m_LabelSetImage->ExistLabel(6) == true);
 
     // Merge label 7 with label 6. Result should be that label 7 is not present anymore.
     m_LabelSetImage->MergeLabel(6, 7);
     CPPUNIT_ASSERT_MESSAGE("Label with value 7 was not removed from the image", m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 6);
 
     // Count all pixels with value 6 = 507
     // Count all pixels with value 7 = 823
     // Check if merged label has 507 + 823 = 1330 pixels
     CPPUNIT_ASSERT_MESSAGE("Labels were not correctly merged", m_LabelSetImage->GetStatistics()->GetCountOfMaxValuedVoxels() == 1330);
 
     CPPUNIT_ASSERT_MESSAGE("Label with ID 3 does not exist after initialization",
       m_LabelSetImage->ExistLabel(3) == true);
     CPPUNIT_ASSERT_MESSAGE("Label with ID 5 does not exist after initialization",
       m_LabelSetImage->ExistLabel(5) == true);
 
     // Merge labels 5 and 6 with 3. Result should be that labels 5 and 6 are not present anymore.
     std::vector<mitk::Label::PixelType> vectorOfSourcePixelValues{ 5, 6 };
     m_LabelSetImage->MergeLabels(3, vectorOfSourcePixelValues);
     // Values within the image are 0, 1, 3, 5, 6, 7 - New Max value should be 3
     CPPUNIT_ASSERT_MESSAGE("Labels with value 5 and 6 were not removed from the image", m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 3);
 
     // Count all pixels with value 3 = 1893
     // Count all pixels with value 5 = 2143
     // Count all pixels with value 6 = 1330
     // Check if merged label has 1893 + 2143 + 1330 = 5366 pixels
     CPPUNIT_ASSERT_MESSAGE("Labels were not correctly merged", m_LabelSetImage->GetStatistics()->GetCountOfMaxValuedVoxels() == 5366);
   }
 
   void TestCreateLabelMask()
   {
     mitk::Image::Pointer image =
       mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("Multilabel/LabelSetTestInitializeImage.nrrd"));
 
     m_LabelSetImage = nullptr;
     m_LabelSetImage = mitk::LabelSetImage::New();
     m_LabelSetImage->InitializeByLabeledImage(image);
 
     auto labelMask = m_LabelSetImage->CreateLabelMask(6);
 
     mitk::AutoCropImageFilter::Pointer cropFilter = mitk::AutoCropImageFilter::New();
     cropFilter->SetInput(labelMask);
     cropFilter->SetBackgroundValue(0);
     cropFilter->SetMarginFactor(1.15);
     cropFilter->Update();
     auto maskImage = cropFilter->GetOutput();
 
     // Count all pixels with value 6 = 507
     CPPUNIT_ASSERT_MESSAGE("Label mask not correctly created", maskImage->GetStatistics()->GetCountOfMaxValuedVoxels() == 507);
   }
 };
 
 MITK_TEST_SUITE_REGISTRATION(mitkLabelSetImage)
diff --git a/Modules/Multilabel/Testing/mitkLabelSetTest.cpp b/Modules/Multilabel/Testing/mitkLabelSetTest.cpp
index 8db2f35179..01adf5f941 100644
--- a/Modules/Multilabel/Testing/mitkLabelSetTest.cpp
+++ b/Modules/Multilabel/Testing/mitkLabelSetTest.cpp
@@ -1,170 +1,170 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include <mitkLabelSet.h>
 #include <mitkLabelSetImage.h>
 #include <mitkStringProperty.h>
 #include <mitkTestFixture.h>
 #include <mitkTestingMacros.h>
 
 class mitkLabelSetTestSuite : public mitk::TestFixture
 {
   CPPUNIT_TEST_SUITE(mitkLabelSetTestSuite);
   MITK_TEST(TestSetLayer);
   MITK_TEST(TestSetActiveLabel);
   MITK_TEST(TestRemoveLabel);
   MITK_TEST(TestAddLabel);
   MITK_TEST(TestRenameLabel);
   MITK_TEST(TestSetAllLabelsVisible);
   MITK_TEST(TestSetAllLabelsLocked);
   MITK_TEST(TestRemoveAllLabels);
   CPPUNIT_TEST_SUITE_END();
 
 private:
   mitk::LabelSet::Pointer m_LabelSet;
   mitk::LabelSet::PixelType m_InitialNumberOfLabels;
 
   void AddLabels(mitk::LabelSet::PixelType numOfLabels)
   {
     mitk::Label::Pointer label;
     const std::string namePrefix = "Label_";
     const mitk::Color gray(0.5f);
 
     for (mitk::Label::PixelType i = 0; i < numOfLabels; ++i)
     {
       label = mitk::Label::New();
       label->SetName(namePrefix + std::to_string(i));
       label->SetValue(i);
       label->SetVisible((i % 2 == 0));
       label->SetLayer(i % 3);
       label->SetColor(gray);
 
       m_LabelSet->AddLabel(label);
     }
   }
 
 public:
   void setUp() override
   {
     m_InitialNumberOfLabels = 6;
     m_LabelSet = mitk::LabelSet::New();
 
     this->AddLabels(m_InitialNumberOfLabels);
     m_LabelSet->SetLayer(0);
     m_LabelSet->SetActiveLabel(0);
   }
 
   void tearDown() override
   {
     m_LabelSet = nullptr;
   }
 
   void TestSetLayer()
   {
     CPPUNIT_ASSERT_MESSAGE("Wrong initial layer", m_LabelSet->GetLayer() == 0);
 
     m_LabelSet->SetLayer(1);
     CPPUNIT_ASSERT_MESSAGE("Wrong layer", m_LabelSet->GetLayer() == 1);
   }
 
   void TestSetActiveLabel()
   {
-    CPPUNIT_ASSERT_MESSAGE("Wrong initial active label", m_LabelSet->GetActiveLabel()->GetValue() == 0);
+    CPPUNIT_ASSERT_MESSAGE("Wrong initial active label", m_LabelSet->GetActiveLabel() == nullptr);
 
     m_LabelSet->SetActiveLabel(1);
     CPPUNIT_ASSERT_MESSAGE("Wrong active label", m_LabelSet->GetActiveLabel()->GetValue() == 1);
   }
 
   void TestRemoveLabel()
   {
     CPPUNIT_ASSERT_MESSAGE("Wrong initial number of label", m_LabelSet->GetNumberOfLabels() == m_InitialNumberOfLabels);
 
     // Remove a label that is not the active label
     m_LabelSet->SetActiveLabel(2);
     m_LabelSet->RemoveLabel(1);
 
     mitk::LabelSet::PixelType numLabels = m_InitialNumberOfLabels - 1;
 
     CPPUNIT_ASSERT_MESSAGE("Label was not removed", m_LabelSet->ExistLabel(1) == false);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of label", m_LabelSet->GetNumberOfLabels() == numLabels);
     CPPUNIT_ASSERT_MESSAGE("Wrong active label", m_LabelSet->GetActiveLabel()->GetValue() == 2);
 
     // Remove active label - now the succeeding label should be active
     m_LabelSet->RemoveLabel(2);
     CPPUNIT_ASSERT_MESSAGE("Wrong active label", m_LabelSet->GetActiveLabel()->GetValue() == 3);
     CPPUNIT_ASSERT_MESSAGE("Label was not removed", m_LabelSet->ExistLabel(2) == false);
     CPPUNIT_ASSERT_MESSAGE("Wrong initial number of label", m_LabelSet->GetNumberOfLabels() == --numLabels);
   }
 
   void TestAddLabel()
   {
     auto newLabel = mitk::Label::New();
     newLabel->SetValue(1);
     m_LabelSet->AddLabel(newLabel);
 
     // Since label with value 1 already exists the new label will get the value m_InitialNumberOfValues
-    CPPUNIT_ASSERT_MESSAGE("Wrong label value", m_LabelSet->GetActiveLabel()->GetValue() == m_InitialNumberOfLabels);
+    CPPUNIT_ASSERT_MESSAGE("Wrong label value", m_LabelSet->GetActiveLabel()->GetValue() == m_InitialNumberOfLabels+1);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of label", m_LabelSet->GetNumberOfLabels() == static_cast<decltype(m_LabelSet->GetNumberOfLabels())>(m_InitialNumberOfLabels + 1));
   }
 
   void TestRenameLabel()
   {
     const mitk::Color white(1.0f);
     const std::string name = "MyAwesomeLabel";
 
-    m_LabelSet->RenameLabel(0, name, white);
+    m_LabelSet->RenameLabel(1, name, white);
 
-    const auto* label = m_LabelSet->GetLabel(0);
+    const auto* label = m_LabelSet->GetLabel(1);
     CPPUNIT_ASSERT_MESSAGE("Wrong label name", label->GetName() == name );
 
     const auto& color = label->GetColor();
     CPPUNIT_ASSERT_MESSAGE("Wrong color", color == white);
   }
 
   void TestSetAllLabelsVisible()
   {
     const auto numLabels = static_cast<mitk::LabelSet::PixelType>(m_LabelSet->GetNumberOfLabels());
 
     m_LabelSet->SetAllLabelsVisible(true);
 
-    for (mitk::LabelSet::PixelType i = 0; i < numLabels; ++i)
+    for (mitk::LabelSet::PixelType i = 1; i < numLabels; ++i)
       CPPUNIT_ASSERT_MESSAGE("Label not visible", m_LabelSet->GetLabel(i)->GetVisible() == true);
 
     m_LabelSet->SetAllLabelsVisible(false);
 
-    for (mitk::LabelSet::PixelType i = 0; i < numLabels; ++i)
+    for (mitk::LabelSet::PixelType i = 1; i < numLabels; ++i)
       CPPUNIT_ASSERT_MESSAGE("Label visible", m_LabelSet->GetLabel(i)->GetVisible() == false);
   }
 
   void TestSetAllLabelsLocked()
   {
     const auto numLabels = static_cast<mitk::LabelSet::PixelType>(m_LabelSet->GetNumberOfLabels());
 
     m_LabelSet->SetAllLabelsLocked(true);
 
-    for (mitk::LabelSet::PixelType i = 0; i < numLabels; ++i)
+    for (mitk::LabelSet::PixelType i = 1; i < numLabels; ++i)
       CPPUNIT_ASSERT_MESSAGE("Label not locked", m_LabelSet->GetLabel(i)->GetLocked() == true);
 
     m_LabelSet->SetAllLabelsLocked(false);
 
-    for (mitk::LabelSet::PixelType i = 0; i < numLabels; ++i)
+    for (mitk::LabelSet::PixelType i = 1; i < numLabels; ++i)
       CPPUNIT_ASSERT_MESSAGE("Label locked", m_LabelSet->GetLabel(i)->GetLocked() == false);
   }
 
   void TestRemoveAllLabels()
   {
     m_LabelSet->RemoveAllLabels();
     CPPUNIT_ASSERT_MESSAGE("Not all labels were removed", m_LabelSet->GetNumberOfLabels() == 0);
   }
 };
 
 MITK_TEST_SUITE_REGISTRATION(mitkLabelSet)
diff --git a/Modules/Multilabel/Testing/mitkLegacyLabelSetImageIOTest.cpp b/Modules/Multilabel/Testing/mitkLegacyLabelSetImageIOTest.cpp
new file mode 100644
index 0000000000..80a7eb7fba
--- /dev/null
+++ b/Modules/Multilabel/Testing/mitkLegacyLabelSetImageIOTest.cpp
@@ -0,0 +1,142 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+All rights reserved.
+
+Use of this source code is governed by a 3-clause BSD license that can be
+found in the LICENSE file.
+
+============================================================================*/
+
+#include <mitkIOUtil.h>
+#include <mitkLabelSetImage.h>
+
+#include <mitkTestFixture.h>
+#include <mitkTestingMacros.h>
+#include <mitkArbitraryTimeGeometry.h>
+#include <mitkCoreServices.h>
+#include <mitkPropertyPersistenceInfo.h>
+#include <mitkIPropertyPersistence.h>
+
+std::string pathToImage;
+
+class mitkLegacyLabelSetImageIOTestSuite : public mitk::TestFixture
+{
+  CPPUNIT_TEST_SUITE(mitkLegacyLabelSetImageIOTestSuite);
+  MITK_TEST(TestRead3DLabelSetImage_Default);
+  MITK_TEST(TestRead3DLabelSetImage_Adapt);
+  MITK_TEST(TestRead3DLabelSetImage_Split);
+  MITK_TEST(TestRead3DplusTLabelSetImage);
+  CPPUNIT_TEST_SUITE_END();
+
+private:
+  mitk::LabelSet::Pointer m_labelSet1;
+  mitk::LabelSet::Pointer m_labelSet2;
+  mitk::LabelSet::Pointer m_labelSet2_adapted;
+
+public:
+  mitk::Label::Pointer GenerateLabel(mitk::Label::PixelType value, const std::string& name, float r, float g, float b) const
+  {
+    auto label = mitk::Label::New(value, name);
+    mitk::Color color;
+    color.SetRed(r);
+    color.SetGreen(g);
+    color.SetBlue(b);
+    label->SetColor(color);
+
+    return label;
+  }
+
+  void setUp() override
+  {
+    m_labelSet1 = mitk::LabelSet::New();
+    auto label = GenerateLabel(1, "Label 1", 0.745098054f, 0.f, 0.196078435f);
+    m_labelSet1->AddLabel(label,false);
+    label = GenerateLabel(2, "Label 2", 0.952941179, 0.764705896, 0);
+    m_labelSet1->AddLabel(label, false);
+
+    m_labelSet2 = mitk::LabelSet::New();
+    label = GenerateLabel(1, "Label 3", 0.552941203, 0.713725507, 0);
+    m_labelSet2->AddLabel(label, false);
+    label = GenerateLabel(2, "Label 4", 0.631372571, 0.792156875, 0.945098042);
+    m_labelSet2->AddLabel(label, false);
+    label = GenerateLabel(3, "Label 5", 0.639215708, 0.250980407, 0.725490212);
+    m_labelSet2->AddLabel(label, false);
+
+    m_labelSet2_adapted = mitk::LabelSet::New();
+    label = GenerateLabel(3, "Label 3", 0.552941203, 0.713725507, 0);
+    m_labelSet2_adapted->AddLabel(label, false);
+    label = GenerateLabel(4, "Label 4", 0.631372571, 0.792156875, 0.945098042);
+    m_labelSet2_adapted->AddLabel(label, false);
+    label = GenerateLabel(5, "Label 5", 0.639215708, 0.250980407, 0.725490212);
+    m_labelSet2_adapted->AddLabel(label, false);
+    m_labelSet2_adapted->SetLayer(1);
+  }
+
+  void tearDown() override
+  {
+    m_labelSet1 = nullptr;
+    m_labelSet2 = nullptr;
+    m_labelSet2_adapted = nullptr;
+  }
+  
+  void TestRead3DLabelSetImage_Default()
+  {
+    auto testImages = mitk::IOUtil::Load(GetTestDataFilePath("Multilabel/LegacyLabelSetTestImage3D.nrrd"));
+
+    CPPUNIT_ASSERT_MESSAGE("Error reading label set image", testImages.size()==1);
+
+    auto lsimage1 = dynamic_cast<mitk::LabelSetImage*>(testImages[0].GetPointer());
+
+    CPPUNIT_ASSERT_MESSAGE("Number of layers is not correct", lsimage1->GetNumberOfLayers() == 2);
+    CPPUNIT_ASSERT_MESSAGE("Error layer 0 is not equal", mitk::Equal(*m_labelSet1, *(lsimage1->GetLabelSet(0)), mitk::eps, true));
+    CPPUNIT_ASSERT_MESSAGE("Error layer 1 is not equal", mitk::Equal(*m_labelSet2_adapted, *(lsimage1->GetLabelSet(1)), mitk::eps, true));
+
+    CPPUNIT_ASSERT_MESSAGE("Error, read image has different UID", "c236532b-f95a-4f22-a4c6-7abe4e41ad10"== lsimage1->GetUID());
+  }
+
+  void TestRead3DLabelSetImage_Adapt()
+  {
+    mitk::IFileReader::Options options = { {"Multi layer handling", us::Any(std::string("Adapt label values"))} };
+    auto testImages = mitk::IOUtil::Load(GetTestDataFilePath("Multilabel/LegacyLabelSetTestImage3D.nrrd"), options);
+
+    CPPUNIT_ASSERT_MESSAGE("Error reading label set image", testImages.size() == 1);
+
+    auto lsimage1 = dynamic_cast<mitk::LabelSetImage*>(testImages[0].GetPointer());
+
+    CPPUNIT_ASSERT_MESSAGE("Number of layers is not correct", lsimage1->GetNumberOfLayers() == 2);
+    CPPUNIT_ASSERT_MESSAGE("Error layer 0 is not equal", mitk::Equal(*m_labelSet1, *(lsimage1->GetLabelSet(0)), mitk::eps, true));
+    CPPUNIT_ASSERT_MESSAGE("Error layer 1 is not equal", mitk::Equal(*m_labelSet2_adapted, *(lsimage1->GetLabelSet(1)), mitk::eps, true));
+
+    CPPUNIT_ASSERT_MESSAGE("Error, read image has different UID", "c236532b-f95a-4f22-a4c6-7abe4e41ad10" == lsimage1->GetUID());
+  }
+
+  void TestRead3DLabelSetImage_Split()
+  {
+    mitk::IFileReader::Options options = { {"Multi layer handling", us::Any(std::string("Split layers"))} };
+    auto testImages = mitk::IOUtil::Load(GetTestDataFilePath("Multilabel/LegacyLabelSetTestImage3D.nrrd"), options);
+
+    CPPUNIT_ASSERT_MESSAGE("Error reading label set image", testImages.size() == 2);
+
+    auto lsimage1 = dynamic_cast<mitk::LabelSetImage*>(testImages[0].GetPointer());
+    auto lsimage2 = dynamic_cast<mitk::LabelSetImage*>(testImages[1].GetPointer());
+
+    CPPUNIT_ASSERT_MESSAGE("Number of layers in image 1 isnot correct", lsimage1->GetNumberOfLayers() == 1);
+    CPPUNIT_ASSERT_MESSAGE("Number of layers in image 2 is not correct", lsimage2->GetNumberOfLayers() == 1);
+    CPPUNIT_ASSERT_MESSAGE("Error layer 0 is not equal", mitk::Equal(*m_labelSet1, *(lsimage1->GetLabelSet(0)), mitk::eps, true));
+    CPPUNIT_ASSERT_MESSAGE("Error layer 1 is not equal", mitk::Equal(*m_labelSet2, *(lsimage2->GetLabelSet(0)), mitk::eps, true));
+
+    CPPUNIT_ASSERT_MESSAGE("Error, read image has same UID", "c236532b-f95a-4f22-a4c6-7abe4e41ad10" != lsimage1->GetUID());
+
+  }
+
+  void TestRead3DplusTLabelSetImage()
+  {
+
+  }
+
+};
+
+MITK_TEST_SUITE_REGISTRATION(mitkLegacyLabelSetImageIO)
diff --git a/Modules/Multilabel/Testing/mitkTransferLabelTest.cpp b/Modules/Multilabel/Testing/mitkTransferLabelTest.cpp
index 6df3b1d69a..be70fd897f 100644
--- a/Modules/Multilabel/Testing/mitkTransferLabelTest.cpp
+++ b/Modules/Multilabel/Testing/mitkTransferLabelTest.cpp
@@ -1,142 +1,229 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include <mitkIOUtil.h>
 #include <mitkLabelSetImage.h>
 #include <mitkTestFixture.h>
 #include <mitkTestingMacros.h>
 
 class mitkTransferLabelTestSuite : public mitk::TestFixture
 {
   CPPUNIT_TEST_SUITE(mitkTransferLabelTestSuite);
   MITK_TEST(TestTransfer_defaults);
   MITK_TEST(TestTransfer_Merge_RegardLocks);
   MITK_TEST(TestTransfer_Merge_IgnoreLocks);
   MITK_TEST(TestTransfer_Replace_RegardLocks);
   MITK_TEST(TestTransfer_Replace_IgnoreLocks);
   MITK_TEST(TestTransfer_multipleLabels);
+  MITK_TEST(TestTransfer_Merge_RegardLocks_AtTimeStep);
+  MITK_TEST(TestTransfer_Merge_IgnoreLocks_AtTimeStep);
+  MITK_TEST(TestTransfer_Replace_RegardLocks_AtTimeStep);
+  MITK_TEST(TestTransfer_Replace_IgnoreLocks_AtTimeStep);
+  MITK_TEST(TestTransfer_multipleLabels_AtTimeStep);
   CPPUNIT_TEST_SUITE_END();
 
 private:
   mitk::LabelSetImage::Pointer m_SourceImage;
 
 public:
   void setUp() override
   {
     m_SourceImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_source.nrrd"));
   }
 
   void tearDown() override
   {
     m_SourceImage = nullptr;
   }
 
   void TestTransfer_defaults()
   {
     auto destinationImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_destination.nrrd"));
-    auto destinationLockedExteriorImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_destination_lockedExterior.nrrd"));
+    auto destinationLockedUnlabeledImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_destination_lockedExterior.nrrd"));
     auto refmage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_result_replace_regardLocks.nrrd"));
-    auto refLockedExteriorImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_result_replace_regardLocks_lockedExterior.nrrd"));
+    auto refLockedUnlabeledImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_result_replace_regardLocks_lockedExterior.nrrd"));
 
     mitk::TransferLabelContent(m_SourceImage, destinationImage);
-    mitk::TransferLabelContent(m_SourceImage, destinationLockedExteriorImage);
+    mitk::TransferLabelContent(m_SourceImage, destinationLockedUnlabeledImage);
 
     CPPUNIT_ASSERT_MESSAGE("Transfer with default settings failed",
       mitk::Equal(*(destinationImage.GetPointer()), *(refmage.GetPointer()), mitk::eps, false));
     CPPUNIT_ASSERT_MESSAGE("Transfer with default settings + exterior lock failed",
-      mitk::Equal(*(destinationLockedExteriorImage.GetPointer()), *(refLockedExteriorImage.GetPointer()), mitk::eps, false));
+      mitk::Equal(*(destinationLockedUnlabeledImage.GetPointer()), *(refLockedUnlabeledImage.GetPointer()), mitk::eps, false));
   }
 
   void TestTransfer_Merge_RegardLocks()
   {
     auto destinationImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_destination.nrrd"));
-    auto destinationLockedExteriorImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_destination_lockedExterior.nrrd"));
+    auto destinationLockedUnlabeledImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_destination_lockedExterior.nrrd"));
     auto refmage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_result_merge_regardLocks.nrrd"));
-    auto refLockedExteriorImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_result_merge_regardLocks_lockedExterior.nrrd"));
+    auto refLockedUnlabeledImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_result_merge_regardLocks_lockedExterior.nrrd"));
 
     mitk::TransferLabelContent(m_SourceImage, destinationImage, { {1,1} }, mitk::MultiLabelSegmentation::MergeStyle::Merge, mitk::MultiLabelSegmentation::OverwriteStyle::RegardLocks);
-    mitk::TransferLabelContent(m_SourceImage, destinationLockedExteriorImage, { {1,1} }, mitk::MultiLabelSegmentation::MergeStyle::Merge, mitk::MultiLabelSegmentation::OverwriteStyle::RegardLocks);
+    mitk::TransferLabelContent(m_SourceImage, destinationLockedUnlabeledImage, { {1,1} }, mitk::MultiLabelSegmentation::MergeStyle::Merge, mitk::MultiLabelSegmentation::OverwriteStyle::RegardLocks);
 
     CPPUNIT_ASSERT_MESSAGE("Transfer with merge + regardLocks settings failed",
       mitk::Equal(*(destinationImage.GetPointer()), *(refmage.GetPointer()), mitk::eps, false));
     CPPUNIT_ASSERT_MESSAGE("Transfer with merge + regardLocks + exterior lock settings failed",
-      mitk::Equal(*(destinationLockedExteriorImage.GetPointer()), *(refLockedExteriorImage.GetPointer()), mitk::eps, false));
+      mitk::Equal(*(destinationLockedUnlabeledImage.GetPointer()), *(refLockedUnlabeledImage.GetPointer()), mitk::eps, false));
   }
 
   void TestTransfer_Merge_IgnoreLocks()
   {
     auto destinationImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_destination.nrrd"));
-    auto destinationLockedExteriorImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_destination_lockedExterior.nrrd"));
+    auto destinationLockedUnlabeledImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_destination_lockedExterior.nrrd"));
     auto refmage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_result_merge_ignoreLocks.nrrd"));
-    auto refLockedExteriorImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_result_merge_ignoreLocks_lockedExterior.nrrd"));
+    auto refLockedUnlabeledImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_result_merge_ignoreLocks_lockedExterior.nrrd"));
 
     mitk::TransferLabelContent(m_SourceImage, destinationImage, { {1,1} }, mitk::MultiLabelSegmentation::MergeStyle::Merge, mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks);
-    mitk::TransferLabelContent(m_SourceImage, destinationLockedExteriorImage, { {1,1} }, mitk::MultiLabelSegmentation::MergeStyle::Merge, mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks);
+    mitk::TransferLabelContent(m_SourceImage, destinationLockedUnlabeledImage, { {1,1} }, mitk::MultiLabelSegmentation::MergeStyle::Merge, mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks);
 
     CPPUNIT_ASSERT_MESSAGE("Transfer with merge + ignoreLocks settings failed",
       mitk::Equal(*(destinationImage.GetPointer()), *(refmage.GetPointer()), mitk::eps, false));
     CPPUNIT_ASSERT_MESSAGE("Transfer with merge + ignoreLocks + exterior lock settings failed",
-      mitk::Equal(*(destinationLockedExteriorImage.GetPointer()), *(refLockedExteriorImage.GetPointer()), mitk::eps, false));
+      mitk::Equal(*(destinationLockedUnlabeledImage.GetPointer()), *(refLockedUnlabeledImage.GetPointer()), mitk::eps, false));
   }
 
   void TestTransfer_Replace_RegardLocks()
   {
     auto destinationImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_destination.nrrd"));
-    auto destinationLockedExteriorImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_destination_lockedExterior.nrrd"));
+    auto destinationLockedUnlabeledImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_destination_lockedExterior.nrrd"));
     auto refmage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_result_replace_regardLocks.nrrd"));
-    auto refLockedExteriorImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_result_replace_regardLocks_lockedExterior.nrrd"));
+    auto refLockedUnlabeledImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_result_replace_regardLocks_lockedExterior.nrrd"));
 
     mitk::TransferLabelContent(m_SourceImage, destinationImage, { {1,1} }, mitk::MultiLabelSegmentation::MergeStyle::Replace, mitk::MultiLabelSegmentation::OverwriteStyle::RegardLocks);
-    mitk::TransferLabelContent(m_SourceImage, destinationLockedExteriorImage, { {1,1} }, mitk::MultiLabelSegmentation::MergeStyle::Replace, mitk::MultiLabelSegmentation::OverwriteStyle::RegardLocks);
+    mitk::TransferLabelContent(m_SourceImage, destinationLockedUnlabeledImage, { {1,1} }, mitk::MultiLabelSegmentation::MergeStyle::Replace, mitk::MultiLabelSegmentation::OverwriteStyle::RegardLocks);
 
     CPPUNIT_ASSERT_MESSAGE("Transfer with replace + regardLocks settings failed",
       mitk::Equal(*(destinationImage.GetPointer()), *(refmage.GetPointer()), mitk::eps, false));
     CPPUNIT_ASSERT_MESSAGE("Transfer with replace + regardLocks + exterior lock settings failed",
-      mitk::Equal(*(destinationLockedExteriorImage.GetPointer()), *(refLockedExteriorImage.GetPointer()), mitk::eps, false));
+      mitk::Equal(*(destinationLockedUnlabeledImage.GetPointer()), *(refLockedUnlabeledImage.GetPointer()), mitk::eps, false));
   }
 
   void TestTransfer_Replace_IgnoreLocks()
   {
     auto destinationImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_destination.nrrd"));
-    auto destinationLockedExteriorImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_destination_lockedExterior.nrrd"));
+    auto destinationLockedUnlabeledImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_destination_lockedExterior.nrrd"));
     auto refmage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_result_replace_ignoreLocks.nrrd"));
-    auto refLockedExteriorImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_result_replace_ignoreLocks_lockedExterior.nrrd"));
+    auto refLockedUnlabeledImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_result_replace_ignoreLocks_lockedExterior.nrrd"));
 
     mitk::TransferLabelContent(m_SourceImage, destinationImage, { {1,1} }, mitk::MultiLabelSegmentation::MergeStyle::Replace, mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks);
-    mitk::TransferLabelContent(m_SourceImage, destinationLockedExteriorImage, { {1,1} }, mitk::MultiLabelSegmentation::MergeStyle::Replace, mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks);
+    mitk::TransferLabelContent(m_SourceImage, destinationLockedUnlabeledImage, { {1,1} }, mitk::MultiLabelSegmentation::MergeStyle::Replace, mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks);
 
     CPPUNIT_ASSERT_MESSAGE("Transfer with replace + ignoreLocks settings failed",
       mitk::Equal(*(destinationImage.GetPointer()), *(refmage.GetPointer()), mitk::eps, false));
     CPPUNIT_ASSERT_MESSAGE("Transfer with replace + ignoreLocks + exterior lock settings failed",
-      mitk::Equal(*(destinationLockedExteriorImage.GetPointer()), *(refLockedExteriorImage.GetPointer()), mitk::eps, false));
+      mitk::Equal(*(destinationLockedUnlabeledImage.GetPointer()), *(refLockedUnlabeledImage.GetPointer()), mitk::eps, false));
   }
 
 
   void TestTransfer_multipleLabels()
   {
     auto destinationImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_destination.nrrd"));
-    auto destinationLockedExteriorImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_destination_lockedExterior.nrrd"));
+    auto destinationLockedUnlabeledImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_destination_lockedExterior.nrrd"));
     auto refmage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_result_multipleLabels.nrrd"));
-    auto refLockedExteriorImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_result_multipleLabels_lockedExterior.nrrd"));
+    auto refLockedUnlabeledImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_result_multipleLabels_lockedExterior.nrrd"));
 
     mitk::TransferLabelContent(m_SourceImage, destinationImage, { {1,1}, {3,1}, {2,4}, {4,2} }, mitk::MultiLabelSegmentation::MergeStyle::Replace, mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks);
-    mitk::TransferLabelContent(m_SourceImage, destinationLockedExteriorImage, { {1,1}, {3,1}, {2,4}, {4,2} }, mitk::MultiLabelSegmentation::MergeStyle::Replace, mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks);
+    mitk::TransferLabelContent(m_SourceImage, destinationLockedUnlabeledImage, { {1,1}, {3,1}, {2,4}, {4,2} }, mitk::MultiLabelSegmentation::MergeStyle::Replace, mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks);
 
     CPPUNIT_ASSERT_MESSAGE("Transfer multiple labels (1->1, 3->1, 2->4, 4->2) with replace + ignoreLocks settings failed",
       mitk::Equal(*(destinationImage.GetPointer()), *(refmage.GetPointer()), mitk::eps, false));
     CPPUNIT_ASSERT_MESSAGE("Transfer multiple labels (1->1, 3->1, 2->4, 4->2) with replace + ignoreLocks + exterior lock settings failed",
-      mitk::Equal(*(destinationLockedExteriorImage.GetPointer()), *(refLockedExteriorImage.GetPointer()), mitk::eps, false));
+      mitk::Equal(*(destinationLockedUnlabeledImage.GetPointer()), *(refLockedUnlabeledImage.GetPointer()), mitk::eps, false));
   }
 
+  void TestTransfer_Merge_RegardLocks_AtTimeStep()
+  {
+    auto destinationImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_destination.nrrd"));
+    auto destinationLockedUnlabeledImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_destination_lockedExterior.nrrd"));
+    auto refmage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_result_merge_regardLocks.nrrd"));
+    auto refLockedUnlabeledImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_result_merge_regardLocks_lockedExterior.nrrd"));
+
+    mitk::TransferLabelContentAtTimeStep(m_SourceImage, destinationImage, 0, { {1,1} }, mitk::MultiLabelSegmentation::MergeStyle::Merge, mitk::MultiLabelSegmentation::OverwriteStyle::RegardLocks);
+    mitk::TransferLabelContentAtTimeStep(m_SourceImage, destinationLockedUnlabeledImage, 0, { {1,1} }, mitk::MultiLabelSegmentation::MergeStyle::Merge, mitk::MultiLabelSegmentation::OverwriteStyle::RegardLocks);
+
+    CPPUNIT_ASSERT_MESSAGE("Transfer with merge + regardLocks settings failed",
+      mitk::Equal(*(destinationImage.GetPointer()), *(refmage.GetPointer()), mitk::eps, false));
+    CPPUNIT_ASSERT_MESSAGE("Transfer with merge + regardLocks + exterior lock settings failed",
+      mitk::Equal(*(destinationLockedUnlabeledImage.GetPointer()), *(refLockedUnlabeledImage.GetPointer()), mitk::eps, false));
+  }
+
+  void TestTransfer_Merge_IgnoreLocks_AtTimeStep()
+  {
+    auto destinationImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_destination.nrrd"));
+    auto destinationLockedUnlabeledImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_destination_lockedExterior.nrrd"));
+    auto refmage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_result_merge_ignoreLocks.nrrd"));
+    auto refLockedUnlabeledImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_result_merge_ignoreLocks_lockedExterior.nrrd"));
+
+    mitk::TransferLabelContentAtTimeStep(m_SourceImage, destinationImage, 0, { {1,1} }, mitk::MultiLabelSegmentation::MergeStyle::Merge, mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks);
+    mitk::TransferLabelContentAtTimeStep(m_SourceImage, destinationLockedUnlabeledImage, 0, { {1,1} }, mitk::MultiLabelSegmentation::MergeStyle::Merge, mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks);
+
+    CPPUNIT_ASSERT_MESSAGE("Transfer with merge + ignoreLocks settings failed",
+      mitk::Equal(*(destinationImage.GetPointer()), *(refmage.GetPointer()), mitk::eps, false));
+    CPPUNIT_ASSERT_MESSAGE("Transfer with merge + ignoreLocks + exterior lock settings failed",
+      mitk::Equal(*(destinationLockedUnlabeledImage.GetPointer()), *(refLockedUnlabeledImage.GetPointer()), mitk::eps, false));
+  }
+
+  void TestTransfer_Replace_RegardLocks_AtTimeStep()
+  {
+    auto destinationImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_destination.nrrd"));
+    auto destinationLockedUnlabeledImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_destination_lockedExterior.nrrd"));
+    auto refmage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_result_replace_regardLocks.nrrd"));
+    auto refLockedUnlabeledImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_result_replace_regardLocks_lockedExterior.nrrd"));
+
+    mitk::TransferLabelContentAtTimeStep(m_SourceImage, destinationImage, 0, { {1,1} }, mitk::MultiLabelSegmentation::MergeStyle::Replace, mitk::MultiLabelSegmentation::OverwriteStyle::RegardLocks);
+    mitk::TransferLabelContentAtTimeStep(m_SourceImage, destinationLockedUnlabeledImage, 0, { {1,1} }, mitk::MultiLabelSegmentation::MergeStyle::Replace, mitk::MultiLabelSegmentation::OverwriteStyle::RegardLocks);
+
+    CPPUNIT_ASSERT_MESSAGE("Transfer with replace + regardLocks settings failed",
+      mitk::Equal(*(destinationImage.GetPointer()), *(refmage.GetPointer()), mitk::eps, false));
+    CPPUNIT_ASSERT_MESSAGE("Transfer with replace + regardLocks + exterior lock settings failed",
+      mitk::Equal(*(destinationLockedUnlabeledImage.GetPointer()), *(refLockedUnlabeledImage.GetPointer()), mitk::eps, false));
+  }
+
+  void TestTransfer_Replace_IgnoreLocks_AtTimeStep()
+  {
+    auto destinationImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_destination.nrrd"));
+    auto destinationLockedUnlabeledImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_destination_lockedExterior.nrrd"));
+    auto refmage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_result_replace_ignoreLocks.nrrd"));
+    auto refLockedUnlabeledImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_result_replace_ignoreLocks_lockedExterior.nrrd"));
+
+    mitk::TransferLabelContentAtTimeStep(m_SourceImage, destinationImage, 0, { {1,1} }, mitk::MultiLabelSegmentation::MergeStyle::Replace, mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks);
+    mitk::TransferLabelContentAtTimeStep(m_SourceImage, destinationLockedUnlabeledImage, 0, { {1,1} }, mitk::MultiLabelSegmentation::MergeStyle::Replace, mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks);
+
+    CPPUNIT_ASSERT_MESSAGE("Transfer with replace + ignoreLocks settings failed",
+      mitk::Equal(*(destinationImage.GetPointer()), *(refmage.GetPointer()), mitk::eps, false));
+    CPPUNIT_ASSERT_MESSAGE("Transfer with replace + ignoreLocks + exterior lock settings failed",
+      mitk::Equal(*(destinationLockedUnlabeledImage.GetPointer()), *(refLockedUnlabeledImage.GetPointer()), mitk::eps, false));
+  }
+
+
+  void TestTransfer_multipleLabels_AtTimeStep()
+  {
+    auto destinationImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_destination.nrrd"));
+    auto destinationLockedUnlabeledImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_destination_lockedExterior.nrrd"));
+    auto refmage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_result_multipleLabels.nrrd"));
+    auto refLockedUnlabeledImage = mitk::IOUtil::Load<mitk::LabelSetImage>(GetTestDataFilePath("Multilabel/LabelTransferTest_result_multipleLabels_lockedExterior.nrrd"));
+
+    mitk::TransferLabelContentAtTimeStep(m_SourceImage, destinationImage, 0, { {1,1}, {3,1}, {2,4}, {4,2} }, mitk::MultiLabelSegmentation::MergeStyle::Replace, mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks);
+    mitk::TransferLabelContentAtTimeStep(m_SourceImage, destinationLockedUnlabeledImage, 0, { {1,1}, {3,1}, {2,4}, {4,2} }, mitk::MultiLabelSegmentation::MergeStyle::Replace, mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks);
+
+    CPPUNIT_ASSERT_MESSAGE("Transfer multiple labels (1->1, 3->1, 2->4, 4->2) with replace + ignoreLocks settings failed",
+      mitk::Equal(*(destinationImage.GetPointer()), *(refmage.GetPointer()), mitk::eps, false));
+    CPPUNIT_ASSERT_MESSAGE("Transfer multiple labels (1->1, 3->1, 2->4, 4->2) with replace + ignoreLocks + exterior lock settings failed",
+      mitk::Equal(*(destinationLockedUnlabeledImage.GetPointer()), *(refLockedUnlabeledImage.GetPointer()), mitk::eps, false));
+  }
+
+
 };
 
 MITK_TEST_SUITE_REGISTRATION(mitkTransferLabel)
diff --git a/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.cpp b/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.cpp
index 68fad097df..e10302229c 100644
--- a/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.cpp
+++ b/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.cpp
@@ -1,704 +1,704 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #ifndef __mitkDICOMSegmentationIO__cpp
 #define __mitkDICOMSegmentationIO__cpp
 
 #include "mitkDICOMSegmentationIO.h"
 
 #include "mitkDICOMSegIOMimeTypes.h"
 #include "mitkDICOMSegmentationConstants.h"
 #include <mitkDICOMDCMTKTagScanner.h>
 #include <mitkDICOMIOHelper.h>
 #include <mitkDICOMProperty.h>
 #include <mitkIDICOMTagsOfInterest.h>
 #include <mitkImageAccessByItk.h>
 #include <mitkImageCast.h>
 #include <mitkLocaleSwitch.h>
 #include <mitkPropertyNameHelper.h>
 
 
 // itk
 #include <itkThresholdImageFilter.h>
 
 // dcmqi
 #include <dcmqi/ImageSEGConverter.h>
 
 // us
 #include <usGetModuleContext.h>
 #include <usModuleContext.h>
 
 namespace mitk
 {
   DICOMSegmentationIO::DICOMSegmentationIO()
     : AbstractFileIO(LabelSetImage::GetStaticNameOfClass(),
       mitk::MitkDICOMSEGIOMimeTypes::DICOMSEG_MIMETYPE_NAME(),
       "DICOM Segmentation")
   {
     AbstractFileWriter::SetRanking(10);
     AbstractFileReader::SetRanking(10);
     this->RegisterService();
   }
 
   std::vector<mitk::DICOMTagPath> DICOMSegmentationIO::GetDICOMTagsOfInterest()
   {
     std::vector<mitk::DICOMTagPath> result;
     result.emplace_back(DICOMSegmentationConstants::SEGMENT_SEQUENCE_PATH());
 
     result.emplace_back(DICOMSegmentationConstants::SEGMENT_NUMBER_PATH());
     result.emplace_back(DICOMSegmentationConstants::SEGMENT_LABEL_PATH());
     result.emplace_back(DICOMSegmentationConstants::SEGMENT_ALGORITHM_TYPE_PATH());
 
     result.emplace_back(DICOMSegmentationConstants::ANATOMIC_REGION_SEQUENCE_PATH());
     result.emplace_back(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_VALUE_PATH());
     result.emplace_back(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_SCHEME_PATH());
     result.emplace_back(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_MEANING_PATH());
 
     result.emplace_back(DICOMSegmentationConstants::SEGMENTED_PROPERTY_CATEGORY_SEQUENCE_PATH());
     result.emplace_back(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_VALUE_PATH());
     result.emplace_back(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_SCHEME_PATH());
     result.emplace_back(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_MEANING_PATH());
 
     result.emplace_back(DICOMSegmentationConstants::SEGMENTED_PROPERTY_TYPE_SEQUENCE_PATH());
     result.emplace_back(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_VALUE_PATH());
     result.emplace_back(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_SCHEME_PATH());
     result.emplace_back(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_MEANING_PATH());
 
     result.emplace_back(DICOMSegmentationConstants::SEGMENTED_PROPERTY_MODIFIER_SEQUENCE_PATH());
     result.emplace_back(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_VALUE_PATH());
     result.emplace_back(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_SCHEME_PATH());
     result.emplace_back(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_MEANING_PATH());
 
     return result;
   }
 
   IFileIO::ConfidenceLevel DICOMSegmentationIO::GetWriterConfidenceLevel() const
   {
     if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported)
       return Unsupported;
 
     // Check if the input file is a segmentation
     const LabelSetImage *input = dynamic_cast<const LabelSetImage *>(this->GetInput());
 
     if (input)
     {
       if ((input->GetDimension() != 3))
       {
         MITK_INFO << "DICOM segmentation writer is tested only with 3D images, sorry.";
         return Unsupported;
       }
 
       // Check if input file has dicom information for the referenced image (original DICOM image, e.g. CT) Still necessary, see write()
       mitk::StringLookupTableProperty::Pointer dicomFilesProp =
       dynamic_cast<mitk::StringLookupTableProperty *>(input->GetProperty("referenceFiles").GetPointer());
 
       if (dicomFilesProp.IsNotNull())
         return Supported;
     }
 
     return Unsupported;
   }
 
   void DICOMSegmentationIO::Write()
   {
     ValidateOutputLocation();
 
     mitk::LocaleSwitch localeSwitch("C");
     LocalFile localFile(this);
     const std::string path = localFile.GetFileName();
 
     auto input = dynamic_cast<const LabelSetImage *>(this->GetInput());
     if (input == nullptr)
       mitkThrow() << "Cannot write non-image data";
 
     // Get DICOM information from referenced image
     vector<std::unique_ptr<DcmDataset>> dcmDatasetsSourceImage;
     std::unique_ptr<DcmFileFormat> readFileFormat(new DcmFileFormat());
     try
     {
       // TODO: Generate dcmdataset witk DICOM tags from property list; ATM the source are the filepaths from the
       // property list
       mitk::StringLookupTableProperty::Pointer filesProp =
         dynamic_cast<mitk::StringLookupTableProperty *>(input->GetProperty("referenceFiles").GetPointer());
 
       if (filesProp.IsNull())
       {
         mitkThrow() << "No property with dicom file path.";
         return;
       }
 
       StringLookupTable filesLut = filesProp->GetValue();
       const StringLookupTable::LookupTableType &lookUpTableMap = filesLut.GetLookupTable();
 
       for (const auto &it : lookUpTableMap)
       {
         const char *fileName = (it.second).c_str();
         if (readFileFormat->loadFile(fileName, EXS_Unknown).good())
         {
           std::unique_ptr<DcmDataset> readDCMDataset(readFileFormat->getAndRemoveDataset());
           dcmDatasetsSourceImage.push_back(std::move(readDCMDataset));
         }
       }
     }
     catch (const std::exception &e)
     {
       MITK_ERROR << "An error occurred while getting the dicom informations: " << e.what() << endl;
       return;
     }
 
     // Iterate over all layers. For each a dcm file will be generated
     for (unsigned int layer = 0; layer < input->GetNumberOfLayers(); ++layer)
     {
       vector<itkInternalImageType::Pointer> segmentations;
 
       try
       {
         // Hack: Remove the const attribute to switch between the layer images. Normally you could get the different
         // layer images by input->GetLayerImage(layer)
         mitk::LabelSetImage *mitkLayerImage = const_cast<mitk::LabelSetImage *>(input);
         mitkLayerImage->SetActiveLayer(layer);
 
         // Cast mitk layer image to itk
         ImageToItk<itkInputImageType>::Pointer imageToItkFilter = ImageToItk<itkInputImageType>::New();
         imageToItkFilter->SetInput(mitkLayerImage);
         // Cast from original itk type to dcmqi input itk image type
         typedef itk::CastImageFilter<itkInputImageType, itkInternalImageType> castItkImageFilterType;
         castItkImageFilterType::Pointer castFilter = castItkImageFilterType::New();
         castFilter->SetInput(imageToItkFilter->GetOutput());
         castFilter->Update();
 
         itkInternalImageType::Pointer itkLabelImage = castFilter->GetOutput();
         itkLabelImage->DisconnectPipeline();
 
         // Iterate over all labels. For each label a segmentation image will be created
         const LabelSet *labelSet = input->GetLabelSet(layer);
         auto labelIter = labelSet->IteratorConstBegin();
         // Ignore background label
         ++labelIter;
 
         for (; labelIter != labelSet->IteratorConstEnd(); ++labelIter)
         {
           // Thresold over the image with the given label value
           itk::ThresholdImageFilter<itkInternalImageType>::Pointer thresholdFilter =
             itk::ThresholdImageFilter<itkInternalImageType>::New();
           thresholdFilter->SetInput(itkLabelImage);
           thresholdFilter->ThresholdOutside(labelIter->first, labelIter->first);
           thresholdFilter->SetOutsideValue(0);
           thresholdFilter->Update();
           itkInternalImageType::Pointer segmentImage = thresholdFilter->GetOutput();
           segmentImage->DisconnectPipeline();
 
           segmentations.push_back(segmentImage);
         }
       }
       catch (const itk::ExceptionObject &e)
       {
         MITK_ERROR << e.GetDescription() << endl;
         return;
       }
 
       // Create segmentation meta information
       const std::string tmpMetaInfoFile = this->CreateMetaDataJsonFile(layer);
 
       MITK_INFO << "Writing image: " << path << std::endl;
       try
       {
         //TODO is there a better way? Interface expects a vector of raw pointer.
         vector<DcmDataset*> rawVecDataset;
         for (const auto& dcmDataSet : dcmDatasetsSourceImage)
           rawVecDataset.push_back(dcmDataSet.get());
 
         // Convert itk segmentation images to dicom image
         std::unique_ptr<dcmqi::ImageSEGConverter> converter = std::make_unique<dcmqi::ImageSEGConverter>();
         std::unique_ptr<DcmDataset> result(converter->itkimage2dcmSegmentation(rawVecDataset, segmentations, tmpMetaInfoFile, false));
 
         // Write dicom file
         DcmFileFormat dcmFileFormat(result.get());
 
         std::string filePath = path.substr(0, path.find_last_of("."));
         // If there is more than one layer, we have to write more than 1 dicom file
         if (input->GetNumberOfLayers() != 1)
           filePath = filePath + std::to_string(layer) + ".dcm";
         else
           filePath = filePath + ".dcm";
 
         dcmFileFormat.saveFile(filePath.c_str(), EXS_LittleEndianExplicit);
       }
       catch (const std::exception &e)
       {
         MITK_ERROR << "An error occurred during writing the DICOM Seg: " << e.what() << endl;
         return;
       }
     } // Write a dcm file for the next layer
   }
 
   IFileIO::ConfidenceLevel DICOMSegmentationIO::GetReaderConfidenceLevel() const
   {
     if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported)
       return Unsupported;
 
     const std::string fileName = this->GetLocalFileName();
 
     DcmFileFormat dcmFileFormat;
     OFCondition status = dcmFileFormat.loadFile(fileName.c_str());
 
     if (status.bad())
       return Unsupported;
 
     OFString modality;
     if (dcmFileFormat.getDataset()->findAndGetOFString(DCM_Modality, modality).good())
     {
       if (modality.compare("SEG") == 0)
         return Supported;
       else
         return Unsupported;
     }
     return Unsupported;
   }
 
   std::vector<BaseData::Pointer> DICOMSegmentationIO::DoRead()
   {
     mitk::LocaleSwitch localeSwitch("C");
 
     LabelSetImage::Pointer labelSetImage;
     std::vector<BaseData::Pointer> result;
 
     const std::string path = this->GetLocalFileName();
 
     MITK_INFO << "loading " << path << std::endl;
 
     if (path.empty())
       mitkThrow() << "Empty filename in mitk::ItkImageIO ";
 
     try
     {
       // Get the dcm data set from file path
       DcmFileFormat dcmFileFormat;
       OFCondition status = dcmFileFormat.loadFile(path.c_str());
       if (status.bad())
         mitkThrow() << "Can't read the input file!";
 
       DcmDataset *dataSet = dcmFileFormat.getDataset();
       if (dataSet == nullptr)
         mitkThrow() << "Can't read data from input file!";
 
       //=============================== dcmqi part ====================================
       // Read the DICOM SEG images (segItkImages) and DICOM tags (metaInfo)
       std::unique_ptr<dcmqi::ImageSEGConverter> converter = std::make_unique<dcmqi::ImageSEGConverter>();
       pair<map<unsigned, itkInternalImageType::Pointer>, string> dcmqiOutput =
         converter->dcmSegmentation2itkimage(dataSet);
 
       map<unsigned, itkInternalImageType::Pointer> segItkImages = dcmqiOutput.first;
 
       dcmqi::JSONSegmentationMetaInformationHandler metaInfo(dcmqiOutput.second.c_str());
       metaInfo.read();
 
       MITK_INFO << "Input " << metaInfo.getJSONOutputAsString();
       //===============================================================================
 
       // Get the label information from segment attributes for each itk image
       vector<map<unsigned, dcmqi::SegmentAttributes *>>::const_iterator segmentIter =
         metaInfo.segmentsAttributesMappingList.begin();
 
       // For each itk image add a layer to the LabelSetImage output
       for (auto &element : segItkImages)
       {
         // Get the labeled image and cast it to mitkImage
         typedef itk::CastImageFilter<itkInternalImageType, itkInputImageType> castItkImageFilterType;
         castItkImageFilterType::Pointer castFilter = castItkImageFilterType::New();
         castFilter->SetInput(element.second);
         castFilter->Update();
 
         Image::Pointer layerImage;
         CastToMitkImage(castFilter->GetOutput(), layerImage);
 
         // Get pixel value of the label
         itkInternalImageType::ValueType segValue = 1;
         typedef itk::ImageRegionIterator<const itkInternalImageType> IteratorType;
         // Iterate over the image to find the pixel value of the label
         IteratorType iter(element.second, element.second->GetLargestPossibleRegion());
         iter.GoToBegin();
         while (!iter.IsAtEnd())
         {
           itkInputImageType::PixelType value = iter.Get();
-          if (value != 0)
+          if (value != LabelSetImage::UnlabeledValue)
           {
             segValue = value;
             break;
           }
           ++iter;
         }
         // Get Segment information map
         map<unsigned, dcmqi::SegmentAttributes *> segmentMap = (*segmentIter);
         map<unsigned, dcmqi::SegmentAttributes *>::const_iterator segmentMapIter = (*segmentIter).begin();
         dcmqi::SegmentAttributes *segmentAttribute = (*segmentMapIter).second;
 
         OFString labelName;
 
         if (segmentAttribute->getSegmentedPropertyTypeCodeSequence() != nullptr)
         {
           segmentAttribute->getSegmentedPropertyTypeCodeSequence()->getCodeMeaning(labelName);
           if (segmentAttribute->getSegmentedPropertyTypeModifierCodeSequence() != nullptr)
           {
             OFString modifier;
             segmentAttribute->getSegmentedPropertyTypeModifierCodeSequence()->getCodeMeaning(modifier);
             labelName.append(" (").append(modifier).append(")");
           }
         }
         else
         {
           labelName = std::to_string(segmentAttribute->getLabelID()).c_str();
           if (labelName.empty())
             labelName = "Unnamed";
         }
 
         float tmp[3] = { 0.0, 0.0, 0.0 };
         if (segmentAttribute->getRecommendedDisplayRGBValue() != nullptr)
         {
           tmp[0] = segmentAttribute->getRecommendedDisplayRGBValue()[0] / 255.0;
           tmp[1] = segmentAttribute->getRecommendedDisplayRGBValue()[1] / 255.0;
           tmp[2] = segmentAttribute->getRecommendedDisplayRGBValue()[2] / 255.0;
         }
 
         Label *newLabel = nullptr;
         // If labelSetImage do not exists (first image)
         if (labelSetImage.IsNull())
         {
           // Initialize the labelSetImage with the read image
           labelSetImage = LabelSetImage::New();
           labelSetImage->InitializeByLabeledImage(layerImage);
           // Already a label was generated, so set the information to this
           newLabel = labelSetImage->GetActiveLabel(labelSetImage->GetActiveLayer());
           newLabel->SetName(labelName.c_str());
           newLabel->SetColor(Color(tmp));
           newLabel->SetValue(segValue);
         }
         else
         {
           // Add a new layer to the labelSetImage. Background label is set automatically
           labelSetImage->AddLayer(layerImage);
 
           // Add new label
           newLabel = new Label;
           newLabel->SetName(labelName.c_str());
           newLabel->SetColor(Color(tmp));
           newLabel->SetValue(segValue);
           labelSetImage->GetLabelSet(labelSetImage->GetActiveLayer())->AddLabel(newLabel);
         }
 
         // Add some more label properties
         this->SetLabelProperties(newLabel, segmentAttribute);
         ++segmentIter;
       }
 
       labelSetImage->GetLabelSet()->SetAllLabelsVisible(true);
 
       // Add some general DICOM Segmentation properties
       mitk::IDICOMTagsOfInterest *toiSrv = DICOMIOHelper::GetTagsOfInterestService();
       auto tagsOfInterest = toiSrv->GetTagsOfInterest();
       DICOMTagPathList tagsOfInterestList;
       for (const auto &tag : tagsOfInterest)
       {
         tagsOfInterestList.push_back(tag.first);
       }
 
       mitk::DICOMDCMTKTagScanner::Pointer scanner = mitk::DICOMDCMTKTagScanner::New();
       scanner->SetInputFiles({ GetInputLocation() });
       scanner->AddTagPaths(tagsOfInterestList);
       scanner->Scan();
 
       mitk::DICOMDatasetAccessingImageFrameList frames = scanner->GetFrameInfoList();
       if (frames.empty())
       {
         MITK_ERROR << "Error reading the DICOM Seg file" << std::endl;
         return result;
       }
 
       auto findings = DICOMIOHelper::ExtractPathsOfInterest(tagsOfInterestList, frames);
       DICOMIOHelper::SetProperties(labelSetImage, findings);
 
       // Set active layer to the first layer of the labelset image
       if (labelSetImage->GetNumberOfLayers() > 1 && labelSetImage->GetActiveLayer() != 0)
         labelSetImage->SetActiveLayer(0);
     }
     catch (const std::exception &e)
     {
       MITK_ERROR << "An error occurred while reading the DICOM Seg file: " << e.what();
       return result;
     }
     catch (...)
     {
       MITK_ERROR << "An error occurred in dcmqi while reading the DICOM Seg file";
       return result;
     }
 
     result.push_back(labelSetImage.GetPointer());
     return result;
   }
 
   const std::string mitk::DICOMSegmentationIO::CreateMetaDataJsonFile(int layer)
   {
     const mitk::LabelSetImage *image = dynamic_cast<const mitk::LabelSetImage *>(this->GetInput());
 
     const std::string output;
     dcmqi::JSONSegmentationMetaInformationHandler handler;
 
 
     // 1. Metadata attributes that will be listed in the resulting DICOM SEG object
     std::string contentCreatorName;
     if (!image->GetPropertyList()->GetStringProperty(GeneratePropertyNameForDICOMTag(0x0070, 0x0084).c_str(),
       contentCreatorName))
       contentCreatorName = "MITK";
     handler.setContentCreatorName(contentCreatorName);
 
     std::string clinicalTrailSeriesId;
     if (!image->GetPropertyList()->GetStringProperty(GeneratePropertyNameForDICOMTag(0x0012, 0x0071).c_str(),
       clinicalTrailSeriesId))
       clinicalTrailSeriesId = "Session 1";
     handler.setClinicalTrialSeriesID(clinicalTrailSeriesId);
 
     std::string clinicalTrialTimePointID;
     if (!image->GetPropertyList()->GetStringProperty(GeneratePropertyNameForDICOMTag(0x0012, 0x0050).c_str(),
       clinicalTrialTimePointID))
       clinicalTrialTimePointID = "0";
     handler.setClinicalTrialTimePointID(clinicalTrialTimePointID);
 
     std::string clinicalTrialCoordinatingCenterName = "";
     if (!image->GetPropertyList()->GetStringProperty(GeneratePropertyNameForDICOMTag(0x0012, 0x0060).c_str(),
       clinicalTrialCoordinatingCenterName))
       clinicalTrialCoordinatingCenterName = "Unknown";
     handler.setClinicalTrialCoordinatingCenterName(clinicalTrialCoordinatingCenterName);
 
     std::string seriesDescription;
     if (!image->GetPropertyList()->GetStringProperty("name", seriesDescription))
       seriesDescription = "MITK Segmentation";
     handler.setSeriesDescription(seriesDescription);
 
     handler.setSeriesNumber("0" + std::to_string(layer));
     handler.setInstanceNumber("1");
     handler.setBodyPartExamined("");
 
     const LabelSet *labelSet = image->GetLabelSet(layer);
     auto labelIter = labelSet->IteratorConstBegin();
     // Ignore background label
     ++labelIter;
 
     for (; labelIter != labelSet->IteratorConstEnd(); ++labelIter)
     {
       const Label *label = labelIter->second;
 
       if (label != nullptr)
       {
         TemporoSpatialStringProperty *segmentNumberProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
           mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_NUMBER_PATH()).c_str()));
 
         TemporoSpatialStringProperty *segmentLabelProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
           mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_LABEL_PATH()).c_str()));
 
         TemporoSpatialStringProperty *algorithmTypeProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
           mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_ALGORITHM_TYPE_PATH()).c_str()));
 
         TemporoSpatialStringProperty *segmentCategoryCodeValueProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
           mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_VALUE_PATH()).c_str()));
 
         TemporoSpatialStringProperty *segmentCategoryCodeSchemeProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
           mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_SCHEME_PATH()).c_str()));
 
         TemporoSpatialStringProperty *segmentCategoryCodeMeaningProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
           mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_MEANING_PATH()).c_str()));
 
         TemporoSpatialStringProperty *segmentTypeCodeValueProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
           mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_VALUE_PATH()).c_str()));
 
         TemporoSpatialStringProperty *segmentTypeCodeSchemeProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
           mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_SCHEME_PATH()).c_str()));
 
         TemporoSpatialStringProperty *segmentTypeCodeMeaningProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
           mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_MEANING_PATH()).c_str()));
 
         TemporoSpatialStringProperty *segmentModifierCodeValueProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
           mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_VALUE_PATH()).c_str()));
 
         TemporoSpatialStringProperty *segmentModifierCodeSchemeProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
           mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_SCHEME_PATH()).c_str()));
 
         TemporoSpatialStringProperty *segmentModifierCodeMeaningProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
           mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_MEANING_PATH()).c_str()));
 
         dcmqi::SegmentAttributes *segmentAttribute = nullptr;
 
         if (segmentNumberProp->GetValue() == "")
         {
           MITK_ERROR << "Something went wrong with the label ID.";
         }
         else
         {
           int labelId = std::stoi(segmentNumberProp->GetValue());
           segmentAttribute = handler.createAndGetNewSegment(labelId);
         }
         if (segmentAttribute != nullptr)
         {
           segmentAttribute->setSegmentDescription(segmentLabelProp->GetValueAsString());
           segmentAttribute->setSegmentAlgorithmType(algorithmTypeProp->GetValueAsString());
           segmentAttribute->setSegmentAlgorithmName("MITK Segmentation");
           if (segmentCategoryCodeValueProp != nullptr && segmentCategoryCodeSchemeProp != nullptr &&
             segmentCategoryCodeMeaningProp != nullptr)
             segmentAttribute->setSegmentedPropertyCategoryCodeSequence(
               segmentCategoryCodeValueProp->GetValueAsString(),
               segmentCategoryCodeSchemeProp->GetValueAsString(),
               segmentCategoryCodeMeaningProp->GetValueAsString());
           else
             // some default values
             segmentAttribute->setSegmentedPropertyCategoryCodeSequence(
               "M-01000", "SRT", "Morphologically Altered Structure");
 
           if (segmentTypeCodeValueProp != nullptr && segmentTypeCodeSchemeProp != nullptr &&
             segmentTypeCodeMeaningProp != nullptr)
           {
             segmentAttribute->setSegmentedPropertyTypeCodeSequence(segmentTypeCodeValueProp->GetValueAsString(),
               segmentTypeCodeSchemeProp->GetValueAsString(),
               segmentTypeCodeMeaningProp->GetValueAsString());
             handler.setBodyPartExamined(segmentTypeCodeMeaningProp->GetValueAsString());
           }
           else
           {
             // some default values
             segmentAttribute->setSegmentedPropertyTypeCodeSequence("M-03000", "SRT", "Mass");
             handler.setBodyPartExamined("Mass");
           }
           if (segmentModifierCodeValueProp != nullptr && segmentModifierCodeSchemeProp != nullptr &&
             segmentModifierCodeMeaningProp != nullptr)
             segmentAttribute->setSegmentedPropertyTypeModifierCodeSequence(
               segmentModifierCodeValueProp->GetValueAsString(),
               segmentModifierCodeSchemeProp->GetValueAsString(),
               segmentModifierCodeMeaningProp->GetValueAsString());
 
           Color color = label->GetColor();
           segmentAttribute->setRecommendedDisplayRGBValue(color[0] * 255, color[1] * 255, color[2] * 255);
         }
       }
     }
     return handler.getJSONOutputAsString();
   }
 
   void mitk::DICOMSegmentationIO::SetLabelProperties(mitk::Label *label, dcmqi::SegmentAttributes *segmentAttribute)
   {
     // Segment Number:Identification number of the segment.The value of Segment Number(0062, 0004) shall be unique
     // within the Segmentation instance in which it is created
     label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_NUMBER_PATH()).c_str(),
       TemporoSpatialStringProperty::New(std::to_string(label->GetValue())));
 
     // Segment Label: User-defined label identifying this segment.
     label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_LABEL_PATH()).c_str(),
       TemporoSpatialStringProperty::New(label->GetName()));
 
     // Segment Algorithm Type: Type of algorithm used to generate the segment.
     if (!segmentAttribute->getSegmentAlgorithmType().empty())
       label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_ALGORITHM_TYPE_PATH()).c_str(),
         TemporoSpatialStringProperty::New(segmentAttribute->getSegmentAlgorithmType()));
 
     // Add Segmented Property Category Code Sequence tags
     auto categoryCodeSequence = segmentAttribute->getSegmentedPropertyCategoryCodeSequence();
     if (categoryCodeSequence != nullptr)
     {
       OFString codeValue; // (0008,0100) Code Value
       categoryCodeSequence->getCodeValue(codeValue);
       label->SetProperty(
         DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_VALUE_PATH()).c_str(),
         TemporoSpatialStringProperty::New(codeValue.c_str()));
 
       OFString codeScheme; // (0008,0102) Coding Scheme Designator
       categoryCodeSequence->getCodingSchemeDesignator(codeScheme);
       label->SetProperty(
         DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_SCHEME_PATH()).c_str(),
         TemporoSpatialStringProperty::New(codeScheme.c_str()));
 
       OFString codeMeaning; // (0008,0104) Code Meaning
       categoryCodeSequence->getCodeMeaning(codeMeaning);
       label->SetProperty(
         DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_MEANING_PATH()).c_str(),
         TemporoSpatialStringProperty::New(codeMeaning.c_str()));
     }
 
     // Add Segmented Property Type Code Sequence tags
     auto typeCodeSequence = segmentAttribute->getSegmentedPropertyTypeCodeSequence();
     if (typeCodeSequence != nullptr)
     {
       OFString codeValue; // (0008,0100) Code Value
       typeCodeSequence->getCodeValue(codeValue);
       label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_VALUE_PATH()).c_str(),
         TemporoSpatialStringProperty::New(codeValue.c_str()));
 
       OFString codeScheme; // (0008,0102) Coding Scheme Designator
       typeCodeSequence->getCodingSchemeDesignator(codeScheme);
       label->SetProperty(
         DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_SCHEME_PATH()).c_str(),
         TemporoSpatialStringProperty::New(codeScheme.c_str()));
 
       OFString codeMeaning; // (0008,0104) Code Meaning
       typeCodeSequence->getCodeMeaning(codeMeaning);
       label->SetProperty(
         DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_MEANING_PATH()).c_str(),
         TemporoSpatialStringProperty::New(codeMeaning.c_str()));
     }
 
     // Add Segmented Property Type Modifier Code Sequence tags
     auto modifierCodeSequence = segmentAttribute->getSegmentedPropertyTypeModifierCodeSequence();
     if (modifierCodeSequence != nullptr)
     {
       OFString codeValue; // (0008,0100) Code Value
       modifierCodeSequence->getCodeValue(codeValue);
       label->SetProperty(
         DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_VALUE_PATH()).c_str(),
         TemporoSpatialStringProperty::New(codeValue.c_str()));
 
       OFString codeScheme; // (0008,0102) Coding Scheme Designator
       modifierCodeSequence->getCodingSchemeDesignator(codeScheme);
       label->SetProperty(
         DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_SCHEME_PATH()).c_str(),
         TemporoSpatialStringProperty::New(codeScheme.c_str()));
 
       OFString codeMeaning; // (0008,0104) Code Meaning
       modifierCodeSequence->getCodeMeaning(codeMeaning);
       label->SetProperty(
         DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_MEANING_PATH()).c_str(),
         TemporoSpatialStringProperty::New(codeMeaning.c_str()));
     }
 
     // Add Atomic RegionSequence tags
     auto atomicRegionSequence = segmentAttribute->getAnatomicRegionSequence();
     if (atomicRegionSequence != nullptr)
     {
       OFString codeValue; // (0008,0100) Code Value
       atomicRegionSequence->getCodeValue(codeValue);
       label->SetProperty(
         DICOMTagPathToPropertyName(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_VALUE_PATH()).c_str(),
         TemporoSpatialStringProperty::New(codeValue.c_str()));
 
       OFString codeScheme; // (0008,0102) Coding Scheme Designator
       atomicRegionSequence->getCodingSchemeDesignator(codeScheme);
       label->SetProperty(
         DICOMTagPathToPropertyName(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_SCHEME_PATH()).c_str(),
         TemporoSpatialStringProperty::New(codeScheme.c_str()));
 
       OFString codeMeaning; // (0008,0104) Code Meaning
       atomicRegionSequence->getCodeMeaning(codeMeaning);
       label->SetProperty(
         DICOMTagPathToPropertyName(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_MEANING_PATH()).c_str(),
         TemporoSpatialStringProperty::New(codeMeaning.c_str()));
     }
   }
 
   DICOMSegmentationIO *DICOMSegmentationIO::IOClone() const { return new DICOMSegmentationIO(*this); }
 } // namespace
 
 #endif //__mitkDICOMSegmentationIO__cpp
diff --git a/Modules/Multilabel/autoload/IO/files.cmake b/Modules/Multilabel/autoload/IO/files.cmake
index 71bd04bf86..09d98eca07 100644
--- a/Modules/Multilabel/autoload/IO/files.cmake
+++ b/Modules/Multilabel/autoload/IO/files.cmake
@@ -1,13 +1,15 @@
 set(CPP_FILES
-  mitkLabelSetImageIO.cpp
-  mitkLabelSetImageIO.h
-  mitkLabelSetImageSerializer.cpp
-  mitkLabelSetImageSerializer.h
+  mitkLegacyLabelSetImageIO.cpp
+  mitkLegacyLabelSetImageIO.h
+  mitkMultiLabelSegmentationIO.cpp
+  mitkMultiLabelSegmentationIO.h
+  mitkMultiLabelSegmentationSerializer.cpp
+  mitkMultiLabelSegmentationSerializer.h
   mitkMultilabelActivator.cpp
   mitkMultilabelIOMimeTypes.cpp
   mitkMultilabelIOMimeTypes.h
   mitkSegmentationTaskListIO.cpp
   mitkSegmentationTaskListIO.h
   mitkSegmentationTaskListSerializer.cpp
   mitkSegmentationTaskListSerializer.h
 )
diff --git a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp
deleted file mode 100644
index 78d241cf19..0000000000
--- a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp
+++ /dev/null
@@ -1,657 +0,0 @@
-/*============================================================================
-
-The Medical Imaging Interaction Toolkit (MITK)
-
-Copyright (c) German Cancer Research Center (DKFZ)
-All rights reserved.
-
-Use of this source code is governed by a 3-clause BSD license that can be
-found in the LICENSE file.
-
-============================================================================*/
-
-#ifndef __mitkLabelSetImageWriter__cpp
-#define __mitkLabelSetImageWriter__cpp
-
-#include "mitkLabelSetImageIO.h"
-#include "mitkBasePropertySerializer.h"
-#include "mitkIOMimeTypes.h"
-#include "mitkImageAccessByItk.h"
-#include "mitkLabelSetIOHelper.h"
-#include "mitkLabelSetImageConverter.h"
-#include <mitkLocaleSwitch.h>
-#include <mitkArbitraryTimeGeometry.h>
-#include <mitkIPropertyPersistence.h>
-#include <mitkCoreServices.h>
-#include <mitkItkImageIO.h>
-#include <mitkUIDManipulator.h>
-
-// itk
-#include "itkImageFileReader.h"
-#include "itkImageFileWriter.h"
-#include "itkMetaDataDictionary.h"
-#include "itkMetaDataObject.h"
-#include "itkNrrdImageIO.h"
-
-#include <tinyxml2.h>
-
-namespace mitk
-{
-
-  const char* const PROPERTY_NAME_TIMEGEOMETRY_TYPE = "org.mitk.timegeometry.type";
-  const char* const PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS = "org.mitk.timegeometry.timepoints";
-  const char* const PROPERTY_KEY_TIMEGEOMETRY_TYPE = "org_mitk_timegeometry_type";
-  const char* const PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS = "org_mitk_timegeometry_timepoints";
-  const char* const PROPERTY_KEY_UID = "org_mitk_uid";
-
-  LabelSetImageIO::LabelSetImageIO()
-    : AbstractFileIO(LabelSetImage::GetStaticNameOfClass(), IOMimeTypes::NRRD_MIMETYPE(), "MITK Multilabel Image")
-  {
-    this->InitializeDefaultMetaDataKeys();
-    AbstractFileWriter::SetRanking(10);
-    AbstractFileReader::SetRanking(10);
-    this->RegisterService();
-  }
-
-  IFileIO::ConfidenceLevel LabelSetImageIO::GetWriterConfidenceLevel() const
-  {
-    if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported)
-      return Unsupported;
-    const auto *input = static_cast<const LabelSetImage *>(this->GetInput());
-    if (input)
-      return Supported;
-    else
-      return Unsupported;
-  }
-
-  void LabelSetImageIO::Write()
-  {
-    ValidateOutputLocation();
-
-    auto input = dynamic_cast<const LabelSetImage *>(this->GetInput());
-
-    mitk::LocaleSwitch localeSwitch("C");
-
-    mitk::Image::Pointer inputVector = mitk::ConvertLabelSetImageToImage(input);
-
-    // image write
-    if (inputVector.IsNull())
-    {
-      mitkThrow() << "Cannot write non-image data";
-    }
-
-    itk::NrrdImageIO::Pointer nrrdImageIo = itk::NrrdImageIO::New();
-
-    // Clone the image geometry, because we might have to change it
-    // for writing purposes
-    BaseGeometry::Pointer geometry = inputVector->GetGeometry()->Clone();
-
-    // Check if geometry information will be lost
-    if (inputVector->GetDimension() == 2 && !geometry->Is2DConvertable())
-    {
-      MITK_WARN << "Saving a 2D image with 3D geometry information. Geometry information will be lost! You might "
-                   "consider using Convert2Dto3DImageFilter before saving.";
-
-      // set matrix to identity
-      mitk::AffineTransform3D::Pointer affTrans = mitk::AffineTransform3D::New();
-      affTrans->SetIdentity();
-      mitk::Vector3D spacing = geometry->GetSpacing();
-      mitk::Point3D origin = geometry->GetOrigin();
-      geometry->SetIndexToWorldTransform(affTrans);
-      geometry->SetSpacing(spacing);
-      geometry->SetOrigin(origin);
-    }
-
-    LocalFile localFile(this);
-    const std::string path = localFile.GetFileName();
-
-    MITK_INFO << "Writing image: " << path << std::endl;
-
-    try
-    {
-      // Implementation of writer using itkImageIO directly. This skips the use
-      // of templated itkImageFileWriter, which saves the multiplexing on MITK side.
-
-      const unsigned int dimension = inputVector->GetDimension();
-      const unsigned int *const dimensions = inputVector->GetDimensions();
-      const mitk::PixelType pixelType = inputVector->GetPixelType();
-      const mitk::Vector3D mitkSpacing = geometry->GetSpacing();
-      const mitk::Point3D mitkOrigin = geometry->GetOrigin();
-
-      // Due to templating in itk, we are forced to save a 4D spacing and 4D Origin,
-      // though they are not supported in MITK
-      itk::Vector<double, 4u> spacing4D;
-      spacing4D[0] = mitkSpacing[0];
-      spacing4D[1] = mitkSpacing[1];
-      spacing4D[2] = mitkSpacing[2];
-      spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have a valid value here
-
-      itk::Vector<double, 4u> origin4D;
-      origin4D[0] = mitkOrigin[0];
-      origin4D[1] = mitkOrigin[1];
-      origin4D[2] = mitkOrigin[2];
-      origin4D[3] = 0; // There is no support for a 4D origin. However, we should have a valid value here
-
-      // Set the necessary information for imageIO
-      nrrdImageIo->SetNumberOfDimensions(dimension);
-      nrrdImageIo->SetPixelType(pixelType.GetPixelType());
-      nrrdImageIo->SetComponentType(static_cast<int>(pixelType.GetComponentType()) < PixelComponentUserType
-                                      ? pixelType.GetComponentType()
-                                      : itk::IOComponentEnum::UNKNOWNCOMPONENTTYPE);
-      nrrdImageIo->SetNumberOfComponents(pixelType.GetNumberOfComponents());
-
-      itk::ImageIORegion ioRegion(dimension);
-
-      for (unsigned int i = 0; i < dimension; i++)
-      {
-        nrrdImageIo->SetDimensions(i, dimensions[i]);
-        nrrdImageIo->SetSpacing(i, spacing4D[i]);
-        nrrdImageIo->SetOrigin(i, origin4D[i]);
-
-        mitk::Vector3D mitkDirection(0.0);
-        mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i).as_ref());
-        itk::Vector<double, 4u> direction4D;
-        direction4D[0] = mitkDirection[0];
-        direction4D[1] = mitkDirection[1];
-        direction4D[2] = mitkDirection[2];
-
-        // MITK only supports a 3x3 direction matrix. Due to templating in itk, however, we must
-        // save a 4x4 matrix for 4D images. in this case, add an homogneous component to the matrix.
-        if (i == 3)
-        {
-          direction4D[3] = 1; // homogenous component
-        }
-        else
-        {
-          direction4D[3] = 0;
-        }
-        vnl_vector<double> axisDirection(dimension);
-        for (unsigned int j = 0; j < dimension; j++)
-        {
-          axisDirection[j] = direction4D[j] / spacing4D[i];
-        }
-        nrrdImageIo->SetDirection(i, axisDirection);
-
-        ioRegion.SetSize(i, inputVector->GetLargestPossibleRegion().GetSize(i));
-        ioRegion.SetIndex(i, inputVector->GetLargestPossibleRegion().GetIndex(i));
-      }
-
-      // use compression if available
-      nrrdImageIo->UseCompressionOn();
-
-      nrrdImageIo->SetIORegion(ioRegion);
-      nrrdImageIo->SetFileName(path);
-
-      // label set specific meta data
-      char keybuffer[512];
-      char valbuffer[512];
-
-      sprintf(keybuffer, "modality");
-      sprintf(valbuffer, "org.mitk.image.multilabel");
-      itk::EncapsulateMetaData<std::string>(
-        nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer));
-
-      sprintf(keybuffer, "layers");
-      sprintf(valbuffer, "%1d", input->GetNumberOfLayers());
-      itk::EncapsulateMetaData<std::string>(
-        nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer));
-
-      for (unsigned int layerIdx = 0; layerIdx < input->GetNumberOfLayers(); layerIdx++)
-      {
-        sprintf(keybuffer, "layer_%03u", layerIdx);                    // layer idx
-        sprintf(valbuffer, "%1u", input->GetNumberOfLabels(layerIdx)); // number of labels for the layer
-        itk::EncapsulateMetaData<std::string>(
-          nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer));
-
-        auto iter = input->GetLabelSet(layerIdx)->IteratorConstBegin();
-        unsigned int count(0);
-        while (iter != input->GetLabelSet(layerIdx)->IteratorConstEnd())
-        {
-          tinyxml2::XMLDocument document;
-          document.InsertEndChild(document.NewDeclaration());
-          auto *labelElem = mitk::LabelSetIOHelper::GetLabelAsXMLElement(document, iter->second);
-          document.InsertEndChild(labelElem);
-          tinyxml2::XMLPrinter printer;
-          document.Print(&printer);
-
-          sprintf(keybuffer, "org.mitk.label_%03u_%05u", layerIdx, count);
-          itk::EncapsulateMetaData<std::string>(
-            nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), printer.CStr());
-          ++iter;
-          ++count;
-        }
-      }
-      // end label set specific meta data
-
-      // Handle time geometry
-      const auto* arbitraryTG = dynamic_cast<const ArbitraryTimeGeometry*>(input->GetTimeGeometry());
-      if (arbitraryTG)
-      {
-        itk::EncapsulateMetaData<std::string>(nrrdImageIo->GetMetaDataDictionary(),
-          PROPERTY_KEY_TIMEGEOMETRY_TYPE,
-          ArbitraryTimeGeometry::GetStaticNameOfClass());
-
-
-        auto metaTimePoints = ConvertTimePointListToMetaDataObject(arbitraryTG);
-        nrrdImageIo->GetMetaDataDictionary().Set(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS, metaTimePoints);
-      }
-
-      // Handle properties
-      mitk::PropertyList::Pointer imagePropertyList = input->GetPropertyList();
-      for (const auto& property : *imagePropertyList->GetMap())
-      {
-        mitk::CoreServicePointer<IPropertyPersistence> propPersistenceService(mitk::CoreServices::GetPropertyPersistence());
-        IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfo(property.first, GetMimeType()->GetName(), true);
-
-        if (infoList.empty())
-        {
-          continue;
-        }
-
-        std::string value = infoList.front()->GetSerializationFunction()(property.second);
-
-        if (value == mitk::BaseProperty::VALUE_CANNOT_BE_CONVERTED_TO_STRING)
-        {
-          continue;
-        }
-
-        std::string key = infoList.front()->GetKey();
-
-        itk::EncapsulateMetaData<std::string>(nrrdImageIo->GetMetaDataDictionary(), key, value);
-      }
-
-      // Handle UID
-      itk::EncapsulateMetaData<std::string>(nrrdImageIo->GetMetaDataDictionary(), PROPERTY_KEY_UID, input->GetUID());
-
-      ImageReadAccessor imageAccess(inputVector);
-      nrrdImageIo->Write(imageAccess.GetData());
-    }
-    catch (const std::exception &e)
-    {
-      mitkThrow() << e.what();
-    }
-    // end image write
-  }
-
-  IFileIO::ConfidenceLevel LabelSetImageIO::GetReaderConfidenceLevel() const
-  {
-    if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported)
-      return Unsupported;
-    const std::string fileName = this->GetLocalFileName();
-    itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New();
-    io->SetFileName(fileName);
-    io->ReadImageInformation();
-
-    itk::MetaDataDictionary imgMetaDataDictionary = io->GetMetaDataDictionary();
-    std::string value("");
-    itk::ExposeMetaData<std::string>(imgMetaDataDictionary, "modality", value);
-    if (value.compare("org.mitk.image.multilabel") == 0)
-    {
-      return Supported;
-    }
-    else
-      return Unsupported;
-  }
-
-  std::vector<BaseData::Pointer> LabelSetImageIO::DoRead()
-  {
-    mitk::LocaleSwitch localeSwitch("C");
-
-    // begin regular image loading, adapted from mitkItkImageIO
-    itk::NrrdImageIO::Pointer nrrdImageIO = itk::NrrdImageIO::New();
-    Image::Pointer image = Image::New();
-
-    const unsigned int MINDIM = 2;
-    const unsigned int MAXDIM = 4;
-
-    const std::string path = this->GetLocalFileName();
-
-    MITK_INFO << "loading " << path << " via itk::ImageIOFactory... " << std::endl;
-
-    // Check to see if we can read the file given the name or prefix
-    if (path.empty())
-    {
-      mitkThrow() << "Empty filename in mitk::ItkImageIO ";
-    }
-
-    // Got to allocate space for the image. Determine the characteristics of
-    // the image.
-    nrrdImageIO->SetFileName(path);
-    nrrdImageIO->ReadImageInformation();
-
-    unsigned int ndim = nrrdImageIO->GetNumberOfDimensions();
-    if (ndim < MINDIM || ndim > MAXDIM)
-    {
-      MITK_WARN << "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim
-                << " dimensions! Reading as 4D.";
-      ndim = MAXDIM;
-    }
-
-    itk::ImageIORegion ioRegion(ndim);
-    itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize();
-    itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex();
-
-    unsigned int dimensions[MAXDIM];
-    dimensions[0] = 0;
-    dimensions[1] = 0;
-    dimensions[2] = 0;
-    dimensions[3] = 0;
-
-    ScalarType spacing[MAXDIM];
-    spacing[0] = 1.0f;
-    spacing[1] = 1.0f;
-    spacing[2] = 1.0f;
-    spacing[3] = 1.0f;
-
-    Point3D origin;
-    origin.Fill(0);
-
-    unsigned int i;
-    for (i = 0; i < ndim; ++i)
-    {
-      ioStart[i] = 0;
-      ioSize[i] = nrrdImageIO->GetDimensions(i);
-      if (i < MAXDIM)
-      {
-        dimensions[i] = nrrdImageIO->GetDimensions(i);
-        spacing[i] = nrrdImageIO->GetSpacing(i);
-        if (spacing[i] <= 0)
-          spacing[i] = 1.0f;
-      }
-      if (i < 3)
-      {
-        origin[i] = nrrdImageIO->GetOrigin(i);
-      }
-    }
-
-    ioRegion.SetSize(ioSize);
-    ioRegion.SetIndex(ioStart);
-
-    MITK_INFO << "ioRegion: " << ioRegion << std::endl;
-    nrrdImageIO->SetIORegion(ioRegion);
-    void *buffer = new unsigned char[nrrdImageIO->GetImageSizeInBytes()];
-    nrrdImageIO->Read(buffer);
-
-    image->Initialize(MakePixelType(nrrdImageIO), ndim, dimensions);
-    image->SetImportChannel(buffer, 0, Image::ManageMemory);
-
-    // access direction of itk::Image and include spacing
-    mitk::Matrix3D matrix;
-    matrix.SetIdentity();
-    unsigned int j, itkDimMax3 = (ndim >= 3 ? 3 : ndim);
-    for (i = 0; i < itkDimMax3; ++i)
-      for (j = 0; j < itkDimMax3; ++j)
-        matrix[i][j] = nrrdImageIO->GetDirection(j)[i];
-
-    // re-initialize PlaneGeometry with origin and direction
-    PlaneGeometry *planeGeometry = image->GetSlicedGeometry(0)->GetPlaneGeometry(0);
-    planeGeometry->SetOrigin(origin);
-    planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix);
-
-    // re-initialize SlicedGeometry3D
-    SlicedGeometry3D *slicedGeometry = image->GetSlicedGeometry(0);
-    slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2));
-    slicedGeometry->SetSpacing(spacing);
-
-    MITK_INFO << slicedGeometry->GetCornerPoint(false, false, false);
-    MITK_INFO << slicedGeometry->GetCornerPoint(true, true, true);
-
-    // re-initialize TimeGeometry
-    const itk::MetaDataDictionary& dictionary = nrrdImageIO->GetMetaDataDictionary();
-    TimeGeometry::Pointer timeGeometry;
-
-    if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE) || dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TYPE))
-    { // also check for the name because of backwards compatibility. Past code version stored with the name and not with
-      // the key
-      itk::MetaDataObject<std::string>::ConstPointer timeGeometryTypeData;
-      if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE))
-      {
-        timeGeometryTypeData =
-          dynamic_cast<const itk::MetaDataObject<std::string>*>(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TYPE));
-      }
-      else
-      {
-        timeGeometryTypeData =
-          dynamic_cast<const itk::MetaDataObject<std::string>*>(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TYPE));
-      }
-
-      if (timeGeometryTypeData->GetMetaDataObjectValue() == ArbitraryTimeGeometry::GetStaticNameOfClass())
-      {
-        MITK_INFO << "used time geometry: " << ArbitraryTimeGeometry::GetStaticNameOfClass();
-        typedef std::vector<TimePointType> TimePointVector;
-        TimePointVector timePoints;
-
-        if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS))
-        {
-          timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS));
-        }
-        else if (dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS))
-        {
-          timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS));
-        }
-
-        if (timePoints.empty())
-        {
-          MITK_ERROR << "Stored timepoints are empty. Meta information seems to bee invalid. Switch to ProportionalTimeGeometry fallback";
-        }
-        else if (timePoints.size() - 1 != image->GetDimension(3))
-        {
-          MITK_ERROR << "Stored timepoints (" << timePoints.size() - 1 << ") and size of image time dimension ("
-            << image->GetDimension(3) << ") do not match. Switch to ProportionalTimeGeometry fallback";
-        }
-        else
-        {
-          ArbitraryTimeGeometry::Pointer arbitraryTimeGeometry = ArbitraryTimeGeometry::New();
-          TimePointVector::const_iterator pos = timePoints.begin();
-          auto prePos = pos++;
-
-          for (; pos != timePoints.end(); ++prePos, ++pos)
-          {
-            arbitraryTimeGeometry->AppendNewTimeStepClone(slicedGeometry, *prePos, *pos);
-          }
-
-          timeGeometry = arbitraryTimeGeometry;
-        }
-      }
-    }
-
-    if (timeGeometry.IsNull())
-    { // Fallback. If no other valid time geometry has been created, create a ProportionalTimeGeometry
-      MITK_INFO << "used time geometry: " << ProportionalTimeGeometry::GetStaticNameOfClass();
-      ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New();
-      propTimeGeometry->Initialize(slicedGeometry, image->GetDimension(3));
-      timeGeometry = propTimeGeometry;
-    }
-
-    image->SetTimeGeometry(timeGeometry);
-
-    buffer = nullptr;
-    MITK_INFO << "number of image components: " << image->GetPixelType().GetNumberOfComponents();
-
-    // end regular image loading
-
-    LabelSetImage::Pointer output = ConvertImageToLabelSetImage(image);
-
-    // get labels and add them as properties to the image
-    char keybuffer[256];
-
-    unsigned int numberOfLayers = GetIntByKey(dictionary, "layers");
-    std::string _xmlStr;
-    mitk::Label::Pointer label;
-
-    for (unsigned int layerIdx = 0; layerIdx < numberOfLayers; layerIdx++)
-    {
-      sprintf(keybuffer, "layer_%03u", layerIdx);
-      int numberOfLabels = GetIntByKey(dictionary, keybuffer);
-
-      mitk::LabelSet::Pointer labelSet = mitk::LabelSet::New();
-
-      for (int labelIdx = 0; labelIdx < numberOfLabels; labelIdx++)
-      {
-        tinyxml2::XMLDocument doc;
-        sprintf(keybuffer, "label_%03u_%05d", layerIdx, labelIdx);
-        _xmlStr = GetStringByKey(dictionary, keybuffer);
-        doc.Parse(_xmlStr.c_str(), _xmlStr.size());
-
-        auto *labelElem = doc.FirstChildElement("Label");
-        if (labelElem == nullptr)
-          mitkThrow() << "Error parsing NRRD header for mitk::LabelSetImage IO";
-
-        label = mitk::LabelSetIOHelper::LoadLabelFromXMLDocument(labelElem);
-
-        if (label->GetValue() == 0) // set exterior label is needed to hold exterior information
-          output->SetExteriorLabel(label);
-        labelSet->AddLabel(label);
-        labelSet->SetLayer(layerIdx);
-      }
-      output->AddLabelSetToLayer(layerIdx, labelSet);
-    }
-
-    for (auto iter = dictionary.Begin(), iterEnd = dictionary.End(); iter != iterEnd;
-      ++iter)
-    {
-      if (iter->second->GetMetaDataObjectTypeInfo() == typeid(std::string))
-      {
-        const std::string& key = iter->first;
-        std::string assumedPropertyName = key;
-        std::replace(assumedPropertyName.begin(), assumedPropertyName.end(), '_', '.');
-
-        std::string mimeTypeName = GetMimeType()->GetName();
-
-        // Check if there is already a info for the key and our mime type.
-        mitk::CoreServicePointer<IPropertyPersistence> propPersistenceService(mitk::CoreServices::GetPropertyPersistence());
-        IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfoByKey(key);
-
-        auto predicate = [&mimeTypeName](const PropertyPersistenceInfo::ConstPointer& x) {
-          return x.IsNotNull() && x->GetMimeTypeName() == mimeTypeName;
-        };
-        auto finding = std::find_if(infoList.begin(), infoList.end(), predicate);
-
-        if (finding == infoList.end())
-        {
-          auto predicateWild = [](const PropertyPersistenceInfo::ConstPointer& x) {
-            return x.IsNotNull() && x->GetMimeTypeName() == PropertyPersistenceInfo::ANY_MIMETYPE_NAME();
-          };
-          finding = std::find_if(infoList.begin(), infoList.end(), predicateWild);
-        }
-
-        PropertyPersistenceInfo::ConstPointer info;
-
-        if (finding != infoList.end())
-        {
-          assumedPropertyName = (*finding)->GetName();
-          info = *finding;
-        }
-        else
-        { // we have not found anything suitable so we generate our own info
-          auto newInfo = PropertyPersistenceInfo::New();
-          newInfo->SetNameAndKey(assumedPropertyName, key);
-          newInfo->SetMimeTypeName(PropertyPersistenceInfo::ANY_MIMETYPE_NAME());
-          info = newInfo;
-        }
-
-        std::string value =
-          dynamic_cast<itk::MetaDataObject<std::string>*>(iter->second.GetPointer())->GetMetaDataObjectValue();
-
-        mitk::BaseProperty::Pointer loadedProp = info->GetDeserializationFunction()(value);
-
-        if (loadedProp.IsNull())
-        {
-          MITK_ERROR << "Property cannot be correctly deserialized and is skipped. Check if data format is valid. Problematic property value string: \"" << value << "\"; Property info used to deserialized: " << info;
-          break;
-        }
-
-        output->SetProperty(assumedPropertyName.c_str(), loadedProp);
-
-        // Read properties should be persisted unless they are default properties
-        // which are written anyway
-        bool isDefaultKey = false;
-
-        for (const auto& defaultKey : m_DefaultMetaDataKeys)
-        {
-          if (defaultKey.length() <= assumedPropertyName.length())
-          {
-            // does the start match the default key
-            if (assumedPropertyName.substr(0, defaultKey.length()).find(defaultKey) != std::string::npos)
-            {
-              isDefaultKey = true;
-              break;
-            }
-          }
-        }
-
-        if (!isDefaultKey)
-        {
-          propPersistenceService->AddInfo(info);
-        }
-      }
-    }
-
-    // Handle UID
-    if (dictionary.HasKey(PROPERTY_KEY_UID))
-    {
-      itk::MetaDataObject<std::string>::ConstPointer uidData = dynamic_cast<const itk::MetaDataObject<std::string>*>(dictionary.Get(PROPERTY_KEY_UID));
-      if (uidData.IsNotNull())
-      {
-        mitk::UIDManipulator uidManipulator(output);
-        uidManipulator.SetUID(uidData->GetMetaDataObjectValue());
-      }
-    }
-
-    MITK_INFO << "...finished!";
-
-    std::vector<BaseData::Pointer> result;
-    result.push_back(output.GetPointer());
-    return result;
-  }
-
-  int LabelSetImageIO::GetIntByKey(const itk::MetaDataDictionary &dic, const std::string &str)
-  {
-    std::vector<std::string> imgMetaKeys = dic.GetKeys();
-    std::vector<std::string>::const_iterator itKey = imgMetaKeys.begin();
-    std::string metaString("");
-    for (; itKey != imgMetaKeys.end(); itKey++)
-    {
-      itk::ExposeMetaData<std::string>(dic, *itKey, metaString);
-      if (itKey->find(str.c_str()) != std::string::npos)
-      {
-        return atoi(metaString.c_str());
-      }
-    }
-    return 0;
-  }
-
-  std::string LabelSetImageIO::GetStringByKey(const itk::MetaDataDictionary &dic, const std::string &str)
-  {
-    std::vector<std::string> imgMetaKeys = dic.GetKeys();
-    std::vector<std::string>::const_iterator itKey = imgMetaKeys.begin();
-    std::string metaString("");
-    for (; itKey != imgMetaKeys.end(); itKey++)
-    {
-      itk::ExposeMetaData<std::string>(dic, *itKey, metaString);
-      if (itKey->find(str.c_str()) != std::string::npos)
-      {
-        return metaString;
-      }
-    }
-    return metaString;
-  }
-
-  LabelSetImageIO *LabelSetImageIO::IOClone() const { return new LabelSetImageIO(*this); }
-
-  void LabelSetImageIO::InitializeDefaultMetaDataKeys()
-  {
-    this->m_DefaultMetaDataKeys.push_back("NRRD.space");
-    this->m_DefaultMetaDataKeys.push_back("NRRD.kinds");
-    this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TYPE);
-    this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS);
-    this->m_DefaultMetaDataKeys.push_back("ITK.InputFilterName");
-    this->m_DefaultMetaDataKeys.push_back("label.");
-    this->m_DefaultMetaDataKeys.push_back("layer.");
-    this->m_DefaultMetaDataKeys.push_back("layers");
-    this->m_DefaultMetaDataKeys.push_back("org.mitk.label.");
-  }
-
-} // namespace
-
-#endif //__mitkLabelSetImageWriter__cpp
diff --git a/Modules/Multilabel/autoload/IO/mitkLegacyLabelSetImageIO.cpp b/Modules/Multilabel/autoload/IO/mitkLegacyLabelSetImageIO.cpp
new file mode 100644
index 0000000000..342097f104
--- /dev/null
+++ b/Modules/Multilabel/autoload/IO/mitkLegacyLabelSetImageIO.cpp
@@ -0,0 +1,272 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+All rights reserved.
+
+Use of this source code is governed by a 3-clause BSD license that can be
+found in the LICENSE file.
+
+============================================================================*/
+
+#ifndef __mitkLabelSetImageWriter__cpp
+#define __mitkLabelSetImageWriter__cpp
+
+#include "mitkLegacyLabelSetImageIO.h"
+#include "mitkBasePropertySerializer.h"
+#include "mitkMultilabelIOMimeTypes.h"
+#include "mitkImageAccessByItk.h"
+#include "mitkMultiLabelIOHelper.h"
+#include "mitkLabelSetImageConverter.h"
+#include <mitkLocaleSwitch.h>
+#include <mitkArbitraryTimeGeometry.h>
+#include <mitkIPropertyPersistence.h>
+#include <mitkCoreServices.h>
+#include <mitkItkImageIO.h>
+#include <mitkUIDManipulator.h>
+
+// itk
+#include "itkImageFileReader.h"
+#include "itkImageFileWriter.h"
+#include "itkMetaDataDictionary.h"
+#include "itkMetaDataObject.h"
+#include "itkNrrdImageIO.h"
+
+#include <tinyxml2.h>
+
+
+namespace mitk
+{
+
+  const constexpr char* const OPTION_NAME_MULTI_LAYER = "Multi layer handling";
+  const constexpr char* const OPTION_NAME_MULTI_LAYER_ADAPT = "Adapt label values";
+  const constexpr char* const OPTION_NAME_MULTI_LAYER_SPLIT = "Split layers";
+
+  LegacyLabelSetImageIO::LegacyLabelSetImageIO()
+    : AbstractFileReader(MitkMultilabelIOMimeTypes::LEGACYLABELSET_MIMETYPE(), "MITK LabelSetImage (legacy)")
+  {
+    this->InitializeDefaultMetaDataKeys();
+    AbstractFileReader::SetRanking(10);
+
+    IFileIO::Options options;
+    std::vector<std::string> multiLayerStrategy;
+    multiLayerStrategy.push_back(OPTION_NAME_MULTI_LAYER_ADAPT);
+    multiLayerStrategy.push_back(OPTION_NAME_MULTI_LAYER_SPLIT);
+    options[OPTION_NAME_MULTI_LAYER] = multiLayerStrategy;
+    this->SetDefaultOptions(options);
+
+    this->RegisterService();
+  }
+
+
+  IFileIO::ConfidenceLevel LegacyLabelSetImageIO::GetConfidenceLevel() const
+  {
+    if (AbstractFileReader::GetConfidenceLevel() == Unsupported)
+      return Unsupported;
+    const std::string fileName = this->GetLocalFileName();
+    itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New();
+    io->SetFileName(fileName);
+    io->ReadImageInformation();
+
+    itk::MetaDataDictionary imgMetaDataDictionary = io->GetMetaDataDictionary();
+    std::string value("");
+    itk::ExposeMetaData<std::string>(imgMetaDataDictionary, "modality", value);
+    if (value.compare("org.mitk.image.multilabel") == 0)
+    {
+      return Supported;
+    }
+    else
+      return Unsupported;
+  }
+
+  std::vector<mitk::LabelSet::Pointer> ExtractLabelSetsFromMetaData(const itk::MetaDataDictionary& dictionary)
+  {
+    std::vector<mitk::LabelSet::Pointer> result;
+
+    // get labels and add them as properties to the image
+    char keybuffer[256];
+
+    unsigned int numberOfLayers = MultiLabelIOHelper::GetIntByKey(dictionary, "layers");
+    std::string _xmlStr;
+    mitk::Label::Pointer label;
+
+    for (unsigned int layerIdx = 0; layerIdx < numberOfLayers; layerIdx++)
+    {
+      sprintf(keybuffer, "layer_%03u", layerIdx);
+      int numberOfLabels = MultiLabelIOHelper::GetIntByKey(dictionary, keybuffer);
+
+      mitk::LabelSet::Pointer labelSet = mitk::LabelSet::New();
+
+      for (int labelIdx = 0; labelIdx < numberOfLabels; labelIdx++)
+      {
+        tinyxml2::XMLDocument doc;
+        sprintf(keybuffer, "label_%03u_%05d", layerIdx, labelIdx);
+        _xmlStr = MultiLabelIOHelper::GetStringByKey(dictionary, keybuffer);
+        doc.Parse(_xmlStr.c_str(), _xmlStr.size());
+
+        auto* labelElem = doc.FirstChildElement("Label");
+        if (labelElem == nullptr)
+          mitkThrow() << "Error parsing NRRD header for mitk::LabelSetImage IO";
+
+        label = mitk::MultiLabelIOHelper::LoadLabelFromXMLDocument(labelElem);
+
+        if (label->GetValue() != mitk::LabelSetImage::UnlabeledValue)
+        {
+          labelSet->AddLabel(label);
+          labelSet->SetLayer(layerIdx);
+        }
+        else
+        {
+          MITK_INFO << "Multi label image contains a label specification for unlabeled pixels. This legacy information is ignored.";
+        }
+      }
+      result.push_back(labelSet);
+    }
+
+    return result;
+  }
+
+  std::vector<BaseData::Pointer> LegacyLabelSetImageIO::DoRead()
+  {
+    itk::NrrdImageIO::Pointer nrrdImageIO = itk::NrrdImageIO::New();
+
+    std::vector<BaseData::Pointer> result;
+
+    auto rawimage = ItkImageIO::LoadRawMitkImageFromImageIO(nrrdImageIO, this->GetLocalFileName());
+
+    const itk::MetaDataDictionary& dictionary = nrrdImageIO->GetMetaDataDictionary();
+
+    std::vector<Image::Pointer> groupImages = { rawimage };
+    if (rawimage->GetChannelDescriptor().GetPixelType().GetPixelType() == itk::IOPixelEnum::VECTOR)
+    {
+      groupImages = SplitVectorImage(rawimage);
+    }
+
+    auto labelsets = ExtractLabelSetsFromMetaData(dictionary);
+
+    if (labelsets.size() != groupImages.size())
+    {
+      mitkThrow() << "Loaded data is in an invalid state. Number of extracted layer images and labels sets does not match. Found layer images: " << groupImages.size() << "; found labelsets: " << labelsets.size();
+    }
+
+    auto props = ItkImageIO::ExtractMetaDataAsPropertyList(nrrdImageIO->GetMetaDataDictionary(), this->GetMimeType()->GetName(), this->m_DefaultMetaDataKeys);
+
+    const Options userOptions = this->GetOptions();
+
+    const auto multiLayerStrategy = userOptions.find(OPTION_NAME_MULTI_LAYER)->second.ToString();
+
+    if (multiLayerStrategy == OPTION_NAME_MULTI_LAYER_SPLIT)
+    { //just split layers in different multi label images
+      auto labelSetIterator = labelsets.begin();
+      for (auto image : groupImages)
+      {
+        auto output = ConvertImageToLabelSetImage(image);
+        output->AddLabelSetToLayer(0, *labelSetIterator);
+        output->GetLabelSet(0)->SetLayer(0);
+
+        //meta data handling
+        for (auto& [name, prop] : *(props->GetMap()))
+        {
+          output->SetProperty(name, prop->Clone()); //need to clone to avoid that all outputs pointing to the same prop instances.
+        }
+        // Handle UID
+        //Remark if we split the legacy label set into distinct layer images, the outputs should have new IDs. So we don't get the old one.
+
+        result.push_back(output.GetPointer());
+        labelSetIterator++;
+      }
+    }
+    else
+    { //Avoid label id collision.
+      LabelSetImage::LabelValueType maxValue = LabelSetImage::UnlabeledValue;
+      auto imageIterator = groupImages.begin();
+      std::vector<mitk::LabelSet::Pointer> adaptedLabelSets;
+
+      for (auto labelset : labelsets)
+      {
+        const auto setValues = labelset->GetUsedLabelValues();
+
+        //generate mapping table;
+        std::vector<std::pair<Label::PixelType, Label::PixelType> > labelMapping;
+        for (auto vIter = setValues.crbegin(); vIter != setValues.crend(); vIter++)
+        { //have to use reverse loop because TransferLabelContent (used to adapt content in the same image; see below)
+          //would potentially corrupt otherwise the content due to "value collision between old values still present
+          //and already adapted values. By going from highest value to lowest, we avoid that.
+          if (LabelSetImage::UnlabeledValue != *vIter)
+            labelMapping.push_back({ *vIter, *vIter + maxValue });
+        }
+
+
+        if (LabelSetImage::UnlabeledValue != maxValue)
+        {
+          //adapt labelset
+          auto mappedLabelSet = GenerateLabelSetWithMappedValues(labelset, labelMapping);
+          adaptedLabelSets.emplace_back(mappedLabelSet);
+
+          //adapt image (it is an inplace operation. the image instance stays the same.
+          TransferLabelContent(*imageIterator, *imageIterator, mappedLabelSet, LabelSetImage::UnlabeledValue, LabelSetImage::UnlabeledValue,
+            false, labelMapping, MultiLabelSegmentation::MergeStyle::Replace, MultiLabelSegmentation::OverwriteStyle::IgnoreLocks);
+        }
+        else
+        {
+          adaptedLabelSets.emplace_back(labelset);
+        }
+
+        const auto setMaxValue = *(std::max_element(setValues.begin(), setValues.end()));
+        maxValue += setMaxValue;
+        imageIterator++;
+      }
+
+      auto output = ConvertImageVectorToLabelSetImage(groupImages, rawimage->GetTimeGeometry());
+
+      LabelSetImage::GroupIndexType id = 0;
+      for (auto labelset : adaptedLabelSets)
+      {
+        output->AddLabelSetToLayer(id, labelset);
+        id++;
+      }
+
+      //meta data handling
+      for (auto& [name, prop] : *(props->GetMap()))
+      {
+        output->SetProperty(name, prop->Clone()); //need to clone to avoid that all outputs pointing to the same prop instances.
+      }
+
+      // Handle UID
+      if (dictionary.HasKey(PROPERTY_KEY_UID))
+      {
+        itk::MetaDataObject<std::string>::ConstPointer uidData = dynamic_cast<const itk::MetaDataObject<std::string>*>(dictionary.Get(PROPERTY_KEY_UID));
+        if (uidData.IsNotNull())
+        {
+          mitk::UIDManipulator uidManipulator(output);
+          uidManipulator.SetUID(uidData->GetMetaDataObjectValue());
+        }
+      }
+      result.push_back(output.GetPointer());
+    }
+
+    MITK_INFO << "...finished!";
+    return result;
+  }
+
+  LegacyLabelSetImageIO *LegacyLabelSetImageIO::Clone() const { return new LegacyLabelSetImageIO(*this); }
+
+  void LegacyLabelSetImageIO::InitializeDefaultMetaDataKeys()
+  {
+    this->m_DefaultMetaDataKeys.push_back("NRRD.space");
+    this->m_DefaultMetaDataKeys.push_back("NRRD.kinds");
+    this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TYPE);
+    this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS);
+    this->m_DefaultMetaDataKeys.push_back("ITK.InputFilterName");
+    this->m_DefaultMetaDataKeys.push_back("label.");
+    this->m_DefaultMetaDataKeys.push_back("layer.");
+    this->m_DefaultMetaDataKeys.push_back("layers");
+    this->m_DefaultMetaDataKeys.push_back("modality");
+    this->m_DefaultMetaDataKeys.push_back("org.mitk.label.");
+    this->m_DefaultMetaDataKeys.push_back("MITK.IO.");
+  }
+
+} // namespace
+
+#endif //__mitkLabelSetImageWriter__cpp
diff --git a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.h b/Modules/Multilabel/autoload/IO/mitkLegacyLabelSetImageIO.h
similarity index 66%
copy from Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.h
copy to Modules/Multilabel/autoload/IO/mitkLegacyLabelSetImageIO.h
index 4e443688ec..0a99aebd99 100644
--- a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.h
+++ b/Modules/Multilabel/autoload/IO/mitkLegacyLabelSetImageIO.h
@@ -1,69 +1,59 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
-#ifndef mitkLabelSetImageIO_h
-#define mitkLabelSetImageIO_h
+#ifndef mitkLegacyLabelSetImageIO_h
+#define mitkLegacyLabelSetImageIO_h
 
-#include <mitkAbstractFileIO.h>
+#include <mitkAbstractFileReader.h>
 #include <mitkLabelSetImage.h>
 
 namespace mitk
 {
   /**
   * Writes a LabelSetImage to a file.
   * mitk::Identifiable UID is supported and will be serialized.
   * @ingroup Process
   */
   // The export macro should be removed. Currently, the unit
   // tests directly instantiate this class.
-  class LabelSetImageIO : public mitk::AbstractFileIO
+  class LegacyLabelSetImageIO : public mitk::AbstractFileReader
   {
   public:
     typedef mitk::LabelSetImage InputType;
 
-    LabelSetImageIO();
+    LegacyLabelSetImageIO();
 
     // -------------- AbstractFileReader -------------
 
     using AbstractFileReader::Read;
 
-    ConfidenceLevel GetReaderConfidenceLevel() const override;
-
-    // -------------- AbstractFileWriter -------------
-
-    void Write() override;
-    ConfidenceLevel GetWriterConfidenceLevel() const override;
-
-    // -------------- LabelSetImageIO specific functions -------------
-
-    int GetIntByKey(const itk::MetaDataDictionary &dic, const std::string &str);
-    std::string GetStringByKey(const itk::MetaDataDictionary &dic, const std::string &str);
+    ConfidenceLevel GetConfidenceLevel() const override;
 
   protected:
     /**
     * @brief Reads a number of mitk::LabelSetImages from the file system
     * @return a vector of mitk::LabelSetImages
     * @throws throws an mitk::Exception if an error ocurrs during parsing the nrrd header
     */
     std::vector<itk::SmartPointer<BaseData>> DoRead() override;
 
     // Fills the m_DefaultMetaDataKeys vector with default values
     virtual void InitializeDefaultMetaDataKeys();
 
   private:
-    LabelSetImageIO *IOClone() const override;
+    LegacyLabelSetImageIO *Clone() const override;
 
     std::vector<std::string> m_DefaultMetaDataKeys;
   };
 } // end of namespace mitk
 
 #endif
diff --git a/Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationIO.cpp b/Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationIO.cpp
new file mode 100644
index 0000000000..b9e9fba8ea
--- /dev/null
+++ b/Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationIO.cpp
@@ -0,0 +1,224 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+All rights reserved.
+
+Use of this source code is governed by a 3-clause BSD license that can be
+found in the LICENSE file.
+
+============================================================================*/
+
+#include "mitkMultiLabelSegmentationIO.h"
+#include "mitkBasePropertySerializer.h"
+#include "mitkIOMimeTypes.h"
+#include "mitkImageAccessByItk.h"
+#include "mitkMultiLabelIOHelper.h"
+#include "mitkLabelSetImageConverter.h"
+#include <mitkLocaleSwitch.h>
+#include <mitkArbitraryTimeGeometry.h>
+#include <mitkIPropertyPersistence.h>
+#include <mitkCoreServices.h>
+#include <mitkItkImageIO.h>
+#include <mitkUIDManipulator.h>
+
+// itk
+#include "itkImageFileReader.h"
+#include "itkImageFileWriter.h"
+#include "itkMetaDataDictionary.h"
+#include "itkMetaDataObject.h"
+#include "itkNrrdImageIO.h"
+
+#include <tinyxml2.h>
+
+namespace mitk
+{
+
+  const constexpr char* const MULTILABEL_SEGMENTATION_MODALITY_KEY = "modality";
+  const constexpr char* const MULTILABEL_SEGMENTATION_MODALITY_VALUE = "org.mitk.multilabel.segmentation";
+  const constexpr char* const MULTILABEL_SEGMENTATION_VERSION_KEY = "org.mitk.multilabel.segmentation.version";
+  const constexpr int MULTILABEL_SEGMENTATION_VERSION_VALUE = 1;
+  const constexpr char* const MULTILABEL_SEGMENTATION_LABELS_INFO_KEY = "org.mitk.multilabel.segmentation.labelgroups";
+  const constexpr char* const MULTILABEL_SEGMENTATION_UNLABELEDLABEL_LOCK_KEY = "org.mitk.multilabel.segmentation.unlabeledlabellock";
+
+  MultiLabelSegmentationIO::MultiLabelSegmentationIO()
+    : AbstractFileIO(LabelSetImage::GetStaticNameOfClass(), IOMimeTypes::NRRD_MIMETYPE(), "MITK Multilabel Segmentation")
+  {
+    this->InitializeDefaultMetaDataKeys();
+    AbstractFileWriter::SetRanking(10);
+    AbstractFileReader::SetRanking(10);
+    this->RegisterService();
+  }
+
+  IFileIO::ConfidenceLevel MultiLabelSegmentationIO::GetWriterConfidenceLevel() const
+  {
+    if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported)
+      return Unsupported;
+    const auto *input = static_cast<const LabelSetImage *>(this->GetInput());
+    if (input)
+      return Supported;
+    else
+      return Unsupported;
+  }
+
+  void MultiLabelSegmentationIO::Write()
+  {
+    ValidateOutputLocation();
+
+    auto input = dynamic_cast<const LabelSetImage *>(this->GetInput());
+
+    mitk::LocaleSwitch localeSwitch("C");
+
+    mitk::Image::Pointer inputVector = mitk::ConvertLabelSetImageToImage(input);
+
+    // image write
+    if (inputVector.IsNull())
+    {
+      mitkThrow() << "Cannot write non-image data";
+    }
+
+    itk::NrrdImageIO::Pointer nrrdImageIo = itk::NrrdImageIO::New();
+
+    ItkImageIO::PreparImageIOToWriteImage(nrrdImageIo, inputVector);
+
+    LocalFile localFile(this);
+    const std::string path = localFile.GetFileName();
+
+    MITK_INFO << "Writing image: " << path << std::endl;
+
+    try
+    {
+      itk::EncapsulateMetaData<std::string>(
+        nrrdImageIo->GetMetaDataDictionary(), std::string(MULTILABEL_SEGMENTATION_MODALITY_KEY), std::string(MULTILABEL_SEGMENTATION_MODALITY_VALUE));
+
+      //nrrd does only support string meta information. So we have to convert before.
+      itk::EncapsulateMetaData<std::string>(
+        nrrdImageIo->GetMetaDataDictionary(), std::string(MULTILABEL_SEGMENTATION_VERSION_KEY), std::to_string(MULTILABEL_SEGMENTATION_VERSION_VALUE));
+
+      auto json = MultiLabelIOHelper::SerializeMultLabelGroupsToJSON(input);
+      itk::EncapsulateMetaData<std::string>(
+        nrrdImageIo->GetMetaDataDictionary(), std::string(MULTILABEL_SEGMENTATION_LABELS_INFO_KEY), json.dump());
+      // end label set specific meta data
+
+      //nrrd does only support string meta information. So we have to convert before.
+      itk::EncapsulateMetaData<std::string>(
+        nrrdImageIo->GetMetaDataDictionary(), std::string(MULTILABEL_SEGMENTATION_UNLABELEDLABEL_LOCK_KEY), std::to_string(input->GetUnlabeledLabelLock()));
+
+      // Handle properties
+      ItkImageIO::SavePropertyListAsMetaData(nrrdImageIo->GetMetaDataDictionary(), input->GetPropertyList(), this->GetMimeType()->GetName());
+
+      // Handle UID
+      itk::EncapsulateMetaData<std::string>(nrrdImageIo->GetMetaDataDictionary(), PROPERTY_KEY_UID, input->GetUID());
+
+      // use compression if available
+      nrrdImageIo->UseCompressionOn();
+      nrrdImageIo->SetFileName(path);
+
+      ImageReadAccessor imageAccess(inputVector);
+      nrrdImageIo->Write(imageAccess.GetData());
+    }
+    catch (const std::exception &e)
+    {
+      mitkThrow() << e.what();
+    }
+  }
+
+  IFileIO::ConfidenceLevel MultiLabelSegmentationIO::GetReaderConfidenceLevel() const
+  {
+    if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported)
+      return Unsupported;
+    const std::string fileName = this->GetLocalFileName();
+    itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New();
+    io->SetFileName(fileName);
+    io->ReadImageInformation();
+
+    itk::MetaDataDictionary imgMetaDataDictionary = io->GetMetaDataDictionary();
+    std::string value("");
+    itk::ExposeMetaData<std::string>(imgMetaDataDictionary, "modality", value);
+    if (value.compare(MULTILABEL_SEGMENTATION_MODALITY_VALUE) == 0)
+    {
+      return Supported;
+    }
+    else
+      return Unsupported;
+  }
+
+  std::vector<BaseData::Pointer> MultiLabelSegmentationIO::DoRead()
+  {
+    itk::NrrdImageIO::Pointer nrrdImageIO = itk::NrrdImageIO::New();
+
+    std::vector<BaseData::Pointer> result;
+
+    auto rawimage = ItkImageIO::LoadRawMitkImageFromImageIO(nrrdImageIO, this->GetLocalFileName());
+
+    const itk::MetaDataDictionary& dictionary = nrrdImageIO->GetMetaDataDictionary();
+
+    //check version
+    auto version = MultiLabelIOHelper::GetIntByKey(dictionary, MULTILABEL_SEGMENTATION_VERSION_KEY);
+    if (version > MULTILABEL_SEGMENTATION_VERSION_VALUE)
+    {
+      mitkThrow() << "Data to read has unsupported version. Software is to old to ensure correct reading. Please use a compatible version of MITK or store data in another format. Version of data: " << version << "; Supported versions up to: "<<MULTILABEL_SEGMENTATION_VERSION_VALUE;
+    }
+
+    //generate multi label images
+    auto output = ConvertImageToLabelSetImage(rawimage);
+
+    //get label set definitions
+    auto jsonStr = MultiLabelIOHelper::GetStringByKey(dictionary, MULTILABEL_SEGMENTATION_LABELS_INFO_KEY);
+    nlohmann::json jlabelsets = nlohmann::json::parse(jsonStr);
+    std::vector<mitk::LabelSet::Pointer> labelsets = MultiLabelIOHelper::DeserializeMultiLabelGroupsFromJSON(jlabelsets);
+
+    if (labelsets.size() != output->GetNumberOfLayers())
+    {
+      mitkThrow() << "Loaded data is in an invalid state. Number of extracted layer images and labels sets does not match. Found layer images: " << output->GetNumberOfLayers() << "; found labelsets: " << labelsets.size();
+    }
+
+    LabelSetImage::GroupIndexType id = 0;
+    for (auto labelset : labelsets)
+    {
+      output->AddLabelSetToLayer(id, labelset);
+      id++;
+    }
+
+    bool unlabeledLock = MultiLabelIOHelper::GetIntByKey(dictionary, MULTILABEL_SEGMENTATION_UNLABELEDLABEL_LOCK_KEY) != 0;
+    output->SetUnlabeledLabelLock(unlabeledLock);
+
+    //meta data handling
+    auto props = ItkImageIO::ExtractMetaDataAsPropertyList(nrrdImageIO->GetMetaDataDictionary(), this->GetMimeType()->GetName(), this->m_DefaultMetaDataKeys);
+    for (auto& [name, prop] : *(props->GetMap()))
+    {
+      output->SetProperty(name, prop->Clone()); //need to clone to avoid that all outputs pointing to the same prop instances.
+    }
+
+    // Handle UID
+    if (dictionary.HasKey(PROPERTY_KEY_UID))
+    {
+      itk::MetaDataObject<std::string>::ConstPointer uidData = dynamic_cast<const itk::MetaDataObject<std::string>*>(dictionary.Get(PROPERTY_KEY_UID));
+      if (uidData.IsNotNull())
+      {
+        mitk::UIDManipulator uidManipulator(output);
+        uidManipulator.SetUID(uidData->GetMetaDataObjectValue());
+      }
+    }
+    result.push_back(output.GetPointer());
+
+    MITK_INFO << "...finished!";
+    return result;
+  }
+
+  MultiLabelSegmentationIO *MultiLabelSegmentationIO::IOClone() const { return new MultiLabelSegmentationIO(*this); }
+
+  void MultiLabelSegmentationIO::InitializeDefaultMetaDataKeys()
+  {
+    this->m_DefaultMetaDataKeys.push_back("NRRD.space");
+    this->m_DefaultMetaDataKeys.push_back("NRRD.kinds");
+    this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TYPE);
+    this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS);
+    this->m_DefaultMetaDataKeys.push_back("ITK.InputFilterName");
+    this->m_DefaultMetaDataKeys.push_back("org.mitk.multilabel.");
+    this->m_DefaultMetaDataKeys.push_back("MITK.IO.");
+    this->m_DefaultMetaDataKeys.push_back(MULTILABEL_SEGMENTATION_MODALITY_KEY);
+  }
+
+} // namespace
diff --git a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.h b/Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationIO.h
similarity index 78%
rename from Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.h
rename to Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationIO.h
index 4e443688ec..b2a5757143 100644
--- a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.h
+++ b/Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationIO.h
@@ -1,69 +1,64 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
-#ifndef mitkLabelSetImageIO_h
-#define mitkLabelSetImageIO_h
+#ifndef mitkMultiLabelSegmentationIO_h
+#define mitkMultiLabelSegmentationIO_h
 
 #include <mitkAbstractFileIO.h>
 #include <mitkLabelSetImage.h>
 
 namespace mitk
 {
   /**
   * Writes a LabelSetImage to a file.
   * mitk::Identifiable UID is supported and will be serialized.
   * @ingroup Process
   */
   // The export macro should be removed. Currently, the unit
   // tests directly instantiate this class.
-  class LabelSetImageIO : public mitk::AbstractFileIO
+  class MultiLabelSegmentationIO : public mitk::AbstractFileIO
   {
   public:
     typedef mitk::LabelSetImage InputType;
 
-    LabelSetImageIO();
+    MultiLabelSegmentationIO();
 
     // -------------- AbstractFileReader -------------
 
     using AbstractFileReader::Read;
 
     ConfidenceLevel GetReaderConfidenceLevel() const override;
 
     // -------------- AbstractFileWriter -------------
 
     void Write() override;
     ConfidenceLevel GetWriterConfidenceLevel() const override;
 
-    // -------------- LabelSetImageIO specific functions -------------
-
-    int GetIntByKey(const itk::MetaDataDictionary &dic, const std::string &str);
-    std::string GetStringByKey(const itk::MetaDataDictionary &dic, const std::string &str);
-
   protected:
     /**
     * @brief Reads a number of mitk::LabelSetImages from the file system
     * @return a vector of mitk::LabelSetImages
     * @throws throws an mitk::Exception if an error ocurrs during parsing the nrrd header
     */
     std::vector<itk::SmartPointer<BaseData>> DoRead() override;
 
     // Fills the m_DefaultMetaDataKeys vector with default values
     virtual void InitializeDefaultMetaDataKeys();
 
   private:
-    LabelSetImageIO *IOClone() const override;
+    MultiLabelSegmentationIO *IOClone() const override;
 
     std::vector<std::string> m_DefaultMetaDataKeys;
   };
 } // end of namespace mitk
 
 #endif
diff --git a/Modules/Multilabel/autoload/IO/mitkLabelSetImageSerializer.cpp b/Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationSerializer.cpp
similarity index 82%
rename from Modules/Multilabel/autoload/IO/mitkLabelSetImageSerializer.cpp
rename to Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationSerializer.cpp
index 892c656dbd..9a05adcc3c 100644
--- a/Modules/Multilabel/autoload/IO/mitkLabelSetImageSerializer.cpp
+++ b/Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationSerializer.cpp
@@ -1,64 +1,64 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
-#include "mitkLabelSetImageSerializer.h"
+#include "mitkMultiLabelSegmentationSerializer.h"
 #include "mitkLabelSetImage.h"
 
 #include <itksys/SystemTools.hxx>
 
 #include <mitkIOUtil.h>
 
-MITK_REGISTER_SERIALIZER(LabelSetImageSerializer)
+MITK_REGISTER_SERIALIZER(MultiLabelSegmentationSerializer)
 
-mitk::LabelSetImageSerializer::LabelSetImageSerializer()
+mitk::MultiLabelSegmentationSerializer::MultiLabelSegmentationSerializer()
 {
 }
 
-mitk::LabelSetImageSerializer::~LabelSetImageSerializer()
+mitk::MultiLabelSegmentationSerializer::~MultiLabelSegmentationSerializer()
 {
 }
 
-std::string mitk::LabelSetImageSerializer::Serialize()
+std::string mitk::MultiLabelSegmentationSerializer::Serialize()
 {
   const auto *image = dynamic_cast<const LabelSetImage *>(m_Data.GetPointer());
   if (image == nullptr)
   {
     MITK_ERROR << " Object at " << (const void *)this->m_Data
                << " is not an mitk::LabelSetImage. Cannot serialize as LabelSetImage.";
     return "";
   }
 
   std::string filename(this->GetUniqueFilenameInWorkingDirectory());
   filename += "_";
   filename += m_FilenameHint;
   filename += ".nrrd";
 
   std::string fullname(m_WorkingDirectory);
   fullname += "/";
   fullname += itksys::SystemTools::ConvertToOutputPath(filename.c_str());
 
   try
   {
     mitk::IOUtil::Save(image, fullname);
     //    LabelSetImageWriter::Pointer writer = LabelSetImageWriter::New();
     //    writer->SetFileName(fullname);
     //    writer->SetInput(const_cast<LabelSetImage*>(image));
     //    writer->Write();
   }
   catch (std::exception &e)
   {
     MITK_ERROR << " Error serializing object at " << (const void *)this->m_Data << " to " << fullname << ": "
                << e.what();
     return "";
   }
   return filename;
 }
diff --git a/Modules/Multilabel/autoload/IO/mitkLabelSetImageSerializer.h b/Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationSerializer.h
similarity index 66%
rename from Modules/Multilabel/autoload/IO/mitkLabelSetImageSerializer.h
rename to Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationSerializer.h
index 3b205dd51c..5c6fdab21d 100644
--- a/Modules/Multilabel/autoload/IO/mitkLabelSetImageSerializer.h
+++ b/Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationSerializer.h
@@ -1,37 +1,37 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
-#ifndef mitkLabelSetImageSerializer_h
-#define mitkLabelSetImageSerializer_h
+#ifndef mitkMultiLabelSegmentationSerializer_h
+#define mitkMultiLabelSegmentationSerializer_h
 
 #include "mitkBaseDataSerializer.h"
 
 namespace mitk
 {
   /**
   \brief Serializes mitk::LabelSetImage for mitk::SceneIO
   */
-  class LabelSetImageSerializer : public BaseDataSerializer
+  class MultiLabelSegmentationSerializer : public BaseDataSerializer
   {
   public:
-    mitkClassMacro(LabelSetImageSerializer, BaseDataSerializer);
+    mitkClassMacro(MultiLabelSegmentationSerializer, BaseDataSerializer);
     itkFactorylessNewMacro(Self);
     itkCloneMacro(Self);
     std::string Serialize() override;
 
   protected:
-    LabelSetImageSerializer();
-    ~LabelSetImageSerializer() override;
+    MultiLabelSegmentationSerializer();
+    ~MultiLabelSegmentationSerializer() override;
   };
 } // namespace
 
 #endif
diff --git a/Modules/Multilabel/autoload/IO/mitkMultilabelActivator.cpp b/Modules/Multilabel/autoload/IO/mitkMultilabelActivator.cpp
index 58201f45c4..237c838341 100644
--- a/Modules/Multilabel/autoload/IO/mitkMultilabelActivator.cpp
+++ b/Modules/Multilabel/autoload/IO/mitkMultilabelActivator.cpp
@@ -1,55 +1,59 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include <usGetModuleContext.h>
 #include <usModule.h>
 #include <usModuleActivator.h>
 #include <usModuleContext.h>
 
-#include "mitkLabelSetImageIO.h"
+#include "mitkLegacyLabelSetImageIO.h"
+#include "mitkMultiLabelSegmentationIO.h"
 #include "mitkMultilabelIOMimeTypes.h"
 #include "mitkSegmentationTaskListIO.h"
 
 namespace mitk
 {
   /**
   \brief Registers services for multilabel module.
   */
   class MultilabelIOModuleActivator : public us::ModuleActivator
   {
     std::vector<AbstractFileIO *> m_FileIOs;
+    std::unique_ptr<IFileReader> m_LegacyLabelSetImageIOReader;
 
   public:
     void Load(us::ModuleContext *context) override
     {
       auto mimeTypes = MitkMultilabelIOMimeTypes::Get();
 
       us::ServiceProperties props;
       props[us::ServiceConstants::SERVICE_RANKING()] = 10;
 
       for (const auto &mimeType : mimeTypes)
         context->RegisterService(mimeType, props);
 
-      m_FileIOs.push_back(new LabelSetImageIO());
+      m_LegacyLabelSetImageIOReader = std::make_unique<LegacyLabelSetImageIO>();
+
+      m_FileIOs.push_back(new MultiLabelSegmentationIO());
       m_FileIOs.push_back(new SegmentationTaskListIO);
     }
     void Unload(us::ModuleContext *) override
     {
       for (auto &elem : m_FileIOs)
       {
         delete elem;
       }
     }
   };
 }
 
 US_EXPORT_MODULE_ACTIVATOR(mitk::MultilabelIOModuleActivator)
diff --git a/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.cpp b/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.cpp
index 231f8d4aed..aee9aa0727 100644
--- a/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.cpp
+++ b/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.cpp
@@ -1,75 +1,146 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "mitkMultilabelIOMimeTypes.h"
 #include <mitkIOMimeTypes.h>
-
+#include <mitkLogMacros.h>
 #include <filesystem>
 #include <fstream>
 
 #include <nlohmann/json.hpp>
 
+#include <itkNrrdImageIO.h>
+#include "itkMetaDataObject.h"
+
 mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType::MitkSegmentationTaskListMimeType()
   : CustomMimeType(SEGMENTATIONTASKLIST_MIMETYPE_NAME())
 {
   this->AddExtension("json");
   this->SetCategory("MITK Segmentation Task List");
   this->SetComment("MITK Segmentation Task List");
 }
 
 bool mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType::AppliesTo(const std::string& path) const
 {
   bool result = CustomMimeType::AppliesTo(path);
 
   if (!std::filesystem::exists(path)) // T18572
     return result;
 
   std::ifstream file(path);
 
   if (!file.is_open())
     return false;
 
   auto json = nlohmann::json::parse(file, nullptr, false);
 
   if (json.is_discarded() || !json.is_object())
     return false;
 
   if ("MITK Segmentation Task List" != json.value("FileFormat", ""))
     return false;
 
   if (1 != json.value<int>("Version", 0))
     return false;
 
   return true;
 }
 
 mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType* mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType::Clone() const
 {
   return new MitkSegmentationTaskListMimeType(*this);
 }
 
 mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType mitk::MitkMultilabelIOMimeTypes::SEGMENTATIONTASKLIST_MIMETYPE()
 {
   return MitkSegmentationTaskListMimeType();
 }
 
 std::string mitk::MitkMultilabelIOMimeTypes::SEGMENTATIONTASKLIST_MIMETYPE_NAME()
 {
   return IOMimeTypes::DEFAULT_BASE_NAME() + ".segmentationtasklist";
 }
 
+mitk::MitkMultilabelIOMimeTypes::LegacyLabelSetMimeType::LegacyLabelSetMimeType()
+  : CustomMimeType(LEGACYLABELSET_MIMETYPE_NAME())
+{
+  this->AddExtension("nrrd");
+  this->SetCategory("MITK LabelSetImage");
+  this->SetComment("MITK LabelSetImage (legacy format)");
+}
+
+bool mitk::MitkMultilabelIOMimeTypes::LegacyLabelSetMimeType::AppliesTo(const std::string& path) const
+{
+  bool canRead = CustomMimeType::AppliesTo(path);
+
+  if (!std::filesystem::exists(path)) // T18572
+    return canRead;
+
+  if (!canRead)
+  {
+    return false;
+  }
+
+  std::string value("");
+  try
+  {
+    std::ifstream file(path);
+
+    if (!file.is_open())
+      return false;
+
+    itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New();
+    io->SetFileName(path);
+    io->ReadImageInformation();
+
+    itk::MetaDataDictionary imgMetaDataDictionary = io->GetMetaDataDictionary();
+    itk::ExposeMetaData<std::string>(imgMetaDataDictionary, "modality", value);
+  }
+  catch(const std::exception& e)
+  {
+    MITK_DEBUG << "Error while try to anylize NRRD file for LegacyLabelSetMimeType. File: " << path <<"; Error: " << e.what();
+  }
+  catch(...)
+  {
+    MITK_DEBUG << "Unkown error while try to anylize NRRD file for LegacyLabelSetMimeType. File: " << path;
+  }
+
+  if (value.compare("org.mitk.image.multilabel") != 0)
+  {
+    return false;
+  }
+
+  return true;
+}
+
+mitk::MitkMultilabelIOMimeTypes::LegacyLabelSetMimeType* mitk::MitkMultilabelIOMimeTypes::LegacyLabelSetMimeType::Clone() const
+{
+  return new LegacyLabelSetMimeType(*this);
+}
+
+mitk::MitkMultilabelIOMimeTypes::LegacyLabelSetMimeType mitk::MitkMultilabelIOMimeTypes::LEGACYLABELSET_MIMETYPE()
+{
+  return LegacyLabelSetMimeType();
+}
+
+std::string mitk::MitkMultilabelIOMimeTypes::LEGACYLABELSET_MIMETYPE_NAME()
+{
+  return IOMimeTypes::DEFAULT_BASE_NAME() + ".legacylabelsetimage";
+}
+
 std::vector<mitk::CustomMimeType*> mitk::MitkMultilabelIOMimeTypes::Get()
 {
   std::vector<CustomMimeType*> mimeTypes;
   mimeTypes.push_back(SEGMENTATIONTASKLIST_MIMETYPE().Clone());
+  mimeTypes.push_back(LEGACYLABELSET_MIMETYPE().Clone());
   return mimeTypes;
 }
diff --git a/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.h b/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.h
index 7343895f13..1f92d4692a 100644
--- a/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.h
+++ b/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.h
@@ -1,39 +1,51 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #ifndef mitkMultilabelIOMimeTypes_h
 #define mitkMultilabelIOMimeTypes_h
 
 #include <mitkCustomMimeType.h>
 #include <MitkMultilabelIOExports.h>
 
 namespace mitk
 {
   namespace MitkMultilabelIOMimeTypes
   {
     class MITKMULTILABELIO_EXPORT MitkSegmentationTaskListMimeType : public CustomMimeType
     {
     public:
       MitkSegmentationTaskListMimeType();
 
       bool AppliesTo(const std::string& path) const override;
       MitkSegmentationTaskListMimeType* Clone() const override;
     };
 
     MITKMULTILABELIO_EXPORT MitkSegmentationTaskListMimeType SEGMENTATIONTASKLIST_MIMETYPE();
     MITKMULTILABELIO_EXPORT std::string SEGMENTATIONTASKLIST_MIMETYPE_NAME();
 
+    class MITKMULTILABELIO_EXPORT LegacyLabelSetMimeType : public CustomMimeType
+    {
+    public:
+      LegacyLabelSetMimeType();
+
+      bool AppliesTo(const std::string& path) const override;
+      LegacyLabelSetMimeType* Clone() const override;
+    };
+
+    MITKMULTILABELIO_EXPORT LegacyLabelSetMimeType LEGACYLABELSET_MIMETYPE();
+    MITKMULTILABELIO_EXPORT std::string LEGACYLABELSET_MIMETYPE_NAME();
+
     MITKMULTILABELIO_EXPORT std::vector<CustomMimeType*> Get();
   }
 }
 
 #endif
diff --git a/Modules/Multilabel/files.cmake b/Modules/Multilabel/files.cmake
index 0524d4f98c..8723b71a3b 100644
--- a/Modules/Multilabel/files.cmake
+++ b/Modules/Multilabel/files.cmake
@@ -1,21 +1,21 @@
 set(CPP_FILES
   mitkLabel.cpp
   mitkLabelSet.cpp
   mitkLabelSetImage.cpp
   mitkLabelSetImageConverter.cpp
   mitkLabelSetImageSource.cpp
   mitkLabelSetImageHelper.cpp
   mitkLabelSetImageSurfaceStampFilter.cpp
   mitkLabelSetImageToSurfaceFilter.cpp
   mitkLabelSetImageToSurfaceThreadedFilter.cpp
   mitkLabelSetImageVtkMapper2D.cpp
   mitkMultilabelObjectFactory.cpp
-  mitkLabelSetIOHelper.cpp
+  mitkMultiLabelIOHelper.cpp
   mitkDICOMSegmentationPropertyHelper.cpp
   mitkDICOMSegmentationConstants.cpp
   mitkSegmentationTaskList.cpp
 )
 
 set(RESOURCE_FILES
 
 )
diff --git a/Modules/Multilabel/mitkLabel.cpp b/Modules/Multilabel/mitkLabel.cpp
index f2b6df44c5..6972b9f126 100644
--- a/Modules/Multilabel/mitkLabel.cpp
+++ b/Modules/Multilabel/mitkLabel.cpp
@@ -1,298 +1,304 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "mitkLabel.h"
 
 #include "itkProcessObject.h"
 #include <itkCommand.h>
 #include <mitkProperties.h>
 #include <mitkDICOMSegmentationPropertyHelper.h>
 #include <mitkStringProperty.h>
 
 const mitk::Label::PixelType mitk::Label::MAX_LABEL_VALUE = std::numeric_limits<mitk::Label::PixelType>::max();
 
 mitk::Label::Label() : PropertyList()
 {
   if (GetProperty("locked") == nullptr)
     SetLocked(true);
   if (GetProperty("visible") == nullptr)
     SetVisible(true);
   if (GetProperty("opacity") == nullptr)
     SetOpacity(0.6);
   if (GetProperty("center.coordinates") == nullptr)
   {
     mitk::Point3D pnt;
     pnt.SetElement(0, 0);
     pnt.SetElement(1, 0);
     pnt.SetElement(2, 0);
     SetCenterOfMassCoordinates(pnt);
   }
   if (GetProperty("center.index") == nullptr)
   {
     mitk::Point3D pnt;
     pnt.SetElement(0, 0);
     pnt.SetElement(1, 0);
     pnt.SetElement(2, 0);
     SetCenterOfMassIndex(pnt);
   }
   if (GetProperty("color") == nullptr)
   {
     mitk::Color col;
     col.Set(0, 0, 0);
     SetColor(col);
   }
   if (GetProperty("name") == nullptr)
     SetName("noName!");
   if (GetProperty("value") == nullptr)
     SetValue(0);
   if (GetProperty("layer") == nullptr)
     SetLayer(0);
 
   DICOMSegmentationPropertyHelper::SetDICOMSegmentProperties(this);
 }
 
+mitk::Label::Label(PixelType value, const std::string& name) : Label()
+{
+  this->SetValue(value);
+  this->SetName(name);
+}
+
 mitk::Label::Label(const Label &other) : PropertyList(other)
 // copyconstructer of property List handles the coping action
 {
   auto *map = this->GetMap();
   auto it = map->begin();
   auto end = map->end();
 
   for (; it != end; ++it)
   {
     itk::SimpleMemberCommand<Label>::Pointer command = itk::SimpleMemberCommand<Label>::New();
     command->SetCallbackFunction(this, &Label::Modified);
     it->second->AddObserver(itk::ModifiedEvent(), command);
   }
 }
 
 mitk::Label::~Label()
 {
 }
 
 void mitk::Label::SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName, bool fallBackOnDefaultContext)
 {
   itk::SimpleMemberCommand<Label>::Pointer command = itk::SimpleMemberCommand<Label>::New();
   command->SetCallbackFunction(this, &Label::Modified);
   property->AddObserver(itk::ModifiedEvent(), command);
 
   Superclass::SetProperty(propertyKey, property, contextName, fallBackOnDefaultContext);
 }
 
 void mitk::Label::SetLocked(bool locked)
 {
   mitk::BoolProperty *property = dynamic_cast<mitk::BoolProperty *>(GetProperty("locked"));
   if (property != nullptr)
     // Update Property
     property->SetValue(locked);
   else
     // Create new Property
     SetBoolProperty("locked", locked);
 }
 
 bool mitk::Label::GetLocked() const
 {
   bool locked;
   GetBoolProperty("locked", locked);
   return locked;
 }
 
 void mitk::Label::SetVisible(bool visible)
 {
   mitk::BoolProperty *property = dynamic_cast<mitk::BoolProperty *>(GetProperty("visible"));
   if (property != nullptr)
     // Update Property
     property->SetValue(visible);
   else
     // Create new Property
     SetBoolProperty("visible", visible);
 }
 
 bool mitk::Label::GetVisible() const
 {
   bool visible;
   GetBoolProperty("visible", visible);
   return visible;
 }
 
 void mitk::Label::SetOpacity(float opacity)
 {
   mitk::FloatProperty *property = dynamic_cast<mitk::FloatProperty *>(GetProperty("opacity"));
   if (property != nullptr)
     // Update Property
     property->SetValue(opacity);
   else
     // Create new Property
     SetFloatProperty("opacity", opacity);
 }
 
 float mitk::Label::GetOpacity() const
 {
   float opacity;
   GetFloatProperty("opacity", opacity);
   return opacity;
 }
 
 void mitk::Label::SetName(const std::string &name)
 {
   SetStringProperty("name", name.c_str());
 }
 
 std::string mitk::Label::GetName() const
 {
   std::string name;
   GetStringProperty("name", name);
   return name;
 }
 
 void mitk::Label::SetValue(PixelType pixelValue)
 {
   mitk::UShortProperty *property = dynamic_cast<mitk::UShortProperty *>(GetProperty("value"));
   if (property != nullptr)
     // Update Property
     property->SetValue(pixelValue);
   else
     // Create new Property
     SetProperty("value", mitk::UShortProperty::New(pixelValue));
 }
 
 mitk::Label::PixelType mitk::Label::GetValue() const
 {
   PixelType pixelValue;
   mitk::UShortProperty *property = dynamic_cast<UShortProperty *>(GetProperty("value"));
   assert(property);
   pixelValue = property->GetValue();
   return pixelValue;
 }
 
 void mitk::Label::SetLayer(unsigned int layer)
 {
   mitk::UIntProperty *property = dynamic_cast<mitk::UIntProperty *>(GetProperty("layer"));
   if (property != nullptr)
     // Update Property
     property->SetValue(layer);
   else
     // Create new Property
     SetProperty("layer", mitk::UIntProperty::New(layer));
 }
 
 unsigned int mitk::Label::GetLayer() const
 {
   unsigned int layer;
   mitk::UIntProperty *prop = dynamic_cast<mitk::UIntProperty *>(GetProperty("layer"));
   layer = prop->GetValue();
   return layer;
 }
 
 const mitk::Color &mitk::Label::GetColor() const
 {
   mitk::ColorProperty *colorProp = dynamic_cast<mitk::ColorProperty *>(GetProperty("color"));
   return colorProp->GetColor();
 }
 
 void mitk::Label::SetColor(const mitk::Color &_color)
 {
   mitk::ColorProperty *colorProp = dynamic_cast<mitk::ColorProperty *>(GetProperty("color"));
   if (colorProp != nullptr)
     // Update Property
     colorProp->SetColor(_color);
   else
     // Create new Property
     SetProperty("color", mitk::ColorProperty::New(_color));
 }
 
 void mitk::Label::SetCenterOfMassIndex(const mitk::Point3D &center)
 {
   mitk::Point3dProperty *property = dynamic_cast<mitk::Point3dProperty *>(GetProperty("center.index"));
   if (property != nullptr)
     // Update Property
     property->SetValue(center);
   else
     // Create new Property
     SetProperty("center.index", mitk::Point3dProperty::New(center));
 }
 
 mitk::Point3D mitk::Label::GetCenterOfMassIndex() const
 {
   mitk::Point3dProperty *property = dynamic_cast<mitk::Point3dProperty *>(GetProperty("center.index"));
   return property->GetValue();
 }
 
 void mitk::Label::SetCenterOfMassCoordinates(const mitk::Point3D &center)
 {
   mitk::Point3dProperty *property = dynamic_cast<mitk::Point3dProperty *>(GetProperty("center.coordinates"));
   if (property != nullptr)
     // Update Property
     property->SetValue(center);
   else
     // Create new Property
     SetProperty("center.coordinates", mitk::Point3dProperty::New(center));
 }
 
 mitk::Point3D mitk::Label::GetCenterOfMassCoordinates() const
 {
   mitk::Point3dProperty *property = dynamic_cast<mitk::Point3dProperty *>(GetProperty("center.coordinates"));
   return property->GetValue();
 }
 
 itk::LightObject::Pointer mitk::Label::InternalClone() const
 {
   itk::LightObject::Pointer result(new Self(*this));
   result->UnRegister();
   return result;
 }
 
 void mitk::Label::PrintSelf(std::ostream & /*os*/, itk::Indent /*indent*/) const
 {
   // todo
 }
 
 bool mitk::Equal(const mitk::Label &leftHandSide, const mitk::Label &rightHandSide, ScalarType /*eps*/, bool verbose)
 {
   MITK_INFO(verbose) << "--- Label Equal ---";
 
   bool returnValue = true;
   // have to be replaced until a PropertyList Equal was implemented :
   // returnValue = mitk::Equal((const mitk::PropertyList &)leftHandSide,(const mitk::PropertyList
   // &)rightHandSide,eps,verbose);
 
   const mitk::PropertyList::PropertyMap *lhsmap = leftHandSide.GetMap();
   const mitk::PropertyList::PropertyMap *rhsmap = rightHandSide.GetMap();
 
   returnValue = lhsmap->size() == rhsmap->size();
 
   if (!returnValue)
   {
     MITK_INFO(verbose) << "Labels in label container are not equal.";
     return returnValue;
   }
 
   auto lhsmapIt = lhsmap->begin();
   auto lhsmapItEnd = lhsmap->end();
 
   for (; lhsmapIt != lhsmapItEnd; ++lhsmapIt)
   {
     if (rhsmap->find(lhsmapIt->first) == rhsmap->end())
     {
       returnValue = false;
       break;
     }
   }
 
   if (!returnValue)
   {
     MITK_INFO(verbose) << "Labels in label container are not equal.";
     return returnValue;
   }
 
   return returnValue;
 }
diff --git a/Modules/Multilabel/mitkLabel.h b/Modules/Multilabel/mitkLabel.h
index 2b8ee27bd7..0d64a124df 100644
--- a/Modules/Multilabel/mitkLabel.h
+++ b/Modules/Multilabel/mitkLabel.h
@@ -1,104 +1,106 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #ifndef mitkLabel_h
 #define mitkLabel_h
 
 #include "MitkMultilabelExports.h"
 #include <mitkColorProperty.h>
 #include <mitkPropertyList.h>
 #include <mitkVector.h>
 
 namespace mitk
 {
   //##
   //##Documentation
   //## @brief A data structure describing a label.
   //## @ingroup Data
   //##
   class MITKMULTILABEL_EXPORT Label : public PropertyList
   {
   public:
     mitkClassMacro(Label, mitk::PropertyList);
 
-    itkNewMacro(Self);
-
     typedef unsigned short PixelType;
 
+    itkNewMacro(Self);
+    mitkNewMacro2Param(Self, PixelType, const std::string&);
+
     /// The maximum value a label can get: Since the value is of type unsigned short MAX_LABEL_VALUE = 65535
     static const PixelType MAX_LABEL_VALUE;
 
     void SetLocked(bool locked);
     bool GetLocked() const;
 
     void SetVisible(bool visible);
     bool GetVisible() const;
 
     void SetOpacity(float opacity);
     float GetOpacity() const;
 
     void SetName(const std::string &name);
     std::string GetName() const;
 
     void SetCenterOfMassIndex(const mitk::Point3D &center);
     mitk::Point3D GetCenterOfMassIndex() const;
 
     void SetCenterOfMassCoordinates(const mitk::Point3D &center);
     mitk::Point3D GetCenterOfMassCoordinates() const;
 
     void SetColor(const mitk::Color &);
     const mitk::Color &GetColor() const;
 
     void SetValue(PixelType pixelValue);
     PixelType GetValue() const;
 
     void SetLayer(unsigned int layer);
     unsigned int GetLayer() const;
 
     void SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override;
 
     using itk::Object::Modified;
     void Modified() { Superclass::Modified(); }
     Label();
+    Label(PixelType value, const std::string& name);
     ~Label() override;
 
   protected:
     void PrintSelf(std::ostream &os, itk::Indent indent) const override;
 
     Label(const Label &other);
 
   private:
     itk::LightObject::Pointer InternalClone() const override;
   };
 
   /**
   * @brief Equal A function comparing two labels for beeing equal in data
   *
   * @ingroup MITKTestingAPI
   *
   * Following aspects are tested for equality:
   *  - Lebel equality via Equal-PropetyList
   *
   * @param rightHandSide An image to be compared
   * @param leftHandSide An image to be compared
   * @param eps Tolarence for comparison. You can use mitk::eps in most cases.
   * @param verbose Flag indicating if the user wants detailed console output or not.
   * @return true, if all subsequent comparisons are true, false otherwise
   */
   MITKMULTILABEL_EXPORT bool Equal(const mitk::Label &leftHandSide,
                                    const mitk::Label &rightHandSide,
                                    ScalarType eps,
                                    bool verbose);
 
 } // namespace mitk
 
 #endif
diff --git a/Modules/Multilabel/mitkLabelSet.cpp b/Modules/Multilabel/mitkLabelSet.cpp
index 71d5c4908f..990790fe81 100644
--- a/Modules/Multilabel/mitkLabelSet.cpp
+++ b/Modules/Multilabel/mitkLabelSet.cpp
@@ -1,355 +1,384 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "mitkLabelSet.h"
 #include "mitkDICOMSegmentationPropertyHelper.h"
 
 #include <itkCommand.h>
 
 mitk::LabelSet::LabelSet() : m_ActiveLabelValue(0), m_Layer(0)
 {
   m_LookupTable = mitk::LookupTable::New();
   m_LookupTable->SetType(mitk::LookupTable::MULTILABEL);
+  m_ReservedLabelValuesFunctor = nullptr;
 }
 
 mitk::LabelSet::~LabelSet()
 {
   m_LabelContainer.clear();
 }
 
 mitk::LabelSet::LabelSet(const LabelSet &other)
   : itk::Object(),
     m_LookupTable(other.GetLookupTable()->Clone()),
-    m_ActiveLabelValue(other.GetActiveLabel()->GetValue()),
+    m_ActiveLabelValue(other.m_ActiveLabelValue),
     m_Layer(other.GetLayer())
 {
   // clone Labels
   auto otherIt = other.IteratorConstBegin();
   for (; otherIt != other.IteratorConstEnd(); ++otherIt)
   {
     m_LabelContainer[otherIt->first] = otherIt->second->Clone();
 
-    itk::SimpleMemberCommand<LabelSet>::Pointer command = itk::SimpleMemberCommand<LabelSet>::New();
+    auto command = itk::MemberCommand<LabelSet>::New();
     command->SetCallbackFunction(this, &LabelSet::OnLabelModified);
     m_LabelContainer[otherIt->first]->AddObserver(itk::ModifiedEvent(), command);
   }
+  m_ReservedLabelValuesFunctor = other.m_ReservedLabelValuesFunctor;
 }
 
-void mitk::LabelSet::OnLabelModified()
+std::vector<mitk::LabelSet::LabelValueType> mitk::LabelSet::GetUsedLabelValues() const
 {
-  ModifyLabelEvent.Send();
+  std::vector<LabelValueType> result = { 0 };
+
+  if (m_ReservedLabelValuesFunctor != nullptr)
+  {
+    result = m_ReservedLabelValuesFunctor();
+  }
+  else
+  {
+    for (auto [value, label] : this->m_LabelContainer)
+    {
+      result.emplace_back(value);
+    }
+  }
+  return result;
+}
+
+void mitk::LabelSet::OnLabelModified(const Object* sender, const itk::EventObject&)
+{
+  auto label = dynamic_cast<const Label*>(sender);
+  if (nullptr == label)
+    mitkThrow() << "LabelSet is in wrong state. LabelModified event is not send by a label instance.";
+
+  ModifyLabelEvent.Send(label->GetValue());
   Superclass::Modified();
 }
 
 mitk::LabelSet::LabelContainerConstIteratorType mitk::LabelSet::IteratorConstEnd() const
 {
   return m_LabelContainer.end();
 }
 
 mitk::LabelSet::LabelContainerConstIteratorType mitk::LabelSet::IteratorConstBegin() const
 {
   return m_LabelContainer.begin();
 }
 
 mitk::LabelSet::LabelContainerIteratorType mitk::LabelSet::IteratorEnd()
 {
   return m_LabelContainer.end();
 }
 
 mitk::LabelSet::LabelContainerIteratorType mitk::LabelSet::IteratorBegin()
 {
   return m_LabelContainer.begin();
 }
 
 unsigned int mitk::LabelSet::GetNumberOfLabels() const
 {
   return m_LabelContainer.size();
 }
 
 void mitk::LabelSet::SetLayer(unsigned int layer)
 {
   m_Layer = layer;
   Modified();
 }
 
 void mitk::LabelSet::SetActiveLabel(PixelType pixelValue)
 {
   m_ActiveLabelValue = pixelValue;
   ActiveLabelEvent.Send(pixelValue);
   Modified();
 }
 
 bool mitk::LabelSet::ExistLabel(PixelType pixelValue)
 {
   return m_LabelContainer.count(pixelValue) > 0 ? true : false;
 }
 
-// TODO Parameter as Smartpointer
-void mitk::LabelSet::AddLabel(mitk::Label *label)
+mitk::Label* mitk::LabelSet::AddLabel(mitk::Label *label, bool addAsClone)
 {
   unsigned int max_size = mitk::Label::MAX_LABEL_VALUE + 1;
   if (m_LabelContainer.size() >= max_size)
-    return;
+    return nullptr;
 
-  mitk::Label::Pointer newLabel(label->Clone());
+  mitk::Label::Pointer newLabel = addAsClone ? label->Clone() : Label::Pointer(label);
 
   // TODO use layer of label parameter
   newLabel->SetLayer(m_Layer);
 
-  PixelType pixelValue;
-  if (m_LabelContainer.empty())
-  {
-    pixelValue = newLabel->GetValue();
-  }
-  else
-  {
-    pixelValue = m_LabelContainer.rbegin()->first;
+  auto pixelValue = newLabel->GetValue();
+  auto usedValues = this->GetUsedLabelValues();
+  auto finding = std::find(usedValues.begin(), usedValues.end(), pixelValue);
 
-    if (pixelValue >= newLabel->GetValue() && m_LabelContainer.find(newLabel->GetValue()) != m_LabelContainer.end())
-    {
-      ++pixelValue;
-      newLabel->SetValue(pixelValue);
-    }
-    else
-    {
-      pixelValue = newLabel->GetValue();
-    }
+  if (!usedValues.empty() && usedValues.end() != finding)
+  {
+    pixelValue = usedValues.back()+1;
+    newLabel->SetValue(pixelValue);
   }
 
   // new map entry
   m_LabelContainer[pixelValue] = newLabel;
   UpdateLookupTable(pixelValue);
 
   // add DICOM information of the label
   DICOMSegmentationPropertyHelper::SetDICOMSegmentProperties(newLabel);
 
-  itk::SimpleMemberCommand<LabelSet>::Pointer command = itk::SimpleMemberCommand<LabelSet>::New();
+  auto command = itk::MemberCommand<LabelSet>::New();
   command->SetCallbackFunction(this, &LabelSet::OnLabelModified);
   newLabel->AddObserver(itk::ModifiedEvent(), command);
-  // newLabel->AddObserver(itk::ModifiedEvent(),command);
 
+  AddLabelEvent.Send(newLabel->GetValue());
   SetActiveLabel(newLabel->GetValue());
-  AddLabelEvent.Send();
   Modified();
+
+  return newLabel;
 }
 
-void mitk::LabelSet::AddLabel(const std::string &name, const mitk::Color &color)
+mitk::Label* mitk::LabelSet::AddLabel(const std::string &name, const mitk::Color &color)
 {
   mitk::Label::Pointer newLabel = mitk::Label::New();
   newLabel->SetName(name);
   newLabel->SetColor(color);
-  AddLabel(newLabel);
+  return AddLabel(newLabel);
 }
 
 void mitk::LabelSet::RenameLabel(PixelType pixelValue, const std::string &name, const mitk::Color &color)
 {
   mitk::Label *label = GetLabel(pixelValue);
   label->SetName(name);
   label->SetColor(color);
 
   // change DICOM information of the label
   DICOMSegmentationPropertyHelper::SetDICOMSegmentProperties(label);
 }
 
 void mitk::LabelSet::SetLookupTable(mitk::LookupTable *lut)
 {
   m_LookupTable = lut;
   Modified();
 }
 
 void mitk::LabelSet::PrintSelf(std::ostream & /*os*/, itk::Indent /*indent*/) const
 {
 }
 
 void mitk::LabelSet::RemoveLabel(PixelType pixelValue)
 {
+  if (LabelSetImage::UnlabeledValue == pixelValue)
+    return;
+
   auto it = m_LabelContainer.rbegin();
   PixelType nextActivePixelValue = it->first;
 
   for (; it != m_LabelContainer.rend(); ++it)
   {
     if (it->first == pixelValue)
     {
       it->second->RemoveAllObservers();
       m_LabelContainer.erase(pixelValue);
       break;
     }
     nextActivePixelValue = it->first;
   }
 
   if (m_ActiveLabelValue == pixelValue)
   {
     if (ExistLabel(nextActivePixelValue))
-      SetActiveLabel(nextActivePixelValue);
+    {
+      this->SetActiveLabel(nextActivePixelValue);
+    }
+    else if (!m_LabelContainer.empty())
+    {
+      this->SetActiveLabel(m_LabelContainer.rbegin()->first);
+    }
     else
-      SetActiveLabel(m_LabelContainer.rbegin()->first);
+    {
+      this->SetActiveLabel(0);
+    }
   }
 
-  RemoveLabelEvent.Send();
+  RemoveLabelEvent.Send(pixelValue);
 
   Modified();
 }
 
 void mitk::LabelSet::RemoveAllLabels()
 {
   auto _it = IteratorBegin();
   for (; _it != IteratorConstEnd();)
   {
-    RemoveLabelEvent.Send();
+    auto labelValue = _it->first;
     m_LabelContainer.erase(_it++);
+    RemoveLabelEvent.Send(labelValue);
   }
   AllLabelsModifiedEvent.Send();
 }
 
 void mitk::LabelSet::SetNextActiveLabel()
 {
   auto it = m_LabelContainer.find(m_ActiveLabelValue);
 
   if (it != m_LabelContainer.end())
     ++it;
 
   if (it == m_LabelContainer.end())
   {
     it = m_LabelContainer.begin();
     if (m_LabelContainer.size() > 1)
       ++it; // ...skip background label!
   }
 
   SetActiveLabel(it->first);
 }
 
 void mitk::LabelSet::SetAllLabelsLocked(bool value)
 {
   auto _end = m_LabelContainer.end();
   auto _it = m_LabelContainer.begin();
   for (; _it != _end; ++_it)
     _it->second->SetLocked(value);
   AllLabelsModifiedEvent.Send();
   Modified();
 }
 
 void mitk::LabelSet::SetAllLabelsVisible(bool value)
 {
   auto _end = m_LabelContainer.end();
   auto _it = m_LabelContainer.begin();
   for (; _it != _end; ++_it)
   {
     _it->second->SetVisible(value);
     UpdateLookupTable(_it->first);
   }
   AllLabelsModifiedEvent.Send();
   Modified();
 }
 
 void mitk::LabelSet::UpdateLookupTable(PixelType pixelValue)
 {
   const mitk::Color &color = GetLabel(pixelValue)->GetColor();
 
   double rgba[4];
   m_LookupTable->GetTableValue(static_cast<int>(pixelValue), rgba);
   rgba[0] = color.GetRed();
   rgba[1] = color.GetGreen();
   rgba[2] = color.GetBlue();
   if (GetLabel(pixelValue)->GetVisible())
     rgba[3] = GetLabel(pixelValue)->GetOpacity();
   else
     rgba[3] = 0.0;
   m_LookupTable->SetTableValue(static_cast<int>(pixelValue), rgba);
 }
 
 mitk::Label *mitk::LabelSet::GetLabel(PixelType pixelValue)
 {
   if (m_LabelContainer.find(pixelValue) == m_LabelContainer.end())
     return nullptr;
   return m_LabelContainer[pixelValue];
 }
 
 const mitk::Label *mitk::LabelSet::GetLabel(PixelType pixelValue) const
 {
   auto it = m_LabelContainer.find(pixelValue);
   if (it == m_LabelContainer.end())
     return nullptr;
   return it->second.GetPointer();
 }
 
 bool mitk::Equal(const mitk::LabelSet &leftHandSide, const mitk::LabelSet &rightHandSide, ScalarType eps, bool verbose)
 {
   bool returnValue = true;
   // LabelSetmembers
 
   MITK_INFO(verbose) << "--- LabelSet Equal ---";
 
   // m_LookupTable;
   const mitk::LookupTable *lhsLUT = leftHandSide.GetLookupTable();
   const mitk::LookupTable *rhsLUT = rightHandSide.GetLookupTable();
 
   returnValue = *lhsLUT == *rhsLUT;
   if (!returnValue)
   {
     MITK_INFO(verbose) << "Lookup tabels not equal.";
     return returnValue;
     ;
   }
 
   // m_ActiveLabel;
-  returnValue = mitk::Equal(*leftHandSide.GetActiveLabel(), *rightHandSide.GetActiveLabel(), eps, verbose);
-  if (!returnValue)
+  if (leftHandSide.GetActiveLabel() != rightHandSide.GetActiveLabel())
   {
-    MITK_INFO(verbose) << "Active label not equal.";
-    return returnValue;
-    ;
+    returnValue = mitk::Equal(*leftHandSide.GetActiveLabel(), *rightHandSide.GetActiveLabel(), eps, verbose);
+    if (!returnValue)
+    {
+      MITK_INFO(verbose) << "Active label not equal.";
+      return returnValue;
+      ;
+    }
   }
 
   // m_Layer;
   returnValue = leftHandSide.GetLayer() == rightHandSide.GetLayer();
   if (!returnValue)
   {
     MITK_INFO(verbose) << "Layer index not equal.";
     return returnValue;
     ;
   }
 
   // container size;
   returnValue = leftHandSide.GetNumberOfLabels() == rightHandSide.GetNumberOfLabels();
   if (!returnValue)
   {
     MITK_INFO(verbose) << "Number of labels not equal.";
     return returnValue;
     ;
   }
 
   // Label container (map)
 
   // m_LabelContainer;
   auto lhsit = leftHandSide.IteratorConstBegin();
   auto rhsit = rightHandSide.IteratorConstBegin();
   for (; lhsit != leftHandSide.IteratorConstEnd(); ++lhsit, ++rhsit)
   {
     returnValue = rhsit->first == lhsit->first;
     if (!returnValue)
     {
       MITK_INFO(verbose) << "Label in label container not equal.";
       return returnValue;
       ;
     }
 
     returnValue = mitk::Equal(*(rhsit->second), *(lhsit->second), eps, verbose);
     if (!returnValue)
     {
       MITK_INFO(verbose) << "Label in label container not equal.";
       return returnValue;
       ;
     }
   }
 
   return returnValue;
 }
diff --git a/Modules/Multilabel/mitkLabelSet.h b/Modules/Multilabel/mitkLabelSet.h
index 5eb54c1b58..14a95e93a4 100644
--- a/Modules/Multilabel/mitkLabelSet.h
+++ b/Modules/Multilabel/mitkLabelSet.h
@@ -1,239 +1,263 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #ifndef mitkLabelSet_h
 #define mitkLabelSet_h
 
 #include "MitkMultilabelExports.h"
 #include <mitkLookupTable.h>
 #include <mitkMessage.h>
 
 #include <itkObject.h>
 #include <itkObjectFactory.h>
+#include <itkEventObject.h>
 
 #include <mitkLabel.h>
 
 namespace mitk
 {
   //
   // Documentation
   // @brief LabelSet containing the labels corresponding to a segmentation session.
   // @ingroup Data
   //
 
   class MITKMULTILABEL_EXPORT LabelSet : public itk::Object
   {
   public:
     mitkClassMacroItkParent(LabelSet, itk::Object);
     itkNewMacro(Self);
 
     typedef mitk::Label::PixelType PixelType;
 
-    typedef std::map<PixelType, Label::Pointer> LabelContainerType;
+    using LabelValueType = mitk::Label::PixelType;
+    typedef std::map<LabelValueType, Label::Pointer> LabelContainerType;
     typedef LabelContainerType::const_iterator LabelContainerConstIteratorType;
     typedef LabelContainerType::iterator LabelContainerIteratorType;
 
     /**
     * \brief AddLabelEvent is emitted whenever a new label has been added to the LabelSet.
     *
+    * The registered method will be called with the label value of the added label.
     * Observers should register to this event by calling myLabelSet->AddLabelEvent.AddListener(myObject,
     * MyObject::MyMethod).
     * After registering, myObject->MyMethod() will be called every time a new label has been added to the LabelSet.
     * Observers should unregister by calling myLabelSet->AddLabelEvent.RemoveListener(myObject, MyObject::MyMethod).
     *
     * member variable is not needed to be locked in multi-threaded scenarios since the LabelSetEvent is a typedef for
     * a Message1 object which is thread safe
     */
-    Message<> AddLabelEvent;
+    Message1<LabelValueType> AddLabelEvent;
 
     /**
     * \brief RemoveLabelEvent is emitted whenever a new label has been removed from the LabelSet.
     *
+    * The registered method will be called with the label value of the removed label.
     * Observers should register to this event by calling myLabelSet->RemoveLabelEvent.AddListener(myObject,
     * MyObject::MyMethod).
     * After registering, myObject->MyMethod() will be called every time a new label has been removed from the LabelSet.
     * Observers should unregister by calling myLabelSet->RemoveLabelEvent.RemoveListener(myObject, MyObject::MyMethod).
     *
     * member variable is not needed to be locked in multi-threaded scenarios since the LabelSetEvent is a typedef for
     * a Message object which is thread safe
     */
-    Message<> RemoveLabelEvent;
+    Message1<LabelValueType> RemoveLabelEvent;
 
     /**
     * \brief ModifyLabelEvent is emitted whenever a label has been modified from the LabelSet.
     *
+    * The registered method will be called with the label value of the modified label.
     * Observers should register to this event by calling myLabelSet->ModifyLabelEvent.AddListener(myObject,
     * MyObject::MyMethod).
     * After registering, myObject->MyMethod() will be called every time a new label has been removed from the LabelSet.
     * Observers should unregister by calling myLabelSet->ModifyLabelEvent.RemoveListener(myObject, MyObject::MyMethod).
     *
     * member variable is not needed to be locked in multi-threaded scenarios since the LabelSetEvent is a typedef for
     * a Message object which is thread safe
     */
-    Message<> ModifyLabelEvent;
+    Message1<LabelValueType> ModifyLabelEvent;
 
     /**
     * \brief ActiveLabelEvent is emitted whenever a label has been set as active in the LabelSet.
     */
     Message1<PixelType> ActiveLabelEvent;
 
     /**
     * \brief AllLabelsModifiedEvent is emitted whenever a new label has been removed from the LabelSet.
     *
     * Observers should register to this event by calling myLabelSet->AllLabelsModifiedEvent.AddListener(myObject,
     * MyObject::MyMethod).
     * After registering, myObject->MyMethod() will be called every time a new label has been removed from the LabelSet.
     * Observers should unregister by calling myLabelSet->AllLabelsModifiedEvent.RemoveListener(myObject,
     * MyObject::MyMethod).
     *
     * member variable is not needed to be locked in multi-threaded scenarios since the LabelSetEvent is a typedef for
     * a Message object which is thread safe
     */
     Message<> AllLabelsModifiedEvent;
 
     /** \brief Returns a const iterator poiting to the begining of the container.
     */
     LabelContainerConstIteratorType IteratorConstBegin() const;
 
     /** \brief Returns a const iterator pointing to the end of the container.
     */
     LabelContainerConstIteratorType IteratorConstEnd() const;
 
     /** \brief Returns a iterator poiting to the begining of the container.
     */
     LabelContainerIteratorType IteratorBegin();
 
     /** \brief Returns a iterator pointing to the end of the container.
     */
     LabelContainerIteratorType IteratorEnd();
 
     /** \brief
      * Recall itk::Object::Modified event from a label and send a ModifyLabelEvent
     */
-    void OnLabelModified();
+    void OnLabelModified(const Object*, const itk::EventObject&);
 
     /** \brief
     */
     void SetLayer(unsigned int);
 
     /** \brief
     */
     void SetActiveLabel(PixelType);
 
     /** \brief
     */
     void RemoveLabel(PixelType);
 
     /** \brief
     */
     bool ExistLabel(PixelType);
 
-    /** \brief
+    /** \brief Adds a label to the label set.
+    * @remark If the pixel value of the label is already used in the label set, the label
+    * will get a new none conflicting value assigned.
+    * @param label Instance of an label that should be added or used as template
+    * @param addAsClone flag that control if the passed instance should be added or
+    * a clone of the instance.
+    * @return Instance of the label as it was added to the label set.
     */
-    void AddLabel(mitk::Label *label);
+    mitk::Label* AddLabel(mitk::Label *label, bool addAsClone = true);
 
     /** \brief
     */
-    void AddLabel(const std::string &name, const Color &color);
+    mitk::Label* AddLabel(const std::string &name, const Color &color);
 
     /** \brief
     */
     void RenameLabel(PixelType, const std::string &, const Color &);
 
     /** \brief
     */
     unsigned int GetNumberOfLabels() const;
 
     /** \brief
     */
     void SetAllLabelsVisible(bool);
 
     /** \brief
     */
     void SetAllLabelsLocked(bool);
 
     /** \brief
     */
     void RemoveAllLabels();
 
     void SetNextActiveLabel();
 
     /** \brief
     */
     Label *GetActiveLabel() { return GetLabel(m_ActiveLabelValue); }
     /** \brief
     */
     const Label *GetActiveLabel() const { return GetLabel(m_ActiveLabelValue); }
     /** \brief
     */
     Label *GetLabel(PixelType pixelValue);
 
     /** \brief
     */
     const Label *GetLabel(PixelType pixelValue) const;
 
     itkGetMacro(Layer, int);
 
     itkGetConstMacro(Layer, int);
 
     itkGetModifiableObjectMacro(LookupTable, mitk::LookupTable);
 
       /** \brief
       */
       void SetLookupTable(LookupTable *lut);
 
     /** \brief
     */
     void UpdateLookupTable(PixelType pixelValue);
 
+    using ReservedLabelValuesFunctor = std::function<std::vector<LabelValueType>()>;
+    ReservedLabelValuesFunctor m_ReservedLabelValuesFunctor;
+
+    std::vector<LabelValueType> GetUsedLabelValues() const;
+
   protected:
     LabelSet();
     LabelSet(const LabelSet &);
 
     mitkCloneMacro(Self);
 
-      ~LabelSet() override;
+    ~LabelSet() override;
 
     void PrintSelf(std::ostream &os, itk::Indent indent) const override;
 
     LabelContainerType m_LabelContainer;
 
     LookupTable::Pointer m_LookupTable;
 
     PixelType m_ActiveLabelValue;
 
     unsigned int m_Layer;
   };
 
   /**
   * @brief Equal A function comparing two label sets  for beeing equal in data
   *
   * @ingroup MITKTestingAPI
   *
   * Following aspects are tested for equality:
   *  - LabelSetmembers
   *  - Label container (map)
   *
   * @param rightHandSide An image to be compared
   * @param leftHandSide An image to be compared
   * @param eps Tolarence for comparison. You can use mitk::eps in most cases.
   * @param verbose Flag indicating if the user wants detailed console output or not.
   * @return true, if all subsequent comparisons are true, false otherwise
   */
   MITKMULTILABEL_EXPORT bool Equal(const mitk::LabelSet &leftHandSide,
                                    const mitk::LabelSet &rightHandSide,
                                    ScalarType eps,
                                    bool verbose);
 
+  /**
+  * Method takes a label set and generates a new label set with the same labels but updated labels values according to
+  * the passed labelMapping.
+  * @pre sourceLabelSet is valid
+  */
+  MITKMULTILABEL_EXPORT LabelSet::Pointer GenerateLabelSetWithMappedValues(const LabelSet* sourceLabelSet,
+    std::vector<std::pair<Label::PixelType, Label::PixelType> > labelMapping = { {1,1} });
+
 } // namespace mitk
 
 #endif
diff --git a/Modules/Multilabel/mitkLabelSetImage.cpp b/Modules/Multilabel/mitkLabelSetImage.cpp
index de863b6070..827557b625 100644
--- a/Modules/Multilabel/mitkLabelSetImage.cpp
+++ b/Modules/Multilabel/mitkLabelSetImage.cpp
@@ -1,1153 +1,1553 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "mitkLabelSetImage.h"
 
 #include "mitkImageAccessByItk.h"
 #include "mitkImageCast.h"
 #include "mitkImagePixelReadAccessor.h"
 #include "mitkImagePixelWriteAccessor.h"
 #include "mitkInteractionConst.h"
 #include "mitkLookupTableProperty.h"
 #include "mitkPadImageFilter.h"
 #include "mitkRenderingManager.h"
 #include "mitkDICOMSegmentationPropertyHelper.h"
 #include "mitkDICOMQIPropertyHelper.h"
 
 #include <vtkCell.h>
 #include <vtkTransform.h>
 #include <vtkTransformPolyDataFilter.h>
 
 #include <itkImageRegionIterator.h>
 #include <itkQuadEdgeMesh.h>
 #include <itkTriangleMeshToBinaryImageFilter.h>
 #include <itkLabelGeometryImageFilter.h>
 //#include <itkRelabelComponentImageFilter.h>
 
 #include <itkCommand.h>
 
 #include <itkBinaryFunctorImageFilter.h>
 
 
 template <typename TPixel, unsigned int VDimensions>
 void SetToZero(itk::Image<TPixel, VDimensions> *source)
 {
   source->FillBuffer(0);
 }
 
 template <unsigned int VImageDimension = 3>
 void CreateLabelMaskProcessing(mitk::Image *layerImage, mitk::Image *mask, mitk::LabelSet::PixelType index)
 {
   mitk::ImagePixelReadAccessor<mitk::LabelSet::PixelType, VImageDimension> readAccessor(layerImage);
   mitk::ImagePixelWriteAccessor<mitk::LabelSet::PixelType, VImageDimension> writeAccessor(mask);
 
   std::size_t numberOfPixels = 1;
   for (int dim = 0; dim < static_cast<int>(VImageDimension); ++dim)
     numberOfPixels *= static_cast<std::size_t>(readAccessor.GetDimension(dim));
 
   auto src = readAccessor.GetData();
   auto dest = writeAccessor.GetData();
 
   for (std::size_t i = 0; i < numberOfPixels; ++i)
   {
     if (index == *(src + i))
       *(dest + i) = 1;
   }
 }
 
 mitk::LabelSetImage::LabelSetImage()
-  : mitk::Image(), m_ActiveLayer(0), m_activeLayerInvalid(false), m_ExteriorLabel(nullptr)
-{
-  // Iniitlaize Background Label
-  mitk::Color color;
-  color.Set(0, 0, 0);
-  m_ExteriorLabel = mitk::Label::New();
-  m_ExteriorLabel->SetColor(color);
-  m_ExteriorLabel->SetName("Exterior");
-  m_ExteriorLabel->SetOpacity(0.0);
-  m_ExteriorLabel->SetLocked(false);
-  m_ExteriorLabel->SetValue(0);
-
+  : mitk::Image(), m_UnlabeledLabelLock(false), m_ActiveLayer(0), m_activeLayerInvalid(false)
+{
   // Add some DICOM Tags as properties to segmentation image
   DICOMSegmentationPropertyHelper::DeriveDICOMSegmentationProperties(this);
 }
 
 mitk::LabelSetImage::LabelSetImage(const mitk::LabelSetImage &other)
   : Image(other),
+    m_UnlabeledLabelLock(other.m_UnlabeledLabelLock),
     m_ActiveLayer(other.GetActiveLayer()),
-    m_activeLayerInvalid(false),
-    m_ExteriorLabel(other.GetExteriorLabel()->Clone())
+    m_activeLayerInvalid(false)
 {
   for (unsigned int i = 0; i < other.GetNumberOfLayers(); i++)
   {
     // Clone LabelSet data
     mitk::LabelSet::Pointer lsClone = other.GetLabelSet(i)->Clone();
-    // add modified event listener to LabelSet (listen to LabelSet changes)
-    itk::SimpleMemberCommand<Self>::Pointer command = itk::SimpleMemberCommand<Self>::New();
-    command->SetCallbackFunction(this, &mitk::LabelSetImage::OnLabelSetModified);
-    lsClone->AddObserver(itk::ModifiedEvent(), command);
+
+    this->RegisterLabelSet(lsClone);
+
     m_LabelSetContainer.push_back(lsClone);
 
     // clone layer Image data
     mitk::Image::Pointer liClone = other.GetLayerImage(i)->Clone();
     m_LayerContainer.push_back(liClone);
   }
 
   // Add some DICOM Tags as properties to segmentation image
   DICOMSegmentationPropertyHelper::DeriveDICOMSegmentationProperties(this);
 }
 
 void mitk::LabelSetImage::OnLabelSetModified()
 {
   Superclass::Modified();
 }
 
-void mitk::LabelSetImage::SetExteriorLabel(mitk::Label *label)
-{
-  m_ExteriorLabel = label;
-}
-
-mitk::Label *mitk::LabelSetImage::GetExteriorLabel()
-{
-  return m_ExteriorLabel;
-}
-
-const mitk::Label *mitk::LabelSetImage::GetExteriorLabel() const
-{
-  return m_ExteriorLabel;
-}
-
 void mitk::LabelSetImage::Initialize(const mitk::Image *other)
 {
   mitk::PixelType pixelType(mitk::MakeScalarPixelType<LabelSetImage::PixelType>());
   if (other->GetDimension() == 2)
   {
     const unsigned int dimensions[] = {other->GetDimension(0), other->GetDimension(1), 1};
     Superclass::Initialize(pixelType, 3, dimensions);
   }
   else
   {
     Superclass::Initialize(pixelType, other->GetDimension(), other->GetDimensions());
   }
 
   auto originalGeometry = other->GetTimeGeometry()->Clone();
   this->SetTimeGeometry(originalGeometry);
 
   // initialize image memory to zero
   if (4 == this->GetDimension())
   {
     AccessFixedDimensionByItk(this, SetToZero, 4);
   }
   else
   {
     AccessByItk(this, SetToZero);
   }
 
   // Transfer some general DICOM properties from the source image to derived image (e.g. Patient information,...)
   DICOMQIPropertyHelper::DeriveDICOMSourceProperties(other, this);
 
   // Add a inital LabelSet ans corresponding image data to the stack
   if (this->GetNumberOfLayers() == 0)
   {
     AddLayer();
   }
 }
 
 mitk::LabelSetImage::~LabelSetImage()
 {
+  for (auto ls : m_LabelSetContainer)
+  {
+    this->ReleaseLabelSet(ls);
+  }
   m_LabelSetContainer.clear();
 }
 
 mitk::Image *mitk::LabelSetImage::GetLayerImage(unsigned int layer)
 {
   return m_LayerContainer[layer];
 }
 
 const mitk::Image *mitk::LabelSetImage::GetLayerImage(unsigned int layer) const
 {
   return m_LayerContainer[layer];
 }
 
 unsigned int mitk::LabelSetImage::GetActiveLayer() const
 {
   return m_ActiveLayer;
 }
 
 unsigned int mitk::LabelSetImage::GetNumberOfLayers() const
 {
   return m_LabelSetContainer.size();
 }
 
+void mitk::LabelSetImage::RegisterLabelSet(mitk::LabelSet* ls)
+{
+  // add modified event listener to LabelSet (listen to LabelSet changes)
+  itk::SimpleMemberCommand<Self>::Pointer command = itk::SimpleMemberCommand<Self>::New();
+  command->SetCallbackFunction(this, &mitk::LabelSetImage::OnLabelSetModified);
+  ls->AddObserver(itk::ModifiedEvent(), command);
+
+  ls->AddLabelEvent.AddListener(mitk::MessageDelegate1<LabelSetImage, LabelValueType>(
+    this, &LabelSetImage::OnLabelAdded));
+  ls->ModifyLabelEvent.AddListener(mitk::MessageDelegate1<LabelSetImage, LabelValueType>(
+    this, &LabelSetImage::OnLabelModified));
+  ls->RemoveLabelEvent.AddListener(mitk::MessageDelegate1<LabelSetImage, LabelValueType>(
+    this, &LabelSetImage::OnLabelRemoved));
+
+  ls->m_ReservedLabelValuesFunctor = [this]() {return this->GetUsedLabelValues(); };
+}
+
+void mitk::LabelSetImage::ReleaseLabelSet(mitk::LabelSet* ls)
+{
+  ls->RemoveAllObservers();
+
+  ls->AddLabelEvent.RemoveListener(mitk::MessageDelegate1<LabelSetImage, LabelValueType>(
+    this, &LabelSetImage::OnLabelAdded));
+  ls->ModifyLabelEvent.RemoveListener(mitk::MessageDelegate1<LabelSetImage, LabelValueType>(
+    this, &LabelSetImage::OnLabelModified));
+  ls->RemoveLabelEvent.RemoveListener(mitk::MessageDelegate1<LabelSetImage, LabelValueType>(
+    this, &LabelSetImage::OnLabelRemoved));
+
+  ls->m_ReservedLabelValuesFunctor = nullptr;
+}
+
 void mitk::LabelSetImage::RemoveLayer()
 {
   int layerToDelete = GetActiveLayer();
   // remove all observers from active label set
   GetLabelSet(layerToDelete)->RemoveAllObservers();
 
   // set the active layer to one below, if exists.
   if (layerToDelete != 0)
   {
     SetActiveLayer(layerToDelete - 1);
   }
   else
   {
     // we are deleting layer zero, it should not be copied back into the vector
     m_activeLayerInvalid = true;
   }
 
   // remove labelset and image data
   m_LabelSetContainer.erase(m_LabelSetContainer.begin() + layerToDelete);
   m_LayerContainer.erase(m_LayerContainer.begin() + layerToDelete);
 
   if (layerToDelete == 0)
   {
     this->SetActiveLayer(layerToDelete);
   }
 
+  this->OnGroupRemoved(layerToDelete);
+  this->Modified();
+}
+
+void mitk::LabelSetImage::RemoveGroup(GroupIndexType indexToDelete)
+{
+  const auto activeIndex = GetActiveLayer();
+
+  // remove all observers from active label set
+  GetLabelSet(indexToDelete)->RemoveAllObservers();
+
+  // set the active layer to one below, if exists.
+  if (activeIndex>indexToDelete)
+  {
+    SetActiveLayer(activeIndex - 1);
+  }
+  else if (activeIndex==indexToDelete)
+  {
+    // we are deleting layer zero, it should not be copied back into the vector
+    m_activeLayerInvalid = true;
+  }
+
+  // remove labelset and image data
+  m_LabelSetContainer.erase(m_LabelSetContainer.begin() + indexToDelete);
+  m_LayerContainer.erase(m_LayerContainer.begin() + indexToDelete);
+
+  if (indexToDelete == activeIndex)
+  { //enforces the new active layer to be set and copied
+    auto newActiveIndex = indexToDelete < GetNumberOfLayers() ? indexToDelete : GetNumberOfLayers() - 1;
+    this->SetActiveLayer(newActiveIndex);
+  }
+
+  this->OnGroupRemoved(indexToDelete);
   this->Modified();
 }
 
+mitk::LabelSetImage::LabelValueVectorType mitk::LabelSetImage::GetUsedLabelValues() const
+{
+  LabelValueVectorType result = { UnlabeledValue };
+
+  for (auto [value, label] : m_LabelMap)
+  {
+    result.emplace_back(value);
+  }
+  return result;
+}
+
 unsigned int mitk::LabelSetImage::AddLayer(mitk::LabelSet::Pointer labelSet)
 {
   mitk::Image::Pointer newImage = mitk::Image::New();
   newImage->Initialize(this->GetPixelType(),
                        this->GetDimension(),
                        this->GetDimensions(),
                        this->GetImageDescriptor()->GetNumberOfChannels());
   newImage->SetTimeGeometry(this->GetTimeGeometry()->Clone());
 
   if (newImage->GetDimension() < 4)
   {
     AccessByItk(newImage, SetToZero);
   }
   else
   {
     AccessFixedDimensionByItk(newImage, SetToZero, 4);
   }
 
-  unsigned int newLabelSetId = this->AddLayer(newImage, labelSet);
-
-  return newLabelSetId;
+  return this->AddLayer(newImage, labelSet);
 }
 
 unsigned int mitk::LabelSetImage::AddLayer(mitk::Image::Pointer layerImage, mitk::LabelSet::Pointer labelSet)
 {
   unsigned int newLabelSetId = m_LayerContainer.size();
 
   // Add labelset to layer
   mitk::LabelSet::Pointer ls;
   if (labelSet.IsNotNull())
   {
     ls = labelSet;
   }
   else
   {
     ls = mitk::LabelSet::New();
-    ls->AddLabel(GetExteriorLabel());
-    ls->SetActiveLabel(0 /*Exterior Label*/);
+    ls->SetActiveLabel(UnlabeledValue);
   }
 
   ls->SetLayer(newLabelSetId);
-  // Add exterior Label to label set
-  // mitk::Label::Pointer exteriorLabel = CreateExteriorLabel();
 
   // push a new working image for the new layer
   m_LayerContainer.push_back(layerImage);
 
   // push a new labelset for the new layer
   m_LabelSetContainer.push_back(ls);
 
-  // add modified event listener to LabelSet (listen to LabelSet changes)
-  itk::SimpleMemberCommand<Self>::Pointer command = itk::SimpleMemberCommand<Self>::New();
-  command->SetCallbackFunction(this, &mitk::LabelSetImage::OnLabelSetModified);
-  ls->AddObserver(itk::ModifiedEvent(), command);
+  RegisterLabelSet(ls);
+  this->ReinitMaps();
 
   SetActiveLayer(newLabelSetId);
-  // MITK_INFO << GetActiveLayer();
   this->Modified();
+  this->OnGroupAdded(newLabelSetId);
+
   return newLabelSetId;
 }
 
-void mitk::LabelSetImage::AddLabelSetToLayer(const unsigned int layerIdx, const mitk::LabelSet::Pointer labelSet)
+void mitk::LabelSetImage::AddLabelSetToLayer(const unsigned int layerIdx, const mitk::LabelSet* labelSet)
 {
   if (m_LayerContainer.size() <= layerIdx)
   {
     mitkThrow() << "Trying to add labelSet to non-existing layer.";
   }
 
+  auto clonedLabelSet = labelSet->Clone();
+
+  this->RegisterLabelSet(clonedLabelSet);
+
+  std::vector<GroupIndexType> addedGroups;
+
   if (layerIdx < m_LabelSetContainer.size())
   {
-    m_LabelSetContainer[layerIdx] = labelSet;
+    if (m_LabelSetContainer[layerIdx].IsNotNull())
+    {
+      this->ReleaseLabelSet(m_LabelSetContainer[layerIdx]);
+    }
+
+    m_LabelSetContainer[layerIdx] = clonedLabelSet;
   }
   else
   {
     while (layerIdx >= m_LabelSetContainer.size())
     {
       mitk::LabelSet::Pointer defaultLabelSet = mitk::LabelSet::New();
-      defaultLabelSet->AddLabel(GetExteriorLabel());
-      defaultLabelSet->SetActiveLabel(0 /*Exterior Label*/);
+      defaultLabelSet->SetActiveLabel(UnlabeledValue);
       defaultLabelSet->SetLayer(m_LabelSetContainer.size());
+      this->RegisterLabelSet(defaultLabelSet);
+      this->ReinitMaps();
       m_LabelSetContainer.push_back(defaultLabelSet);
+      addedGroups.emplace_back(m_LabelSetContainer.size() - 1);
     }
-    m_LabelSetContainer.push_back(labelSet);
+    m_LabelSetContainer.push_back(clonedLabelSet);
+    addedGroups.emplace_back(m_LabelSetContainer.size() - 1);
+  }
+
+  this->ReinitMaps();
+
+  for (auto groupID : addedGroups)
+  {
+    this->m_GroupAddedMessage.Send(groupID);
   }
 }
 
 void mitk::LabelSetImage::SetActiveLayer(unsigned int layer)
 {
   try
   {
     if (4 == this->GetDimension())
     {
       if ((layer != GetActiveLayer() || m_activeLayerInvalid) && (layer < this->GetNumberOfLayers()))
       {
         BeforeChangeLayerEvent.Send();
 
         if (m_activeLayerInvalid)
         {
           // We should not write the invalid layer back to the vector
           m_activeLayerInvalid = false;
         }
         else
         {
           AccessFixedDimensionByItk_n(this, ImageToLayerContainerProcessing, 4, (GetActiveLayer()));
         }
         m_ActiveLayer = layer; // only at this place m_ActiveLayer should be manipulated!!! Use Getter and Setter
         AccessFixedDimensionByItk_n(this, LayerContainerToImageProcessing, 4, (GetActiveLayer()));
 
         AfterChangeLayerEvent.Send();
       }
     }
     else
     {
       if ((layer != GetActiveLayer() || m_activeLayerInvalid) && (layer < this->GetNumberOfLayers()))
       {
         BeforeChangeLayerEvent.Send();
 
         if (m_activeLayerInvalid)
         {
           // We should not write the invalid layer back to the vector
           m_activeLayerInvalid = false;
         }
         else
         {
           AccessByItk_1(this, ImageToLayerContainerProcessing, GetActiveLayer());
         }
         m_ActiveLayer = layer; // only at this place m_ActiveLayer should be manipulated!!! Use Getter and Setter
         AccessByItk_1(this, LayerContainerToImageProcessing, GetActiveLayer());
 
         AfterChangeLayerEvent.Send();
       }
     }
   }
   catch (itk::ExceptionObject &e)
   {
     mitkThrow() << e.GetDescription();
   }
   this->Modified();
 }
 
 void mitk::LabelSetImage::ClearBuffer()
 {
   try
   {
     if (this->GetDimension() == 4)
     { //remark: this extra branch was added, because LabelSetImage instances can be
       //dynamic (4D), but AccessByItk by support only supports 2D and 3D.
       //The option to change the CMake default dimensions for AccessByItk was
       //dropped (for details see discussion in T28756)
       AccessFixedDimensionByItk(this, ClearBufferProcessing,4);
     }
     else
     {
       AccessByItk(this, ClearBufferProcessing);
     }
     this->Modified();
   }
   catch (itk::ExceptionObject &e)
   {
     mitkThrow() << e.GetDescription();
   }
 }
 
 bool mitk::LabelSetImage::ExistLabel(PixelType pixelValue) const
 {
   bool exist = false;
   for (unsigned int lidx = 0; lidx < GetNumberOfLayers(); lidx++)
     exist |= m_LabelSetContainer[lidx]->ExistLabel(pixelValue);
   return exist;
 }
 
 bool mitk::LabelSetImage::ExistLabel(PixelType pixelValue, unsigned int layer) const
 {
   bool exist = m_LabelSetContainer[layer]->ExistLabel(pixelValue);
   return exist;
 }
 
 bool mitk::LabelSetImage::ExistLabelSet(unsigned int layer) const
 {
   return layer < m_LabelSetContainer.size();
 }
 
 void mitk::LabelSetImage::MergeLabel(PixelType pixelValue, PixelType sourcePixelValue, unsigned int layer)
 {
   try
   {
     AccessByItk_2(this, MergeLabelProcessing, pixelValue, sourcePixelValue);
   }
   catch (itk::ExceptionObject &e)
   {
     mitkThrow() << e.GetDescription();
   }
   GetLabelSet(layer)->SetActiveLabel(pixelValue);
+  this->m_LabelModifiedMessage.Send(sourcePixelValue);
+  this->m_LabelModifiedMessage.Send(pixelValue);
+  this->m_LabelsChangedMessage.Send({ sourcePixelValue, pixelValue });
   Modified();
 }
 
-void mitk::LabelSetImage::MergeLabels(PixelType pixelValue, std::vector<PixelType>& vectorOfSourcePixelValues, unsigned int layer)
+void mitk::LabelSetImage::MergeLabels(PixelType pixelValue, const std::vector<PixelType>& vectorOfSourcePixelValues, unsigned int layer)
 {
   try
   {
     for (unsigned int idx = 0; idx < vectorOfSourcePixelValues.size(); idx++)
     {
       AccessByItk_2(this, MergeLabelProcessing, pixelValue, vectorOfSourcePixelValues[idx]);
+      this->m_LabelModifiedMessage.Send(vectorOfSourcePixelValues[idx]);
     }
   }
   catch (itk::ExceptionObject &e)
   {
     mitkThrow() << e.GetDescription();
   }
   GetLabelSet(layer)->SetActiveLabel(pixelValue);
+  this->m_LabelModifiedMessage.Send(pixelValue);
+  auto modifiedValues = vectorOfSourcePixelValues;
+  modifiedValues.push_back(pixelValue);
+  this->m_LabelsChangedMessage.Send(modifiedValues);
+
   Modified();
 }
 
-void mitk::LabelSetImage::RemoveLabel(PixelType pixelValue, unsigned int layer)
+void mitk::LabelSetImage::RemoveLabel(LabelValueType pixelValue)
 {
-  this->GetLabelSet(layer)->RemoveLabel(pixelValue);
+  auto groupID = this->GetGroupIndexOfLabel(pixelValue);
+
+  //first erase the pixel content (also triggers a LabelModified event)
   this->EraseLabel(pixelValue);
+  //now remove the label entry itself
+  this->GetLabelSet(groupID)->RemoveLabel(pixelValue);
+  // in the interim version triggered by label set events: this->m_LabelRemovedMessage.Send(pixelValue);
+  this->m_LabelsChangedMessage.Send({ pixelValue });
+  this->m_GroupModifiedMessage.Send(groupID);
 }
 
-void mitk::LabelSetImage::RemoveLabels(std::vector<PixelType>& VectorOfLabelPixelValues, unsigned int layer)
+void mitk::LabelSetImage::RemoveLabels(const std::vector<PixelType>& VectorOfLabelPixelValues)
 {
   for (unsigned int idx = 0; idx < VectorOfLabelPixelValues.size(); idx++)
   {
-    this->RemoveLabel(VectorOfLabelPixelValues[idx], layer);
+    this->RemoveLabel(VectorOfLabelPixelValues[idx]);
+    this->m_LabelsChangedMessage.Send({ VectorOfLabelPixelValues[idx] });
   }
 }
 
 void mitk::LabelSetImage::EraseLabel(PixelType pixelValue)
 {
   try
   {
+    auto groupID = this->GetGroupIndexOfLabel(pixelValue);
+
+    mitk::Image* groupImage = this->GetActiveLayer() != groupID
+       ? this->GetLayerImage(groupID)
+       : this;
+
     if (4 == this->GetDimension())
     {
-      AccessFixedDimensionByItk_1(this, EraseLabelProcessing, 4, pixelValue);
+      AccessFixedDimensionByItk_1(groupImage, EraseLabelProcessing, 4, pixelValue);
     }
     else
     {
-      AccessByItk_1(this, EraseLabelProcessing, pixelValue);
+      AccessByItk_1(groupImage, EraseLabelProcessing, pixelValue);
     }
   }
   catch (const itk::ExceptionObject& e)
   {
     mitkThrow() << e.GetDescription();
   }
+
+  this->m_LabelModifiedMessage.Send(pixelValue);
+  this->m_LabelsChangedMessage.Send({ pixelValue });
   Modified();
 }
 
-void mitk::LabelSetImage::EraseLabels(std::vector<PixelType>& VectorOfLabelPixelValues)
+void mitk::LabelSetImage::EraseLabels(const std::vector<PixelType>& VectorOfLabelPixelValues)
 {
   for (unsigned int idx = 0; idx < VectorOfLabelPixelValues.size(); idx++)
   {
     this->EraseLabel(VectorOfLabelPixelValues[idx]);
   }
 }
 
 mitk::Label *mitk::LabelSetImage::GetActiveLabel(unsigned int layer)
 {
   if (m_LabelSetContainer.size() <= layer)
     return nullptr;
   else
     return m_LabelSetContainer[layer]->GetActiveLabel();
 }
 
 const mitk::Label* mitk::LabelSetImage::GetActiveLabel(unsigned int layer) const
 {
   if (m_LabelSetContainer.size() <= layer)
     return nullptr;
   else
     return m_LabelSetContainer[layer]->GetActiveLabel();
 }
 
 mitk::Label *mitk::LabelSetImage::GetLabel(PixelType pixelValue, unsigned int layer) const
 {
   if (m_LabelSetContainer.size() <= layer)
     return nullptr;
   else
     return m_LabelSetContainer[layer]->GetLabel(pixelValue);
 }
 
 mitk::LabelSet *mitk::LabelSetImage::GetLabelSet(unsigned int layer)
 {
   if (m_LabelSetContainer.size() <= layer)
     return nullptr;
   else
     return m_LabelSetContainer[layer].GetPointer();
 }
 
 const mitk::LabelSet *mitk::LabelSetImage::GetLabelSet(unsigned int layer) const
 {
   if (m_LabelSetContainer.size() <= layer)
     return nullptr;
   else
     return m_LabelSetContainer[layer].GetPointer();
 }
 
 mitk::LabelSet *mitk::LabelSetImage::GetActiveLabelSet()
 {
   if (m_LabelSetContainer.size() == 0)
     return nullptr;
   else
     return m_LabelSetContainer[GetActiveLayer()].GetPointer();
 }
 
 const mitk::LabelSet* mitk::LabelSetImage::GetActiveLabelSet() const
 {
   if (m_LabelSetContainer.size() == 0)
     return nullptr;
   else
     return m_LabelSetContainer[GetActiveLayer()].GetPointer();
 }
 
+void mitk::LabelSetImage::UpdateCenterOfMass(PixelType pixelValue)
+{
+  this->UpdateCenterOfMass(pixelValue, this->GetGroupIndexOfLabel(pixelValue));
+}
+
 void mitk::LabelSetImage::UpdateCenterOfMass(PixelType pixelValue, unsigned int layer)
 {
   if (4 == this->GetDimension())
   {
     AccessFixedDimensionByItk_2(this, CalculateCenterOfMassProcessing, 4, pixelValue, layer);
   }
   else
   {
     AccessByItk_2(this, CalculateCenterOfMassProcessing, pixelValue, layer);
   }
 }
 
 unsigned int mitk::LabelSetImage::GetNumberOfLabels(unsigned int layer) const
 {
   return m_LabelSetContainer[layer]->GetNumberOfLabels();
 }
 
 unsigned int mitk::LabelSetImage::GetTotalNumberOfLabels() const
 {
   unsigned int totalLabels(0);
   auto layerIter = m_LabelSetContainer.begin();
   for (; layerIter != m_LabelSetContainer.end(); ++layerIter)
     totalLabels += (*layerIter)->GetNumberOfLabels();
   return totalLabels;
 }
 
 void mitk::LabelSetImage::MaskStamp(mitk::Image *mask, bool forceOverwrite)
 {
   try
   {
     mitk::PadImageFilter::Pointer padImageFilter = mitk::PadImageFilter::New();
     padImageFilter->SetInput(0, mask);
     padImageFilter->SetInput(1, this);
     padImageFilter->SetPadConstant(0);
     padImageFilter->SetBinaryFilter(false);
     padImageFilter->SetLowerThreshold(0);
     padImageFilter->SetUpperThreshold(1);
 
     padImageFilter->Update();
 
     mitk::Image::Pointer paddedMask = padImageFilter->GetOutput();
 
     if (paddedMask.IsNull())
       return;
 
     AccessByItk_2(this, MaskStampProcessing, paddedMask, forceOverwrite);
   }
   catch (...)
   {
     mitkThrow() << "Could not stamp the provided mask on the selected label.";
   }
 }
 
 mitk::Image::Pointer mitk::LabelSetImage::CreateLabelMask(PixelType index, bool useActiveLayer, unsigned int layer)
 {
   auto previousActiveLayer = this->GetActiveLayer();
   auto mask = mitk::Image::New();
 
   try
   {
     // mask->Initialize(this) does not work here if this label set image has a single slice,
     // since the mask would be automatically flattened to a 2-d image, whereas we expect the
     // original dimension of this label set image. Hence, initialize the mask more explicitly:
     mask->Initialize(this->GetPixelType(), this->GetDimension(), this->GetDimensions());
     mask->SetTimeGeometry(this->GetTimeGeometry()->Clone());
 
     auto byteSize = sizeof(LabelSetImage::PixelType);
     for (unsigned int dim = 0; dim < mask->GetDimension(); ++dim)
       byteSize *= mask->GetDimension(dim);
 
     {
       ImageWriteAccessor accessor(mask);
       memset(accessor.GetData(), 0, byteSize);
     }
 
     if (!useActiveLayer)
       this->SetActiveLayer(layer);
 
     if (4 == this->GetDimension())
     {
       ::CreateLabelMaskProcessing<4>(this, mask, index);
     }
     else if (3 == this->GetDimension())
     {
       ::CreateLabelMaskProcessing(this, mask, index);
     }
     else
     {
       mitkThrow();
     }
   }
   catch (...)
   {
     if (!useActiveLayer)
       this->SetActiveLayer(previousActiveLayer);
 
     mitkThrow() << "Could not create a mask out of the selected label.";
   }
 
   if (!useActiveLayer)
     this->SetActiveLayer(previousActiveLayer);
 
   return mask;
 }
 
 void mitk::LabelSetImage::InitializeByLabeledImage(mitk::Image::Pointer image)
 {
   if (image.IsNull() || image->IsEmpty() || !image->IsInitialized())
     mitkThrow() << "Invalid labeled image.";
 
   try
   {
     this->Initialize(image);
 
     unsigned int byteSize = sizeof(LabelSetImage::PixelType);
     for (unsigned int dim = 0; dim < image->GetDimension(); ++dim)
     {
       byteSize *= image->GetDimension(dim);
     }
 
     mitk::ImageWriteAccessor *accessor = new mitk::ImageWriteAccessor(static_cast<mitk::Image *>(this));
     memset(accessor->GetData(), 0, byteSize);
     delete accessor;
 
     auto geometry = image->GetTimeGeometry()->Clone();
     this->SetTimeGeometry(geometry);
 
     if (image->GetDimension() == 3)
     {
       AccessTwoImagesFixedDimensionByItk(this, image, InitializeByLabeledImageProcessing, 3);
     }
     else if (image->GetDimension() == 4)
     {
       AccessTwoImagesFixedDimensionByItk(this, image, InitializeByLabeledImageProcessing, 4);
     }
     else
     {
       mitkThrow() << image->GetDimension() << "-dimensional label set images not yet supported";
     }
   }
   catch (...)
   {
     mitkThrow() << "Could not intialize by provided labeled image.";
   }
   this->Modified();
 }
 
 template <typename LabelSetImageType, typename ImageType>
 void mitk::LabelSetImage::InitializeByLabeledImageProcessing(LabelSetImageType *labelSetImage, ImageType *image)
 {
   typedef itk::ImageRegionConstIteratorWithIndex<ImageType> SourceIteratorType;
   typedef itk::ImageRegionIterator<LabelSetImageType> TargetIteratorType;
 
   TargetIteratorType targetIter(labelSetImage, labelSetImage->GetRequestedRegion());
   targetIter.GoToBegin();
 
   SourceIteratorType sourceIter(image, image->GetRequestedRegion());
   sourceIter.GoToBegin();
 
   while (!sourceIter.IsAtEnd())
   {
     auto sourceValue = static_cast<PixelType>(sourceIter.Get());
     targetIter.Set(sourceValue);
 
-    if (!this->ExistLabel(sourceValue))
+    if (LabelSetImage::UnlabeledValue!=sourceValue && !this->ExistLabel(sourceValue))
     {
       std::stringstream name;
       name << "object-" << sourceValue;
 
       double rgba[4];
       m_LabelSetContainer[this->GetActiveLayer()]->GetLookupTable()->GetTableValue(sourceValue, rgba);
 
       mitk::Color color;
       color.SetRed(rgba[0]);
       color.SetGreen(rgba[1]);
       color.SetBlue(rgba[2]);
 
       auto label = mitk::Label::New();
       label->SetName(name.str().c_str());
       label->SetColor(color);
       label->SetOpacity(rgba[3]);
       label->SetValue(sourceValue);
 
       this->GetLabelSet()->AddLabel(label);
 
       if (GetActiveLabelSet()->GetNumberOfLabels() >= mitk::Label::MAX_LABEL_VALUE ||
           sourceValue >= mitk::Label::MAX_LABEL_VALUE)
         this->AddLayer();
     }
 
     ++sourceIter;
     ++targetIter;
   }
 }
 
 template <typename ImageType>
 void mitk::LabelSetImage::MaskStampProcessing(ImageType *itkImage, mitk::Image *mask, bool forceOverwrite)
 {
   typename ImageType::Pointer itkMask;
   mitk::CastToItkImage(mask, itkMask);
 
   typedef itk::ImageRegionConstIterator<ImageType> SourceIteratorType;
   typedef itk::ImageRegionIterator<ImageType> TargetIteratorType;
 
   SourceIteratorType sourceIter(itkMask, itkMask->GetLargestPossibleRegion());
   sourceIter.GoToBegin();
 
   TargetIteratorType targetIter(itkImage, itkImage->GetLargestPossibleRegion());
   targetIter.GoToBegin();
 
   int activeLabel = this->GetActiveLabel(GetActiveLayer())->GetValue();
 
   while (!sourceIter.IsAtEnd())
   {
     PixelType sourceValue = sourceIter.Get();
     PixelType targetValue = targetIter.Get();
 
-    if ((sourceValue != 0) &&
-        (forceOverwrite || !this->GetLabel(targetValue)->GetLocked())) // skip exterior and locked labels
+    if ((sourceValue != UnlabeledValue) &&
+        (forceOverwrite || !this->IsLabelLocked(targetValue))) // skip unlabeled pixels and locked labels
     {
       targetIter.Set(activeLabel);
     }
     ++sourceIter;
     ++targetIter;
   }
 
   this->Modified();
 }
 
 template <typename ImageType>
 void mitk::LabelSetImage::CalculateCenterOfMassProcessing(ImageType *itkImage, PixelType pixelValue, unsigned int layer)
 {
   if (ImageType::GetImageDimension() != 3)
   {
     return;
   }
 
   auto labelGeometryFilter = itk::LabelGeometryImageFilter<ImageType>::New();
   labelGeometryFilter->SetInput(itkImage);
   labelGeometryFilter->Update();
   auto centroid = labelGeometryFilter->GetCentroid(pixelValue);
 
   mitk::Point3D pos;
   pos[0] = centroid[0];
   pos[1] = centroid[1];
   pos[2] = centroid[2];
 
   GetLabelSet(layer)->GetLabel(pixelValue)->SetCenterOfMassIndex(pos);
   this->GetSlicedGeometry()->IndexToWorld(pos, pos); // TODO: TimeGeometry?
   GetLabelSet(layer)->GetLabel(pixelValue)->SetCenterOfMassCoordinates(pos);
 }
 
 template <typename ImageType>
 void mitk::LabelSetImage::ClearBufferProcessing(ImageType *itkImage)
 {
   itkImage->FillBuffer(0);
 }
 
 template <typename TPixel, unsigned int VImageDimension>
 void mitk::LabelSetImage::LayerContainerToImageProcessing(itk::Image<TPixel, VImageDimension> *target,
                                                           unsigned int layer)
 {
   typedef itk::Image<TPixel, VImageDimension> ImageType;
   typename ImageType::Pointer itkSource;
   // mitk::CastToItkImage(m_LayerContainer[layer], itkSource);
   itkSource = ImageToItkImage<TPixel, VImageDimension>(m_LayerContainer[layer]);
   typedef itk::ImageRegionConstIterator<ImageType> SourceIteratorType;
   typedef itk::ImageRegionIterator<ImageType> TargetIteratorType;
 
   SourceIteratorType sourceIter(itkSource, itkSource->GetLargestPossibleRegion());
   sourceIter.GoToBegin();
 
   TargetIteratorType targetIter(target, target->GetLargestPossibleRegion());
   targetIter.GoToBegin();
 
   while (!sourceIter.IsAtEnd())
   {
     targetIter.Set(sourceIter.Get());
     ++sourceIter;
     ++targetIter;
   }
 }
 
 template <typename TPixel, unsigned int VImageDimension>
 void mitk::LabelSetImage::ImageToLayerContainerProcessing(itk::Image<TPixel, VImageDimension> *source,
                                                           unsigned int layer) const
 {
   typedef itk::Image<TPixel, VImageDimension> ImageType;
   typename ImageType::Pointer itkTarget;
   // mitk::CastToItkImage(m_LayerContainer[layer], itkTarget);
   itkTarget = ImageToItkImage<TPixel, VImageDimension>(m_LayerContainer[layer]);
 
   typedef itk::ImageRegionConstIterator<ImageType> SourceIteratorType;
   typedef itk::ImageRegionIterator<ImageType> TargetIteratorType;
 
   SourceIteratorType sourceIter(source, source->GetLargestPossibleRegion());
   sourceIter.GoToBegin();
 
   TargetIteratorType targetIter(itkTarget, itkTarget->GetLargestPossibleRegion());
   targetIter.GoToBegin();
 
   while (!sourceIter.IsAtEnd())
   {
     targetIter.Set(sourceIter.Get());
     ++sourceIter;
     ++targetIter;
   }
 }
 
 template <typename ImageType>
 void mitk::LabelSetImage::EraseLabelProcessing(ImageType *itkImage, PixelType pixelValue)
 {
   typedef itk::ImageRegionIterator<ImageType> IteratorType;
 
   IteratorType iter(itkImage, itkImage->GetLargestPossibleRegion());
   iter.GoToBegin();
 
   while (!iter.IsAtEnd())
   {
     PixelType value = iter.Get();
 
     if (value == pixelValue)
     {
       iter.Set(0);
     }
     ++iter;
   }
 }
 
 template <typename ImageType>
 void mitk::LabelSetImage::MergeLabelProcessing(ImageType *itkImage, PixelType pixelValue, PixelType index)
 {
   typedef itk::ImageRegionIterator<ImageType> IteratorType;
 
   IteratorType iter(itkImage, itkImage->GetLargestPossibleRegion());
   iter.GoToBegin();
 
   while (!iter.IsAtEnd())
   {
     if (iter.Get() == index)
     {
       iter.Set(pixelValue);
     }
     ++iter;
   }
 }
 
+
+void mitk::LabelSetImage::OnLabelAdded(LabelValueType labelValue)
+{
+  Label* label = nullptr;
+
+  unsigned int layerID = 0;
+  for (; layerID < this->GetNumberOfLayers(); ++layerID)
+  {
+    label = this->GetLabel(labelValue, layerID);
+    if (nullptr != label) break;
+  }
+
+  if (!label) mitkThrow() << "Wrong internal state. OnLabelAdded was triggered, but label cannot be found. Invalid label: " << labelValue;
+
+  AddLabelToMap(labelValue, label, layerID);
+
+  this->m_LabelAddedMessage.Send(labelValue);
+}
+
+void mitk::LabelSetImage::AddLabelToMap(LabelValueType labelValue, mitk::Label* label, GroupIndexType groupID)
+{
+  if (m_LabelMap.find(labelValue)!=m_LabelMap.end())
+    mitkThrow() << "Segmentation is in an invalid state: Label value collision. A label was added with a LabelValue already in use. LabelValue: " << labelValue;
+
+  m_LabelMap[labelValue] = label;
+  m_LabelToGroupMap[labelValue] = groupID;
+  auto groupFinding = m_GroupToLabelMap.find(groupID);
+  if (groupFinding == m_GroupToLabelMap.end())
+  {
+    m_GroupToLabelMap[groupID] = { labelValue };
+  }
+  else
+  {
+    m_GroupToLabelMap[groupID].push_back(labelValue);
+  }
+}
+
+void mitk::LabelSetImage::OnLabelModified(LabelValueType labelValue)
+{
+  this->m_LabelModifiedMessage.Send(labelValue);
+}
+
+void mitk::LabelSetImage::OnLabelRemoved(LabelValueType labelValue)
+{
+  m_LabelMap.erase(labelValue);
+  auto finding = m_LabelToGroupMap.find(labelValue);
+  if (finding != m_LabelToGroupMap.end())
+  {
+    auto labelsInGroup = m_GroupToLabelMap[finding->second];
+    auto labelFinding = std::find(labelsInGroup.begin(), labelsInGroup.end(),finding->second);
+    if (labelFinding != labelsInGroup.end())
+    {
+      labelsInGroup.erase(labelFinding);
+    }
+    m_LabelToGroupMap.erase(labelValue);
+  }
+
+  this->m_LabelRemovedMessage.Send(labelValue);
+}
+
+void mitk::LabelSetImage::OnGroupAdded(GroupIndexType groupIndex)
+{
+  this->m_GroupToLabelMap.insert(std::make_pair(groupIndex, LabelValueVectorType()));
+
+  this->m_GroupAddedMessage.Send(groupIndex);
+}
+
+void mitk::LabelSetImage::OnGroupModified(GroupIndexType groupIndex)
+{
+  this->m_GroupModifiedMessage.Send(groupIndex);
+}
+
+void mitk::LabelSetImage::OnGroupRemoved(GroupIndexType groupIndex)
+{
+  this->ReinitMaps();
+  this->m_GroupRemovedMessage.Send(groupIndex);
+}
+
+// future implementation for T28524
+//bool mitk::LabelSetImage::ExistLabel(LabelValueType value, GroupIndexType groupIndex) const
+//{
+//  auto finding = m_LabelToGroupMap.find(value);
+//  if (m_LabelToGroupMap.end() != finding)
+//  {
+//    return finding->second == groupIndex;
+//  }
+//  return false;
+//}
+//
+//bool mitk::LabelSetImage::ExistGroup(GroupIndexType index) const
+//{
+//  return index < m_LabelSetContainer.size();
+//}
+
+bool mitk::LabelSetImage::ExistGroup(GroupIndexType index) const
+{
+  return index < m_LabelSetContainer.size();
+}
+
+bool mitk::LabelSetImage::IsLabeInGroup(LabelValueType value) const
+{
+  GroupIndexType dummy;
+  return this->IsLabeInGroup(value, dummy);
+}
+
+bool mitk::LabelSetImage::IsLabeInGroup(LabelValueType value, GroupIndexType& groupIndex) const
+{
+  auto finding = m_LabelToGroupMap.find(value);
+  if (m_LabelToGroupMap.end() != finding)
+  {
+    groupIndex = finding->second;
+    return true;
+  }
+  return false;
+}
+
+mitk::LabelSetImage::GroupIndexType mitk::LabelSetImage::GetGroupIndexOfLabel(LabelValueType value) const
+{
+  auto finding = m_LabelToGroupMap.find(value);
+  if (m_LabelToGroupMap.end() == finding)
+  {
+    mitkThrow()<< "Cannot deduce group index. Passed label value does not exist. Value: "<< value;
+  }
+  return finding->second;
+}
+
+
+const mitk::Label* mitk::LabelSetImage::GetLabel(LabelValueType value) const
+{
+  auto finding = m_LabelMap.find(value);
+  if (m_LabelMap.end() != finding)
+  {
+    return finding->second;
+  }
+  return nullptr;
+};
+
+mitk::Label* mitk::LabelSetImage::GetLabel(LabelValueType value)
+{
+  auto finding = m_LabelMap.find(value);
+  if (m_LabelMap.end() != finding)
+  {
+    return finding->second;
+  }
+  return nullptr;
+};
+
+bool mitk::LabelSetImage::IsLabelLocked(LabelValueType value) const
+{
+  if (value == UnlabeledValue)
+  {
+    return m_UnlabeledLabelLock;
+  }
+
+  const auto label = this->GetLabel(value);
+  return label->GetLocked();
+}
+
+const mitk::LabelSetImage::ConstLabelVectorType mitk::LabelSetImage::GetLabels() const
+{
+  ConstLabelVectorType result;
+  for (auto [value, label] : m_LabelMap)
+  {
+    result.emplace_back(label);
+  }
+  return result;
+}
+
+const mitk::LabelSetImage::LabelVectorType mitk::LabelSetImage::GetLabels()
+{
+  LabelVectorType result;
+  for (auto [value, label] : m_LabelMap)
+  {
+    result.emplace_back(label);
+  }
+  return result;
+}
+
+const mitk::LabelSetImage::ConstLabelVectorType mitk::LabelSetImage::GetLabelsInGroup(GroupIndexType index) const
+{
+  if (!this->ExistGroup(index))
+  {
+    mitkThrow() << "Cannot get labels of an invalid group. Invalid group index: " << index;
+  }
+
+  mitk::LabelSetImage::ConstLabelVectorType result;
+
+  const auto labellist = m_GroupToLabelMap.find(index)->second;
+  for (const auto& labelvalue : labellist)
+  {
+    result.emplace_back(this->GetLabel(labelvalue));
+  }
+
+  return result;
+}
+
+const mitk::LabelSetImage::LabelVectorType mitk::LabelSetImage::GetLabelsInGroup(GroupIndexType index)
+{
+  if (!this->ExistGroup(index))
+  {
+    mitkThrow() << "Cannot get labels of an invalid group. Invalid group index: " << index;
+  }
+
+  mitk::LabelSetImage::LabelVectorType result;
+
+  const auto labellist = m_GroupToLabelMap[index];
+  for (const auto& labelvalue : labellist)
+  {
+    result.emplace_back(this->GetLabel(labelvalue));
+  }
+
+  return result;
+}
+
+void mitk::LabelSetImage::ReinitMaps()
+{
+  this->m_LabelMap.clear();
+  this->m_LabelToGroupMap.clear();
+  this->m_GroupToLabelMap.clear();
+
+  for (GroupIndexType layerID = 0; layerID < this->GetNumberOfLayers(); ++layerID)
+  {
+    auto labelSet = this->GetLabelSet(layerID);
+    for (auto iter = labelSet->IteratorBegin(); iter != labelSet->IteratorEnd(); ++iter)
+    {
+      if (iter->first != UnlabeledValue)
+      {
+        this->AddLabelToMap(iter->first, iter->second, layerID);
+      }
+    }
+  }
+}
+
+
 bool mitk::Equal(const mitk::LabelSetImage &leftHandSide,
                  const mitk::LabelSetImage &rightHandSide,
                  ScalarType eps,
                  bool verbose)
 {
   bool returnValue = true;
 
   /* LabelSetImage members */
 
   MITK_INFO(verbose) << "--- LabelSetImage Equal ---";
 
   // number layers
   returnValue = leftHandSide.GetNumberOfLayers() == rightHandSide.GetNumberOfLayers();
   if (!returnValue)
   {
     MITK_INFO(verbose) << "Number of layers not equal.";
     return false;
   }
 
   // total number labels
   returnValue = leftHandSide.GetTotalNumberOfLabels() == rightHandSide.GetTotalNumberOfLabels();
   if (!returnValue)
   {
     MITK_INFO(verbose) << "Total number of labels not equal.";
     return false;
   }
 
   // active layer
   returnValue = leftHandSide.GetActiveLayer() == rightHandSide.GetActiveLayer();
   if (!returnValue)
   {
     MITK_INFO(verbose) << "Active layer not equal.";
     return false;
   }
 
   if (4 == leftHandSide.GetDimension())
   {
     MITK_INFO(verbose) << "Can not compare image data for 4D images - skipping check.";
   }
   else
   {
     // working image data
     returnValue = mitk::Equal((const mitk::Image &)leftHandSide, (const mitk::Image &)rightHandSide, eps, verbose);
     if (!returnValue)
     {
       MITK_INFO(verbose) << "Working image data not equal.";
       return false;
     }
   }
 
   for (unsigned int layerIndex = 0; layerIndex < leftHandSide.GetNumberOfLayers(); layerIndex++)
   {
     if (4 == leftHandSide.GetDimension())
     {
       MITK_INFO(verbose) << "Can not compare image data for 4D images - skipping check.";
     }
     else
     {
       // layer image data
       returnValue =
         mitk::Equal(*leftHandSide.GetLayerImage(layerIndex), *rightHandSide.GetLayerImage(layerIndex), eps, verbose);
       if (!returnValue)
       {
         MITK_INFO(verbose) << "Layer image data not equal.";
         return false;
       }
     }
     // layer labelset data
 
     returnValue =
       mitk::Equal(*leftHandSide.GetLabelSet(layerIndex), *rightHandSide.GetLabelSet(layerIndex), eps, verbose);
     if (!returnValue)
     {
       MITK_INFO(verbose) << "Layer labelset data not equal.";
       return false;
     }
   }
 
   return returnValue;
 }
 
 /** Functor class that implements the label transfer and is used in conjunction with the itk::BinaryFunctorImageFilter.
 * For details regarding the usage of the filter and the functor patterns, please see info of itk::BinaryFunctorImageFilter.
 */
 template <class TDestinationPixel, class TSourcePixel, class TOutputpixel>
 class LabelTransferFunctor
 {
 
 public:
   LabelTransferFunctor() {};
 
   LabelTransferFunctor(const mitk::LabelSet* destinationLabelSet, mitk::Label::PixelType sourceBackground,
     mitk::Label::PixelType destinationBackground, bool destinationBackgroundLocked,
     mitk::Label::PixelType sourceLabel, mitk::Label::PixelType newDestinationLabel, mitk::MultiLabelSegmentation::MergeStyle mergeStyle,
     mitk::MultiLabelSegmentation::OverwriteStyle overwriteStyle) :
     m_DestinationLabelSet(destinationLabelSet), m_SourceBackground(sourceBackground),
     m_DestinationBackground(destinationBackground), m_DestinationBackgroundLocked(destinationBackgroundLocked),
     m_SourceLabel(sourceLabel), m_NewDestinationLabel(newDestinationLabel), m_MergeStyle(mergeStyle), m_OverwriteStyle(overwriteStyle)
   {
   };
 
   ~LabelTransferFunctor() {};
 
   bool operator!=(const LabelTransferFunctor& other)const
   {
     return !(*this == other);
   }
   bool operator==(const LabelTransferFunctor& other) const
   {
     return this->m_SourceBackground == other.m_SourceBackground &&
       this->m_DestinationBackground == other.m_DestinationBackground &&
       this->m_DestinationBackgroundLocked == other.m_DestinationBackgroundLocked &&
       this->m_SourceLabel == other.m_SourceLabel &&
       this->m_NewDestinationLabel == other.m_NewDestinationLabel &&
       this->m_MergeStyle == other.m_MergeStyle &&
       this->m_OverwriteStyle == other.m_OverwriteStyle &&
       this->m_DestinationLabelSet == other.m_DestinationLabelSet;
   }
 
   LabelTransferFunctor& operator=(const LabelTransferFunctor& other)
   {
     this->m_DestinationLabelSet = other.m_DestinationLabelSet;
     this->m_SourceBackground = other.m_SourceBackground;
     this->m_DestinationBackground = other.m_DestinationBackground;
     this->m_DestinationBackgroundLocked = other.m_DestinationBackgroundLocked;
     this->m_SourceLabel = other.m_SourceLabel;
     this->m_NewDestinationLabel = other.m_NewDestinationLabel;
     this->m_MergeStyle = other.m_MergeStyle;
     this->m_OverwriteStyle = other.m_OverwriteStyle;
 
     return *this;
   }
 
   inline TOutputpixel operator()(const TDestinationPixel& existingDestinationValue, const TSourcePixel& existingSourceValue)
   {
     if (existingSourceValue == this->m_SourceLabel)
     {
       if (mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks == this->m_OverwriteStyle)
       {
         return this->m_NewDestinationLabel;
       }
       else
       {
-        auto label = this->m_DestinationLabelSet->GetLabel(existingDestinationValue);
-        if (nullptr == label || !label->GetLocked())
+        if (existingDestinationValue == m_DestinationBackground)
+        {
+          if (!m_DestinationBackgroundLocked)
+          {
+            return this->m_NewDestinationLabel;
+          }
+        }
+        else
         {
-          return this->m_NewDestinationLabel;
+          auto label = this->m_DestinationLabelSet->GetLabel(existingDestinationValue);
+          if (nullptr == label || !label->GetLocked())
+          {
+            return this->m_NewDestinationLabel;
+          }
         }
       }
     }
     else if (mitk::MultiLabelSegmentation::MergeStyle::Replace == this->m_MergeStyle
       && existingSourceValue == this->m_SourceBackground
       && existingDestinationValue == this->m_NewDestinationLabel
       && (mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks == this->m_OverwriteStyle
           || !this->m_DestinationBackgroundLocked))
     {
       return this->m_DestinationBackground;
     }
 
     return existingDestinationValue;
   }
 
 private:
   const mitk::LabelSet* m_DestinationLabelSet = nullptr;
   mitk::Label::PixelType m_SourceBackground = 0;
   mitk::Label::PixelType m_DestinationBackground = 0;
   bool m_DestinationBackgroundLocked = false;
   mitk::Label::PixelType m_SourceLabel = 1;
   mitk::Label::PixelType m_NewDestinationLabel = 1;
   mitk::MultiLabelSegmentation::MergeStyle m_MergeStyle = mitk::MultiLabelSegmentation::MergeStyle::Replace;
   mitk::MultiLabelSegmentation::OverwriteStyle m_OverwriteStyle = mitk::MultiLabelSegmentation::OverwriteStyle::RegardLocks;
 };
 
-/**Helper function used by TransferLabelContent to allow the templating over different image dimensions in conjunction of AccessFixedPixelTypeByItk_n.*/
+/**Helper function used by TransferLabelContentAtTimeStep to allow the templating over different image dimensions in conjunction of AccessFixedPixelTypeByItk_n.*/
 template<unsigned int VImageDimension>
-void TransferLabelContentHelper(const itk::Image<mitk::Label::PixelType, VImageDimension>* itkSourceImage, mitk::Image* destinationImage,
+void TransferLabelContentAtTimeStepHelper(const itk::Image<mitk::Label::PixelType, VImageDimension>* itkSourceImage, mitk::Image* destinationImage,
   const mitk::LabelSet* destinationLabelSet, mitk::Label::PixelType sourceBackground, mitk::Label::PixelType destinationBackground,
   bool destinationBackgroundLocked, mitk::Label::PixelType sourceLabel, mitk::Label::PixelType newDestinationLabel, mitk::MultiLabelSegmentation::MergeStyle mergeStyle, mitk::MultiLabelSegmentation::OverwriteStyle overwriteStyle)
 {
   typedef itk::Image<mitk::Label::PixelType, VImageDimension> ContentImageType;
   typename ContentImageType::Pointer itkDestinationImage;
   mitk::CastToItkImage(destinationImage, itkDestinationImage);
 
   auto sourceRegion = itkSourceImage->GetLargestPossibleRegion();
   auto relevantRegion = itkDestinationImage->GetLargestPossibleRegion();
   bool overlapping = relevantRegion.Crop(sourceRegion);
 
   if (!overlapping)
   {
-    mitkThrow() << "Invalid call of TransferLabelContent; sourceImage and destinationImage seem to have no overlapping image region.";
+    mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; sourceImage and destinationImage seem to have no overlapping image region.";
   }
 
   typedef LabelTransferFunctor <mitk::Label::PixelType, mitk::Label::PixelType, mitk::Label::PixelType> LabelTransferFunctorType;
   typedef itk::BinaryFunctorImageFilter<ContentImageType, ContentImageType, ContentImageType, LabelTransferFunctorType> FilterType;
 
   LabelTransferFunctorType transferFunctor(destinationLabelSet, sourceBackground, destinationBackground,
     destinationBackgroundLocked, sourceLabel, newDestinationLabel, mergeStyle, overwriteStyle);
 
   auto transferFilter = FilterType::New();
 
   transferFilter->SetFunctor(transferFunctor);
   transferFilter->InPlaceOn();
   transferFilter->SetInput1(itkDestinationImage);
   transferFilter->SetInput2(itkSourceImage);
   transferFilter->GetOutput()->SetRequestedRegion(relevantRegion);
 
   transferFilter->Update();
 }
 
-void mitk::TransferLabelContent(
-  const Image* sourceImage, Image* destinationImage, const mitk::LabelSet* destinationLabelSet, mitk::Label::PixelType sourceBackground,
+void mitk::TransferLabelContentAtTimeStep(
+  const Image* sourceImage, Image* destinationImage, const mitk::LabelSet* destinationLabelSet, const TimeStepType timeStep, mitk::Label::PixelType sourceBackground,
   mitk::Label::PixelType destinationBackground, bool destinationBackgroundLocked, std::vector<std::pair<Label::PixelType, Label::PixelType> > labelMapping,
-  MultiLabelSegmentation::MergeStyle mergeStyle, MultiLabelSegmentation::OverwriteStyle overwriteStlye, const TimeStepType timeStep)
+  MultiLabelSegmentation::MergeStyle mergeStyle, MultiLabelSegmentation::OverwriteStyle overwriteStlye)
 {
   if (nullptr == sourceImage)
   {
-    mitkThrow() << "Invalid call of TransferLabelContent; sourceImage must not be null.";
+    mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; sourceImage must not be null.";
   }
   if (nullptr == destinationImage)
   {
-    mitkThrow() << "Invalid call of TransferLabelContent; destinationImage must not be null.";
+    mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; destinationImage must not be null.";
   }
   if (nullptr == destinationLabelSet)
   {
-    mitkThrow() << "Invalid call of TransferLabelContent; destinationLabelSet must not be null";
+    mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; destinationLabelSet must not be null";
+  }
+
+  if (sourceImage == destinationImage && labelMapping.size() > 1)
+  {
+    MITK_DEBUG << "Warning. Using TransferLabelContentAtTimeStep or TransferLabelContent with equal source and destination and more then on label to transfer, can lead to wrong results. Please see documentation and verify that the usage is OK.";
   }
 
   Image::ConstPointer sourceImageAtTimeStep = SelectImageByTimeStep(sourceImage, timeStep);
   Image::Pointer destinationImageAtTimeStep = SelectImageByTimeStep(destinationImage, timeStep);
 
   if (nullptr == sourceImageAtTimeStep)
   {
-    mitkThrow() << "Invalid call of TransferLabelContent; sourceImage does not have the requested time step: " << timeStep;
+    mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; sourceImage does not have the requested time step: " << timeStep;
   }
   if (nullptr == destinationImageAtTimeStep)
   {
-    mitkThrow() << "Invalid call of TransferLabelContent; destinationImage does not have the requested time step: " << timeStep;
+    mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; destinationImage does not have the requested time step: " << timeStep;
   }
 
   for (const auto& [sourceLabel, newDestinationLabel] : labelMapping)
   {
-    if (nullptr == destinationLabelSet->GetLabel(newDestinationLabel))
+    if (LabelSetImage::UnlabeledValue!=newDestinationLabel && nullptr == destinationLabelSet->GetLabel(newDestinationLabel))
     {
-      mitkThrow() << "Invalid call of TransferLabelContent. Defined destination label does not exist in destinationImage. newDestinationLabel: " << newDestinationLabel;
+      mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep. Defined destination label does not exist in destinationImage. newDestinationLabel: " << newDestinationLabel;
     }
 
-    AccessFixedPixelTypeByItk_n(sourceImageAtTimeStep, TransferLabelContentHelper, (Label::PixelType), (destinationImageAtTimeStep, destinationLabelSet, sourceBackground, destinationBackground, destinationBackgroundLocked, sourceLabel, newDestinationLabel, mergeStyle, overwriteStlye));
+    AccessFixedPixelTypeByItk_n(sourceImageAtTimeStep, TransferLabelContentAtTimeStepHelper, (Label::PixelType), (destinationImageAtTimeStep, destinationLabelSet, sourceBackground, destinationBackground, destinationBackgroundLocked, sourceLabel, newDestinationLabel, mergeStyle, overwriteStlye));
+    destinationLabelSet->ModifyLabelEvent.Send(newDestinationLabel);
   }
   destinationImage->Modified();
 }
 
 void mitk::TransferLabelContent(
-  const LabelSetImage* sourceImage, LabelSetImage* destinationImage, std::vector<std::pair<Label::PixelType, Label::PixelType> > labelMapping,
-  MultiLabelSegmentation::MergeStyle mergeStyle, MultiLabelSegmentation::OverwriteStyle overwriteStlye, const TimeStepType timeStep)
+  const Image* sourceImage, Image* destinationImage, const mitk::LabelSet* destinationLabelSet, mitk::Label::PixelType sourceBackground,
+  mitk::Label::PixelType destinationBackground, bool destinationBackgroundLocked, std::vector<std::pair<Label::PixelType, Label::PixelType> > labelMapping,
+  MultiLabelSegmentation::MergeStyle mergeStyle, MultiLabelSegmentation::OverwriteStyle overwriteStlye)
 {
   if (nullptr == sourceImage)
   {
     mitkThrow() << "Invalid call of TransferLabelContent; sourceImage must not be null.";
   }
+  if (nullptr == destinationImage)
+  {
+    mitkThrow() << "Invalid call of TransferLabelContent; destinationImage must not be null.";
+  }
+
+  const auto sourceTimeStepCount = sourceImage->GetTimeGeometry()->CountTimeSteps();
+  if (sourceTimeStepCount != destinationImage->GetTimeGeometry()->CountTimeSteps())
+  {
+    mitkThrow() << "Invalid call of TransferLabelContent; mismatch between images in number of time steps.";
+  }
+
+  for (mitk::TimeStepType i = 0; i < sourceTimeStepCount; ++i)
+  {
+    TransferLabelContentAtTimeStep(sourceImage, destinationImage, destinationLabelSet, i, sourceBackground,
+      destinationBackground, destinationBackgroundLocked, labelMapping, mergeStyle, overwriteStlye);
+  }
+}
+
+void mitk::TransferLabelContentAtTimeStep(
+  const LabelSetImage* sourceImage, LabelSetImage* destinationImage, const TimeStepType timeStep,
+  std::vector<std::pair<Label::PixelType, Label::PixelType> > labelMapping,
+  MultiLabelSegmentation::MergeStyle mergeStyle, MultiLabelSegmentation::OverwriteStyle overwriteStlye)
+{
+  if (nullptr == sourceImage)
+  {
+    mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; sourceImage must not be null.";
+  }
 
-  const auto sourceBackground = sourceImage->GetExteriorLabel()->GetValue();
-  const auto destinationBackground = destinationImage->GetExteriorLabel()->GetValue();
-  const auto destinationBackgroundLocked = destinationImage->GetExteriorLabel()->GetLocked();
   const auto destinationLabelSet = destinationImage->GetLabelSet(destinationImage->GetActiveLayer());
 
   for (const auto& mappingElement : labelMapping)
   {
-    if (!sourceImage->ExistLabel(mappingElement.first, sourceImage->GetActiveLayer()))
+    if (LabelSetImage::UnlabeledValue != mappingElement.first && !sourceImage->ExistLabel(mappingElement.first, sourceImage->GetActiveLayer()))
     {
-      mitkThrow() << "Invalid call of TransferLabelContent. Defined source label does not exist in sourceImage. SourceLabel: " << mappingElement.first;
+      mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep. Defined source label does not exist in sourceImage. SourceLabel: " << mappingElement.first;
     }
   }
 
-  TransferLabelContent(sourceImage, destinationImage, destinationLabelSet, sourceBackground, destinationBackground, destinationBackgroundLocked,
-    labelMapping, mergeStyle, overwriteStlye, timeStep);
+  TransferLabelContentAtTimeStep(sourceImage, destinationImage, destinationLabelSet, timeStep, LabelSetImage::UnlabeledValue, LabelSetImage::UnlabeledValue, destinationImage->GetUnlabeledLabelLock(),
+    labelMapping, mergeStyle, overwriteStlye);
+}
+
+void mitk::TransferLabelContent(
+  const LabelSetImage* sourceImage, LabelSetImage* destinationImage,
+  std::vector<std::pair<Label::PixelType, Label::PixelType> > labelMapping,
+  MultiLabelSegmentation::MergeStyle mergeStyle, MultiLabelSegmentation::OverwriteStyle overwriteStlye)
+{
+  if (nullptr == sourceImage)
+  {
+    mitkThrow() << "Invalid call of TransferLabelContent; sourceImage must not be null.";
+  }
+  if (nullptr == destinationImage)
+  {
+    mitkThrow() << "Invalid call of TransferLabelContent; destinationImage must not be null.";
+  }
+
+  const auto sourceTimeStepCount = sourceImage->GetTimeGeometry()->CountTimeSteps();
+  if (sourceTimeStepCount != destinationImage->GetTimeGeometry()->CountTimeSteps())
+  {
+    mitkThrow() << "Invalid call of TransferLabelContent; images have no equal number of time steps.";
+  }
+
+  for (mitk::TimeStepType i = 0; i < sourceTimeStepCount; ++i)
+  {
+    TransferLabelContentAtTimeStep(sourceImage, destinationImage, i, labelMapping, mergeStyle, overwriteStlye);
+  }
 }
 
diff --git a/Modules/Multilabel/mitkLabelSetImage.h b/Modules/Multilabel/mitkLabelSetImage.h
index f1bc14bd65..4a83ff86f9 100644
--- a/Modules/Multilabel/mitkLabelSetImage.h
+++ b/Modules/Multilabel/mitkLabelSetImage.h
@@ -1,417 +1,673 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #ifndef mitkLabelSetImage_h
 #define mitkLabelSetImage_h
 
 #include <mitkImage.h>
 #include <mitkLabelSet.h>
 
 #include <MitkMultilabelExports.h>
 
 namespace mitk
 {
   //##Documentation
   //## @brief LabelSetImage class for handling labels and layers in a segmentation session.
   //##
   //## Handles operations for adding, removing, erasing and editing labels and layers.
   //## @ingroup Data
 
   class MITKMULTILABEL_EXPORT LabelSetImage : public Image
   {
   public:
     mitkClassMacro(LabelSetImage, Image);
     itkNewMacro(Self);
 
-      typedef mitk::Label::PixelType PixelType;
+    typedef mitk::Label::PixelType PixelType;
 
     /**
     * \brief BeforeChangeLayerEvent (e.g. used for GUI integration)
     * As soon as active labelset should be changed, the signal emits.
     * Emitted by SetActiveLayer(int layer);
     */
     Message<> BeforeChangeLayerEvent;
 
     /**
     * \brief AfterchangeLayerEvent (e.g. used for GUI integration)
     * As soon as active labelset was changed, the signal emits.
     * Emitted by SetActiveLayer(int layer);
     */
     Message<> AfterChangeLayerEvent;
 
+    ///////////////////////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////////////////////
+    // FUTURE MultiLabelSegmentation:
+    // Section that already contains declarations used in the new class.
+    // So this part of the interface will stay after refactoring towards
+    // the new MultiLabelSegmentation class (see T28524). This section was introduced
+    // because some of the planned features are already urgently needed.
+    ///////////////////////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    using GroupIndexType = std::size_t;
+    using LabelValueType = mitk::Label::PixelType;
+    const static LabelValueType UnlabeledValue = 0;
+    using ConstLabelVectorType = std::vector<Label::ConstPointer>;
+    using LabelVectorType = std::vector<Label::Pointer>;
+    using LabelValueVectorType = std::vector<LabelValueType>;
+
+    /**
+     * @brief Removes the label with the given value.
+     *        The label is removed from the labelset and
+     *        the pixel with the value of the label are set to UnlabeledValue.
+     * @param labelValue the pixel value of the label to be removed
+     */
+    void RemoveLabel(LabelValueType labelValue);
+
+    /**
+     * @brief Removes labels from the mitk::MultiLabelSegmentation.
+     * If a label value does not exist, it will be ignored.
+     * @param vectorOfLabelPixelValues a list of labels to be removed
+     */
+    void RemoveLabels(const LabelValueVectorType& vectorOfLabelPixelValues);
+
+    /**
+     * @brief Removes a whole group including all its labels.
+     * @remark with removing a group all groups with greater index will be reindexed to
+     * close the gap. Hence externaly stored spatial group indices may become invalid.
+     * @param group Group index of the spatial group that should be removed. If the spatial group does not exist, an
+     * exception will be raised.
+     * @pre group index must be valid.
+     */
+    void RemoveGroup(GroupIndexType group);
+
+    //future declaration (T28524) currently conflicted with old declaration
+    ///**
+    //  * \brief  Returns true if the value exists in the MultiLabelSegmentation instance*/
+    //bool ExistLabel(LabelValueType value) const;
+
+    ///**
+    // * @brief Checks if a label belongs in a certain spatial group
+    // * @param value the label value
+    // * @param groupIndex Indexp of the spacial group which should be checked for the label
+    // * @return true if the label exists otherwise false
+    // */
+    //bool ExistLabel(LabelValueType value, GroupIndexType groupIndex) const;
+
+    /**
+      * @brief  Returns true if the spatial group exists in the MultiLabelSegmentation instance.
+      *
+      * @param index Group index of the group that should be checked for existance.
+      */
+    bool ExistGroup(GroupIndexType index) const;
+
+    bool IsLabeInGroup(LabelValueType value) const;
+    bool IsLabeInGroup(LabelValueType value, GroupIndexType& groupIndex) const;
+
+    /** Returns the group id of the based label value.
+    * @pre label value must exists.
+    */
+    GroupIndexType GetGroupIndexOfLabel(LabelValueType value) const;
+
+    /**
+     * @brief Returns the mitk::Label with the given value.
+     * @param value the pixel value of the label
+     * @return the mitk::Label if available otherwise nullptr
+     */
+    const mitk::Label* GetLabel(LabelValueType value) const;
+    mitk::Label* GetLabel(LabelValueType value);
+
+    /** Returns the lock state of the label (including UnlabeledLabel value).
+     @pre Requested label does exist.*/
+    bool IsLabelLocked(LabelValueType value) const;
+
+    /** Returns a vector with all labels currently defined in the MultiLabelSegmentation
+    instance.*/
+    const ConstLabelVectorType GetLabels() const;
+    const LabelVectorType GetLabels();
+
+    /**
+     * @brief Returns a vector of all labels located on the specified group.
+     * @param index the index of the group for which the vector of labels should be retrieved.
+     * If an invalid index is passed an exception will be raised.
+     * @return the respective vector of labels.
+     * @pre group index must exist.
+     */
+    const ConstLabelVectorType GetLabelsInGroup(GroupIndexType index) const;
+    const LabelVectorType GetLabelsInGroup(GroupIndexType index);
+
+    itkGetConstMacro(UnlabeledLabelLock, bool);
+    itkSetMacro(UnlabeledLabelLock, bool);
+    itkBooleanMacro(UnlabeledLabelLock);
+
+    ////////////////////////////////////////////////////////////////////
+    //Message slots that allow to react to changes in an instance
+
+    using LabelEventType = Message1<LabelValueType>;
+    using LabelsEventType = Message1<LabelValueVectorType>;
+    using GroupEventType = Message1<GroupIndexType>;
+
+    /**
+    * \brief LabelAdded is emitted whenever a new label has been added.
+    *
+    * Observers should register to this event by calling this->AddLabelAddedListener(myObject,
+    * MyObject::MyMethod).
+    * After registering, myObject->MyMethod() will be called every time a new label has been added to the MultiLabelSegmentation.
+    * Observers should unregister by calling this->RemoveLabelAddedListener(myObject, MyObject::MyMethod).
+    * The registered method will be called with the label value of the added label.
+    * @remark the usage of the message object is thread safe.
+    */
+    mitkNewMessage1Macro(LabelAdded, LabelValueType);
+
+    /**
+    * \brief LabelModified is emitted whenever a label has been modified.
+    *
+    * A label is modified if either its pixel content was changed, its spatial group or the label instance
+    * information.
+    * If you just want to get notified at the end of a MultiLabelSegmentation instance manipulation in the
+    * case that at least one label was modified (e.g. to avoid getting a signal for each label
+    * individually), use LabelsChanged instead.
+    * Observers should register to this event by calling this->AddLabelModifiedListener(myObject,
+    * MyObject::MyMethod).
+    * After registering, myObject->MyMethod() will be called every time a new label has been added to the MultiLabelSegmentation.
+    * Observers should unregister by calling this->RemoveLabelModifiedListener(myObject, MyObject::MyMethod).
+    * The registered method will be called with the label value of the modified label.
+    * @remark the usage of the message object is thread safe.
+    */
+    mitkNewMessage1Macro(LabelModified, LabelValueType);
+
+    /**
+    * \brief LabelRemoved is emitted whenever a label has been removed.
+    *
+    * Observers should register to this event by calling this->AddLabelRemovedListener(myObject,
+    * MyObject::MyMethod).
+    * After registering, myObject->MyMethod() will be called every time a new label has been added to the MultiLabelSegmentation.
+    * Observers should unregister by calling this->RemoveLabelRemovedListener(myObject, MyObject::MyMethod).
+    * The registered method will be called with the label value of the removed label.*
+    * @remark the usage of the message object is thread safe.
+    */
+    mitkNewMessage1Macro(LabelRemoved, LabelValueType);
+
+    /**
+    * \brief LabelsChanged is emitted when labels are changed (added, removed, modified).
+    *
+    * In difference to the other label events LabelsChanged is send only *one time* after the modification of the
+    * MultiLableImage instance is finished. So e.g. even if 4 labels are changed by a merge operation, this event will
+    * only be sent once (compared to LabelRemoved or LabelModified).
+    * Observers should register to this event by calling myMultiLabelSegmentation->AddLabelsChangedListener(myObject,
+    * MyObject::MyMethod).
+    * After registering, myObject->MyMethod() will be called every time a new label has been removed from the MultiLabelSegmentation.
+    * Observers should unregister by calling myMultiLabelSegmentation->RemoveLabelsChangedListener(myObject,
+    * MyObject::MyMethod).
+    * The registered method will be called with the vector of label values of the modified labels.*
+    * @remark the usage of the message object is thread safe.
+    */
+    mitkNewMessage1Macro(LabelsChanged, LabelValueVectorType);
+
+    /**
+    * \brief GroupAdded is emitted whenever a new group has been added.
+    *
+    * Observers should register to this event by calling this->AddGroupAddedListener(myObject,
+    * MyObject::MyMethod).
+    * After registering, myObject->MyMethod() will be called every time a new group has been added to the MultiLabelSegmentation.
+    * Observers should unregister by calling this->RemoveGroupAddedListener(myObject, MyObject::MyMethod).
+    * The registered method will be called with the group index of the added group.
+    * @remark the usage of the message object is thread safe.
+    */
+    mitkNewMessage1Macro(GroupAdded, GroupIndexType);
+
+    /**
+    * \brief GroupModified is emitted whenever a group has been modified.
+    *
+    * A group is modified if the set of labels associated with it are changed or the group's meta data.
+    * Observers should register to this event by calling this->AddGroupModifiedListener(myObject,
+    * MyObject::MyMethod).
+    * After registering, myObject->MyMethod() will be called every time a new label has been added to the MultiLabelSegmentation.
+    * Observers should unregister by calling this->RemoveGroupModifiedListener(myObject, MyObject::MyMethod).
+    * The registered method will be called with the group index of the added group.
+    * @remark the usage of the message object is thread safe.
+    */
+    mitkNewMessage1Macro(GroupModified, GroupIndexType);
+
+    /**
+    * \brief GroupRemoved is emitted whenever a label has been removed.
+    *
+    * Observers should register to this event by calling this->AddGroupRemovedListener(myObject,
+    * MyObject::MyMethod).
+    * After registering, myObject->MyMethod() will be called every time a new label has been added to the MultiLabelSegmentation.
+    * Observers should unregister by calling this->RemoveGroupRemovedListener(myObject, MyObject::MyMethod).
+    * The registered method will be called with the group index of the removed group.*
+    * @remark the usage of the message object is thread safe.
+    */
+    mitkNewMessage1Macro(GroupRemoved, GroupIndexType);
+
+    protected:
+
+      void OnLabelAdded(LabelValueType labelValue);
+      void AddLabelToMap(LabelValueType labelValue, mitk::Label* label, GroupIndexType groupID);
+      void OnLabelModified(LabelValueType labelValue);
+      void OnLabelRemoved(LabelValueType labelValue);
+      void OnGroupAdded(GroupIndexType groupIndex);
+      void OnGroupModified(GroupIndexType groupIndex);
+      void OnGroupRemoved(GroupIndexType groupIndex);
+
+      /** Reeinitalizes the internal maps based on the current layer/label content
+      * of the instance. */
+      void ReinitMaps();
+
+      using LabelMapType = std::map<LabelValueType, Label::Pointer>;
+      LabelMapType m_LabelMap;
+
+      /**This type is internally used to track which label is currently
+       * associated with which layer.*/
+      using GroupToLabelMapType = std::map<GroupIndexType, LabelValueVectorType>;
+      GroupToLabelMapType m_GroupToLabelMap;
+      using LabelToGroupMapType = std::map<LabelValueType, GroupIndexType>;
+      LabelToGroupMapType m_LabelToGroupMap;
+
+    private:
+      /** Indicates if the MultiLabelSegmentation allows to overwrite unlabeled pixels in normal pixel manipulation operations (e.g. TransferLabelConent).*/
+      bool m_UnlabeledLabelLock;
+
+    public:
+
+      /**
+        * \brief  */
+      void UpdateCenterOfMass(PixelType pixelValue);
+
+    ///////////////////////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////////////////////
+    // END FUTURE MultiLabelSegmentation
+    ///////////////////////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////////////////////
+
     /**
      * @brief Initialize an empty mitk::LabelSetImage using the information
      *        of an mitk::Image
      * @param image the image which is used for initializing the mitk::LabelSetImage
      */
     using mitk::Image::Initialize;
     void Initialize(const mitk::Image *image) override;
 
     /**
       * \brief  */
     void ClearBuffer();
 
     /**
      * @brief Merges the mitk::Label with a given target value with the active label
      *
      * @param pixelValue          the value of the label that should be the new merged label
      * @param sourcePixelValue    the value of the label that should be merged into the specified one
      * @param layer               the layer in which the merge should be performed
      */
     void MergeLabel(PixelType pixelValue, PixelType sourcePixelValue, unsigned int layer = 0);
 
     /**
      * @brief Merges a list of mitk::Labels with the mitk::Label that has a specific value
      *
      * @param pixelValue                  the value of the label that should be the new merged label
      * @param vectorOfSourcePixelValues   the list of label values that should be merge into the specified one
      * @param layer                       the layer in which the merge should be performed
      */
-    void MergeLabels(PixelType pixelValue, std::vector<PixelType>& vectorOfSourcePixelValues, unsigned int layer = 0);
+    void MergeLabels(PixelType pixelValue, const std::vector<PixelType>& vectorOfSourcePixelValues, unsigned int layer = 0);
 
     /**
       * \brief  */
-    void UpdateCenterOfMass(PixelType pixelValue, unsigned int layer = 0);
-
-    /**
-     * @brief Removes the label with the given value.
-     *        The label is removed from the labelset of the given layer and
-     *        the pixel values of the image below the label are reset.
-     * @param pixelValue the pixel value of the label to be removed
-     * @param layer the layer from which the label should be removed
-     */
-    void RemoveLabel(PixelType pixelValue, unsigned int layer = 0);
-
-    /**
-     * @brief Removes a list of labels with th given value.
-     *        The labels are removed from the labelset of the given layer and
-     *        the pixel values of the image below the label are reset.
-     *        Calls mitk::LabelSetImage::EraseLabels().
-     * @param VectorOfLabelPixelValues a list of pixel values of labels to be removed
-     * @param layer the layer from which the labels should be removed
-     */
-    void RemoveLabels(std::vector<PixelType> &VectorOfLabelPixelValues, unsigned int layer = 0);
+    void UpdateCenterOfMass(PixelType pixelValue, unsigned int layer);
 
     /**
      * @brief Erases the label with the given value from the labelset image.
      *        The label itself will not be erased from the respective mitk::LabelSet. In order to
      *        remove the label itself use mitk::LabelSetImage::RemoveLabels()
      * @param pixelValue the pixel value of the label that will be erased from the labelset image
      */
     void EraseLabel(PixelType pixelValue);
 
     /**
      * @brief Erases a list of labels with the given values from the labelset image.
      * @param VectorOfLabelPixelValues the list of pixel values of the labels
      *                                 that will be erased from the labelset image
      */
-    void EraseLabels(std::vector<PixelType> &VectorOfLabelPixelValues);
+    void EraseLabels(const std::vector<PixelType> &VectorOfLabelPixelValues);
 
     /**
       * \brief  Returns true if the value exists in one of the labelsets*/
-    bool ExistLabel(PixelType pixelValue) const;
+    //[[deprecated("Will be changed with T28524")]]
+    DEPRECATED(bool ExistLabel(PixelType pixelValue) const);
 
     /**
      * @brief Checks if a label exists in a certain layer
      * @param pixelValue the label value
      * @param layer the layer in which should be searched for the label
      * @return true if the label exists otherwise false
      */
-    bool ExistLabel(PixelType pixelValue, unsigned int layer) const;
+    //[[deprecated("Will be changed with T28524")]]
+    DEPRECATED(bool ExistLabel(PixelType pixelValue, unsigned int layer) const);
 
     /**
       * \brief  Returns true if the labelset exists*/
-    bool ExistLabelSet(unsigned int layer) const;
+    //[[deprecated("Will be removed with T28524")]]
+    DEPRECATED(bool ExistLabelSet(unsigned int layer) const);
 
     /**
      * @brief Returns the active label of a specific layer
      * @param layer the layer ID for which the active label should be returned
      * @return the active label of the specified layer
      */
-    mitk::Label *GetActiveLabel(unsigned int layer = 0);
-    const mitk::Label* GetActiveLabel(unsigned int layer = 0) const;
+    //[[deprecated("Will be removed with T28524")]]
+    DEPRECATED(mitk::Label *GetActiveLabel(unsigned int layer = 0));
+    //[[deprecated("Will be removed with T28524")]]
+    DEPRECATED(const mitk::Label* GetActiveLabel(unsigned int layer = 0) const);
 
     /**
      * @brief Returns the mitk::Label with the given pixelValue and for the given layer
      * @param pixelValue the pixel value of the label
      * @param layer the layer in which the labels should be located
      * @return the mitk::Label if available otherwise nullptr
      */
-    mitk::Label *GetLabel(PixelType pixelValue, unsigned int layer = 0) const;
+    mitk::Label *GetLabel(PixelType pixelValue, unsigned int layer) const;
 
     /**
      * @brief Returns the currently active mitk::LabelSet
      * @return the mitk::LabelSet of the active layer or nullptr if non is present
      */
-    mitk::LabelSet *GetActiveLabelSet();
-    const mitk::LabelSet* GetActiveLabelSet() const;
+    //[[deprecated ("Will be removed with T28524")]]
+    DEPRECATED(mitk::LabelSet *GetActiveLabelSet());
+    //[[deprecated("Will be removed with T28524")]]
+    DEPRECATED(const mitk::LabelSet* GetActiveLabelSet() const);
 
     /**
      * @brief Gets the mitk::LabelSet for the given layer
      * @param layer the layer for which the mitk::LabelSet should be retrieved
      * @return the respective mitk::LabelSet or nullptr if non exists for the given layer
      */
     mitk::LabelSet *GetLabelSet(unsigned int layer = 0);
     const mitk::LabelSet *GetLabelSet(unsigned int layer = 0) const;
 
     /**
      * @brief Gets the ID of the currently active layer
      * @return the ID of the active layer
      */
     unsigned int GetActiveLayer() const;
 
     /**
      * @brief Get the number of all existing mitk::Labels for a given layer
      * @param layer the layer ID for which the active mitk::Labels should be retrieved
      * @return the number of all existing mitk::Labels for the given layer
      */
     unsigned int GetNumberOfLabels(unsigned int layer = 0) const;
 
     /**
      * @brief Returns the number of all labels summed up across all layers
      * @return the overall number of labels across all layers
      */
     unsigned int GetTotalNumberOfLabels() const;
 
     // This function will need to be ported to an external class
     // it requires knowledge of pixeltype and dimension and includes
     // too much algorithm to be sensibly part of a data class
     ///**
     //  * \brief  */
     // void SurfaceStamp(mitk::Surface* surface, bool forceOverwrite);
 
     /**
       * \brief  */
     mitk::Image::Pointer CreateLabelMask(PixelType index, bool useActiveLayer = true, unsigned int layer = 0);
 
     /**
      * @brief Initialize a new mitk::LabelSetImage by an given image.
      * For all distinct pixel values of the parameter image new labels will
      * be created. If the number of distinct pixel values exceeds mitk::Label::MAX_LABEL_VALUE
      * a new layer will be created
      * @param image the image which is used for initialization
      */
     void InitializeByLabeledImage(mitk::Image::Pointer image);
 
     /**
       * \brief  */
     void MaskStamp(mitk::Image *mask, bool forceOverwrite);
 
     /**
       * \brief  */
     void SetActiveLayer(unsigned int layer);
 
     /**
       * \brief  */
     unsigned int GetNumberOfLayers() const;
 
     /**
      * \brief Adds a new layer to the LabelSetImage. The new layer will be set as the active one.
      * \param labelSet a labelset that will be added to the new layer if provided
      * \return the layer ID of the new layer
      */
     unsigned int AddLayer(mitk::LabelSet::Pointer labelSet = nullptr);
 
     /**
     * \brief Adds a layer based on a provided mitk::Image.
     * \param layerImage is added to the vector of label images
     * \param labelSet   a labelset that will be added to the new layer if provided
     * \return the layer ID of the new layer
     */
     unsigned int AddLayer(mitk::Image::Pointer layerImage, mitk::LabelSet::Pointer labelSet = nullptr);
 
     /**
-    * \brief Add a LabelSet to an existing layer
+    * \brief Add a cloned LabelSet to an existing layer
+    *
+    * Remark: The passed LabelSet instance will be cloned before added to ensure clear ownership
+    * of the new LabelSet addition.
     *
     * This will replace an existing labelSet if one exists. Throws an exceptions if you are trying
     * to add a labelSet to a non-existing layer.
     *
     * If there are no labelSets for layers with an id less than layerIdx default ones will be added
     * for them.
     *
     * \param layerIdx The index of the layer the LabelSet should be added to
     * \param labelSet The LabelSet that should be added
     */
-    void AddLabelSetToLayer(const unsigned int layerIdx, const mitk::LabelSet::Pointer labelSet);
+    void AddLabelSetToLayer(const unsigned int layerIdx, const mitk::LabelSet* labelSet);
 
     /**
      * @brief Removes the active layer and the respective mitk::LabelSet and image information.
      *        The new active layer is the one below, if exists
      */
     void RemoveLayer();
 
     /**
       * \brief  */
     mitk::Image *GetLayerImage(unsigned int layer);
 
     const mitk::Image *GetLayerImage(unsigned int layer) const;
 
     void OnLabelSetModified();
 
-    /**
-     * @brief Sets the label which is used as default exterior label when creating a new layer
-     * @param label the label which will be used as new exterior label
-     */
-    void SetExteriorLabel(mitk::Label *label);
-
-    /**
-     * @brief Gets the mitk::Label which is used as default exterior label
-     * @return the exterior mitk::Label
-     */
-    mitk::Label *GetExteriorLabel();
-
-    const mitk::Label *GetExteriorLabel() const;
-
   protected:
     mitkCloneMacro(Self);
 
       LabelSetImage();
     LabelSetImage(const LabelSetImage &other);
     ~LabelSetImage() override;
 
     template <typename TPixel, unsigned int VImageDimension>
     void LayerContainerToImageProcessing(itk::Image<TPixel, VImageDimension> *source, unsigned int layer);
 
     template <typename TPixel, unsigned int VImageDimension>
     void ImageToLayerContainerProcessing(itk::Image<TPixel, VImageDimension> *source, unsigned int layer) const;
 
     template <typename ImageType>
     void CalculateCenterOfMassProcessing(ImageType *input, PixelType index, unsigned int layer);
 
     template <typename ImageType>
     void ClearBufferProcessing(ImageType *input);
 
     template <typename ImageType>
     void EraseLabelProcessing(ImageType *input, PixelType index);
 
     template <typename ImageType>
     void MergeLabelProcessing(ImageType *input, PixelType pixelValue, PixelType index);
 
     template <typename ImageType>
     void MaskStampProcessing(ImageType *input, mitk::Image *mask, bool forceOverwrite);
 
     template <typename LabelSetImageType, typename ImageType>
     void InitializeByLabeledImageProcessing(LabelSetImageType *input, ImageType *other);
 
+    /** helper needed for ensuring unique values in all layers until the refactoring is done.
+      returns a sorted list of all labels.*/
+    LabelValueVectorType GetUsedLabelValues() const;
+
+    //helper function that ensures
+    void RegisterLabelSet(mitk::LabelSet* ls);
+    void ReleaseLabelSet(mitk::LabelSet* ls);
+
     std::vector<LabelSet::Pointer> m_LabelSetContainer;
     std::vector<Image::Pointer> m_LayerContainer;
 
     int m_ActiveLayer;
 
     bool m_activeLayerInvalid;
-
-    mitk::Label::Pointer m_ExteriorLabel;
   };
 
   /**
   * @brief Equal A function comparing two label set images for beeing equal in meta- and imagedata
   *
   * @ingroup MITKTestingAPI
   *
   * Following aspects are tested for equality:
   *  - LabelSetImage members
   *  - working image data
   *  - layer image data
   *  - labels in label set
   *
   * @param rightHandSide An image to be compared
   * @param leftHandSide An image to be compared
   * @param eps Tolerance for comparison. You can use mitk::eps in most cases.
   * @param verbose Flag indicating if the user wants detailed console output or not.
   * @return true, if all subsequent comparisons are true, false otherwise
   */
   MITKMULTILABEL_EXPORT bool Equal(const mitk::LabelSetImage &leftHandSide,
                                    const mitk::LabelSetImage &rightHandSide,
                                    ScalarType eps,
                                    bool verbose);
 
 
   /** temporery namespace that is used until the new class MultiLabelSegmentation is
     introduced. It allows to already introduce/use some upcoming definitions, while
     refactoring code.*/
   namespace MultiLabelSegmentation
   {
     enum class MergeStyle
     {
       Replace, //The old label content of a lable value will be replaced by its new label content.
                //Therefore pixels that are labeled might become unlabeled again.
                //(This means that a lock of the value is also ignored).
       Merge //The union of old and new label content will be generated.
     };
 
     enum class OverwriteStyle
     {
       RegardLocks, //Locked labels in the same spatial group will not be overwritten/changed.
       IgnoreLocks //Label locks in the same spatial group will be ignored, so these labels might be changed.
     };
   }
 
   /**Helper function that transfers pixels of the specified source label from source image to the destination image by using
-  a specified destination label. Function processes the whole image volume of the specified time step.
+  a specified destination label for a specific timestep. Function processes the whole image volume of the specified time step.
   @remark in its current implementation the function only transfers contents of the active layer of the passed LabelSetImages.
   @remark the function assumes that it is only called with source and destination image of same geometry.
+  @remark CAUTION: The function is not save if sourceImage and destinationImage are the same instance and more than one label is transferred,
+  because the changes are made in-place for performance reasons in multiple passes. If a mapped value A equals an "old value"
+  that occurs later in the mapping, one ends up with a wrong transfer, as a pixel would be first mapped to A and then later again, because
+  it is also an "old" value in the mapping table.
   @param sourceImage Pointer to the LabelSetImage which active layer should be used as source for the transfer.
   @param destinationImage Pointer to the LabelSetImage which active layer should be used as destination for the transfer.
   @param labelMapping Map that encodes the mappings of all label pixel transfers that should be done. First element is the
   label in the source image. The second element is the label that transferred pixels should become in the destination image.
   The order in which the labels will be transfered is the same order of elements in the labelMapping.
   If you use a heterogeneous label mapping (e.g. (1,2); so changing the label while transfering), keep in mind that
   for the MergeStyle and OverwriteStyle only the destination label (second element) is relevant (e.g. what should be
   altered with MergeStyle Replace).
   @param mergeStyle indicates how the transfer should be done (merge or replace). For more details see documentation of
   MultiLabelSegmentation::MergeStyle.
   @param overwriteStlye indicates if label locks in the destination image should be regarded or not. For more details see
   documentation of MultiLabelSegmentation::OverwriteStyle.
   @param timeStep indicate the time step that should be transferred.
   @pre sourceImage and destinationImage must be valid
   @pre sourceImage and destinationImage must contain the indicated timeStep
   @pre sourceImage must contain all indicated sourceLabels in its active layer.
   @pre destinationImage must contain all indicated destinationLabels in its active layer.*/
-  MITKMULTILABEL_EXPORT void TransferLabelContent(const LabelSetImage* sourceImage, LabelSetImage* destinationImage,
-    std::vector<std::pair<Label::PixelType, Label::PixelType> > labelMapping = { {1,1} },
+  MITKMULTILABEL_EXPORT void TransferLabelContentAtTimeStep(const LabelSetImage* sourceImage, LabelSetImage* destinationImage,
+    const TimeStepType timeStep, std::vector<std::pair<Label::PixelType, Label::PixelType> > labelMapping = { {1,1} },
+    MultiLabelSegmentation::MergeStyle mergeStyle = MultiLabelSegmentation::MergeStyle::Replace,
+    MultiLabelSegmentation::OverwriteStyle overwriteStlye = MultiLabelSegmentation::OverwriteStyle::RegardLocks);
+
+  /**Helper function that transfers pixels of the specified source label from source image to the destination image by using
+  a specified destination label. Function processes the whole image volume for all time steps.
+  For more details please see TransferLabelContentAtTimeStep for LabelSetImages.
+  @sa TransferLabelContentAtTimeStep*/
+  MITKMULTILABEL_EXPORT void TransferLabelContent(const LabelSetImage* sourceImage, LabelSetImage* destinationImage, std::vector<std::pair<Label::PixelType, Label::PixelType> > labelMapping = { {1,1} },
     MultiLabelSegmentation::MergeStyle mergeStyle = MultiLabelSegmentation::MergeStyle::Replace,
-    MultiLabelSegmentation::OverwriteStyle overwriteStlye = MultiLabelSegmentation::OverwriteStyle::RegardLocks,
-    const TimeStepType timeStep = 0);
+    MultiLabelSegmentation::OverwriteStyle overwriteStlye = MultiLabelSegmentation::OverwriteStyle::RegardLocks);
+
 
   /**Helper function that transfers pixels of the specified source label from source image to the destination image by using
-  a specified destination label. Function processes the whole image volume of the specified time step.
+  a specified destination label for a specific timestep. Function processes the whole image volume of the specified time step.
   @remark the function assumes that it is only called with source and destination image of same geometry.
+  @remark CAUTION: The function is not save, if sourceImage and destinationImage are the same instance and you transfer more then one
+  label, because the changes are made inplace for performance reasons but not in one pass. If a mapped value A equals a "old value"
+  that is later in the mapping, one ends up with a wrong transfer, as a pixel would be first mapped to A and then latter again, because
+  it is also an "old" value in the mapping table.
+  @param sourceImage Pointer to the image that should be used as source for the transfer.
   @param sourceImage Pointer to the image that should be used as source for the transfer.
   @param destinationImage Pointer to the image that should be used as destination for the transfer.
   @param destinationLabelSet Pointer to the label set specifying labels and lock states in the destination image. Unkown pixel
   values in the destinationImage will be assumed to be unlocked.
   @param sourceBackground Value indicating the background in the source image.
   @param destinationBackground Value indicating the background in the destination image.
   @param destinationBackgroundLocked Value indicating the lock state of the background in the destination image.
   @param labelMapping Map that encodes the mappings of all label pixel transfers that should be done. First element is the
   label in the source image. The second element is the label that transferred pixels should become in the destination image.
   The order in which the labels will be transfered is the same order of elements in the labelMapping.
   If you use a heterogeneous label mapping (e.g. (1,2); so changing the label while transfering), keep in mind that
   for the MergeStyle and OverwriteStyle only the destination label (second element) is relevant (e.g. what should be
   altered with MergeStyle Replace).
   @param mergeStyle indicates how the transfer should be done (merge or replace). For more details see documentation of
   MultiLabelSegmentation::MergeStyle.
   @param overwriteStlye indicates if label locks in the destination image should be regarded or not. For more details see
   documentation of MultiLabelSegmentation::OverwriteStyle.
   @param timeStep indicate the time step that should be transferred.
   @pre sourceImage, destinationImage and destinationLabelSet must be valid
   @pre sourceImage and destinationImage must contain the indicated timeStep
   @pre destinationLabelSet must contain all indicated destinationLabels for mapping.*/
+  MITKMULTILABEL_EXPORT void TransferLabelContentAtTimeStep(const Image* sourceImage, Image* destinationImage, const mitk::LabelSet* destinationLabelSet,
+    const TimeStepType timeStep, mitk::Label::PixelType sourceBackground = LabelSetImage::UnlabeledValue,
+    mitk::Label::PixelType destinationBackground = LabelSetImage::UnlabeledValue,
+    bool destinationBackgroundLocked = false,
+    std::vector<std::pair<Label::PixelType, Label::PixelType> > labelMapping = { {1,1} },
+    MultiLabelSegmentation::MergeStyle mergeStyle = MultiLabelSegmentation::MergeStyle::Replace,
+    MultiLabelSegmentation::OverwriteStyle overwriteStlye = MultiLabelSegmentation::OverwriteStyle::RegardLocks);
+
+  /**Helper function that transfers pixels of the specified source label from source image to the destination image by using
+  a specified destination label. Function processes the whole image volume for all time steps.
+  For more details please see TransferLabelContentAtTimeStep.
+  @sa TransferLabelContentAtTimeStep*/
   MITKMULTILABEL_EXPORT void TransferLabelContent(const Image* sourceImage, Image* destinationImage, const mitk::LabelSet* destinationLabelSet,
-    mitk::Label::PixelType sourceBackground = 0, mitk::Label::PixelType destinationBackground = 0, bool destinationBackgroundLocked = false,
+    mitk::Label::PixelType sourceBackground = LabelSetImage::UnlabeledValue,
+    mitk::Label::PixelType destinationBackground = LabelSetImage::UnlabeledValue,
+    bool destinationBackgroundLocked = false,
     std::vector<std::pair<Label::PixelType, Label::PixelType> > labelMapping = { {1,1} },
     MultiLabelSegmentation::MergeStyle mergeStyle = MultiLabelSegmentation::MergeStyle::Replace,
-    MultiLabelSegmentation::OverwriteStyle overwriteStlye = MultiLabelSegmentation::OverwriteStyle::RegardLocks,
-    const TimeStepType timeStep = 0);
+    MultiLabelSegmentation::OverwriteStyle overwriteStlye = MultiLabelSegmentation::OverwriteStyle::RegardLocks);
+
 } // namespace mitk
 
 #endif
diff --git a/Modules/Multilabel/mitkLabelSetImageConverter.cpp b/Modules/Multilabel/mitkLabelSetImageConverter.cpp
index 8486db5c76..b994182c38 100644
--- a/Modules/Multilabel/mitkLabelSetImageConverter.cpp
+++ b/Modules/Multilabel/mitkLabelSetImageConverter.cpp
@@ -1,145 +1,198 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include <mitkITKImageImport.h>
 #include <mitkImageAccessByItk.h>
 #include <mitkImageCast.h>
 #include <mitkLabelSetImageConverter.h>
 
 #include <itkComposeImageFilter.h>
 #include <itkExtractImageFilter.h>
 #include <itkImageDuplicator.h>
 #include <itkVectorIndexSelectionCastImageFilter.h>
 
 template <typename TPixel, unsigned int VDimension>
 static void ConvertLabelSetImageToImage(const itk::Image<TPixel, VDimension> *,
                                         mitk::LabelSetImage::ConstPointer labelSetImage,
                                         mitk::Image::Pointer &image)
 {
   typedef itk::Image<TPixel, VDimension> ImageType;
   typedef itk::ComposeImageFilter<ImageType> ComposeFilterType;
   typedef itk::ImageDuplicator<ImageType> DuplicatorType;
 
   auto numberOfLayers = labelSetImage->GetNumberOfLayers();
 
   if (numberOfLayers > 1)
   {
     auto vectorImageComposer = ComposeFilterType::New();
     auto activeLayer = labelSetImage->GetActiveLayer();
 
     for (decltype(numberOfLayers) layer = 0; layer < numberOfLayers; ++layer)
     {
       auto layerImage = mitk::ImageToItkImage<TPixel, VDimension>(
         layer != activeLayer ? labelSetImage->GetLayerImage(layer) : labelSetImage);
 
       vectorImageComposer->SetInput(layer, layerImage);
     }
 
     vectorImageComposer->Update();
     // mitk::GrabItkImageMemory does not support 4D, this will handle 4D correctly
     // and create a memory managed copy
     image = mitk::ImportItkImage(vectorImageComposer->GetOutput())->Clone();
   }
   else
   {
     auto layerImage = mitk::ImageToItkImage<TPixel, VDimension>(labelSetImage);
 
     auto duplicator = DuplicatorType::New();
     duplicator->SetInputImage(layerImage);
     duplicator->Update();
 
     // mitk::GrabItkImageMemory does not support 4D, this will handle 4D correctly
     // and create a memory managed copy
     image = mitk::ImportItkImage(duplicator->GetOutput())->Clone();
   }
 }
 
 mitk::Image::Pointer mitk::ConvertLabelSetImageToImage(LabelSetImage::ConstPointer labelSetImage)
 {
   Image::Pointer image;
 
   if (labelSetImage->GetNumberOfLayers() > 0)
   {
     if (labelSetImage->GetDimension() == 4)
     {
       AccessFixedDimensionByItk_n(labelSetImage, ::ConvertLabelSetImageToImage, 4, (labelSetImage, image));
     }
     else
     {
       AccessByItk_2(labelSetImage->GetLayerImage(0), ::ConvertLabelSetImageToImage, labelSetImage, image);
     }
 
     image->SetTimeGeometry(labelSetImage->GetTimeGeometry()->Clone());
   }
 
   return image;
 }
 
+
 template <typename TPixel, unsigned int VDimensions>
-static void ConvertImageToLabelSetImage(const itk::VectorImage<TPixel, VDimensions> *image,
-                                        mitk::LabelSetImage::Pointer &labelSetImage)
+static void SplitVectorImage(const itk::VectorImage<TPixel, VDimensions>* image,
+  std::vector<mitk::Image::Pointer>& result)
 {
   typedef itk::VectorImage<TPixel, VDimensions> VectorImageType;
   typedef itk::Image<TPixel, VDimensions> ImageType;
   typedef itk::VectorIndexSelectionCastImageFilter<VectorImageType, ImageType> VectorIndexSelectorType;
 
-  labelSetImage = mitk::LabelSetImage::New();
-
   auto numberOfLayers = image->GetVectorLength();
   for (decltype(numberOfLayers) layer = 0; layer < numberOfLayers; ++layer)
   {
     auto layerSelector = VectorIndexSelectorType::New();
     layerSelector->SetInput(image);
     layerSelector->SetIndex(layer);
     layerSelector->Update();
 
-    mitk::Image::Pointer layerImage;
-    mitk::CastToMitkImage(layerSelector->GetOutput(), layerImage);
+    mitk::Image::Pointer layerImage = mitk::GrabItkImageMemoryChannel(layerSelector->GetOutput(), nullptr, nullptr, false);
+    result.push_back(layerImage);
+  }
+}
+
+std::vector<mitk::Image::Pointer> mitk::SplitVectorImage(const Image* vecImage)
+{
+  if (nullptr == vecImage)
+  {
+    mitkThrow() << "Invalid usage; nullptr passed to SplitVectorImage.";
+  }
+
+  if (vecImage->GetChannelDescriptor().GetPixelType().GetPixelType() != itk::IOPixelEnum::VECTOR)
+  {
+    mitkThrow() << "Invalid usage of SplitVectorImage; passed image is not a vector image. Present pixel type: "<< vecImage->GetChannelDescriptor().GetPixelType().GetPixelTypeAsString();
+  }
+
+  std::vector<mitk::Image::Pointer> result;
+
+  if (4 == vecImage->GetDimension())
+  {
+    AccessVectorFixedDimensionByItk_n(vecImage, ::SplitVectorImage, 4, (result));
+  }
+  else
+  {
+    AccessVectorPixelTypeByItk_n(vecImage, ::SplitVectorImage, (result));
+  }
+
+  for (auto image : result)
+  {
+    image->SetTimeGeometry(vecImage->GetTimeGeometry()->Clone());
+  }
+
+  return result;
+}
+
+mitk::LabelSetImage::Pointer mitk::ConvertImageToLabelSetImage(Image::Pointer image)
+{
+  std::vector<mitk::Image::Pointer> groupImages;
 
-    if (layer == 0)
+  if (image.IsNotNull())
+  {
+    if (image->GetChannelDescriptor().GetPixelType().GetPixelType() == itk::IOPixelEnum::VECTOR)
     {
-      labelSetImage->InitializeByLabeledImage(layerImage);
+      groupImages = SplitVectorImage(image);
     }
     else
     {
-      labelSetImage->AddLayer(layerImage);
+      groupImages.push_back(image);
     }
   }
+  auto labelSetImage = ConvertImageVectorToLabelSetImage(groupImages, image->GetTimeGeometry());
+
+  return labelSetImage;
 }
 
-mitk::LabelSetImage::Pointer mitk::ConvertImageToLabelSetImage(Image::Pointer image)
+mitk::LabelSetImage::Pointer mitk::ConvertImageVectorToLabelSetImage(const std::vector<mitk::Image::Pointer>& images, const mitk::TimeGeometry* timeGeometry)
 {
-  LabelSetImage::Pointer labelSetImage;
+  LabelSetImage::Pointer labelSetImage = mitk::LabelSetImage::New();
 
-  if (image.IsNotNull())
+  for (auto& groupImage : images)
   {
-    if (image->GetChannelDescriptor().GetPixelType().GetPixelType() == itk::IOPixelEnum::VECTOR)
+    if (groupImage== images.front())
     {
-      if (4 == image->GetDimension())
-      {
-        AccessVectorFixedDimensionByItk_n(image, ::ConvertImageToLabelSetImage, 4, (labelSetImage));
-      }
-      else
-      {
-        AccessVectorPixelTypeByItk_n(image, ::ConvertImageToLabelSetImage, (labelSetImage));
-      }
+      labelSetImage->InitializeByLabeledImage(groupImage);
     }
     else
     {
-      labelSetImage = mitk::LabelSetImage::New();
-      labelSetImage->InitializeByLabeledImage(image);
+      labelSetImage->AddLayer(groupImage);
     }
-    labelSetImage->SetTimeGeometry(image->GetTimeGeometry()->Clone());
   }
 
+  labelSetImage->SetTimeGeometry(timeGeometry->Clone());
   return labelSetImage;
 }
+
+mitk::LabelSet::Pointer mitk::GenerateLabelSetWithMappedValues(const LabelSet* sourceLabelset, std::vector<std::pair<Label::PixelType, Label::PixelType> > labelMapping)
+{
+  if (nullptr == sourceLabelset)
+  {
+    mitkThrow() << "Invalid usage; nullptr passed as labelset to GenerateLabelSetWithMappedValues.";
+  }
+
+  auto result = LabelSet::New();
+
+  for (auto [sourceLabelID, destLabelID] : labelMapping)
+  {
+    auto clonedLabel = sourceLabelset->GetLabel(sourceLabelID)->Clone();
+    clonedLabel->SetValue(destLabelID);
+    result->AddLabel(clonedLabel, false);
+  }
+  result->SetLayer(sourceLabelset->GetLayer());
+
+  return result;
+}
diff --git a/Modules/Multilabel/mitkLabelSetImageConverter.h b/Modules/Multilabel/mitkLabelSetImageConverter.h
index 0677c4b5db..1e1b577d79 100644
--- a/Modules/Multilabel/mitkLabelSetImageConverter.h
+++ b/Modules/Multilabel/mitkLabelSetImageConverter.h
@@ -1,32 +1,40 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #ifndef mitkLabelSetImageConverter_h
 #define mitkLabelSetImageConverter_h
 
 #include <mitkLabelSetImage.h>
 
 namespace mitk
 {
   /**
    * \brief Convert mitk::LabelSetImage to mitk::Image (itk::VectorImage)
    */
   MITKMULTILABEL_EXPORT Image::Pointer ConvertLabelSetImageToImage(LabelSetImage::ConstPointer labelSetImage);
 
   /**
    * \brief Convert mitk::Image to mitk::LabelSetImage, templating and differentation between itk::Image and
    * itk::VectorImage is internal
    */
   MITKMULTILABEL_EXPORT LabelSetImage::Pointer ConvertImageToLabelSetImage(Image::Pointer image);
+  MITKMULTILABEL_EXPORT LabelSetImage::Pointer ConvertImageVectorToLabelSetImage(const std::vector<mitk::Image::Pointer>& images, const TimeGeometry* timeGeometry);
+
+  MITKMULTILABEL_EXPORT std::vector<mitk::Image::Pointer> SplitVectorImage(const Image* vecImage);
+
+  /** Function takes a label set and transfers all labels indicated in the label mapping (first element of pair) into a result label set. In the result label set
+  the cloned labels will have the label value indicated by the mapping (second element of pair).
+  @remark: Only labels will be transfered, nothing else. So things like message observers or m_ReservedLabelValuesFunctor must be copied explicitly.*/
+  MITKMULTILABEL_EXPORT LabelSet::Pointer GenerateLabelSetWithMappedValues(const LabelSet* sourceLabelset, std::vector<std::pair<Label::PixelType, Label::PixelType> > labelMapping);
 }
 
 #endif
diff --git a/Modules/Multilabel/mitkLabelSetImageHelper.cpp b/Modules/Multilabel/mitkLabelSetImageHelper.cpp
index b2147ee2d1..e0a6c46724 100644
--- a/Modules/Multilabel/mitkLabelSetImageHelper.cpp
+++ b/Modules/Multilabel/mitkLabelSetImageHelper.cpp
@@ -1,157 +1,144 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include <mitkLabelSetImageHelper.h>
 
 #include <mitkLabelSetImage.h>
 #include <mitkExceptionMacro.h>
 #include <mitkProperties.h>
 
 #include <array>
 #include <regex>
 #include <vector>
 
 namespace
 {
   template <typename T>
   std::array<int, 3> QuantizeColor(const T* color)
   {
     return {
       static_cast<int>(std::round(color[0] * 255)),
       static_cast<int>(std::round(color[1] * 255)),
       static_cast<int>(std::round(color[2] * 255)) };
   }
 
   mitk::Color FromLookupTableColor(const double* lookupTableColor)
   {
     mitk::Color color;
     color.Set(
       static_cast<float>(lookupTableColor[0]),
       static_cast<float>(lookupTableColor[1]),
       static_cast<float>(lookupTableColor[2]));
     return color;
   }
 }
 
 mitk::DataNode::Pointer mitk::LabelSetImageHelper::CreateEmptySegmentationNode(const std::string& segmentationName)
 {
   auto newSegmentationNode = mitk::DataNode::New();
   newSegmentationNode->SetName(segmentationName);
 
   // initialize "showVolume"-property to false to prevent recalculating the volume while working on the segmentation
   newSegmentationNode->SetProperty("showVolume", mitk::BoolProperty::New(false));
 
   return newSegmentationNode;
 }
 
 
 mitk::DataNode::Pointer mitk::LabelSetImageHelper::CreateNewSegmentationNode(const DataNode* referenceNode,
   const Image* initialSegmentationImage, const std::string& segmentationName)
 {
   std::string newSegmentationName = segmentationName;
   if (newSegmentationName.empty())
   {
     newSegmentationName = referenceNode->GetName();
     newSegmentationName.append("-labels");
   }
 
   if (nullptr == initialSegmentationImage)
   {
     return nullptr;
   }
 
   auto newLabelSetImage = mitk::LabelSetImage::New();
   try
   {
     newLabelSetImage->Initialize(initialSegmentationImage);
   }
   catch (mitk::Exception &e)
   {
     mitkReThrow(e) << "Could not initialize new label set image.";
     return nullptr;
   }
 
-  // set additional image information
-  newLabelSetImage->GetExteriorLabel()->SetProperty("name.parent", mitk::StringProperty::New(referenceNode->GetName()));
-  newLabelSetImage->GetExteriorLabel()->SetProperty("name.image", mitk::StringProperty::New(newSegmentationName));
-
   auto newSegmentationNode = CreateEmptySegmentationNode(newSegmentationName);
   newSegmentationNode->SetData(newLabelSetImage);
 
   return newSegmentationNode;
 }
 
 mitk::Label::Pointer mitk::LabelSetImageHelper::CreateNewLabel(const LabelSetImage* labelSetImage)
 {
   if (nullptr == labelSetImage)
     return nullptr;
 
   const std::regex genericLabelNameRegEx("Label ([1-9][0-9]*)");
   int maxGenericLabelNumber = 0;
 
-  std::vector<std::array<int, 3>> colorsInUse;
-
-  const auto numLabelSets = labelSetImage->GetNumberOfLayers();
+  std::vector<std::array<int, 3>> colorsInUse = { {0,0,0} }; //black is always in use.
 
-  for (std::remove_const_t<decltype(numLabelSets)> i = 0; i < numLabelSets; ++i)
+  for (auto & label : labelSetImage->GetLabels())
   {
-    auto labelSet = labelSetImage->GetLabelSet(i);
-    auto labelEndIter = labelSet->IteratorConstEnd();
+    auto labelName = label->GetName();
+    std::smatch match;
 
-    for (auto labelIter = labelSet->IteratorConstBegin(); labelIter != labelEndIter; ++labelIter)
-    {
-      auto label = labelIter->second;
-      auto labelName = label->GetName();
-      std::smatch match;
+    if (std::regex_match(labelName, match, genericLabelNameRegEx))
+      maxGenericLabelNumber = std::max(maxGenericLabelNumber, std::stoi(match[1].str()));
 
-      if (std::regex_match(labelName, match, genericLabelNameRegEx))
-        maxGenericLabelNumber = std::max(maxGenericLabelNumber, std::stoi(match[1].str()));
+    const auto quantizedLabelColor = QuantizeColor(label->GetColor().data());
 
-      const auto quantizedLabelColor = QuantizeColor(label->GetColor().data());
-
-      if (std::find(colorsInUse.begin(), colorsInUse.end(), quantizedLabelColor) == std::end(colorsInUse))
-        colorsInUse.push_back(quantizedLabelColor);
-    }
+    if (std::find(colorsInUse.begin(), colorsInUse.end(), quantizedLabelColor) == std::end(colorsInUse))
+      colorsInUse.push_back(quantizedLabelColor);
   }
 
   auto newLabel = mitk::Label::New();
   newLabel->SetName("Label " + std::to_string(maxGenericLabelNumber + 1));
 
   auto lookupTable = mitk::LookupTable::New();
   lookupTable->SetType(mitk::LookupTable::LookupTableType::MULTILABEL);
 
   std::array<double, 3> lookupTableColor;
   const int maxTries = 25;
   bool newColorFound = false;
 
   for (int i = 0; i < maxTries; ++i)
   {
     lookupTable->GetColor(i, lookupTableColor.data());
 
     auto quantizedLookupTableColor = QuantizeColor(lookupTableColor.data());
 
     if (std::find(colorsInUse.begin(), colorsInUse.end(), quantizedLookupTableColor) == std::end(colorsInUse))
     {
       newLabel->SetColor(FromLookupTableColor(lookupTableColor.data()));
       newColorFound = true;
       break;
     }
   }
 
   if (!newColorFound)
   {
     lookupTable->GetColor(labelSetImage->GetTotalNumberOfLabels(), lookupTableColor.data());
     newLabel->SetColor(FromLookupTableColor(lookupTableColor.data()));
   }
 
   return newLabel;
 }
diff --git a/Modules/Multilabel/mitkLabelSetImageSurfaceStampFilter.cpp b/Modules/Multilabel/mitkLabelSetImageSurfaceStampFilter.cpp
index 059123787b..d0388bf099 100644
--- a/Modules/Multilabel/mitkLabelSetImageSurfaceStampFilter.cpp
+++ b/Modules/Multilabel/mitkLabelSetImageSurfaceStampFilter.cpp
@@ -1,108 +1,108 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "mitkLabelSetImageSurfaceStampFilter.h"
 
 #include "mitkImageAccessByItk.h"
 #include "mitkImageCast.h"
 
 #include <mitkLabelSetImage.h>
 #include <mitkLabelSetImage.h>
 #include <mitkSurface.h>
 #include <mitkSurfaceToImageFilter.h>
 
 mitk::LabelSetImageSurfaceStampFilter::LabelSetImageSurfaceStampFilter() : m_ForceOverwrite(false)
 {
   this->SetNumberOfIndexedInputs(1);
   this->SetNumberOfRequiredInputs(1);
 }
 
 mitk::LabelSetImageSurfaceStampFilter::~LabelSetImageSurfaceStampFilter()
 {
 }
 
 void mitk::LabelSetImageSurfaceStampFilter::GenerateData()
 {
   // GenerateOutputInformation();
   this->SetNthOutput(0, this->GetInput(0));
 
   mitk::Image::Pointer inputImage = this->GetInput(0);
 
   if (m_Surface.IsNull())
   {
     MITK_ERROR << "Input surface is nullptr.";
     return;
   }
 
   mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New();
   surfaceToImageFilter->MakeOutputBinaryOn();
   surfaceToImageFilter->SetInput(m_Surface);
   surfaceToImageFilter->SetImage(inputImage);
   surfaceToImageFilter->Update();
   mitk::Image::Pointer resultImage = surfaceToImageFilter->GetOutput();
 
   AccessByItk_1(inputImage, ItkImageProcessing, resultImage);
   inputImage->DisconnectPipeline();
 }
 
 template <typename TPixel, unsigned int VImageDimension>
 void mitk::LabelSetImageSurfaceStampFilter::ItkImageProcessing(itk::Image<TPixel, VImageDimension> *itkImage,
                                                                mitk::Image::Pointer resultImage)
 {
   typedef itk::Image<TPixel, VImageDimension> ImageType;
   mitk::LabelSetImage::Pointer LabelSetInputImage = dynamic_cast<LabelSetImage *>(GetInput());
   try
   {
     typename ImageType::Pointer itkResultImage = ImageType::New();
     mitk::CastToItkImage(resultImage, itkResultImage);
 
     typedef itk::ImageRegionConstIterator<ImageType> SourceIteratorType;
     typedef itk::ImageRegionIterator<ImageType> TargetIteratorType;
 
     SourceIteratorType sourceIter(itkResultImage, itkResultImage->GetLargestPossibleRegion());
     sourceIter.GoToBegin();
 
     TargetIteratorType targetIter(itkImage, itkImage->GetLargestPossibleRegion());
     targetIter.GoToBegin();
 
     int activeLabel = (LabelSetInputImage->GetActiveLabel(LabelSetInputImage->GetActiveLayer()))->GetValue();
 
     while (!sourceIter.IsAtEnd())
     {
       auto sourceValue = static_cast<int>(sourceIter.Get());
       auto targetValue = static_cast<int>(targetIter.Get());
 
-      if ((sourceValue != 0) &&
+      if ((sourceValue != LabelSetImage::UnlabeledValue) &&
           (m_ForceOverwrite ||
-           !LabelSetInputImage->GetLabel(targetValue)->GetLocked())) // skip exterior and locked labels
+           !LabelSetInputImage->GetLabel(targetValue)->GetLocked())) // skip unlabled pixels and locked labels
       {
         targetIter.Set(activeLabel);
       }
       ++sourceIter;
       ++targetIter;
     }
   }
   catch (itk::ExceptionObject &e)
   {
     mitkThrow() << e.GetDescription();
   }
   this->Modified();
 }
 
 void mitk::LabelSetImageSurfaceStampFilter::GenerateOutputInformation()
 {
   mitk::Image::Pointer inputImage = (mitk::Image *)this->GetInput();
   mitk::Image::Pointer output = this->GetOutput();
   itkDebugMacro(<< "GenerateOutputInformation()");
   if (inputImage.IsNull())
     return;
 }
diff --git a/Modules/Multilabel/mitkLabelSetIOHelper.cpp b/Modules/Multilabel/mitkMultiLabelIOHelper.cpp
similarity index 57%
rename from Modules/Multilabel/mitkLabelSetIOHelper.cpp
rename to Modules/Multilabel/mitkMultiLabelIOHelper.cpp
index 9833b4151c..80cf783467 100644
--- a/Modules/Multilabel/mitkLabelSetIOHelper.cpp
+++ b/Modules/Multilabel/mitkMultiLabelIOHelper.cpp
@@ -1,280 +1,442 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
-#include "mitkLabelSetIOHelper.h"
+#include "mitkMultiLabelIOHelper.h"
 
 #include "mitkLabelSetImage.h"
 #include <mitkBasePropertySerializer.h>
 
+#include "itkMetaDataDictionary.h"
+#include "itkMetaDataObject.h"
+
 #include <tinyxml2.h>
 
 namespace
 {
   std::string EnsureExtension(const std::string& filename)
   {
     const std::string extension = ".lsetp";
 
     if (filename.size() < extension.size() || std::string::npos == filename.find(extension, filename.size() - extension.size()))
       return filename + extension;
 
     return filename;
   }
 }
 
-bool mitk::LabelSetIOHelper::SaveLabelSetImagePreset(const std::string &presetFilename,
+bool mitk::MultiLabelIOHelper::SaveLabelSetImagePreset(const std::string &presetFilename,
                                                      const mitk::LabelSetImage *inputImage)
 {
   const auto filename = EnsureExtension(presetFilename);
 
   tinyxml2::XMLDocument xmlDocument;
   xmlDocument.InsertEndChild(xmlDocument.NewDeclaration());
 
   auto *rootElement = xmlDocument.NewElement("LabelSetImagePreset");
   rootElement->SetAttribute("layers", inputImage->GetNumberOfLayers());
   xmlDocument.InsertEndChild(rootElement);
 
   for (unsigned int layerIndex = 0; layerIndex < inputImage->GetNumberOfLayers(); layerIndex++)
   {
     auto *layerElement = xmlDocument.NewElement("Layer");
     layerElement->SetAttribute("index", layerIndex);
     layerElement->SetAttribute("labels", inputImage->GetNumberOfLabels(layerIndex));
     rootElement->InsertEndChild(layerElement);
 
     for (unsigned int labelIndex = 0; labelIndex < inputImage->GetNumberOfLabels(layerIndex); labelIndex++)
-      layerElement->InsertEndChild(LabelSetIOHelper::GetLabelAsXMLElement(xmlDocument, inputImage->GetLabel(labelIndex, layerIndex)));
+      layerElement->InsertEndChild(MultiLabelIOHelper::GetLabelAsXMLElement(xmlDocument, inputImage->GetLabel(labelIndex, layerIndex)));
   }
 
   return tinyxml2::XML_SUCCESS == xmlDocument.SaveFile(filename.c_str());
 }
 
-bool mitk::LabelSetIOHelper::LoadLabelSetImagePreset(const std::string &presetFilename,
+bool mitk::MultiLabelIOHelper::LoadLabelSetImagePreset(const std::string &presetFilename,
                                                      mitk::LabelSetImage *inputImage)
 {
   if (nullptr == inputImage)
     return false;
 
   const auto filename = EnsureExtension(presetFilename);
 
   tinyxml2::XMLDocument xmlDocument;
 
   if (tinyxml2::XML_SUCCESS != xmlDocument.LoadFile(filename.c_str()))
   {
     MITK_WARN << "Label set preset file \"" << filename << "\" does not exist or cannot be opened";
     return false;
   }
 
   auto *rootElement = xmlDocument.FirstChildElement("LabelSetImagePreset");
 
   if (nullptr == rootElement)
   {
     MITK_WARN << "Not a valid Label set preset";
     return false;
   }
 
   auto activeLayerBackup = inputImage->GetActiveLayer();
 
   int numberOfLayers = 0;
   rootElement->QueryIntAttribute("layers", &numberOfLayers);
 
   auto* layerElement = rootElement->FirstChildElement("Layer");
 
   if (nullptr == layerElement)
   {
     MITK_WARN << "Label set preset does not contain any layers";
     return false;
   }
 
   for (int layerIndex = 0; layerIndex < numberOfLayers; layerIndex++)
   {
     int numberOfLabels = 0;
     layerElement->QueryIntAttribute("labels", &numberOfLabels);
 
     if (nullptr == inputImage->GetLabelSet(layerIndex))
     {
       inputImage->AddLayer();
     }
     else
     {
       inputImage->SetActiveLayer(layerIndex);
     }
 
     auto *labelElement = layerElement->FirstChildElement("Label");
 
     if (nullptr == labelElement)
       continue;
 
     for (int labelIndex = 0; labelIndex < numberOfLabels; labelIndex++)
     {
-      auto label = mitk::LabelSetIOHelper::LoadLabelFromXMLDocument(labelElement);
+      auto label = mitk::MultiLabelIOHelper::LoadLabelFromXMLDocument(labelElement);
       const auto labelValue = label->GetValue();
 
-      if (0 != labelValue)
+      if (LabelSetImage::UnlabeledValue != labelValue)
       {
         auto* labelSet = inputImage->GetLabelSet(layerIndex);
         auto* alreadyExistingLabel = labelSet->GetLabel(labelValue);
 
         if (nullptr != alreadyExistingLabel)
         {
           // Override existing label with label from preset
           alreadyExistingLabel->ConcatenatePropertyList(label);
           labelSet->UpdateLookupTable(labelValue);
         }
         else
         {
           labelSet->AddLabel(label);
         }
       }
 
       labelElement = labelElement->NextSiblingElement("Label");
 
       if (nullptr == labelElement)
         continue;
     }
 
     layerElement = layerElement->NextSiblingElement("Layer");
 
     if (nullptr == layerElement)
       continue;
   }
 
   inputImage->SetActiveLayer(activeLayerBackup);
 
   return true;
 }
 
-tinyxml2::XMLElement *mitk::LabelSetIOHelper::GetLabelAsXMLElement(tinyxml2::XMLDocument &doc, Label *label)
+tinyxml2::XMLElement *mitk::MultiLabelIOHelper::GetLabelAsXMLElement(tinyxml2::XMLDocument &doc, Label *label)
 {
   auto *labelElem = doc.NewElement("Label");
 
   if (nullptr != label)
   {
     // add XML contents
     const PropertyList::PropertyMap* propmap = label->GetMap();
     for (auto iter = propmap->begin(); iter != propmap->end(); ++iter)
     {
       std::string key = iter->first;
       const BaseProperty* property = iter->second;
       auto* element = PropertyToXMLElement(doc, key, property);
       if (element)
         labelElem->InsertEndChild(element);
     }
   }
 
   return labelElem;
 }
 
-mitk::Label::Pointer mitk::LabelSetIOHelper::LoadLabelFromXMLDocument(const tinyxml2::XMLElement *labelElem)
+mitk::Label::Pointer mitk::MultiLabelIOHelper::LoadLabelFromXMLDocument(const tinyxml2::XMLElement *labelElem)
 {
   // reread
   auto *propElem = labelElem->FirstChildElement("property");
 
   std::string name;
   mitk::BaseProperty::Pointer prop;
 
   mitk::Label::Pointer label = mitk::Label::New();
   while (propElem)
   {
-    LabelSetIOHelper::PropertyFromXMLElement(name, prop, propElem);
+    MultiLabelIOHelper::PropertyFromXMLElement(name, prop, propElem);
     label->SetProperty(name, prop);
     propElem = propElem->NextSiblingElement("property");
   }
 
   return label.GetPointer();
 }
 
-tinyxml2::XMLElement *mitk::LabelSetIOHelper::PropertyToXMLElement(tinyxml2::XMLDocument &doc, const std::string &key, const BaseProperty *property)
+tinyxml2::XMLElement *mitk::MultiLabelIOHelper::PropertyToXMLElement(tinyxml2::XMLDocument &doc, const std::string &key, const BaseProperty *property)
 {
   auto *keyelement = doc.NewElement("property");
   keyelement->SetAttribute("key", key.c_str());
   keyelement->SetAttribute("type", property->GetNameOfClass());
 
   // construct name of serializer class
   std::string serializername(property->GetNameOfClass());
   serializername += "Serializer";
 
   std::list<itk::LightObject::Pointer> allSerializers =
     itk::ObjectFactoryBase::CreateAllInstance(serializername.c_str());
   if (allSerializers.size() < 1)
     MITK_ERROR << "No serializer found for " << property->GetNameOfClass() << ". Skipping object";
 
   if (allSerializers.size() > 1)
     MITK_WARN << "Multiple serializers found for " << property->GetNameOfClass() << "Using arbitrarily the first one.";
 
   for (auto iter = allSerializers.begin(); iter != allSerializers.end();
        ++iter)
   {
     if (auto *serializer = dynamic_cast<BasePropertySerializer *>(iter->GetPointer()))
     {
       serializer->SetProperty(property);
       try
       {
         auto *valueelement = serializer->Serialize(doc);
         if (valueelement)
           keyelement->InsertEndChild(valueelement);
       }
       catch (std::exception &e)
       {
         MITK_ERROR << "Serializer " << serializer->GetNameOfClass() << " failed: " << e.what();
       }
       break;
     }
   }
   return keyelement;
 }
 
-bool mitk::LabelSetIOHelper::PropertyFromXMLElement(std::string &key,
+bool mitk::MultiLabelIOHelper::PropertyFromXMLElement(std::string &key,
                                                     mitk::BaseProperty::Pointer &prop,
                                                     const tinyxml2::XMLElement *elem)
 {
   const char* typeC = elem->Attribute("type");
   std::string type = nullptr != typeC
     ? typeC
     : "";
 
   const char* keyC = elem->Attribute("key");
   key = nullptr != keyC
     ? keyC
     : "";
 
   // construct name of serializer class
   std::string serializername(type);
   serializername += "Serializer";
 
   std::list<itk::LightObject::Pointer> allSerializers =
     itk::ObjectFactoryBase::CreateAllInstance(serializername.c_str());
   if (allSerializers.size() < 1)
     MITK_ERROR << "No serializer found for " << type << ". Skipping object";
 
   if (allSerializers.size() > 1)
     MITK_WARN << "Multiple deserializers found for " << type << "Using arbitrarily the first one.";
 
   for (auto iter = allSerializers.begin(); iter != allSerializers.end();
        ++iter)
   {
     if (auto *serializer = dynamic_cast<BasePropertySerializer *>(iter->GetPointer()))
     {
       try
       {
         prop = serializer->Deserialize(elem->FirstChildElement());
       }
       catch (std::exception &e)
       {
         MITK_ERROR << "Deserializer " << serializer->GetNameOfClass() << " failed: " << e.what();
         return false;
       }
       break;
     }
   }
   if (prop.IsNull())
     return false;
   return true;
 }
+
+int mitk::MultiLabelIOHelper::GetIntByKey(const itk::MetaDataDictionary& dic, const std::string& str)
+{
+  std::vector<std::string> imgMetaKeys = dic.GetKeys();
+  std::vector<std::string>::const_iterator itKey = imgMetaKeys.begin();
+  std::string metaString("");
+  for (; itKey != imgMetaKeys.end(); itKey++)
+  {
+    itk::ExposeMetaData<std::string>(dic, *itKey, metaString);
+    if (itKey->find(str.c_str()) != std::string::npos)
+    {
+      return atoi(metaString.c_str());
+    }
+  }
+  return 0;
+}
+
+std::string mitk::MultiLabelIOHelper::GetStringByKey(const itk::MetaDataDictionary& dic, const std::string& str)
+{
+  std::vector<std::string> imgMetaKeys = dic.GetKeys();
+  std::vector<std::string>::const_iterator itKey = imgMetaKeys.begin();
+  std::string metaString("");
+  for (; itKey != imgMetaKeys.end(); itKey++)
+  {
+    itk::ExposeMetaData<std::string>(dic, *itKey, metaString);
+    if (itKey->find(str.c_str()) != std::string::npos)
+    {
+      return metaString;
+    }
+  }
+  return metaString;
+}
+
+nlohmann::json mitk::MultiLabelIOHelper::SerializeMultLabelGroupsToJSON(const mitk::LabelSetImage* inputImage)
+{
+  if (nullptr == inputImage)
+  {
+    mitkThrow() << "Invalid call of SerializeMultLabelGroupsToJSON. Passed image pointer is null.";
+  }
+
+  nlohmann::json result;
+
+  for (LabelSetImage::GroupIndexType i = 0; i < inputImage->GetNumberOfLayers(); i++)
+  {
+    nlohmann::json jgroup;
+    nlohmann::json jlabels;
+
+    for (const auto& label : inputImage->GetLabelsInGroup(i))
+    {
+      jlabels.emplace_back(SerializeLabelToJSON(label));
+    }
+    jgroup["labels"] = jlabels;
+    result.emplace_back(jgroup);
+  }
+  return result;
+};
+
+std::vector<mitk::LabelSet::Pointer> mitk::MultiLabelIOHelper::DeserializeMultiLabelGroupsFromJSON(const nlohmann::json& listOfLabelSets)
+{
+  std::vector<LabelSet::Pointer> result;
+
+  for (const auto& jlabelset : listOfLabelSets)
+  {
+    LabelSet::Pointer labelSet = LabelSet::New();
+    if (jlabelset.find("labels") != jlabelset.end())
+    {
+      auto jlabels = jlabelset["labels"];
+
+      for (const auto& jlabel : jlabels)
+      {
+        auto label = DeserializeLabelFromJSON(jlabel);
+        labelSet->AddLabel(label, false);
+      }
+    }
+    result.emplace_back(labelSet);
+  }
+
+  return result;
+}
+
+nlohmann::json mitk::MultiLabelIOHelper::SerializeLabelToJSON(const Label* label)
+{
+  if (nullptr == label)
+  {
+    mitkThrow() << "Invalid call of GetLabelAsJSON. Passed label pointer is null.";
+  }
+
+  nlohmann::json j;
+  j["name"] = label->GetName();
+
+  j["value"] = label->GetValue();
+
+  nlohmann::json jcolor;
+  jcolor["type"] = "ColorProperty";
+  jcolor["value"] = {label->GetColor().GetRed(), label->GetColor().GetGreen(), label->GetColor().GetBlue() };
+  j["color"] = jcolor;
+
+  j["locked"] = label->GetLocked();
+  j["opacity"] = label->GetOpacity();
+  j["visible"] = label->GetVisible();
+  return j;
+};
+
+template<typename TValueType> bool GetValueFromJson(const nlohmann::json& labelJson, const std::string& key, TValueType& value)
+{
+  if (labelJson.find(key) != labelJson.end())
+  {
+    try
+    {
+      value = labelJson[key].get<TValueType>();
+      return true;
+    }
+    catch (...)
+    {
+      MITK_ERROR << "Unable to read label information from json. Value has wrong type. Failed key: " << key << "; invalid value: " << labelJson[key].dump();
+      throw;
+    }
+  }
+  return false;
+}
+
+mitk::Label::Pointer mitk::MultiLabelIOHelper::DeserializeLabelFromJSON(const nlohmann::json& labelJson)
+{
+  Label::Pointer resultLabel = Label::New();
+
+  std::string name = "Unkown label name";
+  GetValueFromJson(labelJson, "name", name);
+  resultLabel->SetName(name);
+
+  Label::PixelType value = 1;
+  GetValueFromJson(labelJson, "value", value);
+  resultLabel->SetValue(value);
+
+  if (labelJson.find("color") != labelJson.end())
+  {
+    auto jcolor = labelJson["color"]["value"];
+    Color color;
+    color.SetRed(jcolor[0].get<float>());
+    color.SetGreen(jcolor[1].get<float>());
+    color.SetBlue(jcolor[2].get<float>());
+
+    resultLabel->SetColor(color);
+  }
+
+  bool locked = false;
+  if (GetValueFromJson(labelJson, "locked", locked))
+    resultLabel->SetLocked(locked);
+
+  float opacity = 1.;
+  if (GetValueFromJson(labelJson, "opacity", opacity))
+    resultLabel->SetOpacity(opacity);
+
+
+  bool visible = true;
+  if (GetValueFromJson(labelJson, "visible", visible))
+    resultLabel->SetVisible(visible);
+
+  return resultLabel;
+}
diff --git a/Modules/Multilabel/mitkLabelSetIOHelper.h b/Modules/Multilabel/mitkMultiLabelIOHelper.h
similarity index 67%
rename from Modules/Multilabel/mitkLabelSetIOHelper.h
rename to Modules/Multilabel/mitkMultiLabelIOHelper.h
index a1a6ac8621..a30e22787e 100644
--- a/Modules/Multilabel/mitkLabelSetIOHelper.h
+++ b/Modules/Multilabel/mitkMultiLabelIOHelper.h
@@ -1,98 +1,127 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
-#ifndef mitkLabelSetIOHelper_h
-#define mitkLabelSetIOHelper_h
+#ifndef mitkMultiLabelIOHelper_h
+#define mitkMultiLabelIOHelper_h
+
+#include <mitkLabelSet.h>
 
-#include <MitkMultilabelExports.h>
 #include <itkSmartPointer.h>
+#include <nlohmann/json.hpp>
+
+#include <MitkMultilabelExports.h>
 
 namespace tinyxml2
 {
   class XMLDocument;
   class XMLElement;
 }
 
+namespace itk
+{
+  class MetaDataDictionary;
+}
+
 namespace mitk
 {
-  class BaseProperty;
   class LabelSetImage;
-  class Label;
+
+  const constexpr char* const PROPERTY_NAME_TIMEGEOMETRY_TYPE = "org.mitk.timegeometry.type";
+  const constexpr char* const PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS = "org.mitk.timegeometry.timepoints";
+  const constexpr char* const PROPERTY_KEY_TIMEGEOMETRY_TYPE = "org_mitk_timegeometry_type";
+  const constexpr char* const PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS = "org_mitk_timegeometry_timepoints";
+  const constexpr char* const PROPERTY_KEY_UID = "org_mitk_uid";
 
   /**
-   * @brief The LabelSetIOHelper is a static helper class that supports serialization of mitk::LabelSetImage
+   * @brief The MultiLabelIOHelper is a static helper class that supports serialization of mitk::LabelSetImage
    *
    * This class provides static functions for converting mitk::Label into XML and also allows the serialization
    * of mitk::LabelSet as presets
    */
-  class MITKMULTILABEL_EXPORT LabelSetIOHelper
+  class MITKMULTILABEL_EXPORT MultiLabelIOHelper
   {
   public:
     /**
      * @brief Saves the mitk::LabelSet configuration of inputImage to presetFilename.
      * The preset is stored as "*.lsetp"
      * @param presetFilename the filename including the filesystem path
      * @param inputImage the input image from which the preset should be generated
      * @return true if the serialization was successful and false otherwise
      */
     static bool SaveLabelSetImagePreset(const std::string &presetFilename,
                                         const mitk::LabelSetImage *inputImage);
 
     /**
      * @brief Loads an existing preset for a mitk::LabelSetImage from presetFilename and applies it to inputImage
      * @param presetFilename the filename of the preset including the filesystem path
      * @param inputImage the image to which the loaded preset will be applied
      * @return true if the deserilization was successful and false otherwise
      */
     static bool LoadLabelSetImagePreset(const std::string &presetFilename,
                                         mitk::LabelSetImage *inputImage);
 
     /**
      * @brief Creates a mitk::Label from an XML element
      * @param labelElem the xml element from which a mitk::Label will be created
      * @return the created mitk::Label
      */
     static itk::SmartPointer<mitk::Label> LoadLabelFromXMLDocument(const tinyxml2::XMLElement *labelElem);
 
     /**
      * @brief Creates an XML element from a mitk::Label
      * @param doc
      * @param label the mitk::Label from which the xml element will be created
      * @return the created XML element
      */
     static tinyxml2::XMLElement *GetLabelAsXMLElement(tinyxml2::XMLDocument &doc, Label *label);
 
     /**
      * @brief Since a mitk::Label is basically a mitk::PropertyList this function coverts the label's properties into
      * XML
      * @param doc
      * @param key the property's key which will be used in the XML element
      * @param property the mitk::BaseProperty that should be converted
      * @return the created XML element
      */
     static tinyxml2::XMLElement *PropertyToXMLElement(tinyxml2::XMLDocument& doc, const std::string &key, const BaseProperty *property);
 
     /**
      * @brief Since a mitk::Label is basically a mitk::PropertyList this function coverts a XML element into a property
      * @param key the property's key
      * @param prop the mitk::BaseProperty that will be created
      * @param elem the XML elem from which the property will be created
      * @return true if the conversion was successful and false otherwise
      */
     static bool PropertyFromXMLElement(std::string &key, itk::SmartPointer<mitk::BaseProperty> &prop, const tinyxml2::XMLElement *elem);
 
+    /** Helper that extracts the value of a key in a meta dictionary as int.
+    * If the key does not exist 0 is returned.*/
+    static int GetIntByKey(const itk::MetaDataDictionary& dic, const std::string& key);
+    /** Helper that extracts the value of a key in a meta dictionary as string.
+    * If the key does not exist an empty string is returned.*/
+    static std::string GetStringByKey(const itk::MetaDataDictionary& dic, const std::string& key);
+
+
+    static nlohmann::json SerializeMultLabelGroupsToJSON(const mitk::LabelSetImage* inputImage);
+
+    static std::vector<LabelSet::Pointer> DeserializeMultiLabelGroupsFromJSON(const nlohmann::json& listOfLabelSets);
+
+    static nlohmann::json SerializeLabelToJSON(const Label* label);
+
+    static mitk::Label::Pointer DeserializeLabelFromJSON(const nlohmann::json& labelJson);
+
   private:
-    LabelSetIOHelper();
+    MultiLabelIOHelper();
   };
 }
 
 #endif
diff --git a/Modules/Segmentation/Algorithms/mitkDiffImageApplier.cpp b/Modules/Segmentation/Algorithms/mitkDiffImageApplier.cpp
index 812c2b06fa..86ccf33984 100644
--- a/Modules/Segmentation/Algorithms/mitkDiffImageApplier.cpp
+++ b/Modules/Segmentation/Algorithms/mitkDiffImageApplier.cpp
@@ -1,376 +1,376 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "mitkDiffImageApplier.h"
 
 #include "mitkApplyDiffImageOperation.h"
 #include "mitkImageAccessByItk.h"
 #include "mitkImageCast.h"
 #include "mitkImageTimeSelector.h"
 #include "mitkRenderingManager.h"
 #include "mitkSegmentationInterpolationController.h"
 
 #include <mitkIOUtil.h>
 
 #include <itkImageRegionConstIterator.h>
 #include <itkImageSliceIteratorWithIndex.h>
 
 #include <type_traits>
 
 mitk::DiffImageApplier::DiffImageApplier()
   : m_Image(nullptr),
     m_SliceDifferenceImage(nullptr),
     m_SliceIndex(0),
     m_SliceDimension(0),
     m_TimeStep(0),
     m_Dimension0(0),
     m_Dimension1(0),
     m_DestinationLabel(std::numeric_limits<mitk::Label::PixelType>::max()),
     m_Factor(1.0)
 {
 }
 
 mitk::DiffImageApplier::~DiffImageApplier()
 {
 }
 
 void mitk::DiffImageApplier::SetDestinationLabel(mitk::Label::PixelType label)
 {
   m_DestinationLabel = label;
 }
 
 void mitk::DiffImageApplier::ExecuteOperation(Operation *operation)
 {
   auto *imageOperation = dynamic_cast<ApplyDiffImageOperation *>(operation);
   if (imageOperation // we actually have the kind of operation that we can handle
       &&
       imageOperation->IsImageStillValid()) // AND the image is not yet deleted
   {
     m_Image = imageOperation->GetImage();
     Image::Pointer image3D = m_Image; // will be changed later in case of 3D+t
 
     m_SliceDifferenceImage = imageOperation->GetDiffImage();
     m_TimeStep = imageOperation->GetTimeStep();
 
     m_Factor = imageOperation->GetFactor();
 
     if (m_SliceDifferenceImage->GetDimension() == 2)
     {
       m_SliceIndex = imageOperation->GetSliceIndex();
       m_SliceDimension = imageOperation->GetSliceDimension();
       switch (m_SliceDimension)
       {
         default:
         case 2:
           m_Dimension0 = 0;
           m_Dimension1 = 1;
           break;
         case 1:
           m_Dimension0 = 0;
           m_Dimension1 = 2;
           break;
         case 0:
           m_Dimension0 = 1;
           m_Dimension1 = 2;
           break;
       }
 
       if (m_SliceDifferenceImage->GetDimension() != 2 || (m_Image->GetDimension() < 3 || m_Image->GetDimension() > 4) ||
           m_SliceDifferenceImage->GetDimension(0) != m_Image->GetDimension(m_Dimension0) ||
           m_SliceDifferenceImage->GetDimension(1) != m_Image->GetDimension(m_Dimension1) ||
           m_SliceIndex >= m_Image->GetDimension(m_SliceDimension))
       {
         itkExceptionMacro(
           "Slice and image dimensions differ or slice index is too large. Sorry, cannot work like this.");
         return;
       }
 
       if (m_Image->GetDimension() == 4)
       {
         ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New();
         timeSelector->SetInput(m_Image);
         timeSelector->SetTimeNr(m_TimeStep);
         timeSelector->UpdateLargestPossibleRegion();
         image3D = timeSelector->GetOutput();
       }
 
       AccessFixedDimensionByItk(image3D, ItkImageSwitch2DDiff, 3);
 
       if (m_Factor == 1 || m_Factor == -1)
       {
         if (m_Factor == -1)
         {
           // multiply diff pixels by factor and then send this diff slice
           AccessFixedDimensionByItk(m_SliceDifferenceImage, ItkInvertPixelValues, 2);
         }
 
         // just send the diff to SegmentationInterpolationController
         SegmentationInterpolationController *interpolator =
           SegmentationInterpolationController::InterpolatorForImage(m_Image);
         if (interpolator)
         {
           interpolator->BlockModified(true);
           interpolator->SetChangedSlice(m_SliceDifferenceImage, m_SliceDimension, m_SliceIndex, m_TimeStep);
         }
 
         m_Image->Modified();
 
         if (interpolator)
         {
           interpolator->BlockModified(false);
         }
 
         if (m_Factor == -1) // return to normal values
         {
           AccessFixedDimensionByItk(m_SliceDifferenceImage, ItkInvertPixelValues, 2);
         }
       }
       else // no trivial case, too lazy to do something else
       {
         m_Image->Modified(); // check if interpolation is called. prefer to send diff directly
       }
 
       RenderingManager::GetInstance()->RequestUpdateAll();
     }
     else if (m_SliceDifferenceImage->GetDimension() == 3)
     {
       // ...
       if (m_SliceDifferenceImage->GetDimension(0) != m_Image->GetDimension(0) ||
           m_SliceDifferenceImage->GetDimension(1) != m_Image->GetDimension(1) ||
           m_SliceDifferenceImage->GetDimension(2) != m_Image->GetDimension(2) || m_TimeStep >= m_Image->GetDimension(3))
       {
         itkExceptionMacro("Diff image size differs from original image size. Sorry, cannot work like this.");
         return;
       }
 
       if (m_Image->GetDimension() == 4)
       {
         ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New();
         timeSelector->SetInput(m_Image);
         timeSelector->SetTimeNr(m_TimeStep);
         timeSelector->UpdateLargestPossibleRegion();
         image3D = timeSelector->GetOutput();
       }
 
       auto labelSetImage = dynamic_cast<mitk::LabelSetImage* >(m_Image.GetPointer());
 
       // this will do a long long if/else to find out both pixel types
-      TransferLabelContent(
+      TransferLabelContentAtTimeStep(
         m_SliceDifferenceImage,
         labelSetImage,
         labelSetImage->GetActiveLabelSet(),
+        m_TimeStep,
         0,
         0,
         false,
         {{1, m_DestinationLabel}},
         mitk::MultiLabelSegmentation::MergeStyle::Merge,
-        mitk::MultiLabelSegmentation::OverwriteStyle::RegardLocks,
-        m_TimeStep);
+        mitk::MultiLabelSegmentation::OverwriteStyle::RegardLocks);
 
       if (m_Factor == 1 || m_Factor == -1)
       {
         if (m_Factor == -1)
         {
           // multiply diff pixels by factor and then send this diff slice
           AccessFixedDimensionByItk(m_SliceDifferenceImage, ItkInvertPixelValues, 3);
         }
 
         // just send the diff to SegmentationInterpolationController
         SegmentationInterpolationController *interpolator =
           SegmentationInterpolationController::InterpolatorForImage(m_Image);
         if (interpolator)
         {
           interpolator->BlockModified(true);
           interpolator->SetChangedVolume(m_SliceDifferenceImage, m_TimeStep);
         }
 
         if (interpolator)
         {
           interpolator->BlockModified(false);
         }
 
         if (m_Factor == -1) // return to normal values
         {
           AccessFixedDimensionByItk(m_SliceDifferenceImage, ItkInvertPixelValues, 3);
         }
       }
       else // no trivial case, too lazy to do something else
       {
         m_Image->Modified(); // check if interpolation is called. prefer to send diff directly
       }
 
       RenderingManager::GetInstance()->RequestUpdateAll();
     }
     else
     {
       itkExceptionMacro("Diff image must be 2D or 3D. Sorry, cannot work like this.");
       return;
     }
   }
 
   m_Image = nullptr;
   m_SliceDifferenceImage = nullptr;
 }
 
 mitk::DiffImageApplier *mitk::DiffImageApplier::GetInstanceForUndo()
 {
   static DiffImageApplier::Pointer s_Instance = DiffImageApplier::New();
 
   return s_Instance;
 }
 
 // basically copied from mitk/Core/Algorithms/mitkImageAccessByItk.h
 #define myMITKDiffImageApplierFilterAccessByItk(mitkImage, itkImageTypeFunction, pixeltype, dimension, itkimage2)      \
   if (typeId == MapPixelComponentType<pixeltype>::value)                                                               \
   \
 {                                                                                                                 \
     typedef itk::Image<pixeltype, dimension> ImageType;                                                                \
     typedef mitk::ImageToItk<ImageType> ImageToItkType;                                                                \
     itk::SmartPointer<ImageToItkType> imagetoitk = ImageToItkType::New();                                              \
     const mitk::Image *constImage = mitkImage;                                                                         \
     mitk::Image *nonConstImage = const_cast<mitk::Image *>(constImage);                                                \
     nonConstImage->Update();                                                                                           \
     imagetoitk->SetInput(nonConstImage);                                                                               \
     imagetoitk->Update();                                                                                              \
     itkImageTypeFunction(imagetoitk->GetOutput(), itkimage2);                                                          \
   \
 }
 
 #define myMITKDiffImageApplierFilterAccessAllTypesByItk(mitkImage, itkImageTypeFunction, dimension, itkimage2)                                                                                                                                                                                                                                                                                                                                                                                                                              \
   \
 {                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      \
     myMITKDiffImageApplierFilterAccessByItk(mitkImage, itkImageTypeFunction, double, dimension, itkimage2) else myMITKDiffImageApplierFilterAccessByItk(                                                                                                                                                                                                                                                                                                                                                                                    \
       mitkImage,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            \
       itkImageTypeFunction,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 \
       float,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                \
       dimension,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            \
       itkimage2) else myMITKDiffImageApplierFilterAccessByItk(mitkImage, itkImageTypeFunction, int, dimension, itkimage2) else myMITKDiffImageApplierFilterAccessByItk(mitkImage,                                                                                                                                                                                                                                                                                                                                                           \
                                                                                                                                                                        itkImageTypeFunction,                                                                                                                                                                                                                                                                                                                                                \
                                                                                                                                                                        unsigned int,                                                                                                                                                                                                                                                                                                                                                        \
                                                                                                                                                                        dimension,                                                                                                                                                                                                                                                                                                                                                           \
                                                                                                                                                                        itkimage2) else myMITKDiffImageApplierFilterAccessByItk(mitkImage, itkImageTypeFunction, short, dimension, itkimage2) else myMITKDiffImageApplierFilterAccessByItk(mitkImage, itkImageTypeFunction, unsigned short, dimension, itkimage2) else myMITKDiffImageApplierFilterAccessByItk(mitkImage,                                                                    \
                                                                                                                                                                                                                                                                                                                                                                                                                                                               itkImageTypeFunction,                                                         \
                                                                                                                                                                                                                                                                                                                                                                                                                                                               char,                                                                         \
                                                                                                                                                                                                                                                                                                                                                                                                                                                               dimension,                                                                    \
                                                                                                                                                                                                                                                                                                                                                                                                                                                               itkimage2) else myMITKDiffImageApplierFilterAccessByItk(mitkImage,            \
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       itkImageTypeFunction, \
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       unsigned char,        \
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       dimension,            \
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       itkimage2)            \
   \
 }
 
 template <typename TPixel, unsigned int VImageDimension>
 void mitk::DiffImageApplier::ItkImageSwitch2DDiff(itk::Image<TPixel, VImageDimension> *itkImage)
 {
   const auto typeId = m_SliceDifferenceImage->GetPixelType().GetComponentType();
 
   myMITKDiffImageApplierFilterAccessAllTypesByItk(m_SliceDifferenceImage, ItkImageProcessing2DDiff, 2, itkImage);
 }
 
 template <typename TPixel, unsigned int VImageDimension>
 void mitk::DiffImageApplier::ItkImageSwitch3DDiff(itk::Image<TPixel, VImageDimension> *itkImage)
 {
   const auto typeId = m_SliceDifferenceImage->GetPixelType().GetComponentType();
 
   myMITKDiffImageApplierFilterAccessAllTypesByItk(m_SliceDifferenceImage, ItkImageProcessing3DDiff, 3, itkImage);
 }
 
 template <typename TPixel1, unsigned int VImageDimension1, typename TPixel2, unsigned int VImageDimension2>
 void mitk::DiffImageApplier::ItkImageProcessing2DDiff(itk::Image<TPixel1, VImageDimension1> *diffImage,
                                                       itk::Image<TPixel2, VImageDimension2> *outputImage)
 {
   typedef itk::Image<TPixel1, VImageDimension1> DiffImageType;
   typedef itk::Image<TPixel2, VImageDimension2> VolumeImageType;
 
   typedef itk::ImageSliceIteratorWithIndex<VolumeImageType> OutputSliceIteratorType;
   typedef itk::ImageRegionConstIterator<DiffImageType> DiffSliceIteratorType;
 
   typename VolumeImageType::RegionType sliceInVolumeRegion;
 
   sliceInVolumeRegion = outputImage->GetLargestPossibleRegion();
   sliceInVolumeRegion.SetSize(m_SliceDimension, 1);             // just one slice
   sliceInVolumeRegion.SetIndex(m_SliceDimension, m_SliceIndex); // exactly this slice, please
 
   OutputSliceIteratorType outputIterator(outputImage, sliceInVolumeRegion);
   outputIterator.SetFirstDirection(m_Dimension0);
   outputIterator.SetSecondDirection(m_Dimension1);
 
   DiffSliceIteratorType diffIterator(diffImage, diffImage->GetLargestPossibleRegion());
 
   // iterate over output slice (and over input slice simultaneously)
   outputIterator.GoToBegin();
   diffIterator.GoToBegin();
   while (!outputIterator.IsAtEnd())
   {
     while (!outputIterator.IsAtEndOfSlice())
     {
       while (!outputIterator.IsAtEndOfLine())
       {
         TPixel2 newValue = outputIterator.Get() + (TPixel2)((double)diffIterator.Get() * m_Factor);
         outputIterator.Set(newValue);
         ++outputIterator;
         ++diffIterator;
       }
       outputIterator.NextLine();
     }
     outputIterator.NextSlice();
   }
 }
 
 template <typename TPixel1, unsigned int VImageDimension1, typename TPixel2, unsigned int VImageDimension2>
 void mitk::DiffImageApplier::ItkImageProcessing3DDiff(itk::Image<TPixel1, VImageDimension1> *diffImage,
                                                       itk::Image<TPixel2, VImageDimension2> *outputImage)
 {
   typedef itk::Image<TPixel1, VImageDimension1> DiffImageType;
   typedef itk::Image<TPixel2, VImageDimension2> VolumeImageType;
 
   typedef itk::ImageRegionIterator<VolumeImageType> OutputSliceIteratorType;
   typedef itk::ImageRegionConstIterator<DiffImageType> DiffSliceIteratorType;
 
   OutputSliceIteratorType outputIterator(outputImage, outputImage->GetLargestPossibleRegion());
   DiffSliceIteratorType diffIterator(diffImage, diffImage->GetLargestPossibleRegion());
 
   // iterate over output slice (and over input slice simultaneously)
   outputIterator.GoToBegin();
   diffIterator.GoToBegin();
   while (!outputIterator.IsAtEnd())
   {
     TPixel2 newValue = outputIterator.Get() + (TPixel2)((double)diffIterator.Get() * m_Factor);
     outputIterator.Set(newValue);
     ++outputIterator;
     ++diffIterator;
   }
 }
 
 #ifdef _MSC_VER
 #  pragma warning(push)
 #  pragma warning(disable:4146) // unary minus operator applied to unsigned type, result still unsigned
 #endif
 
 template <typename TPixel, unsigned int VImageDimension>
 void mitk::DiffImageApplier::ItkInvertPixelValues(itk::Image<TPixel, VImageDimension> *itkImage)
 {
   typedef itk::ImageRegionIterator<itk::Image<TPixel, VImageDimension>> IteratorType;
   IteratorType iter(itkImage, itkImage->GetLargestPossibleRegion());
 
   iter.GoToBegin();
   while (!iter.IsAtEnd())
   {
     iter.Set(-(iter.Get()));
     ++iter;
   }
 }
 
 #ifdef _MSC_VER
 #  pragma warning(pop)
 #endif
diff --git a/Modules/Segmentation/Interactions/mitkCloseRegionTool.cpp b/Modules/Segmentation/Interactions/mitkCloseRegionTool.cpp
index 1c173b24be..73c00146ee 100644
--- a/Modules/Segmentation/Interactions/mitkCloseRegionTool.cpp
+++ b/Modules/Segmentation/Interactions/mitkCloseRegionTool.cpp
@@ -1,120 +1,119 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "mitkCloseRegionTool.h"
 
 // us
 #include <usGetModuleContext.h>
 #include <usModule.h>
 #include <usModuleContext.h>
 #include <usModuleResource.h>
 
 #include <mitkImageAccessByItk.h>
 
 #include <itkBinaryFillholeImageFilter.h>
 #include <itkConnectedThresholdImageFilter.h>
 
 namespace mitk
 {
   MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, CloseRegionTool, "Close tool");
 }
 
 const char **mitk::CloseRegionTool::GetXPM() const
 {
   return nullptr;
 }
 
 us::ModuleResource mitk::CloseRegionTool::GetIconResource() const
 {
   us::Module *module = us::GetModuleContext()->GetModule();
   us::ModuleResource resource = module->GetResource("Close.svg");
   return resource;
 }
 
 us::ModuleResource mitk::CloseRegionTool::GetCursorIconResource() const
 {
   us::Module *module = us::GetModuleContext()->GetModule();
   us::ModuleResource resource = module->GetResource("Close_Cursor.svg");
   return resource;
 }
 
 const char *mitk::CloseRegionTool::GetName() const
 {
   return "Close";
 }
 
 
 template <typename TPixel, unsigned int VImageDimension>
 void DoITKRegionClosing(const itk::Image<TPixel, VImageDimension>* oldSegImage,
   mitk::Image::Pointer& filledRegionImage, itk::Index<VImageDimension> seedIndex, mitk::Label::PixelType& seedLabel)
 {
   typedef itk::Image<TPixel, VImageDimension> InputImageType;
   typedef itk::Image<mitk::Label::PixelType, VImageDimension> OutputImageType;
   typedef itk::ConnectedThresholdImageFilter<InputImageType, OutputImageType> RegionGrowingFilterType;
   using FillHoleFilter = itk::BinaryFillholeImageFilter<OutputImageType>;
 
   seedLabel = oldSegImage->GetPixel(seedIndex);
 
   typename OutputImageType::Pointer itkResultImage;
   filledRegionImage = nullptr;
 
   try
   {
     auto regionGrower = RegionGrowingFilterType::New();
     regionGrower->SetInput(oldSegImage);
     regionGrower->SetReplaceValue(1);
     regionGrower->AddSeed(seedIndex);
 
     regionGrower->SetLower(seedLabel);
     regionGrower->SetUpper(seedLabel);
 
     auto filler = FillHoleFilter::New();
     filler->SetInput(regionGrower->GetOutput());
     filler->SetForegroundValue(1);
     filler->Update();
 
     itkResultImage = filler->GetOutput();
   }
   catch (const itk::ExceptionObject&)
   {
     return; // can't work
   }
   catch (...)
   {
     return;
   }
   mitk::CastToMitkImage(itkResultImage, filledRegionImage);
 }
 
 mitk::Image::Pointer mitk::CloseRegionTool::GenerateFillImage(const Image* workingSlice, Point3D seedPoint, mitk::Label::PixelType& seedLabelValue) const
 {
   itk::Index<2> seedIndex;
   workingSlice->GetGeometry()->WorldToIndex(seedPoint, seedIndex);
 
   Image::Pointer fillImage;
 
   AccessFixedDimensionByItk_n(workingSlice, DoITKRegionClosing, 2, (fillImage, seedIndex, seedLabelValue));
 
-  auto labelSetImage = dynamic_cast<const LabelSetImage*>(this->GetWorkingData());
-  if (seedLabelValue == labelSetImage->GetExteriorLabel()->GetValue())
+  if (seedLabelValue == LabelSetImage::UnlabeledValue)
   {
     return nullptr;
   }
 
   return fillImage;
 }
 
 void mitk::CloseRegionTool::PrepareFilling(const Image* /*workingSlice*/, Point3D /*seedPoint*/)
 {
   m_FillLabelValue = m_SeedLabelValue;
   m_MergeStyle = MultiLabelSegmentation::MergeStyle::Merge;
 };
diff --git a/Modules/Segmentation/Interactions/mitkEraseRegionTool.cpp b/Modules/Segmentation/Interactions/mitkEraseRegionTool.cpp
index c3003cf49d..65fa667b0c 100644
--- a/Modules/Segmentation/Interactions/mitkEraseRegionTool.cpp
+++ b/Modules/Segmentation/Interactions/mitkEraseRegionTool.cpp
@@ -1,93 +1,87 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "mitkEraseRegionTool.h"
 
 #include "mitkEraseRegionTool.xpm"
 #include <mitkImagePixelReadAccessor.h>
 #include <mitkImageGenerator.h>
 #include <mitkImageAccessByItk.h>
 
 // us
 #include <usGetModuleContext.h>
 #include <usModule.h>
 #include <usModuleContext.h>
 #include <usModuleResource.h>
 
 namespace mitk
 {
   MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, EraseRegionTool, "Erase tool");
 }
 
 const char **mitk::EraseRegionTool::GetXPM() const
 {
   return mitkEraseRegionTool_xpm;
 }
 
 us::ModuleResource mitk::EraseRegionTool::GetIconResource() const
 {
   us::Module *module = us::GetModuleContext()->GetModule();
   us::ModuleResource resource = module->GetResource("Erase.svg");
   return resource;
 }
 
 us::ModuleResource mitk::EraseRegionTool::GetCursorIconResource() const
 {
   us::Module *module = us::GetModuleContext()->GetModule();
   us::ModuleResource resource = module->GetResource("Erase_Cursor.svg");
   return resource;
 }
 
 const char *mitk::EraseRegionTool::GetName() const
 {
   return "Erase";
 }
 
 template <typename TPixel, unsigned int VImageDimension>
 void DoFillImage(itk::Image<TPixel, VImageDimension>* image)
 {
   image->FillBuffer(1);
 };
 
 mitk::Image::Pointer mitk::EraseRegionTool::GenerateFillImage(const Image* workingSlice, Point3D seedPoint, mitk::Label::PixelType& seedLabelValue) const
 {
-  auto labelSetImage = dynamic_cast<const LabelSetImage*>(this->GetWorkingData());
-
   itk::Index<2> seedIndex;
   workingSlice->GetGeometry()->WorldToIndex(seedPoint, seedIndex);
 
   using AccessorType = ImagePixelReadAccessor<Label::PixelType, 2>;
   AccessorType accessor(workingSlice);
   seedLabelValue = accessor.GetPixelByIndex(seedIndex);
   Image::Pointer fillImage;
 
-  if ( seedLabelValue == labelSetImage->GetExteriorLabel()->GetValue())
+  if ( seedLabelValue == LabelSetImage::UnlabeledValue)
   { //clicked on background remove everything which is not locked.
     fillImage = workingSlice->Clone();
     AccessByItk(fillImage, DoFillImage);
   }
   else
   {
     fillImage = Superclass::GenerateFillImage(workingSlice, seedPoint, seedLabelValue);
   }
 
   return fillImage;
 }
 
 void mitk::EraseRegionTool::PrepareFilling(const Image* /*workingSlice*/, Point3D /*seedPoint*/)
 {
-  auto labelSetImage = dynamic_cast<const LabelSetImage*>(this->GetWorkingData());
-  if (nullptr != labelSetImage)
-  {
-    m_FillLabelValue = labelSetImage->GetExteriorLabel()->GetValue();
-  }
+  m_FillLabelValue = LabelSetImage::UnlabeledValue;
 };
diff --git a/Modules/Segmentation/Interactions/mitkFillRegionBaseTool.cpp b/Modules/Segmentation/Interactions/mitkFillRegionBaseTool.cpp
index 600bb49236..c539a758b6 100644
--- a/Modules/Segmentation/Interactions/mitkFillRegionBaseTool.cpp
+++ b/Modules/Segmentation/Interactions/mitkFillRegionBaseTool.cpp
@@ -1,149 +1,147 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "mitkFillRegionBaseTool.h"
 #include "mitkToolManager.h"
 
 #include "mitkBaseRenderer.h"
 #include "mitkDataStorage.h"
 
 #include "mitkITKImageImport.h"
 #include "mitkImageAccessByItk.h"
 
 #include "mitkRenderingManager.h"
 
 #include <itkConnectedThresholdImageFilter.h>
 
 mitk::FillRegionBaseTool::FillRegionBaseTool() : SegTool2D("MouseReleaseOnly")
 {
 }
 
 mitk::FillRegionBaseTool::~FillRegionBaseTool()
 {
 }
 
 void mitk::FillRegionBaseTool::ConnectActionsAndFunctions()
 {
   CONNECT_FUNCTION("Release", OnClick);
 }
 
 
 template <typename TPixel, unsigned int VImageDimension>
 void DoITKRegionGrowing(const itk::Image<TPixel, VImageDimension>* oldSegImage,
   mitk::Image::Pointer& filledRegionImage, itk::Index<VImageDimension> seedIndex, mitk::Label::PixelType& seedLabel )
 {
   typedef itk::Image<TPixel, VImageDimension> InputImageType;
   typedef itk::Image<mitk::Label::PixelType, VImageDimension> OutputImageType;
   typedef itk::ConnectedThresholdImageFilter<InputImageType, OutputImageType> RegionGrowingFilterType;
 
   seedLabel = oldSegImage->GetPixel(seedIndex);
 
   typename OutputImageType::Pointer itkResultImage;
   filledRegionImage = nullptr;
 
   try
   {
     typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New();
     regionGrower->SetInput(oldSegImage);
     regionGrower->SetReplaceValue(1);
     regionGrower->AddSeed(seedIndex);
 
     regionGrower->SetLower(seedLabel);
     regionGrower->SetUpper(seedLabel);
 
     regionGrower->Update();
     
     itkResultImage = regionGrower->GetOutput();
   }
   catch (const itk::ExceptionObject&)
   {
     return; // can't work
   }
   catch (...)
   {
     return;
   }
   mitk::CastToMitkImage(itkResultImage, filledRegionImage);
 }
 
 void mitk::FillRegionBaseTool::OnClick(StateMachineAction*, InteractionEvent* interactionEvent)
 {
   auto positionEvent = dynamic_cast<mitk::InteractionPositionEvent*>(interactionEvent);
   if (nullptr == positionEvent)
     return;
 
   auto labelSetImage = dynamic_cast<const LabelSetImage*>(this->GetWorkingData());
   if (nullptr == labelSetImage)
   {
     return;
   }
 
   if (!IsPositionEventInsideImageRegion(positionEvent, labelSetImage))
   {
     return;
   }
 
   m_LastEventSender = positionEvent->GetSender();
   m_LastEventSlice = m_LastEventSender->GetSlice();
 
   auto workingSlice = this->GetAffectedWorkingSlice(positionEvent);
 
   auto click = positionEvent->GetPositionInWorld();
 
   m_SeedLabelValue = 0;
   auto fillImage = this->GenerateFillImage(workingSlice, click, m_SeedLabelValue);
 
   if (fillImage.IsNull())
   {
     return; //nothing to fill;
   }
 
-  auto label = labelSetImage->GetLabel(m_SeedLabelValue, labelSetImage->GetActiveLayer());
-
-  if (label->GetLocked() && label->GetValue()!=labelSetImage->GetActiveLabel(labelSetImage->GetActiveLayer())->GetValue())
+  if (labelSetImage->IsLabelLocked(m_SeedLabelValue) && m_SeedLabelValue!=labelSetImage->GetActiveLabel(labelSetImage->GetActiveLayer())->GetValue())
   {
     ErrorMessage.Send("Label of selected region is locked. Tool operation has no effect.");
     return;
   }
 
   this->PrepareFilling(workingSlice, click);
 
   //as fill region tools should always allow to manipulate active label
   //(that is what the user expects/knows when using tools so far:
   //the active label can always be changed even if locked)
   //we realize that by cloning the relevant label set and changing the lock state
   //this fillLabelSet is used for the transfer.
   auto fillLabelSet = labelSetImage->GetActiveLabelSet()->Clone();
   auto activeLabelClone = fillLabelSet->GetLabel(labelSetImage->GetActiveLabel(labelSetImage->GetActiveLayer())->GetValue());
   if (nullptr != activeLabelClone)
   {
     activeLabelClone->SetLocked(false);
   }
 
-  TransferLabelContent(fillImage, workingSlice, fillLabelSet, 0, labelSetImage->GetExteriorLabel()->GetValue(), false, { {1, m_FillLabelValue} }, m_MergeStyle);
+  TransferLabelContentAtTimeStep(fillImage, workingSlice, fillLabelSet, 0, LabelSetImage::UnlabeledValue, LabelSetImage::UnlabeledValue, false, { {1, m_FillLabelValue} }, m_MergeStyle);
 
   this->WriteBackSegmentationResult(positionEvent, workingSlice);
 
   mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow());
 }
 
 mitk::Image::Pointer mitk::FillRegionBaseTool::GenerateFillImage(const Image* workingSlice, Point3D seedPoint, mitk::Label::PixelType& seedLabelValue) const
 {
   itk::Index<2> seedIndex;
   workingSlice->GetGeometry()->WorldToIndex(seedPoint, seedIndex);
 
   Image::Pointer fillImage;
 
   AccessFixedDimensionByItk_n(workingSlice, DoITKRegionGrowing, 2, (fillImage, seedIndex, seedLabelValue));
 
   return fillImage;
 }
diff --git a/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp b/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp
index f8301be7f1..288818e7d7 100644
--- a/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp
+++ b/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp
@@ -1,612 +1,612 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "mitkPaintbrushTool.h"
 
 #include "mitkAbstractTransformGeometry.h"
 #include "mitkBaseRenderer.h"
 #include "mitkToolManager.h"
 
 #include "mitkContourModelUtils.h"
 #include "mitkLevelWindowProperty.h"
 #include "mitkImageWriteAccessor.h"
 
 int mitk::PaintbrushTool::m_Size = 1;
 
 mitk::PaintbrushTool::PaintbrushTool(bool startWithFillMode)
   : FeedbackContourTool("PressMoveReleaseWithCTRLInversionAllMouseMoves"),
   m_FillMode(startWithFillMode),
     m_LastContourSize(0) // other than initial mitk::PaintbrushTool::m_Size (around l. 28)
 {
   m_MasterContour = ContourModel::New();
   m_MasterContour->Initialize();
   m_CurrentPlane = nullptr;
 }
 
 mitk::PaintbrushTool::~PaintbrushTool()
 {
 }
 
 void mitk::PaintbrushTool::ConnectActionsAndFunctions()
 {
   CONNECT_FUNCTION("PrimaryButtonPressed", OnMousePressed);
   CONNECT_FUNCTION("Move", OnPrimaryButtonPressedMoved);
   CONNECT_FUNCTION("MouseMove", OnMouseMoved);
   CONNECT_FUNCTION("Release", OnMouseReleased);
   CONNECT_FUNCTION("InvertLogic", OnInvertLogic);
 }
 
 void mitk::PaintbrushTool::Activated()
 {
   Superclass::Activated();
 
   SizeChanged.Send(m_Size);
   this->GetToolManager()->WorkingDataChanged +=
     mitk::MessageDelegate<mitk::PaintbrushTool>(this, &mitk::PaintbrushTool::OnToolManagerWorkingDataModified);
 
   m_PaintingNode = DataNode::New();
   m_PaintingNode->SetProperty("levelwindow", mitk::LevelWindowProperty::New(mitk::LevelWindow(0, m_InternalFillValue)));
   m_PaintingNode->SetProperty("binary", mitk::BoolProperty::New(true));
 
   m_PaintingNode->SetProperty("outline binary", mitk::BoolProperty::New(true));
   m_PaintingNode->SetProperty("name", mitk::StringProperty::New("Paintbrush_Node"));
   m_PaintingNode->SetProperty("helper object", mitk::BoolProperty::New(true));
   m_PaintingNode->SetProperty("opacity", mitk::FloatProperty::New(0.8));
   m_PaintingNode->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false));
   auto allRenderWindows = BaseRenderer::GetAll3DRenderWindows();
   for (auto mapit = allRenderWindows.begin(); mapit != allRenderWindows.end(); ++mapit)
   {
     m_PaintingNode->SetVisibility(false, mapit->second);
   }
 
   this->UpdateFeedbackColor();
   FeedbackContourTool::SetFeedbackContourVisible(true);
 
   this->GetToolManager()->GetDataStorage()->Add(m_PaintingNode);
 }
 
 void mitk::PaintbrushTool::Deactivated()
 {
   FeedbackContourTool::SetFeedbackContourVisible(false);
   if (this->GetToolManager()->GetDataStorage()->Exists(m_PaintingNode))
     this->GetToolManager()->GetDataStorage()->Remove(m_PaintingNode);
   m_WorkingSlice = nullptr;
   m_PaintingSlice = nullptr;
   m_CurrentPlane = nullptr;
   m_PaintingNode = nullptr;
 
   this->GetToolManager()->WorkingDataChanged -=
     mitk::MessageDelegate<mitk::PaintbrushTool>(this, &mitk::PaintbrushTool::OnToolManagerWorkingDataModified);
 
   Superclass::Deactivated();
 }
 
 void mitk::PaintbrushTool::SetSize(int value)
 {
   m_Size = value;
 }
 
 mitk::Point2D mitk::PaintbrushTool::upperLeft(mitk::Point2D p)
 {
   p[0] -= 0.5;
   p[1] += 0.5;
   return p;
 }
 
 void mitk::PaintbrushTool::UpdateContour(const InteractionPositionEvent *positionEvent)
 {
   // MITK_INFO<<"Update...";
   // examine stateEvent and create a contour that matches the pixel mask that we are going to draw
   // mitk::InteractionPositionEvent* positionEvent = dynamic_cast<mitk::InteractionPositionEvent*>( interactionEvent );
   // const PositionEvent* positionEvent = dynamic_cast<const PositionEvent*>(stateEvent->GetEvent());
   if (!positionEvent)
     return;
 
   // Get Spacing of current Slice
   // mitk::Vector3D vSpacing = m_WorkingSlice->GetSlicedGeometry()->GetPlaneGeometry(0)->GetSpacing();
 
   //
   // Draw a contour in Square according to selected brush size
   //
   int radius = (m_Size) / 2;
   float fradius = static_cast<float>(m_Size) / 2.0f;
 
   ContourModel::Pointer contourInImageIndexCoordinates = ContourModel::New();
 
   // estimate center point of the brush ( relative to the pixel the mouse points on )
   // -- left upper corner for even sizes,
   // -- midpoint for uneven sizes
   mitk::Point2D centerCorrection;
   centerCorrection.Fill(0);
 
   // even --> correction of [+0.5, +0.5]
   bool evenSize = ((m_Size % 2) == 0);
   if (evenSize)
   {
     centerCorrection[0] += 0.5;
     centerCorrection[1] += 0.5;
   }
 
   // we will compute the control points for the upper left quarter part of a circle contour
   std::vector<mitk::Point2D> quarterCycleUpperRight;
   std::vector<mitk::Point2D> quarterCycleLowerRight;
   std::vector<mitk::Point2D> quarterCycleLowerLeft;
   std::vector<mitk::Point2D> quarterCycleUpperLeft;
 
   mitk::Point2D curPoint;
   bool curPointIsInside = true;
   curPoint[0] = 0;
   curPoint[1] = radius;
   quarterCycleUpperRight.push_back(upperLeft(curPoint));
 
   // to estimate if a pixel is inside the circle, we need to compare against the 'outer radius'
   // i.e. the distance from the midpoint [0,0] to the border of the pixel [0,radius]
   // const float outer_radius = static_cast<float>(radius) + 0.5;
 
   while (curPoint[1] > 0)
   {
     // Move right until pixel is outside circle
     float curPointX_squared = 0.0f;
     float curPointY_squared = (curPoint[1] - centerCorrection[1]) * (curPoint[1] - centerCorrection[1]);
     while (curPointIsInside)
     {
       // increment posX and chec
       curPoint[0]++;
       curPointX_squared = (curPoint[0] - centerCorrection[0]) * (curPoint[0] - centerCorrection[0]);
       const float len = sqrt(curPointX_squared + curPointY_squared);
       if (len > fradius)
       {
         // found first Pixel in this horizontal line, that is outside the circle
         curPointIsInside = false;
       }
     }
     quarterCycleUpperRight.push_back(upperLeft(curPoint));
 
     // Move down until pixel is inside circle
     while (!curPointIsInside)
     {
       // increment posX and chec
       curPoint[1]--;
       curPointY_squared = (curPoint[1] - centerCorrection[1]) * (curPoint[1] - centerCorrection[1]);
       const float len = sqrt(curPointX_squared + curPointY_squared);
       if (len <= fradius)
       {
         // found first Pixel in this horizontal line, that is outside the circle
         curPointIsInside = true;
         quarterCycleUpperRight.push_back(upperLeft(curPoint));
       }
 
       // Quarter cycle is full, when curPoint y position is 0
       if (curPoint[1] <= 0)
         break;
     }
   }
 
   // QuarterCycle is full! Now copy quarter cycle to other quarters.
 
   if (!evenSize)
   {
     std::vector<mitk::Point2D>::const_iterator it = quarterCycleUpperRight.begin();
     while (it != quarterCycleUpperRight.end())
     {
       mitk::Point2D p;
       p = *it;
 
       // the contour points in the lower right corner have same position but with negative y values
       p[1] *= -1;
       quarterCycleLowerRight.push_back(p);
 
       // the contour points in the lower left corner have same position
       // but with both x,y negative
       p[0] *= -1;
       quarterCycleLowerLeft.push_back(p);
 
       // the contour points in the upper left corner have same position
       // but with x negative
       p[1] *= -1;
       quarterCycleUpperLeft.push_back(p);
 
       it++;
     }
   }
   else
   {
     std::vector<mitk::Point2D>::const_iterator it = quarterCycleUpperRight.begin();
     while (it != quarterCycleUpperRight.end())
     {
       mitk::Point2D p, q;
       p = *it;
 
       q = p;
       // the contour points in the lower right corner have same position but with negative y values
       q[1] *= -1;
       // correct for moved offset if size even = the midpoint is not the midpoint of the current pixel
       // but its upper rigt corner
       q[1] += 1;
       quarterCycleLowerRight.push_back(q);
 
       q = p;
       // the contour points in the lower left corner have same position
       // but with both x,y negative
       q[1] = -1.0f * q[1] + 1;
       q[0] = -1.0f * q[0] + 1;
       quarterCycleLowerLeft.push_back(q);
 
       // the contour points in the upper left corner have same position
       // but with x negative
       q = p;
       q[0] *= -1;
       q[0] += 1;
       quarterCycleUpperLeft.push_back(q);
 
       it++;
     }
   }
 
   // fill contour with poins in right ordering, starting with the upperRight block
   mitk::Point3D tempPoint;
   for (unsigned int i = 0; i < quarterCycleUpperRight.size(); i++)
   {
     tempPoint[0] = quarterCycleUpperRight[i][0];
     tempPoint[1] = quarterCycleUpperRight[i][1];
     tempPoint[2] = 0;
     contourInImageIndexCoordinates->AddVertex(tempPoint);
   }
   // the lower right has to be parsed in reverse order
   for (int i = quarterCycleLowerRight.size() - 1; i >= 0; i--)
   {
     tempPoint[0] = quarterCycleLowerRight[i][0];
     tempPoint[1] = quarterCycleLowerRight[i][1];
     tempPoint[2] = 0;
     contourInImageIndexCoordinates->AddVertex(tempPoint);
   }
   for (unsigned int i = 0; i < quarterCycleLowerLeft.size(); i++)
   {
     tempPoint[0] = quarterCycleLowerLeft[i][0];
     tempPoint[1] = quarterCycleLowerLeft[i][1];
     tempPoint[2] = 0;
     contourInImageIndexCoordinates->AddVertex(tempPoint);
   }
   // the upper left also has to be parsed in reverse order
   for (int i = quarterCycleUpperLeft.size() - 1; i >= 0; i--)
   {
     tempPoint[0] = quarterCycleUpperLeft[i][0];
     tempPoint[1] = quarterCycleUpperLeft[i][1];
     tempPoint[2] = 0;
     contourInImageIndexCoordinates->AddVertex(tempPoint);
   }
 
   m_MasterContour = contourInImageIndexCoordinates;
 }
 
 void mitk::PaintbrushTool::OnMousePressed(StateMachineAction *, InteractionEvent *interactionEvent)
 {
   if (m_WorkingSlice.IsNull())
     return;
 
   auto* positionEvent = dynamic_cast<mitk::InteractionPositionEvent*>(interactionEvent);
   if (!positionEvent)
     return;
 
   this->ResetWorkingSlice(positionEvent);
 
   m_WorkingSlice->GetGeometry()->WorldToIndex(positionEvent->GetPositionInWorld(), m_LastPosition);
   this->m_PaintingNode->SetVisibility(true);
 
   m_LastEventSender = positionEvent->GetSender();
   m_LastEventSlice = m_LastEventSender->GetSlice();
   m_MasterContour->SetClosed(true);
   this->MouseMoved(interactionEvent, true);
 }
 
 void mitk::PaintbrushTool::OnMouseMoved(StateMachineAction *, InteractionEvent *interactionEvent)
 {
   MouseMoved(interactionEvent, false);
 }
 
 void mitk::PaintbrushTool::OnPrimaryButtonPressedMoved(StateMachineAction *, InteractionEvent *interactionEvent)
 {
   MouseMoved(interactionEvent, true);
 }
 
 /**
   Insert the point to the feedback contour,finish to build the contour and at the same time the painting function
   */
 void mitk::PaintbrushTool::MouseMoved(mitk::InteractionEvent *interactionEvent, bool leftMouseButtonPressed)
 {
   auto *positionEvent = dynamic_cast<mitk::InteractionPositionEvent *>(interactionEvent);
 
   bool newSlice = CheckIfCurrentSliceHasChanged(positionEvent);
   if (newSlice)
   {
     this->ResetWorkingSlice(positionEvent);
   }
 
   if (m_LastContourSize != m_Size)
   {
     UpdateContour(positionEvent);
     m_LastContourSize = m_Size;
   }
 
   Point3D worldCoordinates = positionEvent->GetPositionInWorld();
   Point3D indexCoordinates;
 
   m_WorkingSlice->GetGeometry()->WorldToIndex(worldCoordinates, indexCoordinates);
 
   // round to nearest voxel center (abort if this hasn't changed)
   if (m_Size % 2 == 0) // even
   {
     indexCoordinates[0] = std::round(indexCoordinates[0]);
     indexCoordinates[1] = std::round(indexCoordinates[1]);
   }
   else // odd
   {
     indexCoordinates[0] = std::round(indexCoordinates[0]);
     indexCoordinates[1] = std::round(indexCoordinates[1]);
   }
 
   static Point3D lastPos; // uninitialized: if somebody finds out how this can be initialized in a one-liner, tell me
   if (fabs(indexCoordinates[0] - lastPos[0]) > mitk::eps || fabs(indexCoordinates[1] - lastPos[1]) > mitk::eps ||
       fabs(indexCoordinates[2] - lastPos[2]) > mitk::eps || leftMouseButtonPressed)
   {
     lastPos = indexCoordinates;
   }
   else
   {
     return;
   }
 
   auto contour = ContourModel::New();
   contour->SetClosed(true);
 
   auto it = m_MasterContour->Begin();
   auto end = m_MasterContour->End();
 
   while (it != end)
   {
     auto point = (*it)->Coordinates;
     point[0] += indexCoordinates[0];
     point[1] += indexCoordinates[1];
 
     contour->AddVertex(point);
     ++it;
   }
 
   if (leftMouseButtonPressed)
   {
     ContourModelUtils::FillContourInSlice2(contour, m_PaintingSlice, m_InternalFillValue);
 
     const double dist = indexCoordinates.EuclideanDistanceTo(m_LastPosition);
     const double radius = static_cast<double>(m_Size) / 2.0;
 
     // if points are >= radius away draw rectangle to fill empty holes
     // in between the 2 points
     if (dist > radius)
     {
       const mitk::Point3D &currentPos = indexCoordinates;
       mitk::Point3D direction;
       mitk::Point3D vertex;
       mitk::Point3D normal;
 
       direction[0] = indexCoordinates[0] - m_LastPosition[0];
       direction[1] = indexCoordinates[1] - m_LastPosition[1];
       direction[2] = indexCoordinates[2] - m_LastPosition[2];
 
       direction[0] = direction.GetVnlVector().normalize()[0];
       direction[1] = direction.GetVnlVector().normalize()[1];
       direction[2] = direction.GetVnlVector().normalize()[2];
 
       // 90 degrees rotation of direction
       normal[0] = -1.0 * direction[1];
       normal[1] = direction[0];
 
       auto gapContour = ContourModel::New();
 
       // upper left corner
       vertex[0] = m_LastPosition[0] + (normal[0] * radius);
       vertex[1] = m_LastPosition[1] + (normal[1] * radius);
 
       gapContour->AddVertex(vertex);
 
       // upper right corner
       vertex[0] = currentPos[0] + (normal[0] * radius);
       vertex[1] = currentPos[1] + (normal[1] * radius);
 
       gapContour->AddVertex(vertex);
 
       // lower right corner
       vertex[0] = currentPos[0] - (normal[0] * radius);
       vertex[1] = currentPos[1] - (normal[1] * radius);
 
       gapContour->AddVertex(vertex);
 
       // lower left corner
       vertex[0] = m_LastPosition[0] - (normal[0] * radius);
       vertex[1] = m_LastPosition[1] - (normal[1] * radius);
 
       gapContour->AddVertex(vertex);
 
       ContourModelUtils::FillContourInSlice2(gapContour, m_PaintingSlice, m_InternalFillValue);
     }
   }
   else
   {
     // switched from different renderwindow
     // no activate hover highlighting. Otherwise undo / redo wont work
     this->m_PaintingNode->SetVisibility(false);
   }
 
   m_LastPosition = indexCoordinates;
 
   // visualize contour
   ContourModel::Pointer tmp =
     FeedbackContourTool::BackProjectContourFrom2DSlice(m_WorkingSlice->GetGeometry(), contour);
 
   this->UpdateCurrentFeedbackContour(tmp);
 
   if (newSlice)
   {
     RenderingManager::GetInstance()->RequestUpdateAll();
   }
   else
   {
     assert(positionEvent->GetSender()->GetRenderWindow());
     RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow());
   }
 }
 
 void mitk::PaintbrushTool::OnMouseReleased(StateMachineAction *, InteractionEvent *interactionEvent)
 {
   // When mouse is released write segmentationresult back into image
   auto *positionEvent = dynamic_cast<mitk::InteractionPositionEvent *>(interactionEvent);
   if (!positionEvent)
     return;
 
   DataNode* workingNode(this->GetToolManager()->GetWorkingData(0));
   auto workingImage = dynamic_cast<LabelSetImage*>(workingNode->GetData());
   Label::PixelType activePixelValue = ContourModelUtils::GetActivePixelValue(workingImage);
   if (!m_FillMode)
   {
-    activePixelValue = workingImage->GetExteriorLabel()->GetValue();
+    activePixelValue = LabelSetImage::UnlabeledValue;
   }
 
   //as paintbrush tools should always allow to manipulate active label
   //(that is what the user expects/knows when using tools so far:
   //the active label can always be changed even if locked)
   //we realize that by cloning the relevant label set and changing the lock state
   //this fillLabelSet is used for the transfer.
   auto fillLabelSet = workingImage->GetActiveLabelSet()->Clone();
   auto activeLabelClone = fillLabelSet->GetLabel(workingImage->GetActiveLabel(workingImage->GetActiveLayer())->GetValue());
   if (nullptr != activeLabelClone)
   {
     activeLabelClone->SetLocked(false);
   }
 
-  TransferLabelContent(m_PaintingSlice, m_WorkingSlice, fillLabelSet, 0, workingImage->GetExteriorLabel()->GetValue(), false, { {m_InternalFillValue, activePixelValue} }, mitk::MultiLabelSegmentation::MergeStyle::Merge);
+  TransferLabelContentAtTimeStep(m_PaintingSlice, m_WorkingSlice, fillLabelSet, 0, LabelSetImage::UnlabeledValue, LabelSetImage::UnlabeledValue, false, { {m_InternalFillValue, activePixelValue} }, mitk::MultiLabelSegmentation::MergeStyle::Merge);
 
   this->WriteBackSegmentationResult(positionEvent, m_WorkingSlice->Clone());
 
   // deactivate visibility of helper node
   m_PaintingNode->SetVisibility(false);
   m_PaintingNode->SetData(nullptr);
   m_PaintingSlice = nullptr;
   m_WorkingSlice = nullptr;
 
   RenderingManager::GetInstance()->RequestUpdateAll();
 }
 
 void mitk::PaintbrushTool::UpdateFeedbackColor()
 {
   mitk::Color currentColor;
   if (m_FillMode)
   {
     FeedbackContourTool::SetFeedbackContourColorDefault();
     currentColor.Set(0.0, 1.0, 0.);
   }
   else
   {
     FeedbackContourTool::SetFeedbackContourColor(1.0, 0.0, 0.0);
     currentColor.Set(1.0, 0.0, 0.);
   }
 
   if (m_PaintingNode.IsNotNull())
   {
     m_PaintingNode->SetProperty("color", mitk::ColorProperty::New(currentColor[0], currentColor[1], currentColor[2]));
   }
 }
 
 /**
   Called when the CTRL key is pressed.
   */
 void mitk::PaintbrushTool::OnInvertLogic(StateMachineAction *, InteractionEvent *)
 {
   m_FillMode = !m_FillMode;
   UpdateFeedbackColor();
 
   mitk::RenderingManager::GetInstance()->RequestUpdateAll();
 }
 
 bool mitk::PaintbrushTool::CheckIfCurrentSliceHasChanged(const InteractionPositionEvent *event)
 {
   const PlaneGeometry* planeGeometry((event->GetSender()->GetCurrentWorldPlaneGeometry()));
   const auto* abstractTransformGeometry(
     dynamic_cast<const AbstractTransformGeometry *>(event->GetSender()->GetCurrentWorldPlaneGeometry()));
   if (nullptr == planeGeometry || nullptr != abstractTransformGeometry)
   {
     return false;
   }
 
   bool newPlane = false;
 
   if (m_CurrentPlane.IsNull() || m_WorkingSlice.IsNull()
       //or not the same slice
      || !mitk::MatrixEqualElementWise(planeGeometry->GetIndexToWorldTransform()->GetMatrix(),
        m_CurrentPlane->GetIndexToWorldTransform()->GetMatrix())
      || !mitk::Equal(planeGeometry->GetIndexToWorldTransform()->GetOffset(),
        m_CurrentPlane->GetIndexToWorldTransform()->GetOffset()))
   {
     m_CurrentPlane = planeGeometry;
     newPlane = true;
   }
 
   return newPlane;
 }
 
 void mitk::PaintbrushTool::ResetWorkingSlice(const InteractionPositionEvent* event)
 {
   const PlaneGeometry* planeGeometry((event->GetSender()->GetCurrentWorldPlaneGeometry()));
   const auto* abstractTransformGeometry(
     dynamic_cast<const AbstractTransformGeometry*>(event->GetSender()->GetCurrentWorldPlaneGeometry()));
   if (nullptr == planeGeometry || nullptr != abstractTransformGeometry)
   {
     return;
   }
 
   m_WorkingSlice = nullptr;
   m_PaintingSlice = nullptr;
   m_PaintingNode->SetData(nullptr);
 
   DataNode* workingNode = this->GetToolManager()->GetWorkingData(0);
   if (nullptr == workingNode)
   {
     return;
   }
 
   Image::Pointer image = dynamic_cast<Image*>(workingNode->GetData());
   if (nullptr == image)
   {
     return;
   }
 
   m_WorkingSlice = SegTool2D::GetAffectedImageSliceAs2DImage(event, image)->Clone();
 
   m_PaintingSlice = Image::New();
   m_PaintingSlice->Initialize(m_WorkingSlice);
 
   unsigned int byteSize = m_PaintingSlice->GetPixelType().GetSize();
   for (unsigned int dim = 0; dim < m_PaintingSlice->GetDimension(); ++dim)
   {
     byteSize *= m_PaintingSlice->GetDimension(dim);
   }
   mitk::ImageWriteAccessor writeAccess(m_PaintingSlice.GetPointer(), m_PaintingSlice->GetVolumeData(0));
   memset(writeAccess.GetData(), 0, byteSize);
 
   m_PaintingNode->SetData(m_PaintingSlice);
 }
 
 void mitk::PaintbrushTool::OnToolManagerWorkingDataModified()
 {
   // Here we simply set the current working slice to null. The next time the mouse is moved
   // within a renderwindow a new slice will be extracted from the new working data
   m_WorkingSlice = nullptr;
   m_PaintingSlice = nullptr;
 }
diff --git a/Modules/Segmentation/Interactions/mitkPickingTool.cpp b/Modules/Segmentation/Interactions/mitkPickingTool.cpp
index 533eaa28d2..96afc90601 100644
--- a/Modules/Segmentation/Interactions/mitkPickingTool.cpp
+++ b/Modules/Segmentation/Interactions/mitkPickingTool.cpp
@@ -1,262 +1,256 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "mitkPickingTool.h"
 
 #include "mitkProperties.h"
 #include "mitkToolManager.h"
 
 #include "mitkInteractionPositionEvent.h"
 // us
 #include <usGetModuleContext.h>
 #include <usModule.h>
 #include <usModuleContext.h>
 #include <usModuleResource.h>
 
 #include "mitkITKImageImport.h"
 #include "mitkImageAccessByItk.h"
 #include "mitkImageCast.h"
 #include "mitkImageTimeSelector.h"
 
 #include <itkImage.h>
 #include <itkConnectedThresholdImageFilter.h>
 #include <itkOrImageFilter.h>
 #include <mitkLabelSetImage.h>
 
 namespace mitk
 {
   MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, PickingTool, "PickingTool");
 }
 
 mitk::PickingTool::PickingTool() : SegWithPreviewTool(false, "PressMoveReleaseAndPointSetting")
 {
   this->ResetsToEmptyPreviewOn();
 }
 
 mitk::PickingTool::~PickingTool()
 {
 }
 
 const char **mitk::PickingTool::GetXPM() const
 {
   return nullptr;
 }
 
 const char *mitk::PickingTool::GetName() const
 {
   return "Picking";
 }
 
 us::ModuleResource mitk::PickingTool::GetIconResource() const
 {
   us::Module *module = us::GetModuleContext()->GetModule();
   us::ModuleResource resource = module->GetResource("Picking.svg");
   return resource;
 }
 
 void mitk::PickingTool::Activated()
 {
   Superclass::Activated();
 
   m_PointSet = mitk::PointSet::New();
   //ensure that the seed points are visible for all timepoints.
   dynamic_cast<ProportionalTimeGeometry*>(m_PointSet->GetTimeGeometry())->SetStepDuration(std::numeric_limits<TimePointType>::max());
 
   m_PointSetNode = mitk::DataNode::New();
   m_PointSetNode->SetData(m_PointSet);
   m_PointSetNode->SetName(std::string(this->GetName()) + "_PointSet");
   m_PointSetNode->SetBoolProperty("helper object", true);
   m_PointSetNode->SetColor(0.0, 1.0, 0.0);
   m_PointSetNode->SetVisibility(true);
 
   this->GetDataStorage()->Add(m_PointSetNode, this->GetToolManager()->GetWorkingData(0));
 }
 
 void mitk::PickingTool::Deactivated()
 {
   this->ClearSeeds();
 
   // remove from data storage and disable interaction
   GetDataStorage()->Remove(m_PointSetNode);
   m_PointSetNode = nullptr;
   m_PointSet = nullptr;
 
   Superclass::Deactivated();
 }
 
 void mitk::PickingTool::ConnectActionsAndFunctions()
 {
   CONNECT_FUNCTION("ShiftSecondaryButtonPressed", OnAddPoint);
   CONNECT_FUNCTION("ShiftPrimaryButtonPressed", OnAddPoint);
   CONNECT_FUNCTION("DeletePoint", OnDelete);
 }
 
 void mitk::PickingTool::OnAddPoint(StateMachineAction*, InteractionEvent* interactionEvent)
 {
   if (!this->IsUpdating() && m_PointSet.IsNotNull())
   {
     const auto positionEvent = dynamic_cast<mitk::InteractionPositionEvent*>(interactionEvent);
 
     if (positionEvent != nullptr)
     {
       m_PointSet->InsertPoint(m_PointSet->GetSize(), positionEvent->GetPositionInWorld());
       this->UpdatePreview();
     }
   }
 }
 
 void mitk::PickingTool::OnDelete(StateMachineAction*, InteractionEvent* /*interactionEvent*/)
 {
   if (!this->IsUpdating() && m_PointSet.IsNotNull())
   {
     // delete last seed point
     if (this->m_PointSet->GetSize() > 0)
     {
       m_PointSet->RemovePointAtEnd(0);
 
       this->UpdatePreview();
     }
   }
 }
 
 void mitk::PickingTool::ClearPicks()
 {
   this->ClearSeeds();
   this->UpdatePreview();
 }
 
 bool mitk::PickingTool::HasPicks() const
 {
   return this->m_PointSet.IsNotNull() && this->m_PointSet->GetSize()>0;
 }
 
 void mitk::PickingTool::ClearSeeds()
 {
   if (this->m_PointSet.IsNotNull())
   {
     // renew pointset
     this->m_PointSet = mitk::PointSet::New();
     //ensure that the seed points are visible for all timepoints.
     dynamic_cast<ProportionalTimeGeometry*>(m_PointSet->GetTimeGeometry())->SetStepDuration(std::numeric_limits<TimePointType>::max());
     this->m_PointSetNode->SetData(this->m_PointSet);
   }
 }
 
 template <typename TPixel, unsigned int VImageDimension>
 void DoITKRegionGrowing(const itk::Image<TPixel, VImageDimension>* oldSegImage,
   mitk::Image* segmentation,
   const mitk::PointSet* seedPoints,
   unsigned int timeStep, const mitk::BaseGeometry* inputGeometry, const mitk::Label::PixelType outputValue,
   const mitk::Label::PixelType backgroundValue,
   bool& emptyTimeStep)
 {
   typedef itk::Image<TPixel, VImageDimension> InputImageType;
   typedef itk::Image<mitk::Label::PixelType, VImageDimension> OutputImageType;
   typedef typename InputImageType::IndexType IndexType;
   typedef itk::ConnectedThresholdImageFilter<InputImageType, OutputImageType> RegionGrowingFilterType;
 
   using IndexMapType = std::map < mitk::Label::PixelType, std::vector<IndexType> >;
 
   IndexMapType indexMap;
 
   // convert world coordinates to image indices
   for (auto pos = seedPoints->Begin(); pos != seedPoints->End(); ++pos)
   {
     IndexType seedIndex;
     inputGeometry->WorldToIndex(pos->Value(), seedIndex);
     const auto selectedLabel = oldSegImage->GetPixel(seedIndex);
 
     if (selectedLabel != backgroundValue)
     {
       indexMap[selectedLabel].push_back(seedIndex);
     }
   }
 
   typename OutputImageType::Pointer itkResultImage;
 
   try
   {
     bool first = true;
     typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New();
     regionGrower->SetInput(oldSegImage);
     regionGrower->SetReplaceValue(outputValue);
 
     for (const auto& [label, indeces] : indexMap)
     {
       // perform region growing in desired segmented region
       regionGrower->ClearSeeds();
       for (const auto& index : indeces)
       {
         regionGrower->AddSeed(index);
       }
 
       regionGrower->SetLower(label);
       regionGrower->SetUpper(label);
 
       regionGrower->Update();
 
       if (first)
       {
         itkResultImage = regionGrower->GetOutput();
       }
       else
       {
         typename itk::OrImageFilter<OutputImageType, OutputImageType>::Pointer orFilter =
           itk::OrImageFilter<OutputImageType, OutputImageType>::New();
         orFilter->SetInput1(regionGrower->GetOutput());
         orFilter->SetInput2(itkResultImage);
 
         orFilter->Update();
         itkResultImage = orFilter->GetOutput();
       }
       first = false;
       itkResultImage->DisconnectPipeline();
     }
   }
   catch (const itk::ExceptionObject&)
   {
     return; // can't work
   }
   catch (...)
   {
     return;
   }
 
   if (itkResultImage.IsNotNull())
   {
     segmentation->SetVolume((void*)(itkResultImage->GetPixelContainer()->GetBufferPointer()),timeStep);
   }
   emptyTimeStep = itkResultImage.IsNull();
 
 }
 
 void mitk::PickingTool::DoUpdatePreview(const Image* /*inputAtTimeStep*/, const Image* oldSegAtTimeStep, LabelSetImage* previewImage, TimeStepType timeStep)
 {
   if (nullptr != oldSegAtTimeStep && nullptr != previewImage && m_PointSet.IsNotNull())
   {
     bool emptyTimeStep = true;
     if (this->HasPicks())
     {
-      Label::PixelType backgroundValue = 0;
-      auto labelSetImage = dynamic_cast<const LabelSetImage*>(oldSegAtTimeStep);
-      if (nullptr != labelSetImage)
-      {
-        backgroundValue = labelSetImage->GetExteriorLabel()->GetValue();
-      }
-      AccessFixedDimensionByItk_n(oldSegAtTimeStep, DoITKRegionGrowing, 3, (previewImage, this->m_PointSet, timeStep, oldSegAtTimeStep->GetGeometry(), this->GetUserDefinedActiveLabel(), backgroundValue, emptyTimeStep));
+      AccessFixedDimensionByItk_n(oldSegAtTimeStep, DoITKRegionGrowing, 3, (previewImage, this->m_PointSet, timeStep, oldSegAtTimeStep->GetGeometry(), this->GetUserDefinedActiveLabel(), mitk::LabelSetImage::UnlabeledValue, emptyTimeStep));
     }
     if (emptyTimeStep)
     {
       this->ResetPreviewContentAtTimeStep(timeStep);
     }
   }
 }
diff --git a/Modules/Segmentation/Interactions/mitkSegWithPreviewTool.cpp b/Modules/Segmentation/Interactions/mitkSegWithPreviewTool.cpp
index 15b2fc710e..8c2886ada7 100644
--- a/Modules/Segmentation/Interactions/mitkSegWithPreviewTool.cpp
+++ b/Modules/Segmentation/Interactions/mitkSegWithPreviewTool.cpp
@@ -1,737 +1,739 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "mitkSegWithPreviewTool.h"
 
 #include "mitkToolManager.h"
 
 #include "mitkColorProperty.h"
 #include "mitkProperties.h"
 
 #include "mitkDataStorage.h"
 #include "mitkRenderingManager.h"
 
 #include "mitkImageAccessByItk.h"
 #include "mitkImageCast.h"
 #include "mitkLabelSetImage.h"
 #include "mitkMaskAndCutRoiImageFilter.h"
 #include "mitkPadImageFilter.h"
 #include "mitkNodePredicateGeometry.h"
 #include "mitkSegTool2D.h"
 
 mitk::SegWithPreviewTool::SegWithPreviewTool(bool lazyDynamicPreviews): Tool("dummy"), m_LazyDynamicPreviews(lazyDynamicPreviews)
 {
   m_ProgressCommand = ToolCommand::New();
 }
 
 mitk::SegWithPreviewTool::SegWithPreviewTool(bool lazyDynamicPreviews, const char* interactorType, const us::Module* interactorModule) : Tool(interactorType, interactorModule), m_LazyDynamicPreviews(lazyDynamicPreviews)
 {
   m_ProgressCommand = ToolCommand::New();
 }
 
 mitk::SegWithPreviewTool::~SegWithPreviewTool()
 {
 }
 
 void mitk::SegWithPreviewTool::SetMergeStyle(MultiLabelSegmentation::MergeStyle mergeStyle)
 {
   m_MergeStyle = mergeStyle;
 }
 
 void mitk::SegWithPreviewTool::SetOverwriteStyle(MultiLabelSegmentation::OverwriteStyle overwriteStyle)
 {
   m_OverwriteStyle = overwriteStyle;
 }
 
 void mitk::SegWithPreviewTool::SetLabelTransferMode(LabelTransferMode LabelTransferMode)
 {
   m_LabelTransferMode = LabelTransferMode;
 }
 
 void mitk::SegWithPreviewTool::SetSelectedLabels(const SelectedLabelVectorType& labelsToTransfer)
 {
   m_SelectedLabels = labelsToTransfer;
 }
 
 bool mitk::SegWithPreviewTool::CanHandle(const BaseData* referenceData, const BaseData* workingData) const
 {
   if (!Superclass::CanHandle(referenceData, workingData))
     return false;
 
   if (workingData == nullptr)
     return false;
 
   auto* referenceImage = dynamic_cast<const Image*>(referenceData);
   if (referenceImage == nullptr)
     return false;
 
   auto* labelSet = dynamic_cast<const LabelSetImage*>(workingData);
   if (labelSet != nullptr)
     return true;
 
   auto* workingImage = dynamic_cast<const Image*>(workingData);
   if (workingImage == nullptr)
     return false;
 
   // If the working image is a normal image and not a label set image
   // it must have the same pixel type as a label set.
   return MakeScalarPixelType< DefaultSegmentationDataType >() == workingImage->GetPixelType();
 }
 
 void mitk::SegWithPreviewTool::Activated()
 {
   Superclass::Activated();
 
   this->GetToolManager()->RoiDataChanged +=
     MessageDelegate<SegWithPreviewTool>(this, &SegWithPreviewTool::OnRoiDataChanged);
 
   this->GetToolManager()->SelectedTimePointChanged +=
     MessageDelegate<SegWithPreviewTool>(this, &SegWithPreviewTool::OnTimePointChanged);
 
   m_ReferenceDataNode = this->GetToolManager()->GetReferenceData(0);
   m_SegmentationInputNode = m_ReferenceDataNode;
 
   m_LastTimePointOfUpdate = RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint();
 
   if (m_PreviewSegmentationNode.IsNull())
   {
     m_PreviewSegmentationNode = DataNode::New();
     m_PreviewSegmentationNode->SetProperty("color", ColorProperty::New(0.0, 1.0, 0.0));
     m_PreviewSegmentationNode->SetProperty("name", StringProperty::New(std::string(this->GetName())+" preview"));
     m_PreviewSegmentationNode->SetProperty("opacity", FloatProperty::New(0.3));
     m_PreviewSegmentationNode->SetProperty("binary", BoolProperty::New(true));
     m_PreviewSegmentationNode->SetProperty("helper object", BoolProperty::New(true));
   }
 
   if (m_SegmentationInputNode.IsNotNull())
   {
     this->ResetPreviewNode();
     this->InitiateToolByInput();
   }
   else
   {
     this->GetToolManager()->ActivateTool(-1);
   }
 }
 
 void mitk::SegWithPreviewTool::Deactivated()
 {
   this->GetToolManager()->RoiDataChanged -=
     MessageDelegate<SegWithPreviewTool>(this, &SegWithPreviewTool::OnRoiDataChanged);
 
   this->GetToolManager()->SelectedTimePointChanged -=
     MessageDelegate<SegWithPreviewTool>(this, &SegWithPreviewTool::OnTimePointChanged);
 
   m_SegmentationInputNode = nullptr;
   m_ReferenceDataNode = nullptr;
   m_WorkingPlaneGeometry = nullptr;
 
   try
   {
     if (DataStorage *storage = this->GetToolManager()->GetDataStorage())
     {
       storage->Remove(m_PreviewSegmentationNode);
       RenderingManager::GetInstance()->RequestUpdateAll();
     }
   }
   catch (...)
   {
     // don't care
   }
 
   if (m_PreviewSegmentationNode.IsNotNull())
   {
     m_PreviewSegmentationNode->SetData(nullptr);
   }
 
   Superclass::Deactivated();
 }
 
 void mitk::SegWithPreviewTool::ConfirmSegmentation()
 {
   bool labelChanged = this->EnsureUpToDateUserDefinedActiveLabel();
   if ((m_LazyDynamicPreviews && m_CreateAllTimeSteps) || labelChanged)
   { // The tool should create all time steps but is currently in lazy mode,
     // thus ensure that a preview for all time steps is available.
     this->UpdatePreview(true);
   }
 
   CreateResultSegmentationFromPreview();
 
   RenderingManager::GetInstance()->RequestUpdateAll();
 
   if (!m_KeepActiveAfterAccept)
   {
     this->GetToolManager()->ActivateTool(-1);
   }
 }
 
 void  mitk::SegWithPreviewTool::InitiateToolByInput()
 {
   //default implementation does nothing.
   //implement in derived classes to change behavior
 }
 
 mitk::LabelSetImage* mitk::SegWithPreviewTool::GetPreviewSegmentation()
 {
   if (m_PreviewSegmentationNode.IsNull())
   {
     return nullptr;
   }
 
   return dynamic_cast<LabelSetImage*>(m_PreviewSegmentationNode->GetData());
 }
 
 const mitk::LabelSetImage* mitk::SegWithPreviewTool::GetPreviewSegmentation() const
 {
   if (m_PreviewSegmentationNode.IsNull())
   {
     return nullptr;
   }
 
   return dynamic_cast<LabelSetImage*>(m_PreviewSegmentationNode->GetData());
 }
 
 mitk::DataNode* mitk::SegWithPreviewTool::GetPreviewSegmentationNode()
 {
   return m_PreviewSegmentationNode;
 }
 
 const mitk::Image* mitk::SegWithPreviewTool::GetSegmentationInput() const
 {
   if (m_SegmentationInputNode.IsNull())
   {
     return nullptr;
   }
 
   return dynamic_cast<const Image*>(m_SegmentationInputNode->GetData());
 }
 
 const mitk::Image* mitk::SegWithPreviewTool::GetReferenceData() const
 {
   if (m_ReferenceDataNode.IsNull())
   {
     return nullptr;
   }
 
   return dynamic_cast<const Image*>(m_ReferenceDataNode->GetData());
 }
 
 template <typename ImageType>
 void ClearBufferProcessing(ImageType* itkImage)
 {
   itkImage->FillBuffer(0);
 }
 
 void mitk::SegWithPreviewTool::ResetPreviewContentAtTimeStep(unsigned int timeStep)
 {
   auto previewImage = GetImageByTimeStep(this->GetPreviewSegmentation(), timeStep);
   if (nullptr != previewImage)
   {
     AccessByItk(previewImage, ClearBufferProcessing);
   }
 }
 
 void mitk::SegWithPreviewTool::ResetPreviewContent()
 {
   auto previewImage = this->GetPreviewSegmentation();
   if (nullptr != previewImage)
   {
     auto castedPreviewImage =
       dynamic_cast<LabelSetImage*>(previewImage);
     if (nullptr == castedPreviewImage) mitkThrow() << "Application is on wrong state / invalid tool implementation. Preview image should always be of type LabelSetImage now.";
     castedPreviewImage->ClearBuffer();
   }
 }
 
 void mitk::SegWithPreviewTool::ResetPreviewNode()
 {
   if (m_IsUpdating)
   {
     mitkThrow() << "Used tool is implemented incorrectly. ResetPreviewNode is called while preview update is ongoing. Check implementation!";
   }
 
   itk::RGBPixel<float> previewColor;
   previewColor[0] = 0.0f;
   previewColor[1] = 1.0f;
   previewColor[2] = 0.0f;
 
   const auto image = this->GetSegmentationInput();
   if (nullptr != image)
   {
     LabelSetImage::ConstPointer workingImage =
       dynamic_cast<const LabelSetImage *>(this->GetToolManager()->GetWorkingData(0)->GetData());
 
     if (workingImage.IsNotNull())
     {
       auto newPreviewImage = workingImage->Clone();
       if (this->GetResetsToEmptyPreview())
       {
         newPreviewImage->ClearBuffer();
       }
 
       if (newPreviewImage.IsNull())
       {
         MITK_ERROR << "Cannot create preview helper objects. Unable to clone working image";
         return;
       }
 
       m_PreviewSegmentationNode->SetData(newPreviewImage);
 
       auto* activeLayer = newPreviewImage->GetActiveLabelSet();
       auto* activeLabel = activeLayer->GetActiveLabel();
       if (m_UseSpecialPreviewColor)
       {
         // Let's paint the feedback node green...
         activeLabel->SetColor(previewColor);
         activeLayer->UpdateLookupTable(activeLabel->GetValue());
       }
       activeLabel->SetVisible(true);
     }
     else
     {
       Image::ConstPointer workingImageBin = dynamic_cast<const Image*>(this->GetToolManager()->GetWorkingData(0)->GetData());
       if (workingImageBin.IsNotNull())
       {
         Image::Pointer newPreviewImage;
         if (this->GetResetsToEmptyPreview())
         {
           newPreviewImage = Image::New();
           newPreviewImage->Initialize(workingImageBin);
         }
         else
         {
           auto newPreviewImage = workingImageBin->Clone();
         }
         if (newPreviewImage.IsNull())
         {
           MITK_ERROR << "Cannot create preview helper objects. Unable to clone working image";
           return;
         }
         m_PreviewSegmentationNode->SetData(newPreviewImage);
       }
       else
       {
         mitkThrow() << "Tool is an invalid state. Cannot setup preview node. Working data is an unsupported class and should have not been accepted by CanHandle().";
       }
     }
 
     m_PreviewSegmentationNode->SetColor(previewColor);
     m_PreviewSegmentationNode->SetOpacity(0.5);
 
     int layer(50);
     m_ReferenceDataNode->GetIntProperty("layer", layer);
     m_PreviewSegmentationNode->SetIntProperty("layer", layer + 1);
 
     if (DataStorage *ds = this->GetToolManager()->GetDataStorage())
     {
       if (!ds->Exists(m_PreviewSegmentationNode))
         ds->Add(m_PreviewSegmentationNode, m_ReferenceDataNode);
     }
   }
 }
 
 mitk::SegWithPreviewTool::LabelMappingType mitk::SegWithPreviewTool::GetLabelMapping() const
 {
   LabelMappingType labelMapping = { { this->GetUserDefinedActiveLabel(),this->GetUserDefinedActiveLabel() } };
   if (LabelTransferMode::SelectedLabels == this->m_LabelTransferMode)
   {
     labelMapping.clear();
     for (auto label : this->m_SelectedLabels)
     {
       labelMapping.push_back({ label, label });
     }
   }
   else if (LabelTransferMode::AllLabels == this->m_LabelTransferMode)
   {
     labelMapping.clear();
     const auto labelSet = this->GetPreviewSegmentation()->GetActiveLabelSet();
     for (auto labelIter = labelSet->IteratorConstBegin(); labelIter != labelSet->IteratorConstEnd(); ++labelIter)
     {
       labelMapping.push_back({ labelIter->second->GetValue(),labelIter->second->GetValue() });
     }
   }
 
   return labelMapping;
 }
 
 void mitk::SegWithPreviewTool::TransferImageAtTimeStep(const Image* sourceImage, Image* destinationImage, const TimeStepType timeStep)
 {
   try
   {
     Image::ConstPointer sourceImageAtTimeStep = this->GetImageByTimeStep(sourceImage, timeStep);
 
     if (sourceImageAtTimeStep->GetPixelType() != destinationImage->GetPixelType())
     {
       mitkThrow() << "Cannot transfer images. Tool is in an invalid state, source image and destination image do not have the same pixel type. "
         << "Source pixel type: " << sourceImage->GetPixelType().GetTypeAsString()
         << "; destination pixel type: " << destinationImage->GetPixelType().GetTypeAsString();
     }
 
     if (!Equal(*(sourceImage->GetGeometry(timeStep)), *(destinationImage->GetGeometry(timeStep)), NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, false))
     {
       mitkThrow() << "Cannot transfer images. Tool is in an invalid state, source image and destination image do not have the same geometry.";
     }
 
     if (nullptr != this->GetWorkingPlaneGeometry())
     {
       auto sourceSlice = SegTool2D::GetAffectedImageSliceAs2DImage(this->GetWorkingPlaneGeometry(), sourceImage, timeStep);
       SegTool2D::WriteBackSegmentationResult(this->GetTargetSegmentationNode(), m_WorkingPlaneGeometry, sourceSlice, timeStep);
     }
     else
     { //take care of the full segmentation volume
       auto sourceLSImage = dynamic_cast<const LabelSetImage*>(sourceImage);
       auto destLSImage = dynamic_cast<LabelSetImage*>(destinationImage);
 
       auto labelMapping = this->GetLabelMapping();
-      TransferLabelContent(sourceLSImage, destLSImage, labelMapping, m_MergeStyle, m_OverwriteStyle, timeStep);
+      TransferLabelContentAtTimeStep(sourceLSImage, destLSImage, timeStep, labelMapping, m_MergeStyle, m_OverwriteStyle);
     }
   }
   catch (...)
   {
     Tool::ErrorMessage("Error accessing single time steps of the original image. Cannot create segmentation.");
     throw;
   }
 }
 
 void mitk::SegWithPreviewTool::CreateResultSegmentationFromPreview()
 {
   const auto segInput = this->GetSegmentationInput();
   auto previewImage = this->GetPreviewSegmentation();
   if (nullptr != segInput && nullptr != previewImage)
   {
     DataNode::Pointer resultSegmentationNode = GetTargetSegmentationNode();
 
     if (resultSegmentationNode.IsNotNull())
     {
       const auto timePoint = RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint();
       auto resultSegmentation = dynamic_cast<Image*>(resultSegmentationNode->GetData());
 
       // REMARK: the following code in this scope assumes that previewImage and resultSegmentation
       // are clones of the working referenceImage (segmentation provided to the tool). Therefore they have
       // the same time geometry.
       if (previewImage->GetTimeSteps() != resultSegmentation->GetTimeSteps())
       {
         mitkThrow() << "Cannot confirm/transfer segmentation. Internal tool state is invalid."
           << " Preview segmentation and segmentation result image have different time geometries.";
       }
 
       this->TransferPrepare();
       if (m_CreateAllTimeSteps)
       {
         for (unsigned int timeStep = 0; timeStep < previewImage->GetTimeSteps(); ++timeStep)
         {
           this->TransferImageAtTimeStep(previewImage, resultSegmentation, timeStep);
         }
       }
       else
       {
         const auto timeStep = resultSegmentation->GetTimeGeometry()->TimePointToTimeStep(timePoint);
         this->TransferImageAtTimeStep(previewImage, resultSegmentation, timeStep);
       }
 
       // since we are maybe working on a smaller referenceImage, pad it to the size of the original referenceImage
       if (m_ReferenceDataNode.GetPointer() != m_SegmentationInputNode.GetPointer())
       {
         PadImageFilter::Pointer padFilter = PadImageFilter::New();
 
         padFilter->SetInput(0, resultSegmentation);
         padFilter->SetInput(1, dynamic_cast<Image*>(m_ReferenceDataNode->GetData()));
         padFilter->SetBinaryFilter(true);
         padFilter->SetUpperThreshold(1);
         padFilter->SetLowerThreshold(1);
         padFilter->Update();
 
         resultSegmentationNode->SetData(padFilter->GetOutput());
       }
       this->EnsureTargetSegmentationNodeInDataStorage();
     }
   }
 }
 
 void mitk::SegWithPreviewTool::OnRoiDataChanged()
 {
   DataNode::ConstPointer node = this->GetToolManager()->GetRoiData(0);
 
   if (node.IsNotNull())
   {
     MaskAndCutRoiImageFilter::Pointer roiFilter = MaskAndCutRoiImageFilter::New();
     Image::Pointer image = dynamic_cast<Image *>(m_SegmentationInputNode->GetData());
 
     if (image.IsNull())
       return;
 
     roiFilter->SetInput(image);
     roiFilter->SetRegionOfInterest(node->GetData());
     roiFilter->Update();
 
     DataNode::Pointer tmpNode = DataNode::New();
     tmpNode->SetData(roiFilter->GetOutput());
 
     m_SegmentationInputNode = tmpNode;
   }
   else
     m_SegmentationInputNode = m_ReferenceDataNode;
 
   this->ResetPreviewNode();
   this->InitiateToolByInput();
   this->UpdatePreview();
 }
 
 void mitk::SegWithPreviewTool::OnTimePointChanged()
 {
   if (m_IsTimePointChangeAware && m_PreviewSegmentationNode.IsNotNull() && m_SegmentationInputNode.IsNotNull())
   {
     const auto timePoint = RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint();
 
     const bool isStaticSegOnDynamicImage = m_PreviewSegmentationNode->GetData()->GetTimeSteps() == 1 && m_SegmentationInputNode->GetData()->GetTimeSteps() > 1;
     if (timePoint!=m_LastTimePointOfUpdate && (isStaticSegOnDynamicImage || m_LazyDynamicPreviews))
     { //we only need to update either because we are lazzy
       //or because we have a static segmentation with a dynamic referenceImage 
       this->UpdatePreview();
     }
   }
 }
 
 bool mitk::SegWithPreviewTool::EnsureUpToDateUserDefinedActiveLabel()
 {
   bool labelChanged = true;
 
   const auto workingImage = dynamic_cast<const Image*>(this->GetToolManager()->GetWorkingData(0)->GetData());
   if (const auto& labelSetImage = dynamic_cast<const LabelSetImage*>(workingImage))
   {
     // this is a fix for T28131 / T28986, which should be refactored if T28524 is being worked on
     auto newLabel = labelSetImage->GetActiveLabel(labelSetImage->GetActiveLayer())->GetValue();
     labelChanged = newLabel != m_UserDefinedActiveLabel;
     m_UserDefinedActiveLabel = newLabel;
   }
   else
   {
     m_UserDefinedActiveLabel = 1;
     labelChanged = false;
   }
   return labelChanged;
 }
 
 void mitk::SegWithPreviewTool::UpdatePreview(bool ignoreLazyPreviewSetting)
 {
   const auto inputImage = this->GetSegmentationInput();
   auto previewImage = this->GetPreviewSegmentation();
   int progress_steps = 200;
 
   const auto workingImage = dynamic_cast<const Image*>(this->GetToolManager()->GetWorkingData(0)->GetData());
   this->EnsureUpToDateUserDefinedActiveLabel();
 
   this->CurrentlyBusy.Send(true);
   m_IsUpdating = true;
 
   this->UpdatePrepare();
 
   const auto timePoint = RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint();
 
   try
   {
     if (nullptr != inputImage && nullptr != previewImage)
     {
       m_ProgressCommand->AddStepsToDo(progress_steps);
 
       if (previewImage->GetTimeSteps() > 1 && (ignoreLazyPreviewSetting || !m_LazyDynamicPreviews))
       {
         for (unsigned int timeStep = 0; timeStep < previewImage->GetTimeSteps(); ++timeStep)
         {
           Image::ConstPointer feedBackImage;
           Image::ConstPointer currentSegImage;
 
           auto previewTimePoint = previewImage->GetTimeGeometry()->TimeStepToTimePoint(timeStep);
           auto inputTimeStep = inputImage->GetTimeGeometry()->TimePointToTimeStep(previewTimePoint);
 
           if (nullptr != this->GetWorkingPlaneGeometry())
           { //only extract a specific slice defined by the working plane as feedback referenceImage.
             feedBackImage = SegTool2D::GetAffectedImageSliceAs2DImage(this->GetWorkingPlaneGeometry(), inputImage, inputTimeStep);
             currentSegImage = SegTool2D::GetAffectedImageSliceAs2DImageByTimePoint(this->GetWorkingPlaneGeometry(), workingImage, previewTimePoint);
           }
           else
           { //work on the whole feedback referenceImage
             feedBackImage = this->GetImageByTimeStep(inputImage, inputTimeStep);
             currentSegImage = this->GetImageByTimePoint(workingImage, previewTimePoint);
           }
 
           this->DoUpdatePreview(feedBackImage, currentSegImage, previewImage, timeStep);
         }
       }
       else
       {
         Image::ConstPointer feedBackImage;
         Image::ConstPointer currentSegImage;
 
         if (nullptr != this->GetWorkingPlaneGeometry())
         {
           feedBackImage = SegTool2D::GetAffectedImageSliceAs2DImageByTimePoint(this->GetWorkingPlaneGeometry(), inputImage, timePoint);
           currentSegImage = SegTool2D::GetAffectedImageSliceAs2DImageByTimePoint(this->GetWorkingPlaneGeometry(), workingImage, timePoint);
         }
         else
         {
           feedBackImage = this->GetImageByTimePoint(inputImage, timePoint);
           currentSegImage = this->GetImageByTimePoint(workingImage, timePoint);
         }
 
         auto timeStep = previewImage->GetTimeGeometry()->TimePointToTimeStep(timePoint);
 
         this->DoUpdatePreview(feedBackImage, currentSegImage, previewImage, timeStep);
       }
       RenderingManager::GetInstance()->RequestUpdateAll();
     }
   }
   catch (itk::ExceptionObject & excep)
   {
     MITK_ERROR << "Exception caught: " << excep.GetDescription();
 
     m_ProgressCommand->SetProgress(progress_steps);
 
     std::string msg = excep.GetDescription();
     ErrorMessage.Send(msg);
   }
   catch (...)
   {
     m_ProgressCommand->SetProgress(progress_steps);
     m_IsUpdating = false;
     CurrentlyBusy.Send(false);
     throw;
   }
 
   this->UpdateCleanUp();
   m_LastTimePointOfUpdate = timePoint;
   m_ProgressCommand->SetProgress(progress_steps);
   m_IsUpdating = false;
   CurrentlyBusy.Send(false);
 }
 
 bool mitk::SegWithPreviewTool::IsUpdating() const
 {
   return m_IsUpdating;
 }
 
 void mitk::SegWithPreviewTool::UpdatePrepare()
 {
   // default implementation does nothing
   //reimplement in derived classes for special behavior
 }
 
 void mitk::SegWithPreviewTool::UpdateCleanUp()
 {
   // default implementation does nothing
   //reimplement in derived classes for special behavior
 }
 
 void mitk::SegWithPreviewTool::TransferLabelInformation(LabelMappingType& labelMapping,
   const mitk::LabelSetImage* source, mitk::LabelSetImage* target)
 {
   for (const auto& [sourceLabel, targetLabel] : labelMapping)
   {
-    if (!target->ExistLabel(targetLabel, target->GetActiveLayer()))
+    if (LabelSetImage::UnlabeledValue != sourceLabel &&
+        LabelSetImage::UnlabeledValue != targetLabel &&
+        !target->ExistLabel(targetLabel, target->GetActiveLayer()))
     {
       if (!source->ExistLabel(sourceLabel, source->GetActiveLayer()))
       {
         mitkThrow() << "Cannot prepare segmentation for preview transfer. Preview seems invalid as label is missing. Missing label: " << sourceLabel;
       }
 
-      auto clonedLabel = source->GetLabel(sourceLabel, source->GetActiveLayer())->Clone();
+      auto clonedLabel = source->GetLabel(sourceLabel)->Clone();
       clonedLabel->SetValue(targetLabel);
       target->GetActiveLabelSet()->AddLabel(clonedLabel);
     }
   }
 }
 
 void mitk::SegWithPreviewTool::TransferPrepare()
 {
   auto labelMapping = this->GetLabelMapping();
 
   DataNode::Pointer resultSegmentationNode = GetTargetSegmentationNode();
 
   if (resultSegmentationNode.IsNotNull())
   {
     auto resultSegmentation = dynamic_cast<LabelSetImage*>(resultSegmentationNode->GetData());
 
     if (nullptr == resultSegmentation)
     {
       mitkThrow() << "Cannot prepare segmentation for preview transfer. Tool is in invalid state as segmentation is not existing or of right type";
     }
 
     auto preview = this->GetPreviewSegmentation();
     TransferLabelInformation(labelMapping, preview, resultSegmentation);
   }
 }
 
 mitk::TimePointType mitk::SegWithPreviewTool::GetLastTimePointOfUpdate() const
 {
   return m_LastTimePointOfUpdate;
 }
 
 const char* mitk::SegWithPreviewTool::GetGroup() const
 {
   return "autoSegmentation";
 }
 
 mitk::Image::ConstPointer mitk::SegWithPreviewTool::GetImageByTimeStep(const mitk::Image* image, TimeStepType timestep)
 {
   return SelectImageByTimeStep(image, timestep);
 }
 
 mitk::Image::Pointer mitk::SegWithPreviewTool::GetImageByTimeStep(mitk::Image* image, TimeStepType timestep)
 {
   return SelectImageByTimeStep(image, timestep);
 }
 
 mitk::Image::ConstPointer mitk::SegWithPreviewTool::GetImageByTimePoint(const mitk::Image* image, TimePointType timePoint)
 {
   return SelectImageByTimePoint(image, timePoint);
 }
 
 void mitk::SegWithPreviewTool::EnsureTargetSegmentationNodeInDataStorage() const
 {
   auto targetNode = this->GetTargetSegmentationNode();
   auto dataStorage = this->GetToolManager()->GetDataStorage();
   if (!dataStorage->Exists(targetNode))
   {
     dataStorage->Add(targetNode, this->GetToolManager()->GetReferenceData(0));
   }
 }
 
 std::string mitk::SegWithPreviewTool::GetCurrentSegmentationName()
 {
   auto workingData = this->GetToolManager()->GetWorkingData(0);
 
   return nullptr != workingData
     ? workingData->GetName()
     : "";
 }
 
 
 mitk::DataNode* mitk::SegWithPreviewTool::GetTargetSegmentationNode() const
 {
   return this->GetToolManager()->GetWorkingData(0);
 }
 
 void mitk::SegWithPreviewTool::TransferLabelSetImageContent(const LabelSetImage* source, LabelSetImage* target, TimeStepType timeStep)
 {
   mitk::ImageReadAccessor newMitkImgAcc(source);
 
   LabelMappingType labelMapping;
   const auto labelSet = source->GetActiveLabelSet();
   for (auto labelIter = labelSet->IteratorConstBegin(); labelIter != labelSet->IteratorConstEnd(); ++labelIter)
   {
     labelMapping.push_back({ labelIter->second->GetValue(),labelIter->second->GetValue() });
   }
   TransferLabelInformation(labelMapping, source, target);
 
   target->SetVolume(newMitkImgAcc.GetData(), timeStep);
 }
diff --git a/Modules/Segmentation/Interactions/mitkTool.h b/Modules/Segmentation/Interactions/mitkTool.h
index 1f636b972c..662c9b64c8 100644
--- a/Modules/Segmentation/Interactions/mitkTool.h
+++ b/Modules/Segmentation/Interactions/mitkTool.h
@@ -1,273 +1,273 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #ifndef mitkTool_h
 #define mitkTool_h
 
 #include "itkObjectFactoryBase.h"
 #include "itkVersion.h"
 #include "mitkCommon.h"
 #include "mitkDataNode.h"
 #include "mitkEventStateMachine.h"
 #include "mitkInteractionEventObserver.h"
 #include "mitkLabelSetImage.h"
 #include "mitkMessage.h"
 #include "mitkNodePredicateAnd.h"
 #include "mitkNodePredicateDataType.h"
 #include "mitkNodePredicateDimension.h"
 #include "mitkNodePredicateNot.h"
 #include "mitkNodePredicateOr.h"
 #include "mitkNodePredicateProperty.h"
 #include "mitkToolEvents.h"
 #include "mitkToolFactoryMacro.h"
 #include <MitkSegmentationExports.h>
 #include <mitkLabel.h>
 
 #include <iostream>
 #include <map>
 #include <string>
 
 #include <itkObject.h>
 
 #include "usServiceRegistration.h"
 
 namespace us
 {
   class ModuleResource;
 }
 
 namespace mitk
 {
   class ToolManager;
 
   /**
   \brief Base class of all tools used by mitk::ToolManager.
 
   \sa ToolManager
   \sa SegTool2D
 
   \ingroup Interaction
   \ingroup ToolManagerEtAl
 
   Every tool is a mitk::EventStateMachine, which can follow any transition pattern that it likes.
   Every derived tool should always call SuperClass::Deactivated() at the end of its own implementation of Deactivated,
   because mitk::Tool resets the interaction configuration in this method.
   Only if you are very sure that you covered all possible things that might happen to your own tool,
   you should consider not to reset the configuration.
 
   To learn about the MITK implementation of state machines in general, have a look at \ref InteractionPage.
 
   To derive a non-abstract tool, you inherit from mitk::Tool (or some other base class further down the inheritance
   tree), and in your own parameterless constructor (that is called from the itkFactorylessNewMacro that you use)
   you pass a state machine name (interactor type).
   Names and .xml-files for valid state machines can be found in different "Interaction" directories (which might be enhanced by you).
 
   You have to implement at least GetXPM() and GetName() to provide some identification.
 
   Each Tool knows its ToolManager, which can provide the data that the tool should work on.
 
   \warning Only to be instantiated by mitk::ToolManager (because SetToolManager has to be called). All other uses are
   unsupported.
 
   $Author$
   */
   class MITKSEGMENTATION_EXPORT Tool : public EventStateMachine, public InteractionEventObserver
   {
   public:
     typedef mitk::Label::PixelType DefaultSegmentationDataType;
 
     /**
      * \brief To let GUI process new events (e.g. qApp->processEvents() )
      */
     Message<> GUIProcessEventsMessage;
 
     /**
      * \brief To send error messages (to be shown by some GUI)
      */
     Message1<std::string> ErrorMessage;
 
     /**
      * \brief To send whether the tool is busy (to be shown by some GUI)
      */
     Message1<bool> CurrentlyBusy;
 
     /**
      * \brief To send general messages (to be shown by some GUI)
      */
     Message1<std::string> GeneralMessage;
 
     mitkClassMacro(Tool, EventStateMachine);
 
     // no New(), there should only be subclasses
 
     /**
     \brief Returns an icon in the XPM format.
 
     This icon has to fit into some kind of button in most applications, so make it smaller than 25x25 pixels.
 
     XPM is e.g. supported by The Gimp. But if you open any XPM file in your text editor, you will see that you could
     also "draw" it with an editor.
     */
-    [[deprecated]]
-    virtual const char **GetXPM() const = 0;
+    //[[deprecated]]
+    DEPRECATED(virtual const char **GetXPM() const) = 0;
 
     /**
      * \brief Returns the path of an icon.
      *
      * This icon is preferred to the XPM icon.
      */
     virtual std::string GetIconPath() const { return ""; }
     /**
      * \brief Returns the path of a cursor icon.
      *
      */
     virtual us::ModuleResource GetCursorIconResource() const;
 
     /**
      * @brief Returns the tool button icon of the tool wrapped by a usModuleResource
      * @return a valid ModuleResource or an invalid if this function
      *         is not reimplemented
      */
     virtual us::ModuleResource GetIconResource() const;
 
     /**
     \brief Returns the name of this tool. Make it short!
 
     This name has to fit into some kind of button in most applications, so take some time to think of a good name!
     */
     virtual const char *GetName() const = 0;
 
     /**
     \brief Name of a group.
 
     You can group several tools by assigning a group name. Graphical tool selectors might use this information to group
     tools. (What other reason could there be?)
     */
     virtual const char *GetGroup() const;
 
     virtual void InitializeStateMachine();
 
     /**
      * \brief Interface for GUI creation.
      *
      * This is the basic interface for creation of a GUI object belonging to one tool.
      *
      * Tools that support a GUI (e.g. for display/editing of parameters) should follow some rules:
      *
      *  - A Tool and its GUI are two separate classes
      *  - There may be several instances of a GUI at the same time.
      *  - mitk::Tool is toolkit (Qt, wxWidgets, etc.) independent, the GUI part is of course dependent
      *  - The GUI part inherits both from itk::Object and some GUI toolkit class
      *  - The GUI class name HAS to be constructed like "toolkitPrefix" tool->GetClassName() + "toolkitPostfix", e.g.
      * MyTool -> wxMyToolGUI
      *  - For each supported toolkit there is a base class for tool GUIs, which contains some convenience methods
      *  - Tools notify the GUI about changes using ITK events. The GUI must observe interesting events.
      *  - The GUI base class may convert all ITK events to the GUI toolkit's favoured messaging system (Qt -> signals)
      *  - Calling methods of a tool by its GUI is done directly.
      *    In some cases GUIs don't want to be notified by the tool when they cause a change in a tool.
      *    There is a macro CALL_WITHOUT_NOTICE(method()), which will temporarily disable all notifications during a
      * method call.
      */
     virtual itk::Object::Pointer GetGUI(const std::string &toolkitPrefix, const std::string &toolkitPostfix);
 
     virtual NodePredicateBase::ConstPointer GetReferenceDataPreference() const;
     virtual NodePredicateBase::ConstPointer GetWorkingDataPreference() const;
 
     DataNode::Pointer CreateEmptySegmentationNode(const Image *original,
                                                   const std::string &organName,
                                                   const mitk::Color &color) const;
     DataNode::Pointer CreateSegmentationNode(Image *image, const std::string &organName, const mitk::Color &color) const;
 
     /** Function used to check if a tool can handle the referenceData and (if specified) the working data.
      @pre referenceData must be a valid pointer
      @param referenceData Pointer to the data that should be checked as valid reference for the tool.
      @param workingData Pointer to the data that should be checked as valid working data for this tool.
      This parameter can be null if no working data is specified so far.*/
     virtual bool CanHandle(const BaseData *referenceData, const BaseData *workingData) const;
 
   protected:
     friend class ToolManager;
 
     virtual void SetToolManager(ToolManager *);
     /** Returns the pointer to the tool manager of the tool. May be null.*/
     ToolManager* GetToolManager() const;
 
     /** Returns the data storage provided by the toolmanager. May be null (e.g. if
      ToolManager is not set).*/
     mitk::DataStorage* GetDataStorage() const;
 
     void ConnectActionsAndFunctions() override;
 
     /**
     \brief Called when the tool gets activated.
 
     Derived tools should call their parents implementation at the beginning of the overriding function.
     */
     virtual void Activated();
 
     /**
     \brief Called when the tool gets deactivated.
 
     Derived tools should call their parents implementation at the end of the overriding function.
     */
     virtual void Deactivated();
 
     /**
     \brief Let subclasses change their event configuration.
     */
     std::string m_EventConfig;
 
     Tool(const char *, const us::Module *interactorModule = nullptr); // purposely hidden
     ~Tool() override;
 
     void Notify(InteractionEvent *interactionEvent, bool isHandled) override;
 
     bool FilterEvents(InteractionEvent *, DataNode *) override;
 
   private:
     ToolManager* m_ToolManager;
 
     // for reference data
     NodePredicateDataType::Pointer m_PredicateImages;
     NodePredicateDimension::Pointer m_PredicateDim3;
     NodePredicateDimension::Pointer m_PredicateDim4;
     NodePredicateOr::Pointer m_PredicateDimension;
     NodePredicateAnd::Pointer m_PredicateImage3D;
 
     NodePredicateProperty::Pointer m_PredicateBinary;
     NodePredicateNot::Pointer m_PredicateNotBinary;
 
     NodePredicateProperty::Pointer m_PredicateSegmentation;
     NodePredicateNot::Pointer m_PredicateNotSegmentation;
 
     NodePredicateProperty::Pointer m_PredicateHelper;
     NodePredicateNot::Pointer m_PredicateNotHelper;
 
     NodePredicateAnd::Pointer m_PredicateImageColorful;
 
     NodePredicateAnd::Pointer m_PredicateImageColorfulNotHelper;
 
     NodePredicateAnd::Pointer m_PredicateReference;
 
     // for working data
     NodePredicateAnd::Pointer m_IsSegmentationPredicate;
 
     std::string m_InteractorType;
 
     std::map<us::ServiceReferenceU, EventConfig> m_DisplayInteractionConfigs;
 
     const us::Module *m_InteractorModule;
   };
 
 } // namespace
 
 #endif
diff --git a/Modules/SegmentationUI/CMakeLists.txt b/Modules/SegmentationUI/CMakeLists.txt
index e8ff32e1d3..794aee58ab 100644
--- a/Modules/SegmentationUI/CMakeLists.txt
+++ b/Modules/SegmentationUI/CMakeLists.txt
@@ -1,5 +1,9 @@
 MITK_CREATE_MODULE (
   INCLUDE_DIRS Qmitk SegmentationUtilities
   DEPENDS MitkSegmentation MitkQtWidgetsExt
   PACKAGE_DEPENDS PRIVATE CTK|CTKWidgets nlohmann_json
 )
+
+if(BUILD_TESTING)
+  add_subdirectory(test)
+endif()
\ No newline at end of file
diff --git a/Modules/SegmentationUI/Qmitk/QmitkLabelColorItemDelegate.cpp b/Modules/SegmentationUI/Qmitk/QmitkLabelColorItemDelegate.cpp
new file mode 100644
index 0000000000..5d7adbb801
--- /dev/null
+++ b/Modules/SegmentationUI/Qmitk/QmitkLabelColorItemDelegate.cpp
@@ -0,0 +1,69 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+All rights reserved.
+
+Use of this source code is governed by a 3-clause BSD license that can be
+found in the LICENSE file.
+
+============================================================================*/
+
+#include "QmitkLabelColorItemDelegate.h"
+
+#include <QPainter>
+#include <QColorDialog>
+#include <QMouseEvent>
+
+QmitkLabelColorItemDelegate::QmitkLabelColorItemDelegate(QObject * /*parent*/)
+{
+}
+
+void QmitkLabelColorItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
+  const QModelIndex &index) const
+{
+  QVariant data = index.data(Qt::EditRole);
+
+  if (data.canConvert<QColor>())
+  {
+    QColor color = data.value<QColor>();
+
+    painter->fillRect(option.rect, color);
+  }
+  else
+  {
+    QStyledItemDelegate::paint(painter, option, index);
+  }
+}
+
+bool QmitkLabelColorItemDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &,
+  const QModelIndex &index)
+{
+  Q_ASSERT(event);
+  Q_ASSERT(model);
+
+  // make sure that the item is checkable
+  Qt::ItemFlags flags = model->flags(index);
+  if (!(flags & Qt::ItemIsEditable) || !(flags & Qt::ItemIsEnabled))
+  {
+    return false;
+  }
+
+  // make sure that we have the right event type
+  QMouseEvent* mouseEvent = dynamic_cast<QMouseEvent*>(event);
+  if (nullptr == mouseEvent || mouseEvent->type() != QEvent::MouseButtonRelease || mouseEvent->button() != Qt::LeftButton)
+  {
+    return false;
+  }
+
+  QColor oldColor = index.data(Qt::EditRole).value<QColor>();
+  QColor newColor = QColorDialog::getColor(oldColor, nullptr);
+
+  if (newColor.isValid())
+  {
+    return model->setData(index, QVariant(newColor), Qt::EditRole);
+  }
+
+  return false;
+};
diff --git a/Modules/SegmentationUI/Qmitk/QmitkLabelColorItemDelegate.h b/Modules/SegmentationUI/Qmitk/QmitkLabelColorItemDelegate.h
new file mode 100644
index 0000000000..015ef0dab8
--- /dev/null
+++ b/Modules/SegmentationUI/Qmitk/QmitkLabelColorItemDelegate.h
@@ -0,0 +1,41 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+All rights reserved.
+
+Use of this source code is governed by a 3-clause BSD license that can be
+found in the LICENSE file.
+
+============================================================================*/
+
+#ifndef QmitkLabelColorItemDelegate_h
+#define QmitkLabelColorItemDelegate_h
+
+
+#include <QStyledItemDelegate>
+
+#include <MitkSegmentationUIExports.h>
+
+/**
+\brief An item delegate for rendering and editing label color in a QmitkMultiLabelTreeView.
+*/
+class MITKSEGMENTATIONUI_EXPORT QmitkLabelColorItemDelegate : public QStyledItemDelegate
+{
+  Q_OBJECT
+
+public:
+  ///
+  /// Creates a new PropertyDelegate.
+  ///
+  explicit QmitkLabelColorItemDelegate(QObject *parent = nullptr);
+
+  bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option,
+    const QModelIndex &index) override;
+
+  void paint(QPainter *painter, const QStyleOptionViewItem &option,
+    const QModelIndex &index) const override;
+};
+
+#endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitkLabelToggleItemDelegate.cpp b/Modules/SegmentationUI/Qmitk/QmitkLabelToggleItemDelegate.cpp
new file mode 100644
index 0000000000..997f001082
--- /dev/null
+++ b/Modules/SegmentationUI/Qmitk/QmitkLabelToggleItemDelegate.cpp
@@ -0,0 +1,82 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+All rights reserved.
+
+Use of this source code is governed by a 3-clause BSD license that can be
+found in the LICENSE file.
+
+============================================================================*/
+
+#include "QmitkLabelToggleItemDelegate.h"
+
+
+
+#include <QPainter>
+#include <QMouseEvent>
+
+QmitkLabelToggleItemDelegate::QmitkLabelToggleItemDelegate(const QIcon& onIcon, const QIcon& offIcon, QObject * parent) 
+  :QStyledItemDelegate(parent),
+   m_OnIcon(onIcon),
+   m_OffIcon(offIcon)
+{
+}
+
+void QmitkLabelToggleItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
+  const QModelIndex &index) const
+{
+  QVariant data = index.data(Qt::EditRole);
+
+  if (data.canConvert<bool>())
+  {
+    if (data.toBool())
+    {
+      m_OnIcon.paint(painter, option.rect);
+    }
+    else
+    {
+      m_OffIcon.paint(painter, option.rect);
+    }
+  }
+  else
+  {
+    QStyledItemDelegate::paint(painter, option, index);
+  }
+}
+
+QSize QmitkLabelToggleItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
+{
+  auto defaultSize = QStyledItemDelegate::sizeHint(option, index);
+  return QSize(defaultSize.height(), defaultSize.height());
+}
+
+bool QmitkLabelToggleItemDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &,
+  const QModelIndex &index)
+{
+  Q_ASSERT(event);
+  Q_ASSERT(model);
+
+  // make sure that the item is checkable
+  Qt::ItemFlags flags = model->flags(index);
+  if (!(flags & Qt::ItemIsEditable) || !(flags & Qt::ItemIsEnabled))
+  {
+    return false;
+  }
+
+  // make sure that we have the right event type
+  QMouseEvent* mouseEvent = dynamic_cast<QMouseEvent*>(event);
+  if (nullptr == mouseEvent || mouseEvent->type() != QEvent::MouseButtonRelease || mouseEvent->button() != Qt::LeftButton)
+  {
+    return false;
+  }
+
+  auto visVar = index.data(Qt::EditRole);
+  auto data = visVar.isValid()
+     ? QVariant(!(visVar.toBool()))
+     : QVariant(true);
+  return model->setData(index, data, Qt::EditRole);
+
+  return false;
+};
diff --git a/Modules/SegmentationUI/Qmitk/QmitkLabelToggleItemDelegate.h b/Modules/SegmentationUI/Qmitk/QmitkLabelToggleItemDelegate.h
new file mode 100644
index 0000000000..37d9aeb330
--- /dev/null
+++ b/Modules/SegmentationUI/Qmitk/QmitkLabelToggleItemDelegate.h
@@ -0,0 +1,48 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+All rights reserved.
+
+Use of this source code is governed by a 3-clause BSD license that can be
+found in the LICENSE file.
+
+============================================================================*/
+
+#ifndef QmitkLabelToggleItemDelegate_h
+#define QmitkLabelToggleItemDelegate_h
+
+
+#include <QStyledItemDelegate>
+#include <QIcon>
+
+#include <MitkSegmentationUIExports.h>
+
+/**
+\brief An item delegate for rendering and editing properties that can be toggled (e.g. visibility).
+*/
+class MITKSEGMENTATIONUI_EXPORT QmitkLabelToggleItemDelegate : public QStyledItemDelegate
+{
+  Q_OBJECT
+
+public:
+  ///
+  /// Creates a new PropertyDelegate.
+  ///
+  explicit QmitkLabelToggleItemDelegate(const QIcon& onIcon, const QIcon& offIcon, QObject* parent = nullptr);
+
+  bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option,
+    const QModelIndex& index) override;
+
+  void paint(QPainter* painter, const QStyleOptionViewItem& option,
+    const QModelIndex& index) const override;
+
+  QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
+
+protected:
+  QIcon m_OnIcon;
+  QIcon m_OffIcon;
+};
+
+#endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.cpp b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.cpp
new file mode 100644
index 0000000000..0524b2d066
--- /dev/null
+++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.cpp
@@ -0,0 +1,1065 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+All rights reserved.
+
+Use of this source code is governed by a 3-clause BSD license that can be
+found in the LICENSE file.
+
+============================================================================*/
+
+#include <QmitkMultiLabelInspector.h>
+
+// mitk
+#include <mitkRenderingManager.h>
+#include <mitkLabelSetImageHelper.h>
+#include <mitkDICOMSegmentationPropertyHelper.h>
+
+// Qmitk
+#include <QmitkMultiLabelTreeModel.h>
+#include <QmitkLabelColorItemDelegate.h>
+#include <QmitkLabelToggleItemDelegate.h>
+#include <QmitkStyleManager.h>
+
+// Qt
+#include <QMenu>
+#include <QLabel>
+#include <QWidgetAction>
+#include <QMessageBox>
+
+#include <ui_QmitkMultiLabelInspectorControls.h>
+
+QmitkMultiLabelInspector::QmitkMultiLabelInspector(QWidget* parent/* = nullptr*/)
+  : QWidget(parent), m_Controls(new Ui::QmitkMultiLabelInspector)
+{
+  m_Controls->setupUi(this);
+
+  m_Model = new QmitkMultiLabelTreeModel(this);
+
+  m_Controls->view->setModel(m_Model);
+
+  m_ColorItemDelegate = new QmitkLabelColorItemDelegate(this);
+
+  auto visibleIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/visible.svg"));
+  auto invisibleIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/invisible.svg"));
+  m_VisibilityItemDelegate = new QmitkLabelToggleItemDelegate(visibleIcon, invisibleIcon, this);
+
+  auto lockIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/lock.svg"));
+  auto unlockIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/unlock.svg"));
+  m_LockItemDelegate = new QmitkLabelToggleItemDelegate(lockIcon, unlockIcon, this);
+
+  auto* view = this->m_Controls->view;
+  view->setItemDelegateForColumn(1, m_LockItemDelegate);
+  view->setItemDelegateForColumn(2, m_ColorItemDelegate);
+  view->setItemDelegateForColumn(3, m_VisibilityItemDelegate);
+
+  auto* header = view->header();
+  header->setSectionResizeMode(0,QHeaderView::Stretch);
+  header->setSectionResizeMode(1, QHeaderView::ResizeToContents);
+  header->setSectionResizeMode(2, QHeaderView::ResizeToContents);
+  header->setSectionResizeMode(3, QHeaderView::ResizeToContents);
+  view->setContextMenuPolicy(Qt::CustomContextMenu);
+
+  connect(m_Model, &QAbstractItemModel::modelReset, this, &QmitkMultiLabelInspector::OnModelReset);
+  connect(view->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), SLOT(OnChangeModelSelection(const QItemSelection&, const QItemSelection&)));
+  connect(view, &QAbstractItemView::customContextMenuRequested, this, &QmitkMultiLabelInspector::OnContextMenuRequested);
+  connect(view, &QAbstractItemView::doubleClicked, this, &QmitkMultiLabelInspector::OnItemDoubleClicked);
+}
+
+QmitkMultiLabelInspector::~QmitkMultiLabelInspector()
+{
+  delete m_Controls;
+}
+
+void QmitkMultiLabelInspector::Initialize()
+{
+  m_LastValidSelectedLabels = {};
+  m_ModelManipulationOngoing = false;
+  m_Model->SetSegmentation(m_Segmentation);
+  m_Controls->view->expandAll();
+
+  m_LastValidSelectedLabels = {};
+
+  //in singel selection mode, if at least one label exist select the first label of the mode.
+  if (m_Segmentation.IsNotNull() && !this->GetMultiSelectionMode() && m_Segmentation->GetTotalNumberOfLabels() > 0)
+  {
+    auto firstIndex = m_Model->FirstLabelInstanceIndex(QModelIndex());
+    auto labelVariant = firstIndex.data(QmitkMultiLabelTreeModel::ItemModelRole::LabelInstanceValueRole);
+
+    if (labelVariant.isValid())
+    {
+      this->SetSelectedLabel(labelVariant.value<LabelValueType>());
+      m_Controls->view->selectionModel()->setCurrentIndex(firstIndex, QItemSelectionModel::NoUpdate);
+    }
+  }
+}
+
+void QmitkMultiLabelInspector::SetMultiSelectionMode(bool multiMode)
+{
+  m_Controls->view->setSelectionMode(multiMode
+     ? QAbstractItemView::SelectionMode::MultiSelection
+     : QAbstractItemView::SelectionMode::SingleSelection);
+}
+
+bool QmitkMultiLabelInspector::GetMultiSelectionMode() const
+{
+  return QAbstractItemView::SelectionMode::MultiSelection == m_Controls->view->selectionMode();
+}
+
+void QmitkMultiLabelInspector::SetAllowVisibilityModification(bool visibilityMod)
+{
+  m_AllowVisibilityModification = visibilityMod;
+  this->m_Model->SetAllowVisibilityModification(visibilityMod);
+}
+
+void QmitkMultiLabelInspector::SetAllowLabelModification(bool labelMod)
+{
+  m_AllowLabelModification = labelMod;
+}
+
+bool QmitkMultiLabelInspector::GetAllowVisibilityModification() const
+{
+  return m_AllowVisibilityModification;
+}
+
+void QmitkMultiLabelInspector::SetAllowLockModification(bool lockMod)
+{
+  m_AllowLockModification = lockMod;
+  this->m_Model->SetAllowLockModification(lockMod);
+}
+
+bool QmitkMultiLabelInspector::GetAllowLockModification() const
+{
+  return m_AllowLockModification;
+}
+
+bool QmitkMultiLabelInspector::GetAllowLabelModification() const
+{
+  return m_AllowLabelModification;
+}
+
+void QmitkMultiLabelInspector::SetDefaultLabelNaming(bool defaultLabelNaming)
+{
+  m_DefaultLabelNaming = defaultLabelNaming;
+}
+
+void QmitkMultiLabelInspector::SetMultiLabelSegmentation(mitk::LabelSetImage* segmentation)
+{
+  if (segmentation != m_Segmentation)
+  {
+    m_Segmentation = segmentation;
+    this->Initialize();
+  }
+}
+
+void QmitkMultiLabelInspector::OnModelReset()
+{
+  m_LastValidSelectedLabels = {};
+  m_ModelManipulationOngoing = false;
+}
+
+bool EqualLabelSelections(const QmitkMultiLabelInspector::LabelValueVectorType& selection1, const QmitkMultiLabelInspector::LabelValueVectorType& selection2)
+{
+  if (selection1.size() == selection2.size())
+  {
+    // lambda to compare node pointer inside both lists
+    return std::is_permutation(selection1.begin(), selection1.end(), selection2.begin());
+  }
+
+  return false;
+}
+
+void QmitkMultiLabelInspector::SetSelectedLabels(const LabelValueVectorType& selectedLabels)
+{
+  if (EqualLabelSelections(this->GetSelectedLabels(), selectedLabels))
+  {
+    return;
+  }
+
+  this->UpdateSelectionModel(selectedLabels);
+  m_LastValidSelectedLabels = selectedLabels;
+}
+
+void QmitkMultiLabelInspector::UpdateSelectionModel(const LabelValueVectorType& selectedLabels)
+{
+  // create new selection by retrieving the corresponding indices of the labels
+  QItemSelection newCurrentSelection;
+  for (const auto& labelID : selectedLabels)
+  {
+    QModelIndexList matched = m_Model->match(m_Model->index(0, 0), QmitkMultiLabelTreeModel::ItemModelRole::LabelInstanceValueRole, QVariant(labelID), 1, Qt::MatchRecursive);
+    if (!matched.empty())
+    {
+      newCurrentSelection.select(matched.front(), matched.front());
+    }
+  }
+
+  m_Controls->view->selectionModel()->select(newCurrentSelection, QItemSelectionModel::ClearAndSelect|QItemSelectionModel::Current);
+}
+
+void QmitkMultiLabelInspector::SetSelectedLabel(mitk::LabelSetImage::LabelValueType selectedLabel)
+{
+  this->SetSelectedLabels({ selectedLabel });
+}
+
+QmitkMultiLabelInspector::LabelValueVectorType QmitkMultiLabelInspector::GetSelectedLabelsFromSelectionModel() const
+{
+  LabelValueVectorType result;
+  QModelIndexList selectedIndexes = m_Controls->view->selectionModel()->selectedIndexes();
+  for (const auto& index : qAsConst(selectedIndexes))
+  {
+    QVariant qvariantDataNode = m_Model->data(index, QmitkMultiLabelTreeModel::ItemModelRole::LabelInstanceValueRole);
+    if (qvariantDataNode.canConvert<mitk::LabelSetImage::LabelValueType>())
+    {
+      result.push_back(qvariantDataNode.value<mitk::LabelSetImage::LabelValueType>());
+    }
+  }
+  return result;
+}
+
+QmitkMultiLabelInspector::LabelValueVectorType QmitkMultiLabelInspector::GetSelectedLabels() const
+{
+  return m_LastValidSelectedLabels;
+}
+
+mitk::Label* QmitkMultiLabelInspector::GetFirstSelectedLabelObject() const
+{
+  if (m_LastValidSelectedLabels.empty() || m_Segmentation.IsNull())
+    return nullptr;
+
+  return m_Segmentation->GetLabel(m_LastValidSelectedLabels.front());
+}
+
+void QmitkMultiLabelInspector::OnChangeModelSelection(const QItemSelection& /*selected*/, const QItemSelection& /*deselected*/)
+{
+  if (!m_ModelManipulationOngoing)
+  {
+    auto internalSelection = GetSelectedLabelsFromSelectionModel();
+    if (internalSelection.empty())
+    {
+      //empty selections are not allowed by UI interactions, there should always be at least on label selected.
+      //but selections are e.g. also cleared if the model is updated (e.g. due to addition of labels)
+      UpdateSelectionModel(m_LastValidSelectedLabels);
+    }
+    else
+    {
+      m_LastValidSelectedLabels = internalSelection;
+      emit CurrentSelectionChanged(GetSelectedLabels());
+    }
+  }
+}
+
+void QmitkMultiLabelInspector::WaitCursorOn() const
+{
+  QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
+}
+
+void QmitkMultiLabelInspector::WaitCursorOff() const
+{
+  this->RestoreOverrideCursor();
+}
+
+void QmitkMultiLabelInspector::RestoreOverrideCursor() const
+{
+  QApplication::restoreOverrideCursor();
+}
+
+mitk::Label* QmitkMultiLabelInspector::GetCurrentLabel() const
+{
+  auto currentIndex = this->m_Controls->view->currentIndex();
+  auto labelVariant = currentIndex.data(QmitkMultiLabelTreeModel::ItemModelRole::LabelDataRole);
+  mitk::Label::Pointer currentIndexLabel = nullptr;
+
+  if (labelVariant.isValid())
+  {
+    auto uncastedLabel = labelVariant.value<void*>();
+    currentIndexLabel = static_cast<mitk::Label*>(uncastedLabel);
+  }
+  return currentIndexLabel;
+}
+
+QmitkMultiLabelInspector::IndexLevelType QmitkMultiLabelInspector::GetCurrentLevelType() const
+{
+  auto currentIndex = this->m_Controls->view->currentIndex();
+  auto labelInstanceVariant = currentIndex.data(QmitkMultiLabelTreeModel::ItemModelRole::LabelInstanceDataRole);
+  auto labelVariant = currentIndex.data(QmitkMultiLabelTreeModel::ItemModelRole::LabelDataRole);
+
+  if (labelInstanceVariant.isValid() )
+  {
+    return IndexLevelType::LabelInstance;
+  }
+  else if (labelVariant.isValid())
+  {
+    return IndexLevelType::LabelClass;
+  }
+
+  return IndexLevelType::Group;
+}
+
+QmitkMultiLabelInspector::LabelValueVectorType QmitkMultiLabelInspector::GetCurrentlyAffactedLabelInstances() const
+{
+  auto currentIndex = m_Controls->view->currentIndex();
+  return m_Model->GetLabelsInSubTree(currentIndex);
+}
+
+mitk::Label* QmitkMultiLabelInspector::AddNewLabelInstanceInternal(mitk::Label* templateLabel)
+{
+  if (!m_AllowLabelModification)
+    mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of AddNewLabelInstance.";
+
+  if (nullptr == templateLabel)
+    mitkThrow() << "QmitkMultiLabelInspector is in an invalid state. AddNewLabelInstanceInternal was called with a non existing label as template";
+
+  auto groupID = m_Segmentation->GetGroupIndexOfLabel(templateLabel->GetValue());
+  auto group = m_Segmentation->GetLabelSet(groupID);
+  m_ModelManipulationOngoing = true;
+  auto newLabel = group->AddLabel(templateLabel, true);
+  m_ModelManipulationOngoing = false;
+  this->SetSelectedLabel(newLabel->GetValue());
+
+  auto index = m_Model->indexOfLabel(newLabel->GetValue());
+  if (index.isValid())
+  {
+    m_Controls->view->expand(index.parent());
+  }
+  else
+  {
+    mitkThrow() << "Segmentation or QmitkMultiLabelTreeModel is in an invalid state. Label is not present in the model after adding it to the segmentation. Label value: " << newLabel->GetValue();
+  }
+
+  return newLabel;
+}
+
+mitk::Label* QmitkMultiLabelInspector::AddNewLabelInstance()
+{
+  auto currentLabel = this->GetFirstSelectedLabelObject();
+  if (nullptr == currentLabel)
+    return nullptr;
+
+  return this->AddNewLabelInstanceInternal(currentLabel);
+}
+
+mitk::Label* QmitkMultiLabelInspector::AddNewLabelInternal(const mitk::LabelSetImage::GroupIndexType& containingGroup)
+{
+  mitk::Label::Pointer newLabel = mitk::LabelSetImageHelper::CreateNewLabel(m_Segmentation);
+
+  if (!m_DefaultLabelNaming)
+  {
+    emit LabelRenameRequested(newLabel, false);
+  }
+
+  auto group = m_Segmentation->GetLabelSet(containingGroup);
+  m_ModelManipulationOngoing = true;
+  group->AddLabel(newLabel, false);
+  m_ModelManipulationOngoing = false;
+  this->SetSelectedLabel(newLabel->GetValue());
+
+  auto index = m_Model->indexOfLabel(newLabel->GetValue());
+  if (index.isValid())
+  {
+    m_Controls->view->expand(index.parent());
+  }
+  else
+  {
+    mitkThrow() << "Segmentation or QmitkMultiLabelTreeModel is in an invalid state. Label is not present in the model after adding it to the segmentation. Label value: " << newLabel->GetValue();
+  }
+
+  return newLabel;
+}
+
+mitk::Label* QmitkMultiLabelInspector::AddNewLabel()
+{
+  if (!m_AllowLabelModification)
+    mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of AddNewLabel.";
+
+  if (m_Segmentation.IsNull())
+  {
+    return nullptr;
+  }
+
+  auto currentLabel = this->GetFirstSelectedLabelObject();
+  mitk::LabelSetImage::GroupIndexType groupID = nullptr != currentLabel
+    ? m_Segmentation->GetGroupIndexOfLabel(currentLabel->GetValue())
+    : 0;
+
+  return AddNewLabelInternal(groupID);
+}
+
+void QmitkMultiLabelInspector::DeleteLabel()
+{
+  if (!m_AllowLabelModification)
+    mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of RemoveLabel.";
+
+  if (m_Segmentation.IsNull())
+  {
+    return;
+  }
+
+  auto currentLabel = this->GetFirstSelectedLabelObject();
+  auto question = "Do you really want to remove label \"" + QString::fromStdString(currentLabel->GetName()) + "\"?";
+
+  auto answerButton = QMessageBox::question(this, QString("Remove label"), question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
+
+  if (answerButton == QMessageBox::Yes)
+  {
+    this->DeleteLabelInternal({ currentLabel->GetValue() });
+  }
+}
+
+void QmitkMultiLabelInspector::DeleteLabelInternal(const LabelValueVectorType& labelValues)
+{
+  if (!m_AllowLabelModification)
+    mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of DeleteLabelInternal.";
+
+  if (m_Segmentation.IsNull())
+  {
+    return;
+  }
+
+  QVariant nextLabelVariant;
+
+  this->WaitCursorOn();
+  m_ModelManipulationOngoing = true;
+  for (auto labelValue : labelValues)
+  {
+    if (labelValue == labelValues.back())
+    {
+      auto currentIndex = m_Model->indexOfLabel(labelValue);
+      auto nextIndex = m_Model->ClosestLabelInstanceIndex(currentIndex);
+      nextLabelVariant = nextIndex.data(QmitkMultiLabelTreeModel::ItemModelRole::LabelInstanceValueRole);
+    }
+
+    m_Segmentation->RemoveLabel(labelValue);
+  }
+  m_ModelManipulationOngoing = false;
+  this->WaitCursorOff();
+
+  if (nextLabelVariant.isValid())
+  {
+    auto newLabelValue = nextLabelVariant.value<LabelValueType>();
+    this->SetSelectedLabel(newLabelValue);
+
+    auto index = m_Model->indexOfLabel(newLabelValue); //we have to get index again, because it could have changed due to remove operation.
+    if (index.isValid())
+    {
+      m_Controls->view->expand(index.parent());
+    }
+    else
+    {
+      mitkThrow() << "Segmentation or QmitkMultiLabelTreeModel is in an invalid state. Label is not present in the model after adding it to the segmentation. Label value: " << newLabelValue;
+    }
+  }
+
+  mitk::RenderingManager::GetInstance()->RequestUpdateAll();
+}
+
+mitk::Label* QmitkMultiLabelInspector::AddNewGroup()
+{
+  if (!m_AllowLabelModification)
+    mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of AddNewLabel.";
+
+  if (m_Segmentation.IsNull())
+  {
+    return nullptr;
+  }
+
+  mitk::LabelSetImage::GroupIndexType groupID = 0;
+  mitk::Label* newLabel = nullptr;
+  m_ModelManipulationOngoing = true;
+  try
+  {
+    this->WaitCursorOn();
+    groupID = m_Segmentation->AddLayer();
+    this->WaitCursorOff();
+    newLabel =  this->AddNewLabelInternal(groupID);
+  }
+  catch (mitk::Exception& e)
+  {
+    this->WaitCursorOff();
+    m_ModelManipulationOngoing = false;
+    MITK_ERROR << "Exception caught: " << e.GetDescription();
+    QMessageBox::information(this, "Add group", "Could not add a new group. See error log for details.");
+  }
+  m_ModelManipulationOngoing = false;
+
+  return newLabel;
+}
+
+void QmitkMultiLabelInspector::RemoveGroupInternal(const mitk::LabelSetImage::GroupIndexType& groupID)
+{
+  if (!m_AllowLabelModification)
+    mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of RemoveLabel.";
+
+  if (m_Segmentation.IsNull())
+  {
+    return;
+  }
+
+  auto currentIndex = m_Model->indexOfGroup(groupID);
+  auto nextIndex = m_Model->ClosestLabelInstanceIndex(currentIndex);
+  auto labelVariant = nextIndex.data(QmitkMultiLabelTreeModel::ItemModelRole::LabelInstanceValueRole);
+
+  try
+  {
+    this->WaitCursorOn();
+    m_ModelManipulationOngoing = true;
+    m_Segmentation->RemoveGroup(groupID);
+    m_ModelManipulationOngoing = false;
+    this->WaitCursorOff();
+  }
+  catch (mitk::Exception& e)
+  {
+    m_ModelManipulationOngoing = false;
+    this->WaitCursorOff();
+    MITK_ERROR << "Exception caught: " << e.GetDescription();
+    QMessageBox::information(this, "Delete group", "Could not delete the currently active group. See error log for details.");
+    return;
+  }
+
+  if (labelVariant.isValid())
+  {
+    auto newLabelValue = labelVariant.value<LabelValueType>();
+    this->SetSelectedLabel(newLabelValue);
+
+    auto index = m_Model->indexOfLabel(newLabelValue); //we have to get index again, because it could have changed due to remove operation.
+    if (index.isValid())
+    {
+      m_Controls->view->expand(index.parent());
+    }
+    else
+    {
+      mitkThrow() << "Segmentation or QmitkMultiLabelTreeModel is in an invalid state. Label is not present in the model after adding it to the segmentation. Label value: " << newLabelValue;
+    }
+  }
+
+  mitk::RenderingManager::GetInstance()->RequestUpdateAll();
+}
+
+void QmitkMultiLabelInspector::RemoveGroup()
+{
+  if (!m_AllowLabelModification)
+    mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of RemoveLabel.";
+
+  if (m_Segmentation.IsNull())
+  {
+    return;
+  }
+
+  QString question = "Do you really want to delete the group of the selected label with all labels?";
+  QMessageBox::StandardButton answerButton = QMessageBox::question(
+    this, "Delete group", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
+
+  if (answerButton != QMessageBox::Yes)
+  {
+    return;
+  }
+
+  auto currentLabel = GetFirstSelectedLabelObject();
+  const auto currentGroup = m_Segmentation->GetGroupIndexOfLabel(currentLabel->GetValue());
+
+  this->RemoveGroupInternal(currentGroup);
+}
+
+void QmitkMultiLabelInspector::OnDeleteGroup()
+{
+  if (!m_AllowLabelModification)
+    mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of RemoveLabel.";
+
+  if (m_Segmentation.IsNull())
+  {
+    return;
+  }
+
+  auto currentIndex = this->m_Controls->view->currentIndex();
+  auto groupIDVariant = currentIndex.data(QmitkMultiLabelTreeModel::ItemModelRole::GroupIDRole);
+
+  if (groupIDVariant.isValid())
+  {
+    auto groupID = groupIDVariant.value<mitk::LabelSetImage::GroupIndexType>();
+
+    QString question = "Do you really want to delete the current group with all labels?";
+    QMessageBox::StandardButton answerButton = QMessageBox::question(
+      this, QString("Delete group ") + QString::number(groupID), question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
+
+    if (answerButton != QMessageBox::Yes)
+    {
+      return;
+    }
+
+    this->RemoveGroupInternal(groupID);
+  }
+};
+
+
+void QmitkMultiLabelInspector::OnContextMenuRequested(const QPoint& /*pos*/)
+{
+  if (m_Segmentation.IsNull() || !this->isEnabled())
+    return;
+
+  const auto indexLevel = this->GetCurrentLevelType();
+
+  if (IndexLevelType::Group == indexLevel)
+  {
+    QMenu* menu = new QMenu(this);
+
+    if (m_AllowLabelModification)
+    {
+      QAction* addInstanceAction = new QAction(QIcon(":/Qmitk/RenameLabel.png"), "&Add label", this);
+      QObject::connect(addInstanceAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnAddLabel);
+      menu->addAction(addInstanceAction);
+
+      QAction* removeAction = new QAction(QIcon(":/Qmitk/RemoveLabel.png"), "Delete group", this);
+      QObject::connect(removeAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnDeleteGroup);
+      menu->addAction(removeAction);
+    }
+
+    if (m_AllowLockModification)
+    {
+      menu->addSeparator();
+      QAction* lockAllAction = new QAction(QIcon(":/Qmitk/lock.png"), "Lock group", this);
+      QObject::connect(lockAllAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnLockAffectedLabels);
+      menu->addAction(lockAllAction);
+
+      QAction* unlockAllAction = new QAction(QIcon(":/Qmitk/unlock.png"), "Unlock group", this);
+      QObject::connect(unlockAllAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnUnlockAffectedLabels);
+      menu->addAction(unlockAllAction);
+    }
+
+    if (m_AllowVisibilityModification)
+    {
+      menu->addSeparator();
+
+      QAction* viewAllAction = new QAction(QIcon(":/Qmitk/visible.png"), "View group", this);
+      QObject::connect(viewAllAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnSetAffectedLabelsVisible);
+      menu->addAction(viewAllAction);
+
+      QAction* hideAllAction = new QAction(QIcon(":/Qmitk/invisible.png"), "Hide group", this);
+      QObject::connect(hideAllAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnSetAffectedLabelsInvisible);
+      menu->addAction(hideAllAction);
+
+      menu->addSeparator();
+
+      auto opacityAction = this->CreateOpacityAction();
+      if (nullptr != opacityAction)
+        menu->addAction(opacityAction);
+    }
+    menu->popup(QCursor::pos());
+  }
+  else if (IndexLevelType::LabelClass == indexLevel)
+  {
+    QMenu* menu = new QMenu(this);
+
+    if (m_AllowLabelModification)
+    {
+      QAction* addInstanceAction = new QAction(QIcon(":/Qmitk/RenameLabel.png"), "Add label instance", this);
+      QObject::connect(addInstanceAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnAddLabelInstance);
+      menu->addAction(addInstanceAction);
+
+      QAction* renameAction = new QAction(QIcon(":/Qmitk/RenameLabel.png"), "&Rename label class", this);
+      QObject::connect(renameAction, SIGNAL(triggered(bool)), this, SLOT(OnRenameLabel(bool)));
+      menu->addAction(renameAction);
+
+      QAction* removeAction = new QAction(QIcon(":/Qmitk/RemoveLabel.png"), "&Delete label class", this);
+      QObject::connect(removeAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnDeleteAffectedLabel);
+      menu->addAction(removeAction);
+    }
+
+    if (m_AllowLockModification)
+    {
+      menu->addSeparator();
+      QAction* lockAllAction = new QAction(QIcon(":/Qmitk/lock.png"), "Lock label instances", this);
+      QObject::connect(lockAllAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnLockAffectedLabels);
+      menu->addAction(lockAllAction);
+
+      QAction* unlockAllAction = new QAction(QIcon(":/Qmitk/unlock.png"), "Unlock label instances", this);
+      QObject::connect(unlockAllAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnUnlockAffectedLabels);
+      menu->addAction(unlockAllAction);
+    }
+
+    if (m_AllowVisibilityModification)
+    {
+      menu->addSeparator();
+
+      QAction* viewAllAction = new QAction(QIcon(":/Qmitk/visible.png"), "View label instances", this);
+      QObject::connect(viewAllAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnSetAffectedLabelsVisible);
+      menu->addAction(viewAllAction);
+
+      QAction* hideAllAction = new QAction(QIcon(":/Qmitk/invisible.png"), "Hide label instances", this);
+      QObject::connect(hideAllAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnSetAffectedLabelsInvisible);
+      menu->addAction(hideAllAction);
+
+      QAction* viewOnlyAction = new QAction(QIcon(":/Qmitk/visible.png"), "View only", this);
+      QObject::connect(viewOnlyAction, SIGNAL(triggered(bool)), this, SLOT(OnSetOnlyActiveLabelVisible(bool)));
+      menu->addAction(viewOnlyAction);
+
+      menu->addSeparator();
+
+      auto opacityAction = this->CreateOpacityAction();
+      if (nullptr!=opacityAction)
+        menu->addAction(opacityAction);
+    }
+    menu->popup(QCursor::pos());
+  }
+  else
+  {
+    auto selectedLabelValues = this->GetSelectedLabels();
+    if (selectedLabelValues.empty())
+      return;
+
+    QMenu* menu = new QMenu(this);
+
+    if (this->GetMultiSelectionMode() && selectedLabelValues.size() > 1)
+    {
+      QAction* mergeAction = new QAction(QIcon(":/Qmitk/MergeLabels.png"), "Merge selection on current label", this);
+      QObject::connect(mergeAction, SIGNAL(triggered(bool)), this, SLOT(OnMergeLabels(bool)));
+      menu->addAction(mergeAction);
+
+      QAction* removeLabelsAction = new QAction(QIcon(":/Qmitk/RemoveLabel.png"), "&Delete selected labels", this);
+      QObject::connect(removeLabelsAction, SIGNAL(triggered(bool)), this, SLOT(OnDeleteLabels(bool)));
+      menu->addAction(removeLabelsAction);
+
+      QAction* clearLabelsAction = new QAction(QIcon(":/Qmitk/EraseLabel.png"), "&Clear selected labels", this);
+      QObject::connect(clearLabelsAction, SIGNAL(triggered(bool)), this, SLOT(OnClearLabels(bool)));
+      menu->addAction(clearLabelsAction);
+    }
+    else
+    {
+      if (m_AllowLabelModification)
+      {
+        QAction* addInstanceAction = new QAction(QIcon(":/Qmitk/RenameLabel.png"), "&Add label instance...", this);
+        QObject::connect(addInstanceAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnAddLabelInstance);
+        menu->addAction(addInstanceAction);
+
+        QAction* renameAction = new QAction(QIcon(":/Qmitk/RenameLabel.png"), "&Rename", this);
+        QObject::connect(renameAction, SIGNAL(triggered(bool)), this, SLOT(OnRenameLabel(bool)));
+        menu->addAction(renameAction);
+
+        QAction* removeAction = new QAction(QIcon(":/Qmitk/RemoveLabel.png"), "&Delete", this);
+        QObject::connect(removeAction, &QAction::triggered, this, &QmitkMultiLabelInspector::DeleteLabel);
+        menu->addAction(removeAction);
+
+        QAction* clearAction = new QAction(QIcon(":/Qmitk/EraseLabel.png"), "&Clear content", this);
+        QObject::connect(clearAction, SIGNAL(triggered(bool)), this, SLOT(OnClearLabel(bool)));
+        menu->addAction(clearAction);
+      }
+
+      if (m_AllowVisibilityModification)
+      {
+        menu->addSeparator();
+        QAction* viewOnlyAction = new QAction(QIcon(":/Qmitk/visible.png"), "View only", this);
+        QObject::connect(viewOnlyAction, SIGNAL(triggered(bool)), this, SLOT(OnSetOnlyActiveLabelVisible(bool)));
+        menu->addAction(viewOnlyAction);
+
+        menu->addSeparator();
+
+        auto opacityAction = this->CreateOpacityAction();
+        if (nullptr != opacityAction)
+          menu->addAction(opacityAction);
+      }
+    }
+    menu->popup(QCursor::pos());
+  }
+}
+
+QWidgetAction* QmitkMultiLabelInspector::CreateOpacityAction()
+{
+  auto relevantLabelValues = this->GetCurrentlyAffactedLabelInstances();
+  std::vector<mitk::Label*> relevantLabels;
+
+  if (!relevantLabelValues.empty())
+  {
+    //we assume here that all affacted label belong to one group.
+    auto groupID = m_Segmentation->GetGroupIndexOfLabel(relevantLabelValues.front());
+    auto group = m_Segmentation->GetLabelSet(groupID);
+
+    for (auto value : relevantLabelValues)
+    {
+      auto label = this->m_Segmentation->GetLabel(value);
+      if (nullptr == label)
+        mitkThrow() << "Invalid state. Internal model returned a label value that does not exist in segmentation. Invalid value:" << value;
+      relevantLabels.emplace_back(label);
+    }
+
+    auto* opacitySlider = new QSlider;
+    opacitySlider->setMinimum(0);
+    opacitySlider->setMaximum(100);
+    opacitySlider->setOrientation(Qt::Horizontal);
+
+    auto opacity = relevantLabels.front()->GetOpacity();
+    opacitySlider->setValue(static_cast<int>(opacity * 100));
+    auto segmentation = m_Segmentation;
+
+    QObject::connect(opacitySlider, &QSlider::valueChanged, this, [segmentation, relevantLabels, group](const int value)
+    {
+      auto opacity = static_cast<float>(value) / 100.0f;
+      for (auto label : relevantLabels)
+      {
+        label->SetOpacity(opacity);
+        group->UpdateLookupTable(label->GetValue());
+      }
+      mitk::RenderingManager::GetInstance()->RequestUpdateAll();
+    }
+    );
+
+    QLabel* opacityLabel = new QLabel("Opacity: ");
+    QVBoxLayout* opacityWidgetLayout = new QVBoxLayout;
+    opacityWidgetLayout->setContentsMargins(4, 4, 4, 4);
+    opacityWidgetLayout->addWidget(opacityLabel);
+    opacityWidgetLayout->addWidget(opacitySlider);
+    QWidget* opacityWidget = new QWidget;
+    opacityWidget->setLayout(opacityWidgetLayout);
+    QWidgetAction* opacityAction = new QWidgetAction(this);
+    opacityAction->setDefaultWidget(opacityWidget);
+
+    return opacityAction;
+  }
+
+  return nullptr;
+}
+
+void QmitkMultiLabelInspector::OnClearLabels(bool /*value*/)
+{
+  QString question = "Do you really want to clear the selected labels?";
+
+  QMessageBox::StandardButton answerButton = QMessageBox::question(
+    this, "Clear selected labels", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
+
+  if (answerButton == QMessageBox::Yes)
+  {
+    this->WaitCursorOn();
+    m_Segmentation->EraseLabels(this->GetSelectedLabels());
+    this->WaitCursorOff();
+    mitk::RenderingManager::GetInstance()->RequestUpdateAll();
+  }
+}
+
+void QmitkMultiLabelInspector::OnDeleteAffectedLabel()
+{
+  if (!m_AllowLabelModification)
+    mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of RemoveLabel.";
+
+  if (m_Segmentation.IsNull())
+  {
+    return;
+  }
+
+  auto affectedLabels = GetCurrentlyAffactedLabelInstances();
+  auto currentLabel = m_Segmentation->GetLabel(affectedLabels.front());
+  QString question = "Do you really want to delete all label instances of class \"" + QString::fromStdString(currentLabel->GetName()) + "\"?";
+
+  QMessageBox::StandardButton answerButton =
+    QMessageBox::question(this, "Delete label class", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
+
+  if (answerButton == QMessageBox::Yes)
+  {
+    this->DeleteLabelInternal(affectedLabels);
+  }
+}
+
+void QmitkMultiLabelInspector::OnDeleteLabels(bool /*value*/)
+{
+  QString question = "Do you really want to remove the selected labels?";
+  QMessageBox::StandardButton answerButton = QMessageBox::question(
+    this, "Remove selected labels", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
+
+  if (answerButton == QMessageBox::Yes)
+  {
+    this->WaitCursorOn();
+    m_Segmentation->RemoveLabels(this->GetSelectedLabels());
+    this->WaitCursorOff();
+  }
+}
+
+void QmitkMultiLabelInspector::OnMergeLabels(bool /*value*/)
+{
+  auto currentLabel = GetCurrentLabel();
+  QString question = "Do you really want to merge selected labels into \"" + QString::fromStdString(currentLabel->GetName())+"\"?";
+
+  QMessageBox::StandardButton answerButton = QMessageBox::question(
+    this, "Merge selected label", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
+
+  if (answerButton == QMessageBox::Yes)
+  {
+    this->WaitCursorOn();
+    m_Segmentation->MergeLabels(currentLabel->GetValue(), this->GetSelectedLabels(), m_Segmentation->GetActiveLayer());
+    this->WaitCursorOff();
+
+    mitk::RenderingManager::GetInstance()->RequestUpdateAll();
+  }
+}
+
+void QmitkMultiLabelInspector::OnAddLabel()
+{
+  auto currentIndex = this->m_Controls->view->currentIndex();
+  auto groupIDVariant = currentIndex.data(QmitkMultiLabelTreeModel::ItemModelRole::GroupIDRole);
+
+  if (groupIDVariant.isValid())
+  {
+    auto groupID = groupIDVariant.value<mitk::LabelSetImage::GroupIndexType>();
+    this->AddNewLabelInternal(groupID);
+  }
+}
+
+void QmitkMultiLabelInspector::OnAddLabelInstance()
+{
+  auto currentLabel = this->GetCurrentLabel();
+  if (nullptr == currentLabel)
+    return;
+
+  this->AddNewLabelInstanceInternal(currentLabel);
+}
+
+void QmitkMultiLabelInspector::OnClearLabel(bool /*value*/)
+{
+  auto currentLabel = GetFirstSelectedLabelObject();
+  QString question = "Do you really want to clear the contents of label \"" + QString::fromStdString(currentLabel->GetName())+"\"?";
+
+  QMessageBox::StandardButton answerButton =
+    QMessageBox::question(this, "Clear label", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
+
+  if (answerButton == QMessageBox::Yes)
+  {
+    this->WaitCursorOn();
+    m_Segmentation->EraseLabel(currentLabel->GetValue());
+    this->WaitCursorOff();
+    mitk::RenderingManager::GetInstance()->RequestUpdateAll();
+  }
+}
+
+void QmitkMultiLabelInspector::OnRenameLabel(bool /*value*/)
+{
+  auto relevantLabelValues = this->GetCurrentlyAffactedLabelInstances();
+  auto currentLabel = this->GetCurrentLabel();
+
+  emit LabelRenameRequested(currentLabel, true);
+
+  //we assume here that all affacted label belong to one group.
+  auto groupID = m_Segmentation->GetGroupIndexOfLabel(currentLabel->GetValue());
+  auto group = m_Segmentation->GetLabelSet(groupID);
+
+  for (auto value : relevantLabelValues)
+  {
+    if (value != currentLabel->GetValue())
+    {
+      auto label = this->m_Segmentation->GetLabel(value);
+      if (nullptr == label)
+        mitkThrow() << "Invalid state. Internal model returned a label value that does not exist in segmentation. Invalid value:" << value;
+
+      label->SetName(currentLabel->GetName());
+      label->SetColor(currentLabel->GetColor());
+      group->UpdateLookupTable(label->GetValue());
+      mitk::DICOMSegmentationPropertyHelper::SetDICOMSegmentProperties(label);
+    }
+  }
+}
+
+
+void QmitkMultiLabelInspector::SetLockOfAffectedLabels(bool locked) const
+{
+  auto relevantLabelValues = this->GetCurrentlyAffactedLabelInstances();
+
+  if (!relevantLabelValues.empty())
+  {
+    for (auto value : relevantLabelValues)
+    {
+      auto label = this->m_Segmentation->GetLabel(value);
+      if (nullptr == label)
+        mitkThrow() << "Invalid state. Internal model returned a label value that does not exist in segmentation. Invalid value:" << value;
+      label->SetLocked(locked);
+    }
+    mitk::RenderingManager::GetInstance()->RequestUpdateAll();
+  }
+}
+
+void QmitkMultiLabelInspector::OnUnlockAffectedLabels()
+{
+  this->SetLockOfAffectedLabels(false);
+}
+
+void QmitkMultiLabelInspector::OnLockAffectedLabels()
+{
+  this->SetLockOfAffectedLabels(true);
+}
+
+void QmitkMultiLabelInspector::SetVisibilityOfAffectedLabels(bool visible) const
+{
+  auto relevantLabelValues = this->GetCurrentlyAffactedLabelInstances();
+
+  if (!relevantLabelValues.empty())
+  {
+    //we assume here that all affacted label belong to one group.
+    auto groupID = m_Segmentation->GetGroupIndexOfLabel(relevantLabelValues.front());
+    auto group = m_Segmentation->GetLabelSet(groupID);
+
+    for (auto value : relevantLabelValues)
+    {
+      auto label = this->m_Segmentation->GetLabel(value);
+      if (nullptr == label)
+        mitkThrow() << "Invalid state. Internal model returned a label value that does not exist in segmentation. Invalid value:" << value;
+      label->SetVisible(visible);
+      group->UpdateLookupTable(label->GetValue());
+    }
+    mitk::RenderingManager::GetInstance()->RequestUpdateAll();
+  }
+}
+
+void QmitkMultiLabelInspector::OnSetAffectedLabelsVisible()
+{
+  this->SetVisibilityOfAffectedLabels(true);
+}
+
+void QmitkMultiLabelInspector::OnSetAffectedLabelsInvisible()
+{
+  this->SetVisibilityOfAffectedLabels(false);
+}
+
+void QmitkMultiLabelInspector::OnSetOnlyActiveLabelVisible(bool /*value*/)
+{
+  auto currentLabel = GetFirstSelectedLabelObject();
+  const auto labelID = currentLabel->GetValue();
+  auto groupID = m_Segmentation->GetGroupIndexOfLabel(currentLabel->GetValue());
+  auto group = m_Segmentation->GetLabelSet(groupID);
+  group->SetAllLabelsVisible(false);
+
+  currentLabel->SetVisible(true);
+  group->UpdateLookupTable(labelID);
+  mitk::RenderingManager::GetInstance()->RequestUpdateAll();
+
+  this->PrepareGoToLabel(labelID);
+}
+
+void QmitkMultiLabelInspector::OnItemDoubleClicked(const QModelIndex& index)
+{
+  if (!index.isValid()) return;
+  if (index.column() > 0) return;
+
+  auto labelVariant = index.data(QmitkMultiLabelTreeModel::ItemModelRole::LabelInstanceValueRole);
+
+  if (!labelVariant.isValid()) return;
+
+  const auto labelID = labelVariant.value<mitk::Label::PixelType>();
+
+  if (QApplication::queryKeyboardModifiers().testFlag(Qt::AltModifier))
+  {
+    this->OnRenameLabel(false);
+    return;
+  }
+
+  this->PrepareGoToLabel(labelID);
+}
+
+void QmitkMultiLabelInspector::PrepareGoToLabel(mitk::Label::PixelType labelID) const
+{
+  this->WaitCursorOn();
+  m_Segmentation->UpdateCenterOfMass(labelID);
+  const auto currentLabel = m_Segmentation->GetLabel(labelID);
+  const mitk::Point3D& pos = currentLabel->GetCenterOfMassCoordinates();
+  this->WaitCursorOff();
+
+  if (pos.GetVnlVector().max_value() > 0.0)
+  {
+    emit GoToLabel(currentLabel->GetValue(), pos);
+  }
+}
+
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.h b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.h
new file mode 100644
index 0000000000..69bf4e1e68
--- /dev/null
+++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.h
@@ -0,0 +1,281 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+All rights reserved.
+
+Use of this source code is governed by a 3-clause BSD license that can be
+found in the LICENSE file.
+
+============================================================================*/
+
+#ifndef QMITKMULTILABELSEGMENTATIONINSPECTOR_H
+#define QMITKMULTILABELSEGMENTATIONINSPECTOR_H
+
+#include <MitkSegmentationUIExports.h>
+#include <mitkWeakPointer.h>
+
+#include <QWidget>
+#include <QItemSelectionModel>
+#include <QmitkMultiLabelTreeModel.h>
+
+class QmitkMultiLabelTreeModel;
+class QStyledItemDelegate;
+class QWidgetAction;
+
+namespace Ui
+{
+  class QmitkMultiLabelInspector;
+}
+
+/*
+* @brief This is an inspector that offers a tree view on the labels and groups of a MultiLabelSegmentation instance.
+* It also allows some manipulation operations an the labels/groups accordin to the UI/selection state.
+*/
+class MITKSEGMENTATIONUI_EXPORT QmitkMultiLabelInspector : public QWidget
+{
+  Q_OBJECT
+
+public:
+  QmitkMultiLabelInspector(QWidget* parent = nullptr);
+  ~QmitkMultiLabelInspector();
+
+  bool GetMultiSelectionMode() const;
+
+  bool GetAllowVisibilityModification() const;
+  bool GetAllowLockModification() const;
+  bool GetAllowLabelModification() const;
+
+  using LabelValueType = mitk::LabelSetImage::LabelValueType;
+  using LabelValueVectorType = mitk::LabelSetImage::LabelValueVectorType;
+
+  /**
+  * @brief Retrieve the currently selected labels (equals the last CurrentSelectionChanged values).
+  */
+  LabelValueVectorType GetSelectedLabels() const;
+
+  /** @brief Returns the label that currently has the focus in the tree view.
+   *
+   * The focus is indicated by QTreeView::currentIndex, thus the mouse is over it and it has a dashed border line.
+   *
+   * The current label must not equal the selected label(s). If the mouse is not hovering above a label
+   * (label class or instance item), the method will return nullptr.
+   */
+  mitk::Label* GetCurrentLabel() const;
+
+  enum class IndexLevelType
+  {
+    Group,
+    LabelClass,
+    LabelInstance
+  };
+
+  /** @brief Returns the level of the index that currently has the focus in the tree view.
+   *
+   * The focus is indicated by QTreeView::currentIndex, thus the mouse is over it and it has a dashed border line.
+   */
+  IndexLevelType GetCurrentLevelType() const;
+
+  /** @brief Returns all label values that are currently affected.
+   *
+   * Affected means that these labels (including the one returned by GetCurrentLabel) are in the subtree of the tree
+   * view element that currently has the focus (indicated by QTreeView::currentIndex, thus the mouse is over it and
+   * it has a dashed border line.
+   */
+  LabelValueVectorType GetCurrentlyAffactedLabelInstances() const;
+
+Q_SIGNALS:
+  /**
+  * @brief A signal that will be emitted if the selected labels change.
+  *
+  * @param labels A list of label values that are now selected.
+  */
+  void CurrentSelectionChanged(LabelValueVectorType labels) const;
+
+  /**
+  * @brief A signal that will be emitted if the user has requested to "go to" a certain label.
+  *
+  * Going to a label would be e.g. to focus the renderwindows on the centroid of the label.
+  * @param label The label that should be focused.
+  * @param point in World coordinate that should be focused.
+  */
+  void GoToLabel(LabelValueType label, const mitk::Point3D& point) const;
+
+  /** @brief Signal that is emitted, if a label should be (re)named and default
+  * label naming is deactivated.
+  *
+  * The instance for which a new name is requested is passed with the signal.
+  * @param label Pointer to the instance that needs a (new) name.
+  * @param rename Indicates if it is a renaming or naming of a new label.
+  */
+  void LabelRenameRequested(mitk::Label* label, bool rename) const;
+
+public Q_SLOTS:
+
+  /**
+  * @brief Transform a list of label values into the new selection of the inspector.
+  * @param selectedLabels A list of selected label values.
+  * @remark Using this method to select labels will not trigger the CurrentSelectionChanged signal. Observers
+  * should regard that to avoid signal loops.
+  */
+  void SetSelectedLabels(const LabelValueVectorType& selectedLabels);
+  /**
+  * @brief The passed label will be used as new selection in the widget
+  * @param selectedLabel Value of the selected label.
+  * @remark Using this method to select labels will not trigger the CurrentSelectionChanged signal. Observers
+  * should regard that to avoid signal loops.
+  */
+  void SetSelectedLabel(mitk::LabelSetImage::LabelValueType selectedLabel);
+
+  /** @brief Sets the segmentation that will be used and monitored by the widget.
+  */
+  void SetMultiLabelSegmentation(mitk::LabelSetImage* segmentation);
+
+  void SetMultiSelectionMode(bool multiMode);
+
+  void SetAllowVisibilityModification(bool visiblityMod);
+  void SetAllowLockModification(bool lockMod);
+  void SetAllowLabelModification(bool labelMod);
+
+  void SetDefaultLabelNaming(bool defaultLabelNaming);
+
+  /** @brief Adds an instance of the same label/class like the first label instance
+  * indicated by GetSelectedLabels() to the segmentation.
+  *
+  * This new label instance is returned by the function. If the inspector has no selected label,
+  * no new instance will be generated and nullptr will be returned.
+  *
+  * @remark The new label instance is a clone of the selected label instance.
+  * Therefore all properties but the LabelValue will be the same.
+  *
+  * @pre AllowLabeModification must be set to true.
+  */
+  mitk::Label* AddNewLabelInstance();
+
+  /** @brief Adds a new label to the segmentation.
+  * Depending on the settings the name of
+  * the label will be either default generated or the rename delegate will be used. The label
+  * will be added to the same group as the first currently selected label.
+  *
+  * @pre AllowLabeModification must be set to true.*/
+  mitk::Label* AddNewLabel();
+
+  /** @brief Removes the first currently selected label of the segmentation.
+  * If no label is selected
+  * nothing will happen.
+  *
+  * @pre AllowLabeModification must be set to true.*/
+  void DeleteLabel();
+
+  /** @brief Adds a new group with a new label to segmentation.
+  *
+  * @pre AllowLabeModification must be set to true.*/
+  mitk::Label* AddNewGroup();
+
+  /** @brief Removes the group of the first currently selected label of the segmentation.
+  *If no label is selected nothing will happen.
+  *
+  * @pre AllowLabeModification must be set to true.*/
+  void RemoveGroup();
+
+  void SetVisibilityOfAffectedLabels(bool visible) const;
+  void SetLockOfAffectedLabels(bool visible) const;
+
+protected:
+  void Initialize();
+  void OnModelReset();
+
+  QmitkMultiLabelTreeModel* m_Model;
+  mitk::LabelSetImage::Pointer m_Segmentation;
+
+  LabelValueVectorType m_LastValidSelectedLabels;
+  QStyledItemDelegate* m_LockItemDelegate;
+  QStyledItemDelegate* m_ColorItemDelegate;
+  QStyledItemDelegate* m_VisibilityItemDelegate;
+
+  Ui::QmitkMultiLabelInspector* m_Controls;
+
+  LabelValueVectorType GetSelectedLabelsFromSelectionModel() const;
+  void UpdateSelectionModel(const LabelValueVectorType& selectedLabels);
+
+  /** @brief Helper that returns the label object (if multiple labels are selected the first).
+  */
+  mitk::Label* GetFirstSelectedLabelObject() const;
+
+  mitk::Label* AddNewLabelInternal(const mitk::LabelSetImage::GroupIndexType& containingGroup);
+
+  /**@brief Adds an instance of the same label/class like the passed label value
+  */
+  mitk::Label* AddNewLabelInstanceInternal(mitk::Label* templateLabel);
+
+  void RemoveGroupInternal(const mitk::LabelSetImage::GroupIndexType& groupID);
+  void DeleteLabelInternal(const LabelValueVectorType& labelValues);
+
+private Q_SLOTS:
+  /** @brief Transform a labels selection into a data node list and emit the 'CurrentSelectionChanged'-signal.
+  *
+  * The function adds the selected nodes from the original selection that could not be modified, if
+  * m_SelectOnlyVisibleNodes is false.
+  * This slot is internally connected to the 'selectionChanged'-signal of the selection model of the private member item view.
+  *
+  * @param selected	The newly selected items.
+  * @param deselected	The newly deselected items.
+  */
+  void OnChangeModelSelection(const QItemSelection& selected, const QItemSelection& deselected);
+
+  void OnContextMenuRequested(const QPoint&);
+
+  void OnAddLabel();
+  void OnAddLabelInstance();
+  void OnDeleteGroup();
+  void OnDeleteAffectedLabel();
+  void OnDeleteLabels(bool);
+  void OnClearLabels(bool);
+  void OnMergeLabels(bool);
+
+  void OnRenameLabel(bool);
+  void OnClearLabel(bool);
+
+  void OnUnlockAffectedLabels();
+  void OnLockAffectedLabels();
+
+  void OnSetAffectedLabelsVisible();
+  void OnSetAffectedLabelsInvisible();
+  void OnSetOnlyActiveLabelVisible(bool);
+
+  void OnItemDoubleClicked(const QModelIndex& index);
+
+  void WaitCursorOn() const;
+  void WaitCursorOff() const;
+  void RestoreOverrideCursor() const;
+
+  void PrepareGoToLabel(LabelValueType labelID) const;
+
+  QWidgetAction* CreateOpacityAction();
+
+private:
+  bool m_ShowVisibility = true;
+  bool m_ShowLock = true;
+  bool m_ShowOther = false;
+
+  /** @brief Indicates if the context menu allows changes in visiblity.
+  *
+  * Visiblity includes also color
+  */
+  bool m_AllowVisibilityModification = true;
+
+  /** @brief Indicates if the context menu allows changes in lock state.
+  */
+  bool m_AllowLockModification = true;
+
+  /** @brief Indicates if the context menu allows label modifications (adding, removing, renaming ...)
+  */
+  bool m_AllowLabelModification = false;
+
+  bool m_DefaultLabelNaming = true;
+
+  bool m_ModelManipulationOngoing = false;
+};
+
+#endif // QMITKDATASTORAGELISTINSPECTOR_H
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspectorControls.ui b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspectorControls.ui
new file mode 100644
index 0000000000..867f367d00
--- /dev/null
+++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspectorControls.ui
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QmitkMultiLabelInspector</class>
+ <widget class="QWidget" name="QmitkMultiLabelInspector">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <property name="spacing">
+    <number>0</number>
+   </property>
+   <property name="leftMargin">
+    <number>0</number>
+   </property>
+   <property name="topMargin">
+    <number>0</number>
+   </property>
+   <property name="rightMargin">
+    <number>0</number>
+   </property>
+   <property name="bottomMargin">
+    <number>0</number>
+   </property>
+   <item>
+    <widget class="QmitkMultiLabelTreeView" name="view">
+     <property name="editTriggers">
+      <set>QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked</set>
+     </property>
+     <property name="showDropIndicator" stdset="0">
+      <bool>false</bool>
+     </property>
+     <property name="alternatingRowColors">
+      <bool>false</bool>
+     </property>
+     <property name="selectionMode">
+      <enum>QAbstractItemView::SingleSelection</enum>
+     </property>
+     <property name="selectionBehavior">
+      <enum>QAbstractItemView::SelectRows</enum>
+     </property>
+     <attribute name="headerVisible">
+      <bool>false</bool>
+     </attribute>
+     <attribute name="headerMinimumSectionSize">
+      <number>10</number>
+     </attribute>
+     <attribute name="headerDefaultSectionSize">
+      <number>50</number>
+     </attribute>
+     <attribute name="headerStretchLastSection">
+      <bool>false</bool>
+     </attribute>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>QmitkMultiLabelTreeView</class>
+   <extends>QTreeView</extends>
+   <header>QmitkMultiLabelTreeView.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManager.cpp b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManager.cpp
new file mode 100644
index 0000000000..5656c8b753
--- /dev/null
+++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManager.cpp
@@ -0,0 +1,457 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+All rights reserved.
+
+Use of this source code is governed by a 3-clause BSD license that can be
+found in the LICENSE file.
+
+============================================================================*/
+
+#include "QmitkMultiLabelManager.h"
+
+// mitk
+#include <mitkAutoCropImageFilter.h>
+#include <mitkCoreObjectFactory.h>
+#include <mitkIOUtil.h>
+#include <mitkLabelSetImage.h>
+#include <mitkLabelSetImageToSurfaceThreadedFilter.h>
+#include <mitkRenderingManager.h>
+#include <mitkShowSegmentationAsSurface.h>
+#include <mitkStatusBar.h>
+#include <mitkToolManagerProvider.h>
+
+// Qmitk
+#include <QmitkStyleManager.h>
+
+// Qt
+#include <QLabel>
+#include <QWidgetAction>
+#include <QColorDialog>
+#include <QCompleter>
+#include <QDateTime>
+#include <QFileDialog>
+#include <QMenu>
+#include <QMessageBox>
+#include <QPushButton>
+#include <QShortcut>
+#include <QStringListModel>
+
+
+// itk
+#include <itksys/SystemTools.hxx>
+
+#include "ui_QmitkMultiLabelManagerControls.h"
+
+
+QmitkMultiLabelManager::QmitkMultiLabelManager(QWidget *parent)
+  : QWidget(parent), m_Controls(new Ui::QmitkMultiLabelManagerControls), m_Completer(nullptr), m_ProcessingManualSelection(false)
+{
+  m_Controls->setupUi(this);
+
+  m_Controls->labelSearchBox->setAlwaysShowClearIcon(true);
+  m_Controls->labelSearchBox->setShowSearchIcon(true);
+
+  QStringList completionList;
+  completionList << "";
+  m_Completer = new QCompleter(completionList, this);
+  m_Completer->setCaseSensitivity(Qt::CaseInsensitive);
+  m_Controls->labelSearchBox->setCompleter(m_Completer);
+
+  m_Controls->labelInspector->SetAllowLabelModification(true);
+  connect(m_Controls->labelInspector, &QmitkMultiLabelInspector::CurrentSelectionChanged, this, &QmitkMultiLabelManager::UpdateControls);
+
+  connect(m_Controls->labelSearchBox, SIGNAL(returnPressed()), this, SLOT(OnSearchLabel()));
+
+  QStringListModel *completeModel = static_cast<QStringListModel *>(m_Completer->model());
+  completeModel->setStringList(GetLabelStringList());
+
+  m_Controls->btnSavePreset->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/document-save.svg")));
+  m_Controls->btnLoadPreset->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/document-open.svg")));
+
+  connect(m_Controls->btnAddLabel, &QToolButton::clicked, this->m_Controls->labelInspector, &QmitkMultiLabelInspector::AddNewLabel);
+  connect(m_Controls->btnAddInstance, &QToolButton::clicked, this->m_Controls->labelInspector, &QmitkMultiLabelInspector::AddNewLabelInstance);
+  connect(m_Controls->btnRemoveLabel, &QToolButton::clicked, this->m_Controls->labelInspector, &QmitkMultiLabelInspector::DeleteLabel);
+  connect(m_Controls->btnAddGroup, &QToolButton::clicked, this->m_Controls->labelInspector, &QmitkMultiLabelInspector::AddNewGroup);
+  connect(m_Controls->btnRemoveGroup, &QToolButton::clicked, this->m_Controls->labelInspector, &QmitkMultiLabelInspector::RemoveGroup);
+  connect(m_Controls->btnSavePreset, &QToolButton::clicked, this, &QmitkMultiLabelManager::OnSavePreset);
+  connect(m_Controls->btnSavePreset, &QToolButton::clicked, this, &QmitkMultiLabelManager::OnLoadPreset);
+
+  connect(this->m_Controls->labelInspector, &QmitkMultiLabelInspector::GoToLabel, this, &QmitkMultiLabelManager::OnGoToLabel);
+  connect(this->m_Controls->labelInspector, &QmitkMultiLabelInspector::LabelRenameRequested, this, &QmitkMultiLabelManager::OnLabelRenameRequested);
+  connect(this->m_Controls->labelInspector, &QmitkMultiLabelInspector::CurrentSelectionChanged, this, &QmitkMultiLabelManager::CurrentSelectionChanged);
+
+  auto* renameLabelShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key::Key_L, Qt::CTRL | Qt::Key::Key_R), this);
+  connect(renameLabelShortcut, &QShortcut::activated, this, &QmitkMultiLabelManager::OnRenameLabelShortcutActivated);
+
+  auto* newLabelShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key::Key_L, Qt::CTRL | Qt::Key::Key_A), this);
+  connect(newLabelShortcut, &QShortcut::activated, this->m_Controls->labelInspector, &QmitkMultiLabelInspector::AddNewLabel);
+
+  this->UpdateControls();
+}
+
+QmitkMultiLabelManager::~QmitkMultiLabelManager()
+{
+  delete m_Controls;
+}
+
+QmitkMultiLabelManager::LabelValueVectorType QmitkMultiLabelManager::GetSelectedLabels() const
+{
+  return m_Controls->labelInspector->GetSelectedLabels();
+}
+
+
+void QmitkMultiLabelManager::OnRenameLabelShortcutActivated()
+{
+  auto selectedLabels = this->GetSelectedLabels();
+
+  for (auto labelValue : selectedLabels)
+  {
+    auto currentLabel = this->m_Segmentation->GetLabel(labelValue);
+    emit LabelRenameRequested(currentLabel, true);
+  }
+}
+
+void QmitkMultiLabelManager::OnSelectedLabelChanged(LabelValueVectorType labels)
+{
+  if (labels.empty() || labels.size() > 1) return;
+
+  emit CurrentSelectionChanged(labels);
+}
+
+QStringList &QmitkMultiLabelManager::GetLabelStringList()
+{
+  return m_LabelStringList;
+}
+
+void QmitkMultiLabelManager::SetDefaultLabelNaming(bool defaultLabelNaming)
+{
+  this->m_Controls->labelInspector->SetDefaultLabelNaming(defaultLabelNaming);
+}
+
+void QmitkMultiLabelManager::setEnabled(bool enabled)
+{
+  QWidget::setEnabled(enabled);
+  UpdateControls();
+}
+
+void QmitkMultiLabelManager::SetSelectedLabels(const LabelValueVectorType& selectedLabels)
+{
+  this->m_Controls->labelInspector->SetSelectedLabels(selectedLabels);
+  UpdateControls();
+}
+
+void QmitkMultiLabelManager::SetSelectedLabel(mitk::LabelSetImage::LabelValueType selectedLabel)
+{
+  this->m_Controls->labelInspector->SetSelectedLabel(selectedLabel);
+  UpdateControls();
+}
+
+void QmitkMultiLabelManager::SetMultiLabelSegmentation(mitk::LabelSetImage* segmentation)
+{
+  if (segmentation != this->m_Segmentation.GetPointer())
+  {
+    m_Segmentation = segmentation;
+    this->m_Controls->labelInspector->SetMultiLabelSegmentation(segmentation);
+    UpdateControls();
+  }
+}
+
+void QmitkMultiLabelManager::SetDataStorage(mitk::DataStorage *storage)
+{
+  m_DataStorage = storage;
+}
+
+void QmitkMultiLabelManager::OnSearchLabel()
+{
+  //std::string text = m_Controls->labelSearchBox->text().toStdString();
+  //int pixelValue = -1;
+  //int row = -1;
+  //for (int i = 0; i < m_Controls->m_LabelSetTableWidget->rowCount(); ++i)
+  //{
+  //  if (m_Controls->m_LabelSetTableWidget->item(i, 0)->text().toStdString().compare(text) == 0)
+  //  {
+  //    pixelValue = m_Controls->m_LabelSetTableWidget->item(i, 0)->data(Qt::UserRole).toInt();
+  //    row = i;
+  //    break;
+  //  }
+  //}
+  //if (pixelValue == -1)
+  //{
+  //  return;
+  //}
+
+  //GetWorkingImage()->GetActiveLabelSet()->SetActiveLabel(pixelValue);
+
+  //QTableWidgetItem *nameItem = m_Controls->m_LabelSetTableWidget->item(row, NAME_COL);
+  //if (!nameItem)
+  //{
+  //  return;
+  //}
+
+  //m_Controls->m_LabelSetTableWidget->clearSelection();
+  //m_Controls->m_LabelSetTableWidget->selectRow(row);
+  //m_Controls->m_LabelSetTableWidget->scrollToItem(nameItem);
+
+  //GetWorkingImage()->GetActiveLabelSet()->SetActiveLabel(pixelValue);
+
+  //this->WaitCursorOn();
+  //mitk::Point3D pos =
+  //  GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetCenterOfMassCoordinates();
+
+  //m_ToolManager->WorkingDataChanged();
+
+  //if (pos.GetVnlVector().max_value() > 0.0)
+  //{
+  //  emit goToLabel(pos);
+  //}
+  //else
+  //{
+  //  GetWorkingImage()->UpdateCenterOfMass(pixelValue, GetWorkingImage()->GetActiveLayer());
+  //  mitk::Point3D pos =
+  //    GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetCenterOfMassCoordinates();
+  //  emit goToLabel(pos);
+  //}
+
+  //this->WaitCursorOff();
+}
+
+void QmitkMultiLabelManager::UpdateControls()
+{
+  bool hasWorkingData = m_Segmentation.IsNotNull();
+
+  auto labels = this->m_Controls->labelInspector->GetSelectedLabels();
+
+  m_Controls->labelSearchBox->setEnabled(hasWorkingData);
+  m_Controls->btnAddGroup->setEnabled(hasWorkingData);
+  m_Controls->btnAddInstance->setEnabled(hasWorkingData && labels.size()==1);
+  m_Controls->btnAddLabel->setEnabled(hasWorkingData);
+  m_Controls->btnLoadPreset->setEnabled(hasWorkingData);
+  m_Controls->btnRemoveGroup->setEnabled(hasWorkingData && !labels.empty() && m_Segmentation->GetNumberOfLayers()>1);
+  m_Controls->btnRemoveLabel->setEnabled(hasWorkingData && !labels.empty());
+  m_Controls->btnSavePreset->setEnabled(hasWorkingData);
+
+  if (!hasWorkingData)
+    return;
+
+  QStringListModel *completeModel = dynamic_cast<QStringListModel *>(m_Completer->model());
+  completeModel->setStringList(GetLabelStringList());
+}
+
+void QmitkMultiLabelManager::OnCreateCroppedMask(bool)
+{
+  mitk::ToolManagerProvider::GetInstance()->GetToolManager()->ActivateTool(-1);
+
+  mitk::Image::Pointer maskImage;
+  auto currentLabel = this->m_Segmentation->GetLabel(this->GetSelectedLabels().front());
+  auto pixelValue = currentLabel->GetValue();
+  try
+  {
+    this->WaitCursorOn();
+
+    mitk::AutoCropImageFilter::Pointer cropFilter = mitk::AutoCropImageFilter::New();
+    cropFilter->SetInput(this->m_Segmentation->CreateLabelMask(pixelValue));
+    cropFilter->SetBackgroundValue(0);
+    cropFilter->SetMarginFactor(1.15);
+    cropFilter->Update();
+
+    maskImage = cropFilter->GetOutput();
+
+    this->WaitCursorOff();
+  }
+  catch (mitk::Exception &e)
+  {
+    this->WaitCursorOff();
+    MITK_ERROR << "Exception caught: " << e.GetDescription();
+    QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n");
+    return;
+  }
+
+  if (maskImage.IsNull())
+  {
+    QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n");
+    return;
+  }
+
+  mitk::DataNode::Pointer maskNode = mitk::DataNode::New();
+  std::string name = currentLabel->GetName();
+  name += "-mask";
+  maskNode->SetName(name);
+  maskNode->SetData(maskImage);
+  maskNode->SetBoolProperty("binary", true);
+  maskNode->SetBoolProperty("outline binary", true);
+  maskNode->SetBoolProperty("outline binary shadow", true);
+  maskNode->SetFloatProperty("outline width", 2.0);
+  maskNode->SetColor(currentLabel->GetColor());
+  maskNode->SetOpacity(1.0);
+
+  m_DataStorage->Add(maskNode, this->m_SegmentationNode);
+}
+
+void QmitkMultiLabelManager::OnCreateMask(bool /*triggered*/)
+{
+  mitk::ToolManagerProvider::GetInstance()->GetToolManager()->ActivateTool(-1);
+
+  auto currentLabel = this->m_Segmentation->GetLabel(this->GetSelectedLabels().front());
+  auto pixelValue = currentLabel->GetValue();
+  mitk::Image::Pointer maskImage;
+  try
+  {
+    this->WaitCursorOn();
+    maskImage = m_Segmentation->CreateLabelMask(pixelValue);
+    this->WaitCursorOff();
+  }
+  catch (mitk::Exception &e)
+  {
+    this->WaitCursorOff();
+    MITK_ERROR << "Exception caught: " << e.GetDescription();
+    QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n");
+    return;
+  }
+
+  if (maskImage.IsNull())
+  {
+    QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n");
+    return;
+  }
+
+  mitk::DataNode::Pointer maskNode = mitk::DataNode::New();
+  std::string name = currentLabel->GetName();
+  name += "-mask";
+  maskNode->SetName(name);
+  maskNode->SetData(maskImage);
+  maskNode->SetBoolProperty("binary", true);
+  maskNode->SetBoolProperty("outline binary", true);
+  maskNode->SetBoolProperty("outline binary shadow", true);
+  maskNode->SetFloatProperty("outline width", 2.0);
+  maskNode->SetColor(currentLabel->GetColor());
+  maskNode->SetOpacity(1.0);
+
+  m_DataStorage->Add(maskNode, m_SegmentationNode);
+}
+
+void QmitkMultiLabelManager::OnCreateSmoothedSurface(bool /*triggered*/)
+{
+  mitk::ToolManagerProvider::GetInstance()->GetToolManager()->ActivateTool(-1);
+
+  auto currentLabel = this->m_Segmentation->GetLabel(this->GetSelectedLabels().front());
+  auto pixelValue = currentLabel->GetValue();
+
+  mitk::LabelSetImageToSurfaceThreadedFilter::Pointer surfaceFilter = mitk::LabelSetImageToSurfaceThreadedFilter::New();
+
+  itk::SimpleMemberCommand<QmitkMultiLabelManager>::Pointer successCommand =
+    itk::SimpleMemberCommand<QmitkMultiLabelManager>::New();
+  successCommand->SetCallbackFunction(this, &QmitkMultiLabelManager::OnThreadedCalculationDone);
+  surfaceFilter->AddObserver(mitk::ResultAvailable(), successCommand);
+
+  itk::SimpleMemberCommand<QmitkMultiLabelManager>::Pointer errorCommand =
+    itk::SimpleMemberCommand<QmitkMultiLabelManager>::New();
+  errorCommand->SetCallbackFunction(this, &QmitkMultiLabelManager::OnThreadedCalculationDone);
+  surfaceFilter->AddObserver(mitk::ProcessingError(), errorCommand);
+
+  mitk::DataNode::Pointer groupNode = m_SegmentationNode;
+  surfaceFilter->SetPointerParameter("Group node", groupNode);
+  surfaceFilter->SetPointerParameter("Input", m_Segmentation);
+  surfaceFilter->SetParameter("RequestedLabel", pixelValue);
+  surfaceFilter->SetParameter("Smooth", true);
+  surfaceFilter->SetDataStorage(*m_DataStorage);
+
+  mitk::StatusBar::GetInstance()->DisplayText("Surface creation is running in background...");
+
+  try
+  {
+    surfaceFilter->StartAlgorithm();
+  }
+  catch (mitk::Exception &e)
+  {
+    MITK_ERROR << "Exception caught: " << e.GetDescription();
+    QMessageBox::information(this,
+                             "Create Surface",
+                             "Could not create a surface mesh out of the selected label. See error log for details.\n");
+  }
+}
+
+void QmitkMultiLabelManager::OnCreateDetailedSurface(bool /*triggered*/)
+{
+  mitk::ToolManagerProvider::GetInstance()->GetToolManager()->ActivateTool(-1);
+
+  auto currentLabel = this->m_Segmentation->GetLabel(this->GetSelectedLabels().front());
+  auto pixelValue = currentLabel->GetValue();
+
+  mitk::LabelSetImageToSurfaceThreadedFilter::Pointer surfaceFilter = mitk::LabelSetImageToSurfaceThreadedFilter::New();
+
+  itk::SimpleMemberCommand<QmitkMultiLabelManager>::Pointer successCommand =
+    itk::SimpleMemberCommand<QmitkMultiLabelManager>::New();
+  successCommand->SetCallbackFunction(this, &QmitkMultiLabelManager::OnThreadedCalculationDone);
+  surfaceFilter->AddObserver(mitk::ResultAvailable(), successCommand);
+
+  itk::SimpleMemberCommand<QmitkMultiLabelManager>::Pointer errorCommand =
+    itk::SimpleMemberCommand<QmitkMultiLabelManager>::New();
+  errorCommand->SetCallbackFunction(this, &QmitkMultiLabelManager::OnThreadedCalculationDone);
+  surfaceFilter->AddObserver(mitk::ProcessingError(), errorCommand);
+
+  mitk::DataNode::Pointer groupNode = m_SegmentationNode;
+  surfaceFilter->SetPointerParameter("Group node", groupNode);
+  surfaceFilter->SetPointerParameter("Input", m_Segmentation);
+  surfaceFilter->SetParameter("RequestedLabel", pixelValue);
+  surfaceFilter->SetParameter("Smooth", false);
+  surfaceFilter->SetDataStorage(*m_DataStorage);
+
+  mitk::StatusBar::GetInstance()->DisplayText("Surface creation is running in background...");
+
+  try
+  {
+    surfaceFilter->StartAlgorithm();
+  }
+  catch (mitk::Exception &e)
+  {
+    MITK_ERROR << "Exception caught: " << e.GetDescription();
+    QMessageBox::information(this,
+                             "Create Surface",
+                             "Could not create a surface mesh out of the selected label. See error log for details.\n");
+  }
+}
+
+
+void QmitkMultiLabelManager::OnSavePreset()
+{
+
+}
+
+void QmitkMultiLabelManager::OnLoadPreset()
+{
+
+}
+
+void QmitkMultiLabelManager::OnGoToLabel(mitk::LabelSetImage::LabelValueType label, const mitk::Point3D& position) const
+{
+  emit GoToLabel(label, position);
+}
+
+void QmitkMultiLabelManager::OnLabelRenameRequested(mitk::Label* label, bool rename) const
+{
+  emit LabelRenameRequested(label, rename);
+}
+
+
+void QmitkMultiLabelManager::WaitCursorOn()
+{
+  QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
+}
+
+void QmitkMultiLabelManager::WaitCursorOff()
+{
+  this->RestoreOverrideCursor();
+}
+
+void QmitkMultiLabelManager::RestoreOverrideCursor()
+{
+  QApplication::restoreOverrideCursor();
+}
+
+void QmitkMultiLabelManager::OnThreadedCalculationDone()
+{
+  mitk::StatusBar::GetInstance()->Clear();
+}
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManager.h b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManager.h
new file mode 100644
index 0000000000..f5eb5c7410
--- /dev/null
+++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManager.h
@@ -0,0 +1,169 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+All rights reserved.
+
+Use of this source code is governed by a 3-clause BSD license that can be
+found in the LICENSE file.
+
+============================================================================*/
+
+#ifndef QmitkMultiLabelManager_h
+#define QmitkMultiLabelManager_h
+
+#include <MitkSegmentationUIExports.h>
+
+#include <mitkLabelSetImage.h>
+#include <mitkDataNode.h>
+#include <mitkNumericTypes.h>
+
+#include <QWidget>
+
+class QmitkDataStorageComboBox;
+class QCompleter;
+
+namespace Ui
+{
+  class QmitkMultiLabelManagerControls;
+}
+
+namespace mitk
+{
+  class DataStorage;
+}
+
+class MITKSEGMENTATIONUI_EXPORT QmitkMultiLabelManager : public QWidget
+{
+  Q_OBJECT
+
+public:
+  explicit QmitkMultiLabelManager(QWidget *parent = nullptr);
+  ~QmitkMultiLabelManager() override;
+
+
+  using LabelValueVectorType = mitk::LabelSetImage::LabelValueVectorType;
+
+  /**
+  * @brief Retrieve the currently selected labels (equals the last CurrentSelectionChanged values).
+  */
+  LabelValueVectorType GetSelectedLabels() const;
+
+Q_SIGNALS:
+  /**
+  * @brief A signal that will be emitted if the selected labels change.
+  *
+  * @param labels A list of label values that are now selected.
+  */
+  void CurrentSelectionChanged(LabelValueVectorType labels);
+
+  /**
+  * @brief A signal that will be emitted if the user has requested to "go to" a certain label.
+  *
+  * Going to a label would be e.g. to focus the renderwindows on the centroid of the label.
+  * @param label The label that should be focused.
+  * @param point in World coordinate that should be focused.
+  */
+  void GoToLabel(mitk::LabelSetImage::LabelValueType label, const mitk::Point3D& point) const;
+
+  /** @brief Signal that is emitted, if a label should be (re)named and default
+   * label naming is deactivated.
+   *
+   * The instance for which a new name is requested is passed with the signal.
+   * @param label Pointer to the instance that needs a (new) name.
+   * @param rename Indicates if it is a renaming or naming of a new label.
+   */
+  void LabelRenameRequested(mitk::Label* label, bool rename) const;
+
+public Q_SLOTS:
+
+  /**
+  * @brief Transform a list label values into a model selection and set this as a new selection of the view
+  *
+  * @param selectedLabels A list of data nodes that should be newly selected.
+  */
+  void SetSelectedLabels(const LabelValueVectorType& selectedLabels);
+
+  /**
+  * @brief Selects the passed label instance and sets a new selection of the view
+  *
+  * @param selectedLabel Value of the label instance that should be selected.
+  */  void SetSelectedLabel(mitk::LabelSetImage::LabelValueType selectedLabel);
+
+  /**
+  * @brief Sets the segmentation that will be used /monitored by the widget.
+  *
+  * @param segmentation      A pointer to the segmentation to set.
+  */
+  void SetMultiLabelSegmentation(mitk::LabelSetImage* segmentation);
+
+  void SetDataStorage(mitk::DataStorage *storage);
+
+  void UpdateControls();
+
+  virtual void setEnabled(bool enabled);
+
+  QStringList &GetLabelStringList();
+
+  void SetDefaultLabelNaming(bool defaultLabelNaming);
+
+private Q_SLOTS:
+
+  // LabelSet dependent
+
+  void OnRenameLabelShortcutActivated();
+
+  // reaction to "returnPressed" signal from ...
+  void OnSearchLabel();
+  // reaction to the change of labels. If multiple labels are selected, it is ignored.
+  void OnSelectedLabelChanged(LabelValueVectorType labels);
+
+  // LabelSetImage Dependet
+  void OnCreateDetailedSurface(bool);
+  void OnCreateSmoothedSurface(bool);
+  // reaction to the signal "createMask" from QmitkLabelSetTableWidget
+  void OnCreateMask(bool);
+  // reaction to the signal "createCroppedMask" from QmitkLabelSetTableWidget
+  void OnCreateCroppedMask(bool);
+
+  void OnSavePreset();
+  void OnLoadPreset();
+
+  void OnGoToLabel(mitk::LabelSetImage::LabelValueType label, const mitk::Point3D& position) const;
+  void OnLabelRenameRequested(mitk::Label* label, bool rename) const;
+
+private:
+  enum TableColumns
+  {
+    NAME_COL = 0,
+    LOCKED_COL,
+    COLOR_COL,
+    VISIBLE_COL
+  };
+
+  void WaitCursorOn();
+
+  void WaitCursorOff();
+
+  void RestoreOverrideCursor();
+
+  void OnThreadedCalculationDone();
+
+  Ui::QmitkMultiLabelManagerControls* m_Controls;
+
+  QCompleter *m_Completer;
+
+  QStringList m_OrganColors;
+
+  QStringList m_LabelStringList;
+
+  bool m_ProcessingManualSelection;
+
+  mitk::LabelSetImage::Pointer m_Segmentation;
+  mitk::DataNode::Pointer m_SegmentationNode;
+  mitk::DataStorage* m_DataStorage;
+
+};
+
+#endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManagerControls.ui b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManagerControls.ui
new file mode 100644
index 0000000000..73d3c06203
--- /dev/null
+++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManagerControls.ui
@@ -0,0 +1,201 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QmitkMultiLabelManagerControls</class>
+ <widget class="QWidget" name="QmitkMultiLabelManagerControls">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>312</width>
+    <height>366</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Minimum" vsizetype="MinimumExpanding">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="styleSheet">
+   <string notr="true"/>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <property name="leftMargin">
+    <number>6</number>
+   </property>
+   <property name="topMargin">
+    <number>6</number>
+   </property>
+   <property name="rightMargin">
+    <number>6</number>
+   </property>
+   <property name="bottomMargin">
+    <number>6</number>
+   </property>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QToolButton" name="btnAddLabel">
+       <property name="toolTip">
+        <string>Add a new label to the current group.</string>
+       </property>
+       <property name="text">
+        <string>L+</string>
+       </property>
+       <property name="autoRaise">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QToolButton" name="btnAddInstance">
+       <property name="toolTip">
+        <string>Add a new instance of the current label.</string>
+       </property>
+       <property name="text">
+        <string>I+</string>
+       </property>
+       <property name="iconSize">
+        <size>
+         <width>32</width>
+         <height>32</height>
+        </size>
+       </property>
+       <property name="autoRaise">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QToolButton" name="btnRemoveLabel">
+       <property name="toolTip">
+        <string>Remove the current label.</string>
+       </property>
+       <property name="text">
+        <string>L-</string>
+       </property>
+       <property name="autoRaise">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeType">
+        <enum>QSizePolicy::Expanding</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QToolButton" name="btnAddGroup">
+       <property name="toolTip">
+        <string>Add a new group.</string>
+       </property>
+       <property name="text">
+        <string>G+</string>
+       </property>
+       <property name="autoRaise">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QToolButton" name="btnRemoveGroup">
+       <property name="toolTip">
+        <string>Remove the current group (including all labels).</string>
+       </property>
+       <property name="text">
+        <string>G-</string>
+       </property>
+       <property name="autoRaise">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QToolButton" name="btnSavePreset">
+       <property name="toolTip">
+        <string>Save the labels preset.</string>
+       </property>
+       <property name="text">
+        <string>...</string>
+       </property>
+       <property name="autoRaise">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QToolButton" name="btnLoadPreset">
+       <property name="toolTip">
+        <string>Load a label preset from file.</string>
+       </property>
+       <property name="text">
+        <string>...</string>
+       </property>
+       <property name="autoRaise">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QmitkMultiLabelInspector" name="labelInspector" native="true">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="ctkSearchBox" name="labelSearchBox">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>ctkSearchBox</class>
+   <extends>QLineEdit</extends>
+   <header location="global">ctkSearchBox.h</header>
+  </customwidget>
+  <customwidget>
+   <class>QmitkMultiLabelInspector</class>
+   <extends>QWidget</extends>
+   <header>QmitkMultiLabelInspector.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.cpp b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.cpp
new file mode 100644
index 0000000000..972ad8379f
--- /dev/null
+++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.cpp
@@ -0,0 +1,971 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+All rights reserved.
+
+Use of this source code is governed by a 3-clause BSD license that can be
+found in the LICENSE file.
+
+============================================================================*/
+
+#include "QmitkMultiLabelTreeModel.h"
+
+#include "mitkRenderingManager.h"
+
+#include "QmitkStyleManager.h"
+
+
+class QmitkMultiLabelSegTreeItem
+{
+public:
+  enum class ItemType
+  {
+    Group,
+    Label,
+    Instance
+  };
+
+  QmitkMultiLabelSegTreeItem()
+  {
+  };
+
+  explicit QmitkMultiLabelSegTreeItem(ItemType type, QmitkMultiLabelSegTreeItem* parentItem,
+    mitk::Label* label = nullptr, std::string className = ""): m_parentItem(parentItem), m_ItemType(type), m_Label(label), m_ClassName(className)
+  {
+  };
+
+  ~QmitkMultiLabelSegTreeItem()
+  {
+    for (auto item : m_childItems)
+    {
+      delete item;
+    }
+  };
+
+  void AppendChild(QmitkMultiLabelSegTreeItem* child)
+  {
+      m_childItems.push_back(child);
+  };
+
+  void RemoveChild(std::size_t row)
+  {
+    if (row < m_childItems.size())
+    {
+      delete m_childItems[row];
+      m_childItems.erase(m_childItems.begin() + row);
+    }
+  };
+
+  int Row() const
+  {
+    if (m_parentItem)
+    {
+      auto finding = std::find(m_parentItem->m_childItems.begin(), m_parentItem->m_childItems.end(), this);
+      if (finding != m_parentItem->m_childItems.end())
+      {
+        return std::distance(m_parentItem->m_childItems.begin(), finding);
+      }
+    }
+
+    return 0;
+  };
+
+  QmitkMultiLabelSegTreeItem* ParentItem()
+  {
+    return m_parentItem;
+  };
+
+  const QmitkMultiLabelSegTreeItem* ParentItem() const
+  {
+    return m_parentItem;
+  };
+
+  const QmitkMultiLabelSegTreeItem* NextSibblingItem() const
+  {
+    if (m_parentItem)
+    {
+      const std::vector<QmitkMultiLabelSegTreeItem*>::size_type row = this->Row();
+      if (row + 1 < m_parentItem->m_childItems.size())
+        return m_parentItem->m_childItems[row+1];
+    }
+
+    return nullptr;
+  };
+
+  const QmitkMultiLabelSegTreeItem* PrevSibblingItem() const
+  {
+    if (m_parentItem)
+    {
+      const std::vector<QmitkMultiLabelSegTreeItem*>::size_type row = this->Row();
+      if (row > 0)
+        return m_parentItem->m_childItems[row-1];
+    }
+
+    return nullptr;
+  };
+
+  const QmitkMultiLabelSegTreeItem* RootItem() const
+  {
+    auto item = this;
+    while (item->m_parentItem != nullptr)
+    {
+      item = item->m_parentItem;
+    }
+    return item;
+  };
+
+  std::size_t GetGroupID() const
+  {
+    auto root = this->RootItem();
+    auto item = this;
+    if (root == this) return 0;
+
+    while (root != item->m_parentItem)
+    {
+      item = item->m_parentItem;
+    }
+
+    auto iter = std::find(root->m_childItems.begin(), root->m_childItems.end(), item);
+
+    if (root->m_childItems.end() == iter) mitkThrow() << "Invalid internal state of QmitkMultiLabelTreeModel. Root does not have an currentItem as child that has root as parent.";
+
+    return std::distance(root->m_childItems.begin(), iter);
+  }
+
+  bool HandleAsInstance() const
+  {
+    return (ItemType::Instance == m_ItemType) || ((ItemType::Label == m_ItemType) && (m_childItems.size() == 1));
+  }
+
+  mitk::Label* GetLabel() const
+  {
+    if (ItemType::Instance == m_ItemType)
+    {
+      return m_Label;
+    }
+    if (ItemType::Label == m_ItemType)
+    {
+      if (m_childItems.empty()) mitkThrow() << "Invalid internal state of QmitkMultiLabelTreeModel. Internal label currentItem has no instance currentItem.";
+      return m_childItems[0]->GetLabel();
+    }
+
+    return nullptr;
+  };
+
+  mitk::LabelSetImage::LabelValueType GetLabelValue() const
+  {
+    auto label = this->GetLabel();
+
+    if (nullptr == label)
+    {
+      mitkThrow() << "Invalid internal state of QmitkMultiLabelTreeModel. Called GetLabelValue on an group currentItem.";
+    }
+
+    return label->GetValue();
+  };
+
+  /** returns a vector containing all label values of referenced by this item or its child items.*/
+  std::vector< mitk::LabelSetImage::LabelValueType> GetLabelsInSubTree() const
+  {
+    if (this->m_ItemType == ItemType::Instance)
+    {
+      return { this->GetLabelValue() };
+    }
+
+    std::vector< mitk::LabelSetImage::LabelValueType> result;
+    for (const auto child : this->m_childItems)
+    {
+      auto childresult = child->GetLabelsInSubTree();
+      result.reserve(result.size() + childresult.size());
+      result.insert(result.end(), childresult.begin(), childresult.end());
+    }
+
+    return result;
+  }
+
+  std::vector<QmitkMultiLabelSegTreeItem*> m_childItems;
+  QmitkMultiLabelSegTreeItem* m_parentItem = nullptr;
+  ItemType m_ItemType = ItemType::Group;
+  mitk::Label::Pointer m_Label;
+  std::string m_ClassName;
+};
+
+QModelIndex GetIndexByItem(const QmitkMultiLabelSegTreeItem* start, const QmitkMultiLabelTreeModel* model)
+{
+  QModelIndex parentIndex = QModelIndex();
+  if (nullptr != start->m_parentItem)
+  {
+    parentIndex = GetIndexByItem(start->m_parentItem, model);
+  }
+  else
+  {
+    return parentIndex;
+  }
+
+  return model->index(start->Row(), 0, parentIndex);
+}
+
+QmitkMultiLabelSegTreeItem* GetGroupItem(QmitkMultiLabelTreeModel::GroupIndexType groupIndex, QmitkMultiLabelSegTreeItem* root)
+{
+  if (nullptr != root && groupIndex < root->m_childItems.size())
+  {
+    return root->m_childItems[groupIndex];
+  }
+
+  return nullptr;
+}
+
+QmitkMultiLabelSegTreeItem* GetInstanceItem(QmitkMultiLabelTreeModel::LabelValueType labelValue, QmitkMultiLabelSegTreeItem* root)
+{
+  QmitkMultiLabelSegTreeItem* result = nullptr;
+
+  for (auto item : root->m_childItems)
+  {
+    result = GetInstanceItem(labelValue, item);
+    if (nullptr != result) return result;
+  }
+
+  if (root->m_ItemType == QmitkMultiLabelSegTreeItem::ItemType::Instance && root->GetLabelValue() == labelValue)
+  {
+    return root;
+  }
+
+  return nullptr;
+}
+
+const QmitkMultiLabelSegTreeItem* GetFirstInstanceLikeItem(const QmitkMultiLabelSegTreeItem* startItem)
+{
+  const QmitkMultiLabelSegTreeItem* result = nullptr;
+
+  if (nullptr != startItem)
+  {
+    if (startItem->HandleAsInstance())
+    {
+      result = startItem;
+    }
+    else if (!startItem->m_childItems.empty())
+    {
+      result = GetFirstInstanceLikeItem(startItem->m_childItems.front());
+    }
+  }
+
+  return result;
+}
+
+QmitkMultiLabelSegTreeItem* GetLabelItemInGroup(const std::string& labelName, QmitkMultiLabelSegTreeItem* group)
+{
+  if (nullptr != group)
+  {
+    auto predicate = [labelName](const QmitkMultiLabelSegTreeItem* item) { return labelName == item->m_ClassName; };
+    auto finding = std::find_if(group->m_childItems.begin(), group->m_childItems.end(), predicate);
+    if (group->m_childItems.end() != finding)
+    {
+      return *finding;
+    }
+  }
+
+  return nullptr;
+}
+
+QmitkMultiLabelTreeModel::QmitkMultiLabelTreeModel(QObject *parent) : QAbstractItemModel(parent)
+, m_Observed(false)
+{
+  m_RootItem = std::make_unique<QmitkMultiLabelSegTreeItem>();
+}
+
+QmitkMultiLabelTreeModel ::~QmitkMultiLabelTreeModel()
+{
+  this->SetSegmentation(nullptr);
+};
+
+int QmitkMultiLabelTreeModel::columnCount(const QModelIndex& /*parent*/) const
+{
+  return 4;
+}
+
+int QmitkMultiLabelTreeModel::rowCount(const QModelIndex &parent) const
+{
+  if (parent.column() > 0)
+    return 0;
+
+  if (m_Segmentation.IsNull())
+    return 0;
+
+  QmitkMultiLabelSegTreeItem* parentItem = m_RootItem.get();
+
+  if (parent.isValid())
+    parentItem = static_cast<QmitkMultiLabelSegTreeItem *>(parent.internalPointer());
+
+  if (parentItem->HandleAsInstance())
+  {
+    return 0;
+  }
+
+  return parentItem->m_childItems.size();
+}
+
+QVariant QmitkMultiLabelTreeModel::data(const QModelIndex &index, int role) const
+{
+  if (!index.isValid())
+    return QVariant();
+
+  auto item = static_cast<QmitkMultiLabelSegTreeItem*>(index.internalPointer());
+
+  if (!item)
+    return QVariant();
+
+  if (role == Qt::DisplayRole||role == Qt::EditRole)
+  {
+    if (TableColumns::NAME_COL == index.column())
+    {
+      switch (item->m_ItemType)
+      {
+        case QmitkMultiLabelSegTreeItem::ItemType::Group:
+          return QVariant(QString("Group ")+QString::number(item->GetGroupID()));
+        case QmitkMultiLabelSegTreeItem::ItemType::Label:
+        {
+          auto label = item->GetLabel();
+          if (nullptr == label) mitkThrow() << "Invalid internal state. QmitkMultiLabelTreeModel currentItem is refering to a label that does not exist.";
+          QString name = QString::fromStdString(label->GetName());
+          if (!item->HandleAsInstance())
+          {
+            name = name + QString(" (") + QString::number(item->m_childItems.size()) + QString(" instances)");
+          }
+          return QVariant(name);
+        }
+        case QmitkMultiLabelSegTreeItem::ItemType::Instance:
+        {
+          auto label = item->GetLabel();
+          if (nullptr == label) mitkThrow() << "Invalid internal state. QmitkMultiLabelTreeModel currentItem is refering to a label that does not exist.";
+          return QVariant(QString::fromStdString(label->GetName()) + QString(" #") + QString::number(item->GetLabelValue()));
+        }
+      }
+    }
+    else
+    {
+      if (item->HandleAsInstance())
+      {
+        auto label = item->GetLabel();
+
+        if (TableColumns::LOCKED_COL == index.column())
+        {
+          return QVariant(label->GetLocked());
+        }
+        else if (TableColumns::COLOR_COL == index.column())
+        {
+          return QVariant(QColor(label->GetColor().GetRed() * 255, label->GetColor().GetGreen() * 255, label->GetColor().GetBlue() * 255));
+        }
+        else if (TableColumns::VISIBLE_COL == index.column())
+        {
+          return QVariant(label->GetVisible());
+        }
+      }
+      else
+      {
+
+      }
+    }
+  }
+  else if (role == ItemModelRole::LabelDataRole)
+  {
+    auto label = item->GetLabel();
+    if (nullptr!=label)  return QVariant::fromValue<void*>(label);
+  }
+  else if (role == ItemModelRole::LabelValueRole)
+  {
+    auto label = item->GetLabel();
+    if (nullptr != label)  return QVariant(label->GetValue());
+  }
+  else if (role == ItemModelRole::LabelInstanceDataRole)
+  {
+    if (item->HandleAsInstance())
+    {
+      auto label = item->GetLabel();
+      return QVariant::fromValue<void*>(label);
+    }
+  }
+  else if (role == ItemModelRole::LabelInstanceValueRole)
+  {
+    if (item->HandleAsInstance())
+    {
+      auto label = item->GetLabel();
+      return QVariant(label->GetValue());
+    }
+  }
+  else if (role == ItemModelRole::GroupIDRole)
+  {
+    QVariant v;
+    v.setValue(item->GetGroupID());
+    return v;
+  }
+
+  return QVariant();
+}
+
+mitk::Color QtToMitk(const QColor& color)
+{
+  mitk::Color mitkColor;
+
+  mitkColor.SetRed(color.red() / 255.0f);
+  mitkColor.SetGreen(color.green() / 255.0f);
+  mitkColor.SetBlue(color.blue() / 255.0f);
+
+  return mitkColor;
+}
+
+bool QmitkMultiLabelTreeModel::setData(const QModelIndex& index, const QVariant& value, int role)
+{
+  if (!index.isValid())
+    return false;
+
+  auto item = static_cast<QmitkMultiLabelSegTreeItem*>(index.internalPointer());
+
+  if (!item)
+    return false;
+
+  if (role == Qt::EditRole)
+  {
+    if (TableColumns::NAME_COL != index.column())
+    {
+      if (item->HandleAsInstance())
+      {
+        auto label = item->GetLabel();
+
+        if (TableColumns::LOCKED_COL == index.column())
+        {
+          label->SetLocked(value.toBool());
+        }
+        else if (TableColumns::COLOR_COL == index.column())
+        {
+          label->SetColor(QtToMitk(value.value<QColor>()));
+        }
+        else if (TableColumns::VISIBLE_COL == index.column())
+        {
+          label->SetVisible(value.toBool());
+        }
+        auto groupID = m_Segmentation->GetGroupIndexOfLabel(label->GetValue());
+        m_Segmentation->GetLabelSet(groupID)->UpdateLookupTable(label->GetValue());
+        m_Segmentation->Modified();
+        mitk::RenderingManager::GetInstance()->RequestUpdateAll();
+      }
+      else
+      {
+
+      }
+      return true;
+    }
+  }
+  return false;
+}
+
+QModelIndex QmitkMultiLabelTreeModel::index(int row, int column, const QModelIndex &parent) const
+{
+  if (!hasIndex(row, column, parent))
+    return QModelIndex();
+
+  auto parentItem = m_RootItem.get();
+
+  if (parent.isValid())
+    parentItem = static_cast<QmitkMultiLabelSegTreeItem *>(parent.internalPointer());
+
+  QmitkMultiLabelSegTreeItem *childItem = parentItem->m_childItems[row];
+  if (childItem)
+    return createIndex(row, column, childItem);
+  else
+    return QModelIndex();
+}
+
+QModelIndex QmitkMultiLabelTreeModel::indexOfLabel(mitk::Label::PixelType labelValue) const
+{
+  if (labelValue == mitk::LabelSetImage::UnlabeledValue) return QModelIndex();
+  auto relevantItem = GetInstanceItem(labelValue, this->m_RootItem.get());
+
+  if (nullptr == relevantItem) QModelIndex();
+
+  auto labelItem = relevantItem->ParentItem();
+
+  if (labelItem->m_childItems.size() == 1)
+  { //was the only instance of the label, therefor return the label item instat.
+    relevantItem = labelItem;
+  }
+
+  return GetIndexByItem(relevantItem, this);
+}
+
+QModelIndex QmitkMultiLabelTreeModel::indexOfGroup(mitk::LabelSetImage::GroupIndexType groupIndex) const
+{
+  auto relevantItem = GetGroupItem(groupIndex, this->m_RootItem.get());
+
+  if (nullptr == relevantItem) QModelIndex();
+
+  return GetIndexByItem(relevantItem, this);
+}
+
+QModelIndex QmitkMultiLabelTreeModel::parent(const QModelIndex &child) const
+{
+  if (!child.isValid())
+    return QModelIndex();
+
+  QmitkMultiLabelSegTreeItem *childItem = static_cast<QmitkMultiLabelSegTreeItem *>(child.internalPointer());
+  QmitkMultiLabelSegTreeItem *parentItem = childItem->ParentItem();
+
+  if (parentItem == m_RootItem.get())
+    return QModelIndex();
+
+  return createIndex(parentItem->Row(), 0, parentItem);
+}
+
+QModelIndex QmitkMultiLabelTreeModel::ClosestLabelInstanceIndex(const QModelIndex& currentIndex) const
+{
+  if (!currentIndex.isValid()) return QModelIndex();
+
+  auto currentItem = static_cast<const QmitkMultiLabelSegTreeItem*>(currentIndex.internalPointer());
+  if (!currentItem) return QModelIndex();
+
+  if (currentItem->RootItem() != this->m_RootItem.get()) mitkThrow() << "Invalid call. Passed currentIndex does not seem to be a valid index of this model. It is either outdated or from another model.";
+
+  const QmitkMultiLabelSegTreeItem* resultItem = nullptr;
+  auto searchItem = currentItem;
+  const auto rootItem = currentItem->RootItem();
+
+  while (searchItem != rootItem)
+  {
+    resultItem = GetFirstInstanceLikeItem(searchItem->NextSibblingItem());
+    if (nullptr != resultItem) break;
+
+    //no next closest label instance on this level -> check for closest before
+    resultItem = GetFirstInstanceLikeItem(searchItem->PrevSibblingItem());
+    if (nullptr != resultItem) break;
+
+    //no closest label instance before current on this level -> moeve one level up
+    searchItem = searchItem->ParentItem();
+  }
+
+  if (nullptr == resultItem)
+    return QModelIndex();
+
+  return GetIndexByItem(resultItem, this);
+}
+
+QModelIndex QmitkMultiLabelTreeModel::FirstLabelInstanceIndex(const QModelIndex& currentIndex) const
+{
+  const QmitkMultiLabelSegTreeItem* currentItem = nullptr;
+
+  if (!currentIndex.isValid())
+  {
+    currentItem = this->m_RootItem.get();
+  }
+  else
+  {
+    currentItem = static_cast<const QmitkMultiLabelSegTreeItem*>(currentIndex.internalPointer());
+  }
+
+  if (!currentItem) return QModelIndex();
+
+  if (currentItem->RootItem() != this->m_RootItem.get()) mitkThrow() << "Invalid call. Passed currentIndex does not seem to be a valid index of this model. It is either outdated or from another model.";
+
+  const QmitkMultiLabelSegTreeItem* resultItem = nullptr;
+  resultItem = GetFirstInstanceLikeItem(currentItem);
+
+  if (nullptr == resultItem)
+    return QModelIndex();
+
+  return GetIndexByItem(resultItem, this);
+}
+
+///** Returns the index to the next node in the tree that behaves like an instance (label node with only one instance
+//or instance node). If current index is at the end, an invalid index is returned.*/
+//QModelIndex QmitkMultiLabelTreeModel::PrevLabelInstanceIndex(const QModelIndex& currentIndex) const;
+
+std::vector <QmitkMultiLabelTreeModel::LabelValueType> QmitkMultiLabelTreeModel::GetLabelsInSubTree(const QModelIndex& currentIndex) const
+{
+  const QmitkMultiLabelSegTreeItem* currentItem = nullptr;
+
+  if (!currentIndex.isValid())
+  {
+    currentItem = this->m_RootItem.get();
+  }
+  else
+  {
+    currentItem = static_cast<const QmitkMultiLabelSegTreeItem*>(currentIndex.internalPointer());
+  }
+
+  if (!currentItem) return {};
+
+  return currentItem->GetLabelsInSubTree();
+}
+
+Qt::ItemFlags QmitkMultiLabelTreeModel::flags(const QModelIndex &index) const
+{
+  if (!index.isValid())
+    return Qt::NoItemFlags;
+
+  if (!index.isValid())
+    return Qt::NoItemFlags;
+
+  auto item = static_cast<QmitkMultiLabelSegTreeItem*>(index.internalPointer());
+
+  if (!item)
+    return Qt::NoItemFlags;
+
+  if (TableColumns::NAME_COL != index.column())
+  {
+    if (item->HandleAsInstance() &&
+      ((TableColumns::VISIBLE_COL == index.column() && m_AllowVisibilityModification) ||
+       (TableColumns::COLOR_COL == index.column() && m_AllowVisibilityModification) || //m_AllowVisibilityModification controls visibility and color
+       (TableColumns::LOCKED_COL == index.column() && m_AllowLockModification)))
+    {
+      return Qt::ItemIsEnabled | Qt::ItemIsEditable;
+    }
+    else
+    {
+      return Qt::ItemIsEnabled;
+    }
+  }
+  else
+  {
+    if (item->HandleAsInstance())
+    {
+      return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+    }
+    else
+    {
+      return Qt::ItemIsEnabled;
+    }
+  }
+
+  return Qt::NoItemFlags;
+}
+
+QVariant QmitkMultiLabelTreeModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+  if ((Qt::DisplayRole == role) && (Qt::Horizontal == orientation))
+  {
+    if (TableColumns::NAME_COL == section)
+    {
+      return "Name";
+    }
+    else if (TableColumns::LOCKED_COL == section)
+    {
+      return "Locked";
+    }
+    else if (TableColumns::COLOR_COL == section)
+    {
+      return "Color";
+    }
+    else if (TableColumns::VISIBLE_COL == section)
+    {
+      return "Visibility";
+    }
+  }
+  return QVariant();
+}
+
+const mitk::LabelSetImage* QmitkMultiLabelTreeModel::GetSegmentation() const
+{
+  return m_Segmentation;
+}
+
+
+void QmitkMultiLabelTreeModel::SetSegmentation(mitk::LabelSetImage* segmentation)
+{
+  if (m_Segmentation != segmentation)
+  {
+    this->RemoveObserver();
+    this->m_Segmentation = segmentation;
+    this->AddObserver();
+
+    this->UpdateInternalTree();
+  }
+}
+
+
+/**Helper function that adds a labek into the item tree. Passes back the new created instance iten*/
+QmitkMultiLabelSegTreeItem* AddLabelToGroupTree(mitk::Label* label, QmitkMultiLabelSegTreeItem* groupItem, bool& newLabelItemCreated)
+{
+  if (nullptr == groupItem) return nullptr;
+  if (nullptr == label) return nullptr;
+
+  newLabelItemCreated = false;
+
+  std::set<std::string> labelNames;
+  for (auto labelItem : groupItem->m_childItems)
+  {
+    labelNames.emplace(labelItem->GetLabel()->GetName());
+  }
+
+  QmitkMultiLabelSegTreeItem* labelItem = nullptr;
+  auto finding = labelNames.find(label->GetName());
+  if (finding != labelNames.end())
+  { //other label with same name exists
+    labelItem = groupItem->m_childItems[std::distance(labelNames.begin(), finding)];
+  }
+  else
+  {
+    newLabelItemCreated = true;
+    labelItem = new QmitkMultiLabelSegTreeItem(QmitkMultiLabelSegTreeItem::ItemType::Label, groupItem, nullptr, label->GetName());
+
+    auto predicate = [label](const std::string& name) { return name > label->GetName(); };
+    auto insertFinding = std::find_if(labelNames.begin(), labelNames.end(), predicate);
+
+    groupItem->m_childItems.insert(groupItem->m_childItems.begin() + std::distance(labelNames.begin(), insertFinding), labelItem);
+  }
+
+  auto instanceItem = new QmitkMultiLabelSegTreeItem(QmitkMultiLabelSegTreeItem::ItemType::Instance, labelItem, label);
+
+  auto predicate = [label](const QmitkMultiLabelSegTreeItem* item) { return item->GetLabelValue() > label->GetValue(); };
+  auto insertFinding = std::find_if(labelItem->m_childItems.begin(), labelItem->m_childItems.end(), predicate);
+  labelItem->m_childItems.insert(labelItem->m_childItems.begin() + std::distance(labelItem->m_childItems.begin(), insertFinding), instanceItem);
+
+  return instanceItem;
+}
+
+void QmitkMultiLabelTreeModel::GenerateInternalGroupTree(unsigned int groupID, QmitkMultiLabelSegTreeItem* groupItem)
+{
+  auto labelSet = m_Segmentation->GetLabelSet(groupID);
+
+  for (auto lIter = labelSet->IteratorConstBegin(); lIter != labelSet->IteratorConstEnd(); lIter++)
+  {
+    if (lIter->first== mitk::LabelSetImage::UnlabeledValue) continue;
+
+    bool newItemCreated = false;
+    AddLabelToGroupTree(lIter->second, groupItem, newItemCreated);
+  }
+}
+
+QmitkMultiLabelSegTreeItem* QmitkMultiLabelTreeModel::GenerateInternalTree()
+{
+  auto rootItem = new QmitkMultiLabelSegTreeItem();
+
+  if (m_Segmentation.IsNotNull())
+  {
+    for (unsigned int groupID = 0; groupID < m_Segmentation->GetNumberOfLayers(); ++groupID)
+    {
+      auto groupItem = new QmitkMultiLabelSegTreeItem(QmitkMultiLabelSegTreeItem::ItemType::Group, rootItem);
+      rootItem->AppendChild(groupItem);
+
+      GenerateInternalGroupTree(groupID, groupItem);
+    }
+  }
+
+  return rootItem;
+}
+
+void QmitkMultiLabelTreeModel::UpdateInternalTree()
+{
+  emit beginResetModel();
+  auto newTree = this->GenerateInternalTree();
+  this->m_RootItem.reset(newTree);
+  emit endResetModel();
+  emit modelChanged();
+}
+
+void QmitkMultiLabelTreeModel::AddObserver()
+{
+  if (this->m_Segmentation.IsNotNull())
+  {
+    if (m_Observed)
+    {
+      MITK_DEBUG << "Invalid observer state in QmitkMultiLabelTreeModel. There is already a registered observer. Internal logic is not correct. May be an old observer was not removed.";
+    }
+
+    this->m_Segmentation->AddLabelAddedListener(mitk::MessageDelegate1<QmitkMultiLabelTreeModel, LabelValueType>(
+      this, &QmitkMultiLabelTreeModel::OnLabelAdded));
+    this->m_Segmentation->AddLabelModifiedListener(mitk::MessageDelegate1<QmitkMultiLabelTreeModel, LabelValueType>(
+      this, &QmitkMultiLabelTreeModel::OnLabelModified));
+    this->m_Segmentation->AddLabelRemovedListener(mitk::MessageDelegate1<QmitkMultiLabelTreeModel, LabelValueType>(
+      this, &QmitkMultiLabelTreeModel::OnLabelRemoved));
+    this->m_Segmentation->AddGroupAddedListener(mitk::MessageDelegate1<QmitkMultiLabelTreeModel, GroupIndexType>(
+      this, &QmitkMultiLabelTreeModel::OnGroupAdded));
+    this->m_Segmentation->AddGroupModifiedListener(mitk::MessageDelegate1<QmitkMultiLabelTreeModel, GroupIndexType>(
+      this, &QmitkMultiLabelTreeModel::OnGroupModified));
+    this->m_Segmentation->AddGroupRemovedListener(mitk::MessageDelegate1<QmitkMultiLabelTreeModel, GroupIndexType>(
+      this, &QmitkMultiLabelTreeModel::OnGroupRemoved));
+    m_Observed = true;
+  }
+}
+
+void QmitkMultiLabelTreeModel::RemoveObserver()
+{
+  if (this->m_Segmentation.IsNotNull())
+  {
+    this->m_Segmentation->RemoveLabelAddedListener(mitk::MessageDelegate1<QmitkMultiLabelTreeModel, LabelValueType>(
+      this, &QmitkMultiLabelTreeModel::OnLabelAdded));
+    this->m_Segmentation->RemoveLabelModifiedListener(mitk::MessageDelegate1<QmitkMultiLabelTreeModel, LabelValueType>(
+      this, &QmitkMultiLabelTreeModel::OnLabelModified));
+    this->m_Segmentation->RemoveLabelRemovedListener(mitk::MessageDelegate1<QmitkMultiLabelTreeModel, LabelValueType>(
+      this, &QmitkMultiLabelTreeModel::OnLabelRemoved));
+    this->m_Segmentation->RemoveGroupAddedListener(mitk::MessageDelegate1<QmitkMultiLabelTreeModel, GroupIndexType>(
+      this, &QmitkMultiLabelTreeModel::OnGroupAdded));
+    this->m_Segmentation->RemoveGroupModifiedListener(mitk::MessageDelegate1<QmitkMultiLabelTreeModel, GroupIndexType>(
+      this, &QmitkMultiLabelTreeModel::OnGroupModified));
+    this->m_Segmentation->RemoveGroupRemovedListener(mitk::MessageDelegate1<QmitkMultiLabelTreeModel, GroupIndexType>(
+      this, &QmitkMultiLabelTreeModel::OnGroupRemoved));
+  }
+  m_Observed = false;
+}
+
+void QmitkMultiLabelTreeModel::OnLabelAdded(LabelValueType labelValue)
+{
+  GroupIndexType groupIndex = 0;
+  if (m_Segmentation->IsLabeInGroup(labelValue, groupIndex))
+  {
+    auto label = m_Segmentation->GetLabel(labelValue);
+    if (nullptr == label) mitkThrow() << "Invalid internal state. Segmentation signaled the addition of an label that does not exist in the segmentation. Invalid label value:" << labelValue;
+    if (labelValue == mitk::LabelSetImage::UnlabeledValue) return;
+
+    auto groupItem = GetGroupItem(groupIndex, this->m_RootItem.get());
+
+    bool newLabelCreated = false;
+    auto instanceItem = AddLabelToGroupTree(label, groupItem, newLabelCreated);
+
+    if (newLabelCreated)
+    {
+      if (groupItem->m_childItems.size() == 1)
+      { //first label added
+        auto groupIndex = GetIndexByItem(groupItem, this);
+        emit dataChanged(groupIndex, groupIndex);
+      }
+      else
+      { //whole new label level added to group item
+        auto groupIndex = GetIndexByItem(groupItem, this);
+        this->beginInsertRows(groupIndex, instanceItem->ParentItem()->Row(), instanceItem->ParentItem()->Row());
+        this->endInsertRows();
+      }
+    }
+    else
+    {
+      if (instanceItem->ParentItem()->m_childItems.size() < 3)
+      { //second instance item was added, so label item will now able to colapse
+        // -> the whole label node has to be updated.
+        auto labelIndex = GetIndexByItem(instanceItem->ParentItem(), this);
+        emit dataChanged(labelIndex, labelIndex);
+        this->beginInsertRows(labelIndex, 0, instanceItem->ParentItem()->m_childItems.size()-1);
+        this->endInsertRows();
+      }
+      else
+      {
+        // instance item was added to existing label item with multiple instances
+        //-> just notify the row insertion
+        auto labelIndex = GetIndexByItem(instanceItem->ParentItem(), this);
+        this->beginInsertRows(labelIndex, instanceItem->Row(), instanceItem->Row());
+        this->endInsertRows();
+      }
+    }
+  }
+  else
+  {
+    mitkThrow() << "Group less labels are not supported in the current implementation.";
+  }
+}
+
+void QmitkMultiLabelTreeModel::OnLabelModified(LabelValueType labelValue)
+{
+  if (labelValue == mitk::LabelSetImage::UnlabeledValue) return;
+
+  auto instanceItem = GetInstanceItem(labelValue, this->m_RootItem.get());
+
+  if (nullptr == instanceItem)
+  {
+    mitkThrow() << "Internal invalid state. QmitkMultiLabelTreeModel recieved a LabelModified signal for a label that is not represented in the model. Invalid label: " << labelValue;
+  }
+
+  auto labelItem = instanceItem->ParentItem();
+
+  if (labelItem->m_ClassName == instanceItem->GetLabel()->GetName())
+  { //only the state of the label changed, but not its position in the model tree.
+
+    auto index = GetIndexByItem(labelItem, this);
+    emit dataChanged(index, index);
+  }
+  else
+  { //the name of the label changed and thus its place in the model tree, delete the current item and add a new one
+    this->OnLabelRemoved(labelValue);
+    this->OnLabelAdded(labelValue);
+  }
+}
+
+void QmitkMultiLabelTreeModel::OnLabelRemoved(LabelValueType labelValue)
+{
+  if (labelValue == mitk::LabelSetImage::UnlabeledValue) return;
+  auto instanceItem = GetInstanceItem(labelValue, this->m_RootItem.get());
+
+  if (nullptr == instanceItem) mitkThrow() << "Internal invalid state. QmitkMultiLabelTreeModel recieved a LabelRemoved signal for a label that is not represented in the model. Invalid label: " << labelValue;
+
+  auto labelItem = instanceItem->ParentItem();
+
+  if (labelItem->m_childItems.size() > 2)
+  {
+    auto labelIndex = GetIndexByItem(labelItem, this);
+    this->beginRemoveRows(labelIndex, instanceItem->Row(), instanceItem->Row());
+    labelItem->RemoveChild(instanceItem->Row());
+    this->endRemoveRows();
+  }
+  else if (labelItem->m_childItems.size() == 2)
+  { //After removal only one label is left -> the whole label node is about to be changed (no instances are shown any more).
+    auto labelIndex = GetIndexByItem(labelItem, this);
+    this->beginRemoveRows(labelIndex, instanceItem->Row(), instanceItem->Row());
+    labelItem->RemoveChild(instanceItem->Row());
+    this->endRemoveRows();
+    emit dataChanged(labelIndex, labelIndex);
+  }
+  else
+  { //was the only instance of the label, therefor also remove the label node from the tree.
+    auto groupItem = labelItem->ParentItem();
+    auto groupIndex = GetIndexByItem(groupItem, this);
+    this->beginRemoveRows(groupIndex, labelItem->Row(), labelItem->Row());
+    groupItem->RemoveChild(labelItem->Row());
+    this->endRemoveRows();
+  }
+}
+
+void QmitkMultiLabelTreeModel::OnGroupAdded(GroupIndexType groupIndex)
+{
+  if (m_ShowGroups)
+  {
+    this->beginInsertRows(QModelIndex(), groupIndex, groupIndex);
+    auto rootItem = m_RootItem.get();
+    auto groupItem = new QmitkMultiLabelSegTreeItem(QmitkMultiLabelSegTreeItem::ItemType::Group, rootItem);
+    rootItem->AppendChild(groupItem);
+    this->GenerateInternalGroupTree(groupIndex, groupItem);
+    this->endInsertRows();
+  }
+}
+
+void QmitkMultiLabelTreeModel::OnGroupModified(GroupIndexType /*groupIndex*/)
+{
+  //currently not needed
+}
+
+void QmitkMultiLabelTreeModel::OnGroupRemoved(GroupIndexType groupIndex)
+{
+  if (m_ShowGroups)
+  {
+    this->beginRemoveRows(QModelIndex(), groupIndex, groupIndex);
+    auto root = m_RootItem.get();
+    root->RemoveChild(groupIndex);
+    this->endRemoveRows();
+  }
+}
+
+void QmitkMultiLabelTreeModel::SetAllowVisibilityModification(bool vmod)
+{
+  m_AllowVisibilityModification = vmod;
+}
+
+bool QmitkMultiLabelTreeModel::GetAllowVisibilityModification() const
+{
+  return m_AllowVisibilityModification;
+}
+
+void QmitkMultiLabelTreeModel::SetAllowLockModification(bool lmod)
+{
+  m_AllowLockModification = lmod;
+}
+
+bool QmitkMultiLabelTreeModel::GetAllowLockModification() const
+{
+  return m_AllowLockModification;
+}
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.h b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.h
new file mode 100644
index 0000000000..967f804603
--- /dev/null
+++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.h
@@ -0,0 +1,158 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+All rights reserved.
+
+Use of this source code is governed by a 3-clause BSD license that can be
+found in the LICENSE file.
+
+============================================================================*/
+
+
+#ifndef QmitkMultiLabelTreeModel_h
+#define QmitkMultiLabelTreeModel_h
+
+#include "mitkLabelSetImage.h"
+
+// qt
+#include <QAbstractItemModel>
+
+#include "MitkSegmentationUIExports.h"
+
+
+class QmitkMultiLabelSegTreeItem;
+
+/*!
+\class QmitkMultiLabelTreeModel
+The class is used to represent the information of an MITK MultiLabel segmentation instance (labels, spacial groups...).
+*/
+class MITKSEGMENTATIONUI_EXPORT QmitkMultiLabelTreeModel : public QAbstractItemModel
+{
+    Q_OBJECT
+
+public:
+  using LabelValueType = mitk::LabelSetImage::LabelValueType;
+  using GroupIndexType = mitk::LabelSetImage::GroupIndexType;
+
+  QmitkMultiLabelTreeModel(QObject *parent = nullptr);
+  ~QmitkMultiLabelTreeModel() override;
+
+  void SetSegmentation(mitk::LabelSetImage* segmentation);
+  const mitk::LabelSetImage* GetSegmentation() const;
+
+  Qt::ItemFlags flags(const QModelIndex &index) const override;
+  QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+  bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
+
+  QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
+  int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+  int columnCount(const QModelIndex &parent = QModelIndex()) const override;
+
+  QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
+  QModelIndex parent(const QModelIndex &child) const override;
+
+  /** returns the index of a passed label value (always first column). If label value does not exist in
+  segmentation or segmentation is not set an invalid index will be returned.*/
+  QModelIndex indexOfLabel(mitk::Label::PixelType labelValue) const;
+  QModelIndex indexOfGroup(mitk::LabelSetImage::GroupIndexType groupIndex) const;
+  /** Returns the index to the next node in the tree that behaves like an instance (label node with only one instance
+  or instance node). If current index is at the end, an invalid index is returned.*/
+  QModelIndex ClosestLabelInstanceIndex(const QModelIndex& currentIndex) const;
+  /** Returns the index to the first child node (or itself) in the tree that behaves like an instance (label node with only one instance
+  or instance node). If current index is at the end, an invalid index is returned. If an invalid index is passed into the methods,
+  the search starts at the root; thus the whole tree is search for the first label instance.*/
+  QModelIndex FirstLabelInstanceIndex(const QModelIndex& currentIndex) const;
+
+  ///** Returns the index to the next node in the tree that behaves like an instance (label node with only one instance
+  //or instance node). If current index is at the end, an invalid index is returned.*/
+  //QModelIndex PrevLabelInstanceIndex(const QModelIndex& currentIndex) const;
+
+  /** Returns a vector containing all label values of the passed currentIndex or its child items.*/
+  std::vector <LabelValueType> GetLabelsInSubTree(const QModelIndex& currentIndex) const;
+
+  enum TableColumns
+  {
+    NAME_COL = 0,
+    LOCKED_COL,
+    COLOR_COL,
+    VISIBLE_COL
+  };
+
+  enum ItemModelRole
+  {
+    /**This role returns the label object that is associated with an index.
+       - On group level it always returns an invalid QVariant
+       - On label level (with multiple instances) it returns the first label instance).
+       - On instance level it returns the label instance object.*/
+    LabelDataRole = 64,
+    /**This role returns only the label value of the label that would be returned by
+       LabelDataRole.*/
+    LabelValueRole = 65,
+    /**Simelar to LabelDataRole, but only returns a valid QVariant if index points only to
+      a specific instance (so either instance level or label level with only one instance).
+      You can use that role if you want to assure that only one specific label instance is
+      referenced by the index.*/
+    LabelInstanceDataRole = 66,
+    /**Simelar to LabelValueRole, but like LabelInstanceDataRole only returns a valid QVariant
+      if index points only to a specific instance (so either instance level or label
+      level with only one instance).
+      You can use that role if you want to assure that only one specific label instance is
+      referenced by the index.*/
+    LabelInstanceValueRole = 67,
+    /**This role returns the group ID the item/index belongs to.*/
+    GroupIDRole = 68
+  };
+
+  bool GetAllowVisibilityModification() const;
+  bool GetAllowLockModification() const;
+
+public Q_SLOTS:
+  void SetAllowVisibilityModification(bool vmod);
+  void SetAllowLockModification(bool lmod);
+
+Q_SIGNALS:
+  void dataAvailable();
+  /** Is emitted whenever the model changes are finished (usually a bit later than dataAvailable()).*/
+  void modelChanged();
+
+protected:
+  void OnLabelAdded(LabelValueType labelValue);
+  void OnLabelModified(LabelValueType labelValue);
+  void OnLabelRemoved(LabelValueType labelValue);
+  void OnGroupAdded(GroupIndexType groupIndex);
+  void OnGroupModified(GroupIndexType groupIndex);
+  void OnGroupRemoved(GroupIndexType groupIndex);
+
+private:
+  void AddObserver();
+  void RemoveObserver();
+
+  void UpdateInternalTree();
+  void GenerateInternalGroupTree(unsigned int layerID, QmitkMultiLabelSegTreeItem* layerItem);
+  QmitkMultiLabelSegTreeItem* GenerateInternalTree();
+
+  /* builds a hierarchical tree model for the image statistics
+  1. Level: Image
+  --> 2. Level: Mask [if exist]
+      --> 3. Level: Timestep [if >1 exist] */
+  void BuildHierarchicalModel();
+
+  mitk::LabelSetImage::Pointer m_Segmentation;
+
+  std::mutex m_Mutex;
+  std::unique_ptr<QmitkMultiLabelSegTreeItem> m_RootItem;
+
+  bool m_Observed;
+  bool m_ShowGroups = true;
+
+  bool m_ShowVisibility = true;
+  bool m_ShowLock = true;
+  bool m_ShowOther = false;
+
+  bool m_AllowVisibilityModification = true;
+  bool m_AllowLockModification = true;
+};
+
+#endif // mitkQmitkMultiLabelTreeModel_h
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeView.cpp b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeView.cpp
new file mode 100644
index 0000000000..e6ed2c7835
--- /dev/null
+++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeView.cpp
@@ -0,0 +1,31 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+All rights reserved.
+
+Use of this source code is governed by a 3-clause BSD license that can be
+found in the LICENSE file.
+
+============================================================================*/
+
+#include <QmitkMultiLabelTreeView.h>
+
+#include <QmitkMultiLabelTreeModel.h>
+
+QmitkMultiLabelTreeView::QmitkMultiLabelTreeView(QWidget* parent) : QTreeView(parent)
+{
+}
+
+QItemSelectionModel::SelectionFlags QmitkMultiLabelTreeView::selectionCommand(const QModelIndex& index, const QEvent* event) const
+{
+  auto value = index.data(QmitkMultiLabelTreeModel::ItemModelRole::LabelInstanceValueRole);
+  if (index.column()!=0 || !value.isValid())
+  {
+    return QItemSelectionModel::NoUpdate;
+  }
+
+  return   QAbstractItemView::selectionCommand(index, event);
+}
+
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeView.h b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeView.h
new file mode 100644
index 0000000000..ba9ef13678
--- /dev/null
+++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeView.h
@@ -0,0 +1,34 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+All rights reserved.
+
+Use of this source code is governed by a 3-clause BSD license that can be
+found in the LICENSE file.
+
+============================================================================*/
+
+#ifndef QMITKMULTILABELSEGMENTATIONTREEVIEW_H
+#define QMITKMULTILABELSEGMENTATIONTREEVIEW_H
+
+#include <MitkSegmentationUIExports.h>
+
+#include <QTreeView>
+
+/*
+* @brief This is an inspector that offers a simple list view on a data storage.
+*/
+class MITKSEGMENTATIONUI_EXPORT QmitkMultiLabelTreeView : public QTreeView
+{
+  Q_OBJECT
+
+public:
+  QmitkMultiLabelTreeView(QWidget* parent = nullptr);
+
+protected:
+  QItemSelectionModel::SelectionFlags selectionCommand(const QModelIndex& index, const QEvent* event = nullptr) const override;
+};
+
+#endif // QMITKDATASTORAGELISTINSPECTOR_H
diff --git a/Modules/SegmentationUI/Qmitk/QmitkSegmentationTaskListWidget.cpp b/Modules/SegmentationUI/Qmitk/QmitkSegmentationTaskListWidget.cpp
index 9705dbd1cd..37ea6adefc 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkSegmentationTaskListWidget.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitkSegmentationTaskListWidget.cpp
@@ -1,981 +1,981 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "QmitkSegmentationTaskListWidget.h"
 
 #include <mitkCoreServices.h>
 #include <mitkIPreferencesService.h>
 #include <mitkIPreferences.h>
 
 #include <mitkDICOMQIPropertyHelper.h>
 #include <mitkIOUtil.h>
-#include <mitkLabelSetIOHelper.h>
+#include <mitkMultiLabelIOHelper.h>
 #include <mitkLabelSetImageHelper.h>
 #include <mitkNodePredicateDataType.h>
 #include <mitkNodePredicateFunction.h>
 #include <mitkRenderingManager.h>
 #include <mitkSegmentationHelper.h>
 #include <mitkToolManagerProvider.h>
 
 #include <QmitkStaticDynamicSegmentationDialog.h>
 #include <QmitkStyleManager.h>
 
 #include <QmitkStyleManager.h>
 
 #include <ui_QmitkSegmentationTaskListWidget.h>
 
 #include <QFileSystemWatcher>
 #include <QMessageBox>
 #include <QShortcut>
 
 #include <filesystem>
 
 namespace
 {
   mitk::IPreferences* GetSegmentationPreferences()
   {
     return mitk::CoreServices::GetPreferencesService()->GetSystemPreferences()->Node("/org.mitk.views.segmentation");
   }
 
   std::filesystem::path GetInputLocation(const mitk::BaseData* data)
   {
     std::string result;
 
     if (data != nullptr)
       data->GetPropertyList()->GetStringProperty("MITK.IO.reader.inputlocation", result);
 
     return result;
   }
 
   QString ColorString(const QString& string, const QColor& color, const QColor& backgroundColor = QColor::Invalid)
   {
     if (!color.isValid() && !backgroundColor.isValid())
       return string;
 
     auto result = QStringLiteral("<span style=\"");
     QStringList strings;
 
     if (color.isValid())
       strings << QString("color: %1;").arg(color.name());
 
     if (backgroundColor.isValid())
       strings << QString("background-color: %1;").arg(backgroundColor.name());
 
     result += strings.join(' ') + QString("\">%1</span>").arg(string);
 
     return result;
   }
 
   mitk::DataStorage::SetOfObjects::ConstPointer GetSubset(const mitk::DataStorage* dataStorage, const mitk::NodePredicateBase* condition, const mitk::DataNode* removedDataNode)
   {
     auto subset = dataStorage->GetSubset(condition);
 
     if (nullptr != removedDataNode)
     {
       auto actualSubset = mitk::DataStorage::SetOfObjects::New();
 
       for (auto node : *subset)
       {
         if (node != removedDataNode)
           actualSubset->push_back(node);
       }
 
       return actualSubset;
     }
 
     return subset;
   }
 }
 
 /* This constructor has three objectives:
  *   1. Do widget initialization that cannot be done in the .ui file
  *   2. Connect signals and slots
  *   3. Explicitly trigger a reset to a valid initial widget state
  */
 QmitkSegmentationTaskListWidget::QmitkSegmentationTaskListWidget(QWidget* parent)
   : QWidget(parent),
     m_Ui(new Ui::QmitkSegmentationTaskListWidget),
     m_FileSystemWatcher(new QFileSystemWatcher(this)),
     m_DataStorage(nullptr),
     m_UnsavedChanges(false)
 {
   m_Ui->setupUi(this);
 
   m_Ui->selectionWidget->SetNodePredicate(mitk::TNodePredicateDataType<mitk::SegmentationTaskList>::New());
 
   m_Ui->progressBar->setStyleSheet(QString("QProgressBar::chunk { background-color: %1; }").arg(QmitkStyleManager::GetIconAccentColor()));
 
   m_Ui->storeButton->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/document-save.svg")));
 
   using Self = QmitkSegmentationTaskListWidget;
 
   connect(m_Ui->selectionWidget, &QmitkSingleNodeSelectionWidget::CurrentSelectionChanged, this, &Self::OnSelectionChanged);
   connect(m_Ui->previousButton, &QToolButton::clicked, this, &Self::OnPreviousButtonClicked);
   connect(m_Ui->nextButton, &QToolButton::clicked, this, &Self::OnNextButtonClicked);
   connect(m_Ui->loadButton, &QPushButton::clicked, this, &Self::OnLoadButtonClicked);
   connect(m_Ui->storeButton, &QPushButton::clicked, this, &Self::OnStoreButtonClicked);
   connect(m_Ui->acceptButton, &QPushButton::clicked, this, &Self::OnAcceptButtonClicked);
 
   connect(m_FileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, &Self::OnResultDirectoryChanged);
 
   auto* prevShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key::Key_P), this);
   connect(prevShortcut, &QShortcut::activated, this, &Self::OnPreviousTaskShortcutActivated);
 
   auto* prevUndoneShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key::Key_P), this);
   connect(prevUndoneShortcut, &QShortcut::activated, this, &Self::OnPreviousTaskShortcutActivated);
 
   auto* nextShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key::Key_N), this);
   connect(nextShortcut, &QShortcut::activated, this, &Self::OnNextTaskShortcutActivated);
 
   auto* nextUndoneShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key::Key_N), this);
   connect(nextUndoneShortcut, &QShortcut::activated, this, &Self::OnNextTaskShortcutActivated);
 
   auto* loadShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key::Key_L), this);
   connect(loadShortcut, &QShortcut::activated, this, &Self::OnLoadTaskShortcutActivated);
 
   auto* storeShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key::Key_S), parent);
   connect(storeShortcut, &QShortcut::activated, this, &Self::OnStoreInterimResultShortcutActivated);
 
   auto* acceptShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key::Key_A), parent);
   connect(acceptShortcut, &QShortcut::activated, this, &Self::OnAcceptSegmentationShortcutActivated);
 
   this->ResetControls();
   this->CheckDataStorage();
 }
 
 QmitkSegmentationTaskListWidget::~QmitkSegmentationTaskListWidget()
 {
 }
 
 void QmitkSegmentationTaskListWidget::SetDataStorage(mitk::DataStorage* dataStorage)
 {
   m_DataStorage = dataStorage;
   m_Ui->selectionWidget->SetDataStorage(dataStorage); // Triggers OnSelectionChanged()
   m_Ui->selectionWidget->SetAutoSelectNewNodes(true);
 
   this->CheckDataStorage();
 }
 
 void QmitkSegmentationTaskListWidget::CheckDataStorage(const mitk::DataNode* removedNode)
 {
   QString warning;
 
   if (nullptr == m_DataStorage)
   {
     warning = QStringLiteral(
       "<h3>Developer warning</h3><p>Call <code>SetDataStorage()</code> to fully initialize "
       "this instance of <code>QmitkSegmentationTaskListWidget</code>.</p>");
   }
   else
   {
     auto isTaskList = mitk::TNodePredicateDataType<mitk::SegmentationTaskList>::New();
     auto taskListNodes = GetSubset(m_DataStorage, isTaskList, removedNode);
 
     if (taskListNodes->empty())
     {
       warning = QStringLiteral(
         "<h3>No segmentation task list found</h3><p>Load a segmentation task list to use "
         "this plugin.</p>");
     }
     else if (taskListNodes->Size() > 1)
     {
       warning = QStringLiteral(
         "<h3>More than one segmentation task list found</h3><p>Unload everything but a "
         "single segmentation task list to use this plugin.</p>");
     }
     else
     {
       const auto* taskListNode = (*taskListNodes)[0].GetPointer();
 
       auto isTaskListNode = mitk::NodePredicateFunction::New([taskListNode](const mitk::DataNode* node) {
         return node == taskListNode;
       });
 
       auto isChildOfTaskListNode = mitk::NodePredicateFunction::New([this, isTaskListNode](const mitk::DataNode* node) {
         return !m_DataStorage->GetSources(node, isTaskListNode, false)->empty();
       });
 
       auto isHelperObject = mitk::NodePredicateProperty::New("helper object");
       auto isUndesiredNode = mitk::NodePredicateNot::New(mitk::NodePredicateOr::New(
         isTaskListNode,
         isChildOfTaskListNode,
         isHelperObject));
 
       if (!GetSubset(m_DataStorage, isUndesiredNode, removedNode)->empty())
       {
         warning = QStringLiteral(
           "<h3>Unrelated data found</h3><p>Unload everything but a single segmentation task "
           "list to use this plugin.</p>");
       }
     }
   }
 
   m_Ui->label->setText("<span style=\"color: " + QmitkStyleManager::GetIconAccentColor() + "\">" + warning + "</span>");
   m_Ui->label->setVisible(!warning.isEmpty());
   m_Ui->widget->setVisible(warning.isEmpty());
 }
 
 void QmitkSegmentationTaskListWidget::OnUnsavedChangesSaved()
 {
   if (m_UnsavedChanges)
   {
     m_UnsavedChanges = false;
 
     if (this->ActiveTaskIsShown())
       this->UpdateDetailsLabel();
   }
 }
 
 /* Make sure that the widget transitions into a valid state whenever the
  * selection changes.
  */
 void QmitkSegmentationTaskListWidget::OnSelectionChanged(const QmitkSingleNodeSelectionWidget::NodeList& nodes)
 {
   this->UnloadTasks();
   this->ResetControls();
 
   if (!nodes.empty())
   {
     m_TaskListNode = nodes.front();
     auto taskList = dynamic_cast<mitk::SegmentationTaskList*>(m_TaskListNode->GetData());
 
     if (taskList != nullptr)
     {
       this->OnTaskListChanged(taskList);
       return;
     }
   }
 
   this->SetTaskList(nullptr);
   m_TaskListNode = nullptr;
 }
 
 /* Reset all controls to a default state as a common basis for further
  * adjustments.
  */
 void QmitkSegmentationTaskListWidget::ResetControls()
 {
   m_Ui->progressBar->setEnabled(false);
   m_Ui->progressBar->setFormat("");
   m_Ui->progressBar->setValue(0);
   m_Ui->progressBar->setMaximum(1);
 
   m_Ui->previousButton->setEnabled(false);
   m_Ui->nextButton->setEnabled(false);
 
   this->UpdateLoadButton();
   this->UpdateDetailsLabel();
   this->UpdateStoreAndAcceptButtons();
 }
 
 /* If the segmentation task changed, reset all member variables to expected
  * default values and reset the file system watcher.
  */
 void QmitkSegmentationTaskListWidget::SetTaskList(mitk::SegmentationTaskList* taskList)
 {
   if (m_TaskList != taskList)
   {
     m_TaskList = taskList;
 
     if (taskList != nullptr)
     {
       this->SetCurrentTaskIndex(0);
     }
     else
     {
       this->SetCurrentTaskIndex(std::nullopt);
     }
 
     this->ResetFileSystemWatcher();
   }
 }
 
 void QmitkSegmentationTaskListWidget::ResetFileSystemWatcher()
 {
   auto paths = m_FileSystemWatcher->directories();
 
   if (!paths.empty())
     m_FileSystemWatcher->removePaths(paths);
 
   if (m_TaskList.IsNotNull())
   {
     for (const auto& task : *m_TaskList)
     {
       auto resultPath = m_TaskList->GetAbsolutePath(task.GetResult()).remove_filename();
 
       if (!std::filesystem::exists(resultPath))
       {
         try
         {
           std::filesystem::create_directories(resultPath);
         }
         catch (const std::filesystem::filesystem_error& e)
         {
           MITK_ERROR << e.what();
         }
       }
 
       if (std::filesystem::exists(resultPath))
         m_FileSystemWatcher->addPath(QString::fromStdString(resultPath.string()));
     }
   }
 }
 
 void QmitkSegmentationTaskListWidget::OnResultDirectoryChanged(const QString&)
 {
   // TODO: If a segmentation was modified ("Unsaved changes"), saved ("Done"), and then the file is deleted, the status should be "Unsaved changes" instead of "Not done".
   this->UpdateProgressBar();
   this->UpdateDetailsLabel();
 }
 
 void QmitkSegmentationTaskListWidget::UpdateProgressBar()
 {
   int progress = 0;
 
   for (size_t i = 0; i < m_TaskList->GetNumberOfTasks(); ++i)
   {
     if (m_TaskList->IsDone(i))
       ++progress;
   }
 
   m_Ui->progressBar->setValue(progress);
 }
 
 /* Provided that a valid segmentation task list is currently selected and the
  * widget is in its default state, update all controls accordingly.
  * TODO: Then, load the first unfinished task, if any.
  */
 void QmitkSegmentationTaskListWidget::OnTaskListChanged(mitk::SegmentationTaskList* taskList)
 {
   this->SetTaskList(taskList);
 
   const auto numTasks = taskList->GetNumberOfTasks();
 
   m_Ui->progressBar->setMaximum(numTasks);
   m_Ui->progressBar->setFormat(QStringLiteral("%v/%m Task(s) done"));
   m_Ui->progressBar->setEnabled(true);
 
   this->UpdateProgressBar();
 
   m_Ui->loadButton->setEnabled(true);
 
   if (numTasks > 1)
     m_Ui->nextButton->setEnabled(true);
 
   // TODO: This line should be enough but it is happening too early even before
   // the RenderingManager has any registered render windows, resulting in mismatching
   // renderer and data geometries.
   // this->LoadNextUnfinishedTask();
 }
 
 /* If possible, change the currently displayed task to the previous task.
  * Enable/disable navigation buttons according to the task's position.
  */
 void QmitkSegmentationTaskListWidget::OnPreviousButtonClicked()
 {
   auto current = m_CurrentTaskIndex.value();
 
   // If the shift modifier key is pressed, look for the previous undone task.
   if (QApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier))
   {
     if (current > 0)
     {
       for (decltype(current) i = current; i > 0; --i)
       {
         if (!m_TaskList->IsDone(i - 1))
         {
           this->SetCurrentTaskIndex(i - 1);
           break;
         }
       }
     }
   }
   else
   {
     if (current != 0)
       this->SetCurrentTaskIndex(current - 1);
   }
 
   this->UpdateNavigationButtons();
 }
 
 /* If possible, change the currently displayed task to the next task.
  * Enable/disable navigation buttons according to the task's position.
  */
 void QmitkSegmentationTaskListWidget::OnNextButtonClicked()
 {
   const auto numTasks = m_TaskList->GetNumberOfTasks();
   auto current = m_CurrentTaskIndex.value();
 
   // If the shift modifier key is pressed, look for the next undone task.
   if (QApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier))
   {
     for (std::remove_const_t<decltype(numTasks)> i = current + 1; i < numTasks; ++i)
     {
       if (!m_TaskList->IsDone(i))
       {
         this->SetCurrentTaskIndex(i);
         break;
       }
     }
   }
   else
   {
     if (current < numTasks - 1)
       this->SetCurrentTaskIndex(current + 1);
   }
 
   this->UpdateNavigationButtons();
 }
 
 void QmitkSegmentationTaskListWidget::UpdateNavigationButtons()
 {
   if (m_TaskList.IsNull() || m_TaskList->GetNumberOfTasks() == 0)
   {
     m_Ui->previousButton->setEnabled(false);
     m_Ui->nextButton->setEnabled(false);
 
     return;
   }
 
   const auto maxIndex = m_TaskList->GetNumberOfTasks() - 1;
   const auto current = m_CurrentTaskIndex.value();
 
   m_Ui->previousButton->setEnabled(current != 0);
   m_Ui->nextButton->setEnabled(current != maxIndex);
 }
 
 /* Update affected controls when the currently displayed task changed.
  */
 void QmitkSegmentationTaskListWidget::OnCurrentTaskChanged()
 {
   this->UpdateLoadButton();
   this->UpdateNavigationButtons();
   this->UpdateDetailsLabel();
   this->UpdateStoreAndAcceptButtons();
 }
 
 /* Update the load button according to the currently displayed task.
  */
 void QmitkSegmentationTaskListWidget::UpdateLoadButton()
 {
   auto text = !this->ActiveTaskIsShown()
     ? QStringLiteral("Load task")
     : QStringLiteral("Task");
 
   if (m_CurrentTaskIndex.has_value())
   {
     const auto current = m_CurrentTaskIndex.value();
 
     if (m_TaskList.IsNotNull())
     {
       text += QString(" %1/%2").arg(current + 1).arg(m_TaskList->GetNumberOfTasks());
 
       if (m_TaskList->HasName(current))
         text += QStringLiteral(":\n") + QString::fromStdString(m_TaskList->GetName(current));
     }
 
     m_Ui->loadButton->setDisabled(this->ActiveTaskIsShown());
   }
   else
   {
     m_Ui->loadButton->setEnabled(false);
   }
 
   m_Ui->loadButton->setText(text);
 }
 
 /* Update the details label according to the currently display task.
  * The text is composed of the status of the task and a variable number
  * of text blocks according to the optional values provided by the task.
  */
 void QmitkSegmentationTaskListWidget::UpdateDetailsLabel()
 {
   if (!m_CurrentTaskIndex.has_value())
   {
     m_Ui->detailsLabel->clear();
     return;
   }
 
   const auto current = m_CurrentTaskIndex.value();
   bool isDone = m_TaskList->IsDone(current);
 
   auto details = QString("<p><b>Status: %1</b> / <b>").arg(this->ActiveTaskIsShown()
     ? ColorString("Active", Qt::white, QColor(Qt::green).darker())
     : ColorString("Inactive", Qt::white, QColor(Qt::red).darker()));
 
   if (m_UnsavedChanges && this->ActiveTaskIsShown())
   {
     details += QString("%1</b></p>").arg(ColorString("Unsaved changes", Qt::white, QColor(Qt::red).darker()));
   }
   else
   {
     details += QString("%1</b></p>").arg(isDone
       ? ColorString("Done", Qt::white, QColor(Qt::green).darker())
       : ColorString("Not done", Qt::white, QColor(Qt::red).darker()));
   }
 
   if (m_TaskList->HasDescription(current))
     details += QString("<p><b>Description:</b> %1</p>").arg(QString::fromStdString(m_TaskList->GetDescription(current)));
 
   QStringList stringList;
 
   if (m_TaskList->HasImage(current))
     stringList << QString::fromStdString("<b>Image:</b> " + m_TaskList->GetImage(current).string());
 
   if (m_TaskList->HasSegmentation(current))
     stringList << QString::fromStdString("<b>Segmentation:</b> " + m_TaskList->GetSegmentation(current).string());
 
   if (m_TaskList->HasLabelName(current))
     stringList << QString::fromStdString("<b>Label name:</b> " + m_TaskList->GetLabelName(current));
 
   if (m_TaskList->HasLabelNameSuggestions(current))
     stringList << QString::fromStdString("<b>Label name suggestions:</b> " + m_TaskList->GetLabelNameSuggestions(current).string());
 
   if (m_TaskList->HasPreset(current))
     stringList << QString::fromStdString("<b>Label set preset:</b> " + m_TaskList->GetPreset(current).string());
 
   if (m_TaskList->HasDynamic(current))
     stringList << QString("<b>Segmentation type:</b> %1").arg(m_TaskList->GetDynamic(current) ? "Dynamic" : "Static");
 
   if (!stringList.empty())
     details += QString("<p>%1</p>").arg(stringList.join(QStringLiteral("<br>")));
 
   m_Ui->detailsLabel->setText(details);
 }
 
 void QmitkSegmentationTaskListWidget::UpdateStoreAndAcceptButtons()
 {
   auto activeTaskIsShown = this->ActiveTaskIsShown();
 
   m_Ui->storeButton->setVisible(activeTaskIsShown);
   m_Ui->acceptButton->setEnabled(activeTaskIsShown);
 }
 
 /* Load/activate the currently displayed task. Unload all data nodes from
  * previously active tasks first, but spare and reuse the image if possible.
  */
 void QmitkSegmentationTaskListWidget::OnLoadButtonClicked()
 {
   if (!this->HandleUnsavedChanges() || m_UnsavedChanges)
     return;
 
   m_Ui->loadButton->setEnabled(false);
 
   QApplication::setOverrideCursor(Qt::BusyCursor);
   this->LoadTask(this->GetImageDataNode(m_CurrentTaskIndex.value()));
   QApplication::restoreOverrideCursor();
 }
 
 /* If present, return the image data node for the task with the specified
  * index. Otherwise, return nullptr.
  */
 mitk::DataNode* QmitkSegmentationTaskListWidget::GetImageDataNode(size_t index) const
 {
   const auto imagePath = m_TaskList->GetAbsolutePath(m_TaskList->GetImage(index));
 
   auto imageNodes = m_DataStorage->GetDerivations(m_TaskListNode, mitk::NodePredicateFunction::New([imagePath](const mitk::DataNode* node) {
     return imagePath == GetInputLocation(node->GetData());
   }));
 
   return !imageNodes->empty()
     ? imageNodes->front()
     : nullptr;
 }
 
 /* If present, return the segmentation data node for the task with the
  * specified index. Otherwise, return nullptr.
  */
 mitk::DataNode* QmitkSegmentationTaskListWidget::GetSegmentationDataNode(size_t index) const
 {
   const auto* imageNode = this->GetImageDataNode(index);
 
   if (imageNode != nullptr)
   {
     auto segmentations = m_DataStorage->GetDerivations(imageNode, mitk::TNodePredicateDataType<mitk::LabelSetImage>::New());
 
     if (!segmentations->empty())
       return segmentations->front();
   }
 
   return nullptr;
 }
 
 /* Unload all task data nodes but spare the passed image data node.
  */
 void QmitkSegmentationTaskListWidget::UnloadTasks(const mitk::DataNode* skip)
 {
   this->UnsubscribeFromActiveSegmentation();
 
   if (m_TaskListNode.IsNotNull())
   {
     auto imageNodes = m_DataStorage->GetDerivations(m_TaskListNode, mitk::TNodePredicateDataType<mitk::Image>::New());
 
     for (auto imageNode : *imageNodes)
     {
       m_DataStorage->Remove(m_DataStorage->GetDerivations(imageNode, nullptr, false));
 
       if (imageNode != skip)
         m_DataStorage->Remove(imageNode);
     }
   }
 
   this->SetActiveTaskIndex(std::nullopt);
 }
 
 void QmitkSegmentationTaskListWidget::LoadNextUnfinishedTask()
 {
   const auto current = m_CurrentTaskIndex.value();
   const auto numTasks = m_TaskList->GetNumberOfTasks();
 
   for (size_t unboundNext = current; unboundNext < current + numTasks; ++unboundNext)
   {
     auto next = unboundNext % numTasks;
 
     if (!m_TaskList->IsDone(next))
     {
       this->SetCurrentTaskIndex(next);
       this->OnLoadButtonClicked();
       break;
     }
   }
 }
 
 /* Load/activate the currently displayed task. The task must specify
  * an image. The segmentation is either created from scratch with an optional
  * name for the first label, possibly based on a label set preset specified by
  * the task, or loaded as specified by the task. If a result file does
  * exist, it is chosen as segmentation instead.
  */
 void QmitkSegmentationTaskListWidget::LoadTask(mitk::DataNode::Pointer imageNode)
 {
   this->UnloadTasks(imageNode);
 
   const auto current = m_CurrentTaskIndex.value();
 
   mitk::Image::Pointer image;
   mitk::LabelSetImage::Pointer segmentation;
 
   try
   {
     if (imageNode.IsNull())
     {
       const auto path = m_TaskList->GetAbsolutePath(m_TaskList->GetImage(current));
       image = mitk::IOUtil::Load<mitk::Image>(path.string());
     }
 
     const auto path = m_TaskList->GetAbsolutePath(m_TaskList->GetResult(current));
     const auto interimPath = m_TaskList->GetInterimPath(path);
 
     if (std::filesystem::exists(path))
     {
       segmentation = mitk::IOUtil::Load<mitk::LabelSetImage>(path.string());
     }
     else if (std::filesystem::exists(interimPath))
     {
       segmentation = mitk::IOUtil::Load<mitk::LabelSetImage>(interimPath.string());
     }
     else if (m_TaskList->HasSegmentation(current))
     {
       const auto path = m_TaskList->GetAbsolutePath(m_TaskList->GetSegmentation(current));
       segmentation = mitk::IOUtil::Load<mitk::LabelSetImage>(path.string());
     }
   }
   catch (const mitk::Exception&)
   {
     return;
   }
 
   if (imageNode.IsNull())
   {
     imageNode = mitk::DataNode::New();
     imageNode->SetData(image);
 
     m_DataStorage->Add(imageNode, m_TaskListNode);
 
     mitk::RenderingManager::GetInstance()->InitializeViews(image->GetTimeGeometry());
   }
   else
   {
     image = static_cast<mitk::Image*>(imageNode->GetData());
   }
 
   auto name = "Task " + std::to_string(current + 1);
   imageNode->SetName(name);
 
   if (segmentation.IsNull())
   {
     mitk::Image::ConstPointer templateImage = image;
 
     if (templateImage->GetDimension() > 3)
     {
       if (m_TaskList->HasDynamic(current))
       {
         if (!m_TaskList->GetDynamic(current))
           templateImage = mitk::SegmentationHelper::GetStaticSegmentationTemplate(image);
       }
       else
       {
         QmitkStaticDynamicSegmentationDialog dialog(this);
         dialog.SetReferenceImage(templateImage);
         dialog.exec();
 
         templateImage = dialog.GetSegmentationTemplate();
       }
     }
 
     auto segmentationNode = mitk::LabelSetImageHelper::CreateNewSegmentationNode(imageNode, templateImage, name);
     segmentation = static_cast<mitk::LabelSetImage*>(segmentationNode->GetData());
 
     if (m_TaskList->HasPreset(current))
     {
       const auto path = m_TaskList->GetAbsolutePath(m_TaskList->GetPreset(current));
-      mitk::LabelSetIOHelper::LoadLabelSetImagePreset(path.string(), segmentation);
+      mitk::MultiLabelIOHelper::LoadLabelSetImagePreset(path.string(), segmentation);
     }
     else
     {
       auto label = mitk::LabelSetImageHelper::CreateNewLabel(segmentation);
 
       if (m_TaskList->HasLabelName(current))
         label->SetName(m_TaskList->GetLabelName(current));
 
       segmentation->GetActiveLabelSet()->AddLabel(label);
     }
 
     m_DataStorage->Add(segmentationNode, imageNode);
   }
   else
   {
     auto segmentationNode = mitk::DataNode::New();
     segmentationNode->SetName(name);
     segmentationNode->SetData(segmentation);
 
     m_DataStorage->Add(segmentationNode, imageNode);
   }
 
   // Workaround for T29431. Remove when T26953 is fixed.
   mitk::DICOMQIPropertyHelper::DeriveDICOMSourceProperties(image, segmentation);
 
   auto prefs = GetSegmentationPreferences();
 
   if (prefs != nullptr)
   {
     if (m_TaskList->HasLabelNameSuggestions(current))
     {
       auto path = m_TaskList->GetAbsolutePath(m_TaskList->GetLabelNameSuggestions(current));
 
       prefs->PutBool("default label naming", false);
       prefs->Put("label suggestions", path.string());
       prefs->PutBool("replace standard suggestions", true);
       prefs->PutBool("suggest once", true);
     }
     else
     {
       prefs->PutBool("default label naming", true);
       prefs->Put("label suggestions", "");
     }
   }
 
   m_UnsavedChanges = false;
 
   this->SetActiveTaskIndex(current);
   this->SubscribeToActiveSegmentation();
 
   this->OnCurrentTaskChanged();
 }
 
 void QmitkSegmentationTaskListWidget::SubscribeToActiveSegmentation()
 {
   if (m_ActiveTaskIndex.has_value())
   {
     auto segmentationNode = this->GetSegmentationDataNode(m_ActiveTaskIndex.value());
 
     if (segmentationNode != nullptr)
     {
       auto segmentation = static_cast<mitk::LabelSetImage*>(segmentationNode->GetData());
 
       auto command = itk::SimpleMemberCommand<QmitkSegmentationTaskListWidget>::New();
       command->SetCallbackFunction(this, &QmitkSegmentationTaskListWidget::OnSegmentationModified);
 
       m_SegmentationModifiedObserverTag = segmentation->AddObserver(itk::ModifiedEvent(), command);
     }
   }
 }
 
 void QmitkSegmentationTaskListWidget::UnsubscribeFromActiveSegmentation()
 {
   if (m_ActiveTaskIndex.has_value() && m_SegmentationModifiedObserverTag.has_value())
   {
     auto segmentationNode = this->GetSegmentationDataNode(m_ActiveTaskIndex.value());
 
     if (segmentationNode != nullptr)
     {
       auto segmentation = static_cast<mitk::LabelSetImage*>(segmentationNode->GetData());
       segmentation->RemoveObserver(m_SegmentationModifiedObserverTag.value());
     }
 
     m_SegmentationModifiedObserverTag.reset();
   }
 }
 
 void QmitkSegmentationTaskListWidget::OnSegmentationModified()
 {
   if (!m_UnsavedChanges)
   {
     m_UnsavedChanges = true;
 
     if (m_ActiveTaskIndex.value() == m_CurrentTaskIndex)
       this->UpdateDetailsLabel();
   }
 }
 
 void QmitkSegmentationTaskListWidget::SetActiveTaskIndex(const std::optional<size_t>& index)
 {
   if (m_ActiveTaskIndex != index)
   {
     m_ActiveTaskIndex = index;
     this->UpdateStoreAndAcceptButtons();
   }
 }
 
 void QmitkSegmentationTaskListWidget::SetCurrentTaskIndex(const std::optional<size_t>& index)
 {
   if (m_CurrentTaskIndex != index)
   {
     m_CurrentTaskIndex = index;
     this->OnCurrentTaskChanged();
 
   }
 }
 
 bool QmitkSegmentationTaskListWidget::ActiveTaskIsShown() const
 {
   return m_ActiveTaskIndex.has_value() && m_CurrentTaskIndex.has_value() && m_ActiveTaskIndex == m_CurrentTaskIndex;
 }
 
 bool QmitkSegmentationTaskListWidget::HandleUnsavedChanges(const QString& alternativeTitle)
 {
   if (m_UnsavedChanges)
   {
     const auto active = m_ActiveTaskIndex.value();
     const auto current = m_CurrentTaskIndex.value();
     QString title;
 
     if (alternativeTitle.isEmpty())
     {
       title = QString("Load task %1").arg(current + 1);
 
       if (m_TaskList->HasName(current))
         title += ": " + QString::fromStdString(m_TaskList->GetName(current));
     }
     else
     {
       title = alternativeTitle;
     }
 
     auto text = QString("The currently active task %1 ").arg(active + 1);
 
     if (m_TaskList->HasName(active))
       text += "(" + QString::fromStdString(m_TaskList->GetName(active)) + ") ";
 
     text += "has unsaved changes.";
 
     auto reply = QMessageBox::question(this, title, text, QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Cancel);
 
     switch (reply)
     {
     case QMessageBox::Save:
       this->SaveActiveTask(!std::filesystem::exists(m_TaskList->GetResult(active)));
       break;
 
     case QMessageBox::Discard:
       m_UnsavedChanges = false;
       break;
 
     default:
       return false;
     }
   }
 
   return true;
 }
 
 void QmitkSegmentationTaskListWidget::SaveActiveTask(bool saveAsIntermediateResult)
 {
   if (!m_ActiveTaskIndex.has_value())
     return;
 
   QApplication::setOverrideCursor(Qt::BusyCursor);
 
   try
   {
     const auto active = m_ActiveTaskIndex.value();
     m_TaskList->SaveTask(active, this->GetSegmentationDataNode(active)->GetData(), saveAsIntermediateResult);
     this->OnUnsavedChangesSaved();
   }
   catch (const mitk::Exception& e)
   {
     MITK_ERROR << e;
   }
 
   QApplication::restoreOverrideCursor();
 }
 
 bool QmitkSegmentationTaskListWidget::OnPreShutdown()
 {
   return this->HandleUnsavedChanges(QStringLiteral("Application shutdown"));
 }
 
 void QmitkSegmentationTaskListWidget::OnPreviousTaskShortcutActivated()
 {
   m_Ui->previousButton->click();
 }
 
 void QmitkSegmentationTaskListWidget::OnNextTaskShortcutActivated()
 {
   m_Ui->nextButton->click();
 }
 
 void QmitkSegmentationTaskListWidget::OnLoadTaskShortcutActivated()
 {
   m_Ui->loadButton->click();
 }
 
 void QmitkSegmentationTaskListWidget::OnStoreInterimResultShortcutActivated()
 {
   m_Ui->storeButton->click();
 }
 
 void QmitkSegmentationTaskListWidget::OnAcceptSegmentationShortcutActivated()
 {
   m_Ui->acceptButton->click();
 }
 
 void QmitkSegmentationTaskListWidget::OnStoreButtonClicked()
 {
   this->SaveActiveTask(true);
 }
 
 void QmitkSegmentationTaskListWidget::OnAcceptButtonClicked()
 {
   auto* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager();
   int activeToolId = -1;
 
   if (toolManager != nullptr)
     activeToolId = toolManager->GetActiveToolID();
 
   this->SaveActiveTask();
   this->LoadNextUnfinishedTask();
 
   if (toolManager != nullptr)
     toolManager->ActivateTool(activeToolId);
 }
diff --git a/Modules/SegmentationUI/Qmitk/QmitkSimpleLabelSetListWidget.cpp b/Modules/SegmentationUI/Qmitk/QmitkSimpleLabelSetListWidget.cpp
index e0be0aa079..ce0e57f777 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkSimpleLabelSetListWidget.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitkSimpleLabelSetListWidget.cpp
@@ -1,199 +1,199 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "QmitkSimpleLabelSetListWidget.h"
 
 #include "mitkMessage.h"
 
 #include <qlayout.h>
 
 QmitkSimpleLabelSetListWidget::QmitkSimpleLabelSetListWidget(QWidget* parent) : QWidget(parent), m_LabelList(nullptr), m_Emmiting(false)
 {
   QGridLayout* layout = new QGridLayout(this);
   this->setContentsMargins(0, 0, 0, 0);
 
   m_LabelList = new QListWidget(this);
   m_LabelList->setSelectionMode(QAbstractItemView::MultiSelection);
   m_LabelList->setResizeMode(QListView::Adjust);
   m_LabelList->setAutoScrollMargin(0);
   layout->addWidget(m_LabelList);
 
   connect(m_LabelList, SIGNAL(itemSelectionChanged()), this, SLOT(OnLabelSelectionChanged()));
 }
 
 QmitkSimpleLabelSetListWidget::~QmitkSimpleLabelSetListWidget()
 {
   if (m_LabelSetImage.IsNotNull())
   {
     m_LabelSetImage->BeforeChangeLayerEvent -= mitk::MessageDelegate<QmitkSimpleLabelSetListWidget>(
       this, &QmitkSimpleLabelSetListWidget::OnLooseLabelSetConnection);
     m_LabelSetImage->AfterChangeLayerEvent -= mitk::MessageDelegate<QmitkSimpleLabelSetListWidget>(
       this, &QmitkSimpleLabelSetListWidget::OnEstablishLabelSetConnection);
     OnLooseLabelSetConnection();
   }
 }
 
 QmitkSimpleLabelSetListWidget::LabelVectorType QmitkSimpleLabelSetListWidget::SelectedLabels() const
 {
   auto selectedItems = m_LabelList->selectedItems();
   LabelVectorType result;
 
   QList<QListWidgetItem*>::Iterator it;
   for (it = selectedItems.begin(); it != selectedItems.end(); ++it)
   {
     auto labelValue = (*it)->data(Qt::UserRole).toUInt();
 
 
     auto activeLayerID = m_LabelSetImage->GetActiveLayer();
     auto labelSet = m_LabelSetImage->GetLabelSet(activeLayerID);
     
     result.push_back(labelSet->GetLabel(labelValue));
   }
 
   return result;
 }
 
 const mitk::LabelSetImage* QmitkSimpleLabelSetListWidget::GetLabelSetImage() const
 {
   return m_LabelSetImage;
 }
 
 void QmitkSimpleLabelSetListWidget::SetLabelSetImage(const mitk::LabelSetImage* image)
 {
   if (image != m_LabelSetImage)
   {
     if (m_LabelSetImage.IsNotNull())
     {
       m_LabelSetImage->BeforeChangeLayerEvent -= mitk::MessageDelegate<QmitkSimpleLabelSetListWidget>(
         this, &QmitkSimpleLabelSetListWidget::OnLooseLabelSetConnection);
       m_LabelSetImage->AfterChangeLayerEvent -= mitk::MessageDelegate<QmitkSimpleLabelSetListWidget>(
         this, &QmitkSimpleLabelSetListWidget::OnLayerChanged);
       this->OnLooseLabelSetConnection();
     }
 
     m_LabelSetImage = image;
 
     if (m_LabelSetImage.IsNotNull())
     {
       m_LabelSetImage->BeforeChangeLayerEvent += mitk::MessageDelegate<QmitkSimpleLabelSetListWidget>(
         this, &QmitkSimpleLabelSetListWidget::OnLooseLabelSetConnection);
       m_LabelSetImage->AfterChangeLayerEvent += mitk::MessageDelegate<QmitkSimpleLabelSetListWidget>(
         this, &QmitkSimpleLabelSetListWidget::OnLayerChanged);
       this->OnLayerChanged();
     }
   }
 }
 
 void QmitkSimpleLabelSetListWidget::OnLooseLabelSetConnection()
 {
   if (m_LabelSetImage.IsNull())
     return;
 
   auto activeLayerID = m_LabelSetImage->GetActiveLayer();
   auto labelSet = m_LabelSetImage->GetLabelSet(activeLayerID);
 
   // Reset LabelSetWidget Events
-  labelSet->AddLabelEvent -= mitk::MessageDelegate<QmitkSimpleLabelSetListWidget>(
+  labelSet->AddLabelEvent -= mitk::MessageDelegate1<QmitkSimpleLabelSetListWidget, mitk::LabelSetImage::LabelValueType>(
     this, &QmitkSimpleLabelSetListWidget::OnLabelChanged);
-  labelSet->RemoveLabelEvent -= mitk::MessageDelegate<QmitkSimpleLabelSetListWidget>(
+  labelSet->RemoveLabelEvent -= mitk::MessageDelegate1<QmitkSimpleLabelSetListWidget, mitk::LabelSetImage::LabelValueType>(
     this, &QmitkSimpleLabelSetListWidget::OnLabelChanged);
-  labelSet->ModifyLabelEvent -= mitk::MessageDelegate<QmitkSimpleLabelSetListWidget>(
+  labelSet->ModifyLabelEvent -= mitk::MessageDelegate1<QmitkSimpleLabelSetListWidget, mitk::LabelSetImage::LabelValueType>(
     this, &QmitkSimpleLabelSetListWidget::OnLabelChanged);
 }
 
 void QmitkSimpleLabelSetListWidget::OnEstablishLabelSetConnection()
 {
   if (m_LabelSetImage.IsNull())
     return;
 
   auto activeLayerID = m_LabelSetImage->GetActiveLayer();
   auto labelSet = m_LabelSetImage->GetLabelSet(activeLayerID);
 
   // Reset LabelSetWidget Events
-  labelSet->AddLabelEvent += mitk::MessageDelegate<QmitkSimpleLabelSetListWidget>(
+  labelSet->AddLabelEvent += mitk::MessageDelegate1<QmitkSimpleLabelSetListWidget, mitk::LabelSetImage::LabelValueType>(
     this, &QmitkSimpleLabelSetListWidget::OnLabelChanged);
-  labelSet->RemoveLabelEvent += mitk::MessageDelegate<QmitkSimpleLabelSetListWidget>(
+  labelSet->RemoveLabelEvent += mitk::MessageDelegate1<QmitkSimpleLabelSetListWidget, mitk::LabelSetImage::LabelValueType>(
     this, &QmitkSimpleLabelSetListWidget::OnLabelChanged);
-  labelSet->ModifyLabelEvent += mitk::MessageDelegate<QmitkSimpleLabelSetListWidget>(
+  labelSet->ModifyLabelEvent += mitk::MessageDelegate1<QmitkSimpleLabelSetListWidget, mitk::LabelSetImage::LabelValueType>(
     this, &QmitkSimpleLabelSetListWidget::OnLabelChanged);
 }
 
 void QmitkSimpleLabelSetListWidget::OnLayerChanged()
 {
   this->OnEstablishLabelSetConnection();
   if (!this->m_Emmiting)
   {
     this->ResetList();
 
     this->m_Emmiting = true;
     emit ActiveLayerChanged();
     emit SelectedLabelsChanged(this->SelectedLabels());
     this->m_Emmiting = false;
   }
 }
 
-void QmitkSimpleLabelSetListWidget::OnLabelChanged()
+void QmitkSimpleLabelSetListWidget::OnLabelChanged(mitk::LabelSetImage::LabelValueType /*lv*/)
 {
   if (!this->m_Emmiting)
   {
     this->ResetList();
 
     this->m_Emmiting = true;
     emit ActiveLayerChanged();
     emit SelectedLabelsChanged(this->SelectedLabels());
     this->m_Emmiting = false;
   }
 }
 
 void QmitkSimpleLabelSetListWidget::OnLabelSelectionChanged()
 {
   if (!this->m_Emmiting)
   {
     this->m_Emmiting = true;
     emit SelectedLabelsChanged(this->SelectedLabels());
     this->m_Emmiting = false;
   }
 }
 
 void QmitkSimpleLabelSetListWidget::ResetList()
 {
   m_LabelList->clear();
   
   auto activeLayerID = m_LabelSetImage->GetActiveLayer();
   auto labelSet = m_LabelSetImage->GetLabelSet(activeLayerID);
 
   auto iter = labelSet->IteratorConstBegin();
   for (; iter != labelSet->IteratorConstEnd(); ++iter)
   {
     auto color = iter->second->GetColor();
     QPixmap pixmap(10, 10);
     pixmap.fill(QColor(color[0] * 255, color[1] * 255, color[2] * 255));
     QIcon icon(pixmap);
 
     QListWidgetItem* item = new QListWidgetItem(icon, QString::fromStdString(iter->second->GetName()));
     item->setData(Qt::UserRole, QVariant(iter->second->GetValue()));
     m_LabelList->addItem(item);
   }
 }
 
 void QmitkSimpleLabelSetListWidget::SetSelectedLabels(const LabelVectorType& selectedLabels)
 {
   for (int i = 0; i < m_LabelList->count(); ++i)
   {
     QListWidgetItem* item = m_LabelList->item(i);
     auto labelValue = item->data(Qt::UserRole).toUInt();
 
     auto finding = std::find_if(selectedLabels.begin(), selectedLabels.end(), [labelValue](const mitk::Label* label) {return label->GetValue() == labelValue; });
     item->setSelected(finding != selectedLabels.end());
   }
 }
 
diff --git a/Modules/SegmentationUI/Qmitk/QmitkSimpleLabelSetListWidget.h b/Modules/SegmentationUI/Qmitk/QmitkSimpleLabelSetListWidget.h
index cde5e971d7..7eb5153611 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkSimpleLabelSetListWidget.h
+++ b/Modules/SegmentationUI/Qmitk/QmitkSimpleLabelSetListWidget.h
@@ -1,64 +1,64 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #ifndef QmitkSimpleLabelSetListWidget_h
 #define QmitkSimpleLabelSetListWidget_h
 
 #include "mitkLabel.h"
 #include "mitkLabelSetImage.h"
 #include <MitkSegmentationUIExports.h>
 #include <QListWidget>
 
 /**
   \brief Widget that offers a simple list that displays all labels (color and name) in the active
   layer of a LabelSetImage.
 */
 class MITKSEGMENTATIONUI_EXPORT QmitkSimpleLabelSetListWidget : public QWidget
 {
   Q_OBJECT
 
 public:
   QmitkSimpleLabelSetListWidget(QWidget* parent = nullptr);
   ~QmitkSimpleLabelSetListWidget() override;
 
   using LabelVectorType = std::vector<mitk::Label::ConstPointer>;
 
   LabelVectorType SelectedLabels() const;
   const mitk::LabelSetImage* GetLabelSetImage() const;
 
 signals:
   void SelectedLabelsChanged(const LabelVectorType& selectedLabels);
   void ActiveLayerChanged();
 
 public slots :
   void SetLabelSetImage(const mitk::LabelSetImage* image);
   void SetSelectedLabels(const LabelVectorType& selectedLabels);
 
 protected slots:
 
   void OnLabelSelectionChanged();
 
 protected:
   void OnLayerChanged();
-  void OnLabelChanged();
+  void OnLabelChanged(mitk::LabelSetImage::LabelValueType lv);
 
   void OnLooseLabelSetConnection();
   void OnEstablishLabelSetConnection();
 
   void ResetList();
 
   mitk::LabelSetImage::ConstPointer m_LabelSetImage;
   QListWidget* m_LabelList;
   bool m_Emmiting;
 };
 
 #endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp b/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp
index 7d7d58668d..653eb2a08d 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp
@@ -1,1985 +1,1985 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "QmitkSlicesInterpolator.h"
 #include "QmitkRenderWindow.h"
 #include "QmitkRenderWindowWidget.h"
 
 #include "mitkApplyDiffImageOperation.h"
 #include "mitkColorProperty.h"
 #include "mitkCoreObjectFactory.h"
 #include "mitkDiffImageApplier.h"
 #include "mitkInteractionConst.h"
 #include "mitkLevelWindowProperty.h"
 #include "mitkOperationEvent.h"
 #include "mitkProgressBar.h"
 #include "mitkProperties.h"
 #include "mitkRenderingManager.h"
 #include "mitkSegTool2D.h"
 #include "mitkSliceNavigationController.h"
 #include "mitkSurfaceToImageFilter.h"
 #include "mitkToolManager.h"
 #include "mitkUndoController.h"
 
 #include <mitkExtractSliceFilter.h>
 #include <mitkPlanarCircle.h>
 #include <mitkImageReadAccessor.h>
 #include <mitkImageTimeSelector.h>
 #include <mitkImageWriteAccessor.h>
 #include <mitkPlaneProposer.h>
 #include <mitkUnstructuredGridClusteringFilter.h>
 #include <mitkVtkImageOverwrite.h>
 #include <mitkShapeBasedInterpolationAlgorithm.h>
 #include <itkCommand.h>
 
 #include <mitkImageToContourFilter.h>
 #include <mitkImagePixelReadAccessor.h>
 
 //  Includes for the merge operation
 #include "mitkImageToContourFilter.h"
 
 #include <QCheckBox>
 #include <QCursor>
 #include <QMenu>
 #include <QMessageBox>
 #include <QPushButton>
 #include <QVBoxLayout>
 
 #include <vtkDoubleArray.h>
 #include <vtkFieldData.h>
 #include <vtkPolyVertex.h>
 #include <vtkUnstructuredGrid.h>
 #include <vtkPolyData.h>
 
 #include <array>
 #include <atomic>
 #include <thread>
 #include <vector>
 
 namespace
 {
   template <typename T = mitk::BaseData>
   itk::SmartPointer<T> GetData(const mitk::DataNode* dataNode)
   {
     return nullptr != dataNode
       ? dynamic_cast<T*>(dataNode->GetData())
       : nullptr;
   }
 }
 
 float SURFACE_COLOR_RGB[3] = {0.49f, 1.0f, 0.16f};
 
 const std::map<QAction *, mitk::SliceNavigationController *> QmitkSlicesInterpolator::createActionToSlicer(const QList<QmitkRenderWindow*>& windows)
 {
   std::map<QAction *, mitk::SliceNavigationController *> actionToSliceDimension;
   for (auto* window : windows)
   {
     std::string windowName;
     auto renderWindowWidget = dynamic_cast<QmitkRenderWindowWidget*>(window->parentWidget());
     if (renderWindowWidget)
     {
       windowName = renderWindowWidget->GetCornerAnnotationText();
     }
     else
     {
       windowName = window->GetRenderer()->GetName();
     }
     auto slicer = window->GetSliceNavigationController();
     actionToSliceDimension[new QAction(QString::fromStdString(windowName), nullptr)] = slicer;
   }
 
   return actionToSliceDimension;
 }
 
 // Check whether the given contours are coplanar
 bool AreContoursCoplanar(mitk::SurfaceInterpolationController::ContourPositionInformation leftHandSide,
                       mitk::SurfaceInterpolationController::ContourPositionInformation rightHandSide)
 {
   // Here we check two things:
   // 1. Whether the normals of both contours are at least parallel
   // 2. Whether both contours lie in the same plane
 
   // Check for coplanarity:
   // a. Span a vector between two points one from each contour
   // b. Calculate dot product for the vector and one of the normals
   // c. If the dot is zero the two vectors are orthogonal and the contours are coplanar
 
   double vec[3];
   vec[0] = leftHandSide.ContourPoint[0] - rightHandSide.ContourPoint[0];
   vec[1] = leftHandSide.ContourPoint[1] - rightHandSide.ContourPoint[1];
   vec[2] = leftHandSide.ContourPoint[2] - rightHandSide.ContourPoint[2];
   double n[3];
   n[0] = rightHandSide.ContourNormal[0];
   n[1] = rightHandSide.ContourNormal[1];
   n[2] = rightHandSide.ContourNormal[2];
   double dot = vtkMath::Dot(n, vec);
 
   double n2[3];
   n2[0] = leftHandSide.ContourNormal[0];
   n2[1] = leftHandSide.ContourNormal[1];
   n2[2] = leftHandSide.ContourNormal[2];
 
   // The normals of both contours have to be parallel but not of the same orientation
   double lengthLHS = leftHandSide.ContourNormal.GetNorm();
   double lengthRHS = rightHandSide.ContourNormal.GetNorm();
   double dot2 = vtkMath::Dot(n, n2);
   bool contoursParallel = mitk::Equal(fabs(lengthLHS * lengthRHS), fabs(dot2), 0.001);
 
   if (mitk::Equal(dot, 0.0, 0.001) && contoursParallel)
     return true;
   else
     return false;
 }
 
 mitk::Image::Pointer ExtractSliceFromImage(mitk::Image* image,
                                           const mitk::PlaneGeometry * contourPlane,
                                           unsigned int timeStep)
 {
   vtkSmartPointer<mitkVtkImageOverwrite> reslice = vtkSmartPointer<mitkVtkImageOverwrite>::New();
   // set to false to extract a slice
   reslice->SetOverwriteMode(false);
   reslice->Modified();
 
   mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice);
   extractor->SetInput(image);
   extractor->SetTimeStep(timeStep);
   extractor->SetWorldGeometry(contourPlane);
   extractor->SetVtkOutputRequest(false);
   extractor->SetResliceTransformByGeometry(image->GetTimeGeometry()->GetGeometryForTimeStep(timeStep));
   extractor->Update();
   mitk::Image::Pointer slice = extractor->GetOutput();
   return slice;
 }
 
 
 template <unsigned int VImageDimension = 3>
 std::vector<mitk::Label::PixelType> GetPixelValuesPresentInImage(mitk::LabelSetImage* labelSetImage)
 {
   std::vector<mitk::Label::PixelType> pixelsPresent;
   mitk::ImagePixelReadAccessor<mitk::LabelSet::PixelType, VImageDimension> readAccessor(labelSetImage);
 
   std::size_t numberOfPixels = 1;
   for (size_t dim = 0; dim < VImageDimension; ++dim)
     numberOfPixels *= static_cast<std::size_t>(readAccessor.GetDimension(dim));
 
   auto src = readAccessor.GetData();
   for (std::size_t i = 0; i < numberOfPixels; ++i)
   {
     mitk::Label::PixelType pixelVal = *(src + i);
-    if ( (std::find(pixelsPresent.begin(), pixelsPresent.end(), pixelVal) == pixelsPresent.end()) && (pixelVal != labelSetImage->GetExteriorLabel()->GetValue()) )
+    if ( (std::find(pixelsPresent.begin(), pixelsPresent.end(), pixelVal) == pixelsPresent.end()) && (pixelVal != mitk::LabelSetImage::UnlabeledValue) )
       pixelsPresent.push_back(pixelVal);
   }
   return pixelsPresent;
 }
 
 
 template <unsigned int VImageDimension = 3>
 ModifyLabelActionTrigerred ModifyLabelProcessing(mitk::LabelSetImage* labelSetImage,
                           mitk::SurfaceInterpolationController::Pointer surfaceInterpolator,
                           unsigned int timePoint)
 {
   auto currentLayerID = labelSetImage->GetActiveLayer();
   auto numTimeSteps = labelSetImage->GetTimeSteps();
 
   ModifyLabelActionTrigerred actionTriggered = ModifyLabelActionTrigerred::Null;
   mitk::SurfaceInterpolationController::ContourPositionInformationList &currentContourList =
     surfaceInterpolator->GetContours(timePoint, currentLayerID);
 
   mitk::LabelSetImage::Pointer labelSetImage2 = labelSetImage->Clone();
 
   mitk::ImagePixelReadAccessor<mitk::LabelSet::PixelType, VImageDimension> readAccessor(labelSetImage2.GetPointer());
 
   for (auto& contour : currentContourList)
   {
     mitk::Label::PixelType contourPixelValue;
 
     itk::Index<3> itkIndex;
     labelSetImage2->GetGeometry()->WorldToIndex(contour.ContourPoint, itkIndex);
     if (VImageDimension == 4)
     {
       itk::Index<VImageDimension> time3DIndex;
       for (size_t i = 0; i < itkIndex.size(); ++i)
         time3DIndex[i] = itkIndex[i];
       time3DIndex[3] = timePoint;
       contourPixelValue = readAccessor.GetPixelByIndexSafe(time3DIndex);
     }
     else if (VImageDimension == 3)
     {
       itk::Index<VImageDimension> geomIndex;
       for (size_t i = 0; i < itkIndex.size(); ++i)
         geomIndex[i] = itkIndex[i];
       contourPixelValue = readAccessor.GetPixelByIndexSafe(geomIndex);
     }
 
     if (contour.LabelValue != contourPixelValue)
     {
       if (contourPixelValue == 0)   //  Erase label
       {
         for (size_t t = 0; t < numTimeSteps; ++t)
           surfaceInterpolator->RemoveContours(contour.LabelValue, t, currentLayerID);
         actionTriggered = ModifyLabelActionTrigerred::Erase;
       }
       else
       {
         contour.LabelValue = contourPixelValue;
         actionTriggered = ModifyLabelActionTrigerred::Merge;
       }
     }
   }
   return actionTriggered;
 }
 
 QmitkSlicesInterpolator::QmitkSlicesInterpolator(QWidget *parent, const char * /*name*/)
   : QWidget(parent),
     m_Interpolator(mitk::SegmentationInterpolationController::New()),
     m_SurfaceInterpolator(mitk::SurfaceInterpolationController::GetInstance()),
     m_ToolManager(nullptr),
     m_Initialized(false),
     m_LastSNC(nullptr),
     m_LastSliceIndex(0),
     m_2DInterpolationEnabled(false),
     m_3DInterpolationEnabled(false),
     m_PreviousActiveLabelValue(0),
     m_CurrentActiveLabelValue(0),
     m_PreviousLayerIndex(0),
     m_CurrentLayerIndex(0),
     m_FirstRun(true)
 {
   m_GroupBoxEnableExclusiveInterpolationMode = new QGroupBox("Interpolation", this);
 
   QVBoxLayout *vboxLayout = new QVBoxLayout(m_GroupBoxEnableExclusiveInterpolationMode);
 
   m_EdgeDetector = mitk::FeatureBasedEdgeDetectionFilter::New();
   m_PointScorer = mitk::PointCloudScoringFilter::New();
 
   m_CmbInterpolation = new QComboBox(m_GroupBoxEnableExclusiveInterpolationMode);
   m_CmbInterpolation->addItem("Disabled");
   m_CmbInterpolation->addItem("2-Dimensional");
   m_CmbInterpolation->addItem("3-Dimensional");
   vboxLayout->addWidget(m_CmbInterpolation);
 
   m_BtnApply2D = new QPushButton("Confirm for single slice", m_GroupBoxEnableExclusiveInterpolationMode);
   vboxLayout->addWidget(m_BtnApply2D);
 
   m_BtnApplyForAllSlices2D = new QPushButton("Confirm for all slices", m_GroupBoxEnableExclusiveInterpolationMode);
   vboxLayout->addWidget(m_BtnApplyForAllSlices2D);
 
   m_BtnApply3D = new QPushButton("Confirm", m_GroupBoxEnableExclusiveInterpolationMode);
   vboxLayout->addWidget(m_BtnApply3D);
 
   // T28261
   // m_BtnSuggestPlane = new QPushButton("Suggest a plane", m_GroupBoxEnableExclusiveInterpolationMode);
   // vboxLayout->addWidget(m_BtnSuggestPlane);
 
   m_BtnReinit3DInterpolation = new QPushButton("Reinit Interpolation", m_GroupBoxEnableExclusiveInterpolationMode);
   vboxLayout->addWidget(m_BtnReinit3DInterpolation);
 
   m_ChkShowPositionNodes = new QCheckBox("Show Position Nodes", m_GroupBoxEnableExclusiveInterpolationMode);
   vboxLayout->addWidget(m_ChkShowPositionNodes);
 
   this->HideAllInterpolationControls();
 
   connect(m_CmbInterpolation, SIGNAL(currentIndexChanged(int)), this, SLOT(OnInterpolationMethodChanged(int)));
   connect(m_BtnApply2D, SIGNAL(clicked()), this, SLOT(OnAcceptInterpolationClicked()));
   connect(m_BtnApplyForAllSlices2D, SIGNAL(clicked()), this, SLOT(OnAcceptAllInterpolationsClicked()));
   connect(m_BtnApply3D, SIGNAL(clicked()), this, SLOT(OnAccept3DInterpolationClicked()));
 
 
   connect(m_BtnReinit3DInterpolation, SIGNAL(clicked()), this, SLOT(OnReinit3DInterpolation()));
   connect(m_ChkShowPositionNodes, SIGNAL(toggled(bool)), this, SLOT(OnShowMarkers(bool)));
   connect(m_ChkShowPositionNodes, SIGNAL(toggled(bool)), this, SIGNAL(SignalShowMarkerNodes(bool)));
 
   QHBoxLayout *layout = new QHBoxLayout(this);
   layout->addWidget(m_GroupBoxEnableExclusiveInterpolationMode);
   this->setLayout(layout);
 
   itk::ReceptorMemberCommand<QmitkSlicesInterpolator>::Pointer command =
     itk::ReceptorMemberCommand<QmitkSlicesInterpolator>::New();
   command->SetCallbackFunction(this, &QmitkSlicesInterpolator::OnInterpolationInfoChanged);
   InterpolationInfoChangedObserverTag = m_Interpolator->AddObserver(itk::ModifiedEvent(), command);
 
   itk::ReceptorMemberCommand<QmitkSlicesInterpolator>::Pointer command2 =
     itk::ReceptorMemberCommand<QmitkSlicesInterpolator>::New();
   command2->SetCallbackFunction(this, &QmitkSlicesInterpolator::OnSurfaceInterpolationInfoChanged);
   SurfaceInterpolationInfoChangedObserverTag = m_SurfaceInterpolator->AddObserver(itk::ModifiedEvent(), command2);
 
   auto command3 = itk::ReceptorMemberCommand<QmitkSlicesInterpolator>::New();
   command3->SetCallbackFunction(this, &QmitkSlicesInterpolator::OnInterpolationAborted);
   InterpolationAbortedObserverTag = m_Interpolator->AddObserver(itk::AbortEvent(), command3);
 
   // feedback node and its visualization properties
   m_FeedbackNode = mitk::DataNode::New();
   mitk::CoreObjectFactory::GetInstance()->SetDefaultProperties(m_FeedbackNode);
 
   m_FeedbackNode->SetProperty("binary", mitk::BoolProperty::New(true));
   m_FeedbackNode->SetProperty("outline binary", mitk::BoolProperty::New(true));
   m_FeedbackNode->SetProperty("color", mitk::ColorProperty::New(255.0, 255.0, 0.0));
   m_FeedbackNode->SetProperty("texture interpolation", mitk::BoolProperty::New(false));
   m_FeedbackNode->SetProperty("layer", mitk::IntProperty::New(20));
   m_FeedbackNode->SetProperty("levelwindow", mitk::LevelWindowProperty::New(mitk::LevelWindow(0, 1)));
   m_FeedbackNode->SetProperty("name", mitk::StringProperty::New("Interpolation feedback"));
   m_FeedbackNode->SetProperty("opacity", mitk::FloatProperty::New(0.8));
   m_FeedbackNode->SetProperty("helper object", mitk::BoolProperty::New(true));
 
   m_InterpolatedSurfaceNode = mitk::DataNode::New();
   m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(SURFACE_COLOR_RGB));
   m_InterpolatedSurfaceNode->SetProperty("name", mitk::StringProperty::New("Surface Interpolation feedback"));
   m_InterpolatedSurfaceNode->SetProperty("opacity", mitk::FloatProperty::New(0.5));
   m_InterpolatedSurfaceNode->SetProperty("line width", mitk::FloatProperty::New(4.0f));
   m_InterpolatedSurfaceNode->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false));
   m_InterpolatedSurfaceNode->SetProperty("helper object", mitk::BoolProperty::New(true));
   m_InterpolatedSurfaceNode->SetVisibility(false);
 
   m_3DContourNode = mitk::DataNode::New();
   m_3DContourNode->SetProperty("color", mitk::ColorProperty::New(0.0, 0.0, 0.0));
   m_3DContourNode->SetProperty("hidden object", mitk::BoolProperty::New(true));
   m_3DContourNode->SetProperty("name", mitk::StringProperty::New("Drawn Contours"));
   m_3DContourNode->SetProperty("material.representation", mitk::VtkRepresentationProperty::New(VTK_WIREFRAME));
   m_3DContourNode->SetProperty("material.wireframeLineWidth", mitk::FloatProperty::New(2.0f));
   m_3DContourNode->SetProperty("3DContourContainer", mitk::BoolProperty::New(true));
   m_3DContourNode->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false));
   m_3DContourNode->SetVisibility(false);
 
   QWidget::setContentsMargins(0, 0, 0, 0);
   if (QWidget::layout() != nullptr)
   {
     QWidget::layout()->setContentsMargins(0, 0, 0, 0);
   }
 
 
   // For running 3D Interpolation in background
   // create a QFuture and a QFutureWatcher
 
   connect(&m_Watcher, SIGNAL(started()), this, SLOT(StartUpdateInterpolationTimer()));
   connect(&m_Watcher, SIGNAL(finished()), this, SLOT(OnSurfaceInterpolationFinished()));
   connect(&m_Watcher, SIGNAL(finished()), this, SLOT(StopUpdateInterpolationTimer()));
   m_Timer = new QTimer(this);
   connect(m_Timer, SIGNAL(timeout()), this, SLOT(ChangeSurfaceColor()));
 }
 
 void QmitkSlicesInterpolator::SetDataStorage(mitk::DataStorage::Pointer storage)
 {
   if (m_DataStorage == storage)
   {
     return;
   }
 
   if (m_DataStorage.IsNotNull())
   {
     m_DataStorage->RemoveNodeEvent.RemoveListener(
       mitk::MessageDelegate1<QmitkSlicesInterpolator, const mitk::DataNode*>(this, &QmitkSlicesInterpolator::NodeRemoved)
     );
   }
 
   m_DataStorage = storage;
   m_SurfaceInterpolator->SetDataStorage(storage);
 
   if (m_DataStorage.IsNotNull())
   {
     m_DataStorage->RemoveNodeEvent.AddListener(
       mitk::MessageDelegate1<QmitkSlicesInterpolator, const mitk::DataNode*>(this, &QmitkSlicesInterpolator::NodeRemoved)
     );
   }
 }
 
 mitk::DataStorage *QmitkSlicesInterpolator::GetDataStorage()
 {
   if (m_DataStorage.IsNotNull())
   {
     return m_DataStorage;
   }
   else
   {
     return nullptr;
   }
 }
 
 void QmitkSlicesInterpolator::InitializeWindow(QmitkRenderWindow* window)
 {
   auto slicer = window->GetSliceNavigationController();
 
   if (slicer == nullptr)
   {
     MITK_WARN << "Tried setting up interpolation for a render window that does not have a slice navigation controller set";
     return;
   }
 
   // Has to be initialized
   m_LastSNC = slicer;
   m_TimePoints.insert(slicer, slicer->GetSelectedTimePoint());
 
   itk::MemberCommand<QmitkSlicesInterpolator>::Pointer deleteCommand =
     itk::MemberCommand<QmitkSlicesInterpolator>::New();
   deleteCommand->SetCallbackFunction(this, &QmitkSlicesInterpolator::OnSliceNavigationControllerDeleted);
   m_ControllerToDeleteObserverTag[slicer] = slicer->AddObserver(itk::DeleteEvent(), deleteCommand);
 
   itk::MemberCommand<QmitkSlicesInterpolator>::Pointer timeChangedCommand =
     itk::MemberCommand<QmitkSlicesInterpolator>::New();
   timeChangedCommand->SetCallbackFunction(this, &QmitkSlicesInterpolator::OnTimeChanged);
   m_ControllerToTimeObserverTag[slicer] = slicer->AddObserver(mitk::SliceNavigationController::TimeGeometryEvent(nullptr, 0), timeChangedCommand);
 
   itk::MemberCommand<QmitkSlicesInterpolator>::Pointer sliceChangedCommand =
     itk::MemberCommand<QmitkSlicesInterpolator>::New();
   sliceChangedCommand->SetCallbackFunction(this, &QmitkSlicesInterpolator::OnSliceChanged);
   m_ControllerToSliceObserverTag[slicer] = slicer->AddObserver(mitk::SliceNavigationController::GeometrySliceEvent(nullptr, 0), sliceChangedCommand);
 }
 
 void QmitkSlicesInterpolator::Initialize(mitk::ToolManager *toolManager,
                                          const QList<QmitkRenderWindow*>& windows)
 {
   Q_ASSERT(!windows.empty());
 
   if (m_Initialized)
   {
     // remove old observers
     this->Uninitialize();
   }
 
   m_ToolManager = toolManager;
 
   if (m_ToolManager)
   {
     // set enabled only if a segmentation is selected
     mitk::DataNode *node = m_ToolManager->GetWorkingData(0);
     QWidget::setEnabled(node != nullptr);
 
     // react whenever the set of selected segmentation changes
     m_ToolManager->WorkingDataChanged +=
       mitk::MessageDelegate<QmitkSlicesInterpolator>(this, &QmitkSlicesInterpolator::OnToolManagerWorkingDataModified);
     m_ToolManager->ReferenceDataChanged += mitk::MessageDelegate<QmitkSlicesInterpolator>(
       this, &QmitkSlicesInterpolator::OnToolManagerReferenceDataModified);
 
     // connect to the slice navigation controller. after each change, call the interpolator
     for (auto* window : windows)
     {
       this->InitializeWindow(window);
     }
 
     m_ActionToSlicer = createActionToSlicer(windows);
   }
 
   m_Initialized = true;
 }
 
 void QmitkSlicesInterpolator::Uninitialize()
 {
   if (m_ToolManager.IsNotNull())
   {
     m_ToolManager->WorkingDataChanged -=
       mitk::MessageDelegate<QmitkSlicesInterpolator>(this, &QmitkSlicesInterpolator::OnToolManagerWorkingDataModified);
     m_ToolManager->ReferenceDataChanged -= mitk::MessageDelegate<QmitkSlicesInterpolator>(
       this, &QmitkSlicesInterpolator::OnToolManagerReferenceDataModified);
   }
   for (auto* slicer : m_ControllerToTimeObserverTag.keys())
   {
     slicer->RemoveObserver(m_ControllerToDeleteObserverTag.take(slicer));
     slicer->RemoveObserver(m_ControllerToTimeObserverTag.take(slicer));
     slicer->RemoveObserver(m_ControllerToSliceObserverTag.take(slicer));
   }
 
   auto dataIter = m_SegmentationObserverTags.begin();
   while (dataIter != m_SegmentationObserverTags.end())
   {
     auto labelSetImage = (*dataIter).first;
     labelSetImage->RemoveObserver((*dataIter).second);
     for (size_t layerID = 0; layerID < labelSetImage->GetNumberOfLayers(); ++layerID)
     {
       this->OnRemoveLabelSetConnection(labelSetImage, layerID);
     }
     ++dataIter;
   }
   m_SegmentationObserverTags.clear();
 
   m_ActionToSlicer.clear();
   m_ToolManager = nullptr;
 
   m_Initialized = false;
 }
 
 QmitkSlicesInterpolator::~QmitkSlicesInterpolator()
 {
   if (m_Initialized)
   {
     // remove old observers
     this->Uninitialize();
   }
 
   WaitForFutures();
 
   if (m_DataStorage.IsNotNull())
   {
     m_DataStorage->RemoveNodeEvent.RemoveListener(
       mitk::MessageDelegate1<QmitkSlicesInterpolator, const mitk::DataNode*>(this, &QmitkSlicesInterpolator::NodeRemoved)
     );
     if (m_DataStorage->Exists(m_3DContourNode))
       m_DataStorage->Remove(m_3DContourNode);
     if (m_DataStorage->Exists(m_InterpolatedSurfaceNode))
       m_DataStorage->Remove(m_InterpolatedSurfaceNode);
   }
 
   // remove observer
   m_Interpolator->RemoveObserver(InterpolationAbortedObserverTag);
   m_Interpolator->RemoveObserver(InterpolationInfoChangedObserverTag);
   m_SurfaceInterpolator->RemoveObserver(SurfaceInterpolationInfoChangedObserverTag);
 
   m_SurfaceInterpolator->UnsetSelectedImage();
 
   delete m_Timer;
 }
 
 /**
 External enableization...
 */
 void QmitkSlicesInterpolator::setEnabled(bool enable)
 {
   QWidget::setEnabled(enable);
 
   // Set the gui elements of the different interpolation modi enabled
   if (enable)
   {
     if (m_2DInterpolationEnabled)
     {
       this->Show2DInterpolationControls(true);
       m_Interpolator->Activate2DInterpolation(true);
     }
     else if (m_3DInterpolationEnabled)
     {
       this->Show3DInterpolationControls(true);
       this->Show3DInterpolationResult(true);
     }
   }
   // Set all gui elements of the interpolation disabled
   else
   {
     this->HideAllInterpolationControls();
     this->Show3DInterpolationResult(false);
   }
 }
 
 void QmitkSlicesInterpolator::On2DInterpolationEnabled(bool status)
 {
   OnInterpolationActivated(status);
   m_Interpolator->Activate2DInterpolation(status);
 }
 
 void QmitkSlicesInterpolator::On3DInterpolationEnabled(bool status)
 {
   On3DInterpolationActivated(status);
 }
 
 void QmitkSlicesInterpolator::OnInterpolationDisabled(bool status)
 {
   if (status)
   {
     OnInterpolationActivated(!status);
     On3DInterpolationActivated(!status);
     this->Show3DInterpolationResult(false);
   }
 }
 
 void QmitkSlicesInterpolator::HideAllInterpolationControls()
 {
   this->Show2DInterpolationControls(false);
   this->Show3DInterpolationControls(false);
 }
 
 void QmitkSlicesInterpolator::Show2DInterpolationControls(bool show)
 {
   m_BtnApply2D->setVisible(show);
   m_BtnApplyForAllSlices2D->setVisible(show);
 }
 
 void QmitkSlicesInterpolator::Show3DInterpolationControls(bool show)
 {
   m_BtnApply3D->setVisible(show);
 
   // T28261
   // m_BtnSuggestPlane->setVisible(show);
 
   m_ChkShowPositionNodes->setVisible(show);
   m_BtnReinit3DInterpolation->setVisible(show);
 }
 
 void QmitkSlicesInterpolator::OnInterpolationMethodChanged(int index)
 {
   switch (index)
   {
     case 0: // Disabled
       m_GroupBoxEnableExclusiveInterpolationMode->setTitle("Interpolation");
       this->HideAllInterpolationControls();
       this->OnInterpolationActivated(false);
       this->On3DInterpolationActivated(false);
       this->Show3DInterpolationResult(false);
       m_Interpolator->Activate2DInterpolation(false);
       break;
 
     case 1: // 2D
       m_GroupBoxEnableExclusiveInterpolationMode->setTitle("Interpolation (Enabled)");
       this->HideAllInterpolationControls();
       this->Show2DInterpolationControls(true);
       this->OnInterpolationActivated(true);
       this->On3DInterpolationActivated(false);
       m_Interpolator->Activate2DInterpolation(true);
       break;
 
     case 2: // 3D
       m_GroupBoxEnableExclusiveInterpolationMode->setTitle("Interpolation (Enabled)");
       this->HideAllInterpolationControls();
       this->Show3DInterpolationControls(true);
       this->OnInterpolationActivated(false);
       this->On3DInterpolationActivated(true);
       m_Interpolator->Activate2DInterpolation(false);
       break;
 
     default:
       MITK_ERROR << "Unknown interpolation method!";
       m_CmbInterpolation->setCurrentIndex(0);
       break;
   }
 }
 
 void QmitkSlicesInterpolator::OnShowMarkers(bool state)
 {
   mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers =
     m_DataStorage->GetSubset(mitk::NodePredicateProperty::New("isContourMarker", mitk::BoolProperty::New(true)));
 
   for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End();
        ++it)
   {
     it->Value()->SetProperty("helper object", mitk::BoolProperty::New(!state));
   }
 }
 
 void QmitkSlicesInterpolator::OnToolManagerWorkingDataModified()
 {
   if (m_ToolManager->GetWorkingData(0) != nullptr)
   {
     m_Segmentation = dynamic_cast<mitk::Image *>(m_ToolManager->GetWorkingData(0)->GetData());
     auto labelSetImage = dynamic_cast<mitk::LabelSetImage *>(m_ToolManager->GetWorkingData(0)->GetData());
     m_BtnReinit3DInterpolation->setEnabled(true);
     try {
       if (m_SegmentationObserverTags.find(labelSetImage) == m_SegmentationObserverTags.end())
       {
         auto command2 = itk::MemberCommand<QmitkSlicesInterpolator>::New();
         command2->SetCallbackFunction(this, &QmitkSlicesInterpolator::OnModifyLabelChanged);
         auto workingImage = dynamic_cast<mitk::LabelSetImage *>(m_ToolManager->GetWorkingData(0)->GetData());
         m_SegmentationObserverTags[workingImage] = workingImage->AddObserver(itk::ModifiedEvent(), command2);
       }
     }
     catch (const std::exception& e)
     {
       MITK_ERROR << "Error casting node data to LabelSetImage\n";
     }
   }
   else
   {
     // If no workingdata is set, remove the interpolation feedback
     this->GetDataStorage()->Remove(m_FeedbackNode);
     m_FeedbackNode->SetData(nullptr);
     this->GetDataStorage()->Remove(m_3DContourNode);
     m_3DContourNode->SetData(nullptr);
     this->GetDataStorage()->Remove(m_InterpolatedSurfaceNode);
     m_InterpolatedSurfaceNode->SetData(nullptr);
     m_BtnReinit3DInterpolation->setEnabled(false);
     m_CmbInterpolation->setCurrentIndex(0);
     return;
 
   }
   // Updating the current selected segmentation for the 3D interpolation
   this->SetCurrentContourListID();
 
   if (m_2DInterpolationEnabled)
   {
     OnInterpolationActivated(true); // re-initialize if needed
   }
 }
 
 void QmitkSlicesInterpolator::OnToolManagerReferenceDataModified()
 {
 }
 
 void QmitkSlicesInterpolator::OnTimeChanged(itk::Object *sender, const itk::EventObject &e)
 {
   // Check if we really have a GeometryTimeEvent
   if (!dynamic_cast<const mitk::SliceNavigationController::GeometryTimeEvent *>(&e))
     return;
 
   mitk::SliceNavigationController *slicer = dynamic_cast<mitk::SliceNavigationController *>(sender);
   Q_ASSERT(slicer);
 
   const auto timePoint = slicer->GetSelectedTimePoint();
   m_TimePoints[slicer] = timePoint;
 
   if (m_Watcher.isRunning())
     m_Watcher.waitForFinished();
 
   if (timePoint != m_SurfaceInterpolator->GetCurrentTimePoint())
   {
     m_SurfaceInterpolator->SetCurrentTimePoint(timePoint);
     if (m_3DInterpolationEnabled)
     {
       m_3DContourNode->SetData(nullptr);
       m_InterpolatedSurfaceNode->SetData(nullptr);
     }
     m_SurfaceInterpolator->Modified();
   }
 
   if (m_LastSNC == slicer)
   {
     slicer->SendSlice(); // will trigger a new interpolation
   }
 }
 
 void QmitkSlicesInterpolator::OnSliceChanged(itk::Object *sender, const itk::EventObject &e)
 {
   // Check whether we really have a GeometrySliceEvent
   if (!dynamic_cast<const mitk::SliceNavigationController::GeometrySliceEvent *>(&e))
     return;
 
   mitk::SliceNavigationController *slicer = dynamic_cast<mitk::SliceNavigationController *>(sender);
 
   if(m_2DInterpolationEnabled)
   {
     this->On2DInterpolationEnabled(m_2DInterpolationEnabled);
   }
   if (TranslateAndInterpolateChangedSlice(e, slicer))
   {
     slicer->GetRenderer()->RequestUpdate();
   }
 }
 
 bool QmitkSlicesInterpolator::TranslateAndInterpolateChangedSlice(const itk::EventObject &e,
                                                                   mitk::SliceNavigationController *slicer)
 {
   if (!m_2DInterpolationEnabled)
     return false;
 
   try
   {
     const mitk::SliceNavigationController::GeometrySliceEvent &event =
       dynamic_cast<const mitk::SliceNavigationController::GeometrySliceEvent &>(e);
 
     mitk::TimeGeometry *tsg = event.GetTimeGeometry();
     if (tsg && m_TimePoints.contains(slicer) && tsg->IsValidTimePoint(m_TimePoints[slicer]))
     {
       mitk::SlicedGeometry3D *slicedGeometry =
         dynamic_cast<mitk::SlicedGeometry3D *>(tsg->GetGeometryForTimePoint(m_TimePoints[slicer]).GetPointer());
 
       if (slicedGeometry)
       {
         m_LastSNC = slicer;
         mitk::PlaneGeometry *plane =
           dynamic_cast<mitk::PlaneGeometry *>(slicedGeometry->GetPlaneGeometry(event.GetPos()));
         if (plane)
         {
           Interpolate(plane, m_TimePoints[slicer], slicer);
         }
         return true;
       }
     }
   }
   catch (const std::bad_cast &)
   {
     return false; // so what
   }
 
   return false;
 }
 
 void QmitkSlicesInterpolator::OnLayerChanged()
 {
   auto* workingNode = m_ToolManager->GetWorkingData(0);
 
   if (workingNode != nullptr)
   {
     m_3DContourNode->SetData(nullptr);
     this->Show3DInterpolationResult(false);
   }
 
   if (m_3DInterpolationEnabled)
   {
     m_SurfaceInterpolator->Modified();
   }
   if (m_2DInterpolationEnabled)
   {
     m_FeedbackNode->SetData(nullptr);
     this->OnInterpolationActivated(true);
     m_LastSNC->SendSlice();
   }
   mitk::RenderingManager::GetInstance()->RequestUpdateAll();
   this->UpdateVisibleSuggestion();
 }
 
 void QmitkSlicesInterpolator::Interpolate(mitk::PlaneGeometry *plane,
                                           mitk::TimePointType timePoint,
                                           mitk::SliceNavigationController *slicer)
 {
   if (m_ToolManager)
   {
     mitk::DataNode *node = m_ToolManager->GetWorkingData(0);
     if (node)
     {
       m_Segmentation = dynamic_cast<mitk::Image *>(node->GetData());
 
       if (m_Segmentation)
       {
         if (!m_Segmentation->GetTimeGeometry()->IsValidTimePoint(timePoint))
         {
           MITK_WARN << "Cannot interpolate segmentation. Passed time point is not within the time bounds of WorkingImage. Time point: " << timePoint;
           return;
         }
         const auto timeStep = m_Segmentation->GetTimeGeometry()->TimePointToTimeStep(timePoint);
 
         int clickedSliceDimension = -1;
         int clickedSliceIndex = -1;
 
         // calculate real slice position, i.e. slice of the image
         mitk::SegTool2D::DetermineAffectedImageSlice(m_Segmentation, plane, clickedSliceDimension, clickedSliceIndex);
 
         mitk::Image::Pointer interpolation =
           m_Interpolator->Interpolate(clickedSliceDimension, clickedSliceIndex, plane, timeStep);
         m_FeedbackNode->SetData(interpolation);
 
         //  maybe just have a variable that stores the active label color.
         if (m_ToolManager)
         {
           auto* workingNode = m_ToolManager->GetWorkingData(0);
           if (workingNode != nullptr)
           {
             auto activeColor = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData())->GetActiveLabelSet()->GetActiveLabel()->GetColor();
             m_FeedbackNode->SetProperty("color", mitk::ColorProperty::New(activeColor));
           }
         }
 
         m_LastSNC = slicer;
         m_LastSliceIndex = clickedSliceIndex;
       }
     }
   }
 }
 
 void QmitkSlicesInterpolator::OnSurfaceInterpolationFinished()
 {
   mitk::Surface::Pointer interpolatedSurface = m_SurfaceInterpolator->GetInterpolationResult();
 
   mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0);
 
   mitk::PlaneGeometry::Pointer slicingPlane = mitk::PlaneGeometry::New();
   mitk::Vector3D slicingPlaneNormalVector;
   FillVector3D(slicingPlaneNormalVector,0.0,1.0,0.0);
   mitk::Point3D origin;
   FillVector3D(origin, 0.0, 0.0, 0.0);
   slicingPlane->InitializePlane(origin, slicingPlaneNormalVector);
 
   if (interpolatedSurface.IsNotNull() && workingNode)
   {
     m_BtnApply3D->setEnabled(true);
 
     // T28261
     // m_BtnSuggestPlane->setEnabled(true);
 
     m_InterpolatedSurfaceNode->SetData(interpolatedSurface);
 
     m_3DContourNode->SetData(m_SurfaceInterpolator->GetContoursAsSurface());
 
     this->Show3DInterpolationResult(true);
 
     if (!m_DataStorage->Exists(m_InterpolatedSurfaceNode))
     {
       m_DataStorage->Add(m_InterpolatedSurfaceNode);
     }
   }
   else if (interpolatedSurface.IsNull())
   {
     m_BtnApply3D->setEnabled(false);
 
     // T28261
     // m_BtnSuggestPlane->setEnabled(false);
 
     if (m_DataStorage->Exists(m_InterpolatedSurfaceNode))
     {
       this->Show3DInterpolationResult(false);
     }
   }
 
   m_BtnReinit3DInterpolation->setEnabled(true);
 
   for (auto* slicer : m_ControllerToTimeObserverTag.keys())
   {
     slicer->GetRenderer()->RequestUpdate();
   }
   m_SurfaceInterpolator->ReinitializeInterpolation();
 }
 
 void QmitkSlicesInterpolator::OnAcceptInterpolationClicked()
 {
   if (m_Segmentation && m_FeedbackNode->GetData())
   {
     // Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk
     // reslicer
     vtkSmartPointer<mitkVtkImageOverwrite> reslice = vtkSmartPointer<mitkVtkImageOverwrite>::New();
 
     // Set slice as input
     mitk::Image::Pointer slice = dynamic_cast<mitk::Image *>(m_FeedbackNode->GetData());
     reslice->SetInputSlice(slice->GetSliceData()->GetVtkImageAccessor(slice)->GetVtkImageData());
     // set overwrite mode to true to write back to the image volume
     reslice->SetOverwriteMode(true);
     reslice->Modified();
 
     const auto timePoint = m_LastSNC->GetSelectedTimePoint();
     if (!m_Segmentation->GetTimeGeometry()->IsValidTimePoint(timePoint))
     {
       MITK_WARN << "Cannot accept interpolation. Time point selected by SliceNavigationController is not within the time bounds of segmentation. Time point: " << timePoint;
       return;
     }
 
 
     mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice);
     extractor->SetInput(m_Segmentation);
     const auto timeStep = m_Segmentation->GetTimeGeometry()->TimePointToTimeStep(timePoint);
     extractor->SetTimeStep(timeStep);
     extractor->SetWorldGeometry(m_LastSNC->GetCurrentPlaneGeometry());
     extractor->SetVtkOutputRequest(true);
     extractor->SetResliceTransformByGeometry(m_Segmentation->GetTimeGeometry()->GetGeometryForTimeStep(timeStep));
     extractor->Modified();
     extractor->Update();
 
     // the image was modified within the pipeline, but not marked so
     m_Segmentation->Modified();
     m_Segmentation->GetVtkImageData()->Modified();
 
     m_FeedbackNode->SetData(nullptr);
     mitk::RenderingManager::GetInstance()->RequestUpdateAll();
   }
 }
 
 void QmitkSlicesInterpolator::AcceptAllInterpolations(mitk::SliceNavigationController *slicer)
 {
   /*
    * What exactly is done here:
    * 1. We create an empty diff image for the current segmentation
    * 2. All interpolated slices are written into the diff image
    * 3. Then the diffimage is applied to the original segmentation
    */
   if (m_Segmentation)
   {
     mitk::Image::Pointer segmentation3D = m_Segmentation;
     unsigned int timeStep = 0;
     const auto timePoint = slicer->GetSelectedTimePoint();
 
     if (4 == m_Segmentation->GetDimension())
     {
       const auto* geometry = m_Segmentation->GetTimeGeometry();
 
       if (!geometry->IsValidTimePoint(timePoint))
       {
         MITK_WARN << "Cannot accept all interpolations. Time point selected by passed SliceNavigationController is not within the time bounds of segmentation. Time point: " << timePoint;
         return;
       }
 
       mitk::Image::Pointer activeLabelImage;
       try
       {
         auto labelSetImage = dynamic_cast<mitk::LabelSetImage *>(m_Segmentation);
         activeLabelImage = labelSetImage->CreateLabelMask(labelSetImage->GetActiveLabelSet()->GetActiveLabel()->GetValue(), true, 0);
       }
       catch (const std::exception& e)
       {
         MITK_ERROR << e.what() << " | NO LABELSETIMAGE IN WORKING NODE\n";
       }
 
       m_Interpolator->SetSegmentationVolume(activeLabelImage);
 
       timeStep = geometry->TimePointToTimeStep(timePoint);
 
       auto timeSelector = mitk::ImageTimeSelector::New();
       timeSelector->SetInput(m_Segmentation);
       timeSelector->SetTimeNr(timeStep);
       timeSelector->Update();
 
       segmentation3D = timeSelector->GetOutput();
     }
 
     // Create an empty diff image for the undo operation
     auto diffImage = mitk::Image::New();
     diffImage->Initialize(segmentation3D);
 
     // Create scope for ImageWriteAccessor so that the accessor is destroyed right after use
     {
       mitk::ImageWriteAccessor accessor(diffImage);
 
       // Set all pixels to zero
       auto pixelType = mitk::MakeScalarPixelType<mitk::Tool::DefaultSegmentationDataType>();
 
       // For legacy purpose support former pixel type of segmentations (before multilabel)
       if (itk::IOComponentEnum::UCHAR == m_Segmentation->GetImageDescriptor()->GetChannelDescriptor().GetPixelType().GetComponentType())
         pixelType = mitk::MakeScalarPixelType<unsigned char>();
 
       memset(accessor.GetData(), 0, pixelType.GetSize() * diffImage->GetDimension(0) * diffImage->GetDimension(1) * diffImage->GetDimension(2));
     }
 
     // Since we need to shift the plane it must be clone so that the original plane isn't altered
     auto slicedGeometry = m_Segmentation->GetSlicedGeometry();
     auto planeGeometry = slicer->GetCurrentPlaneGeometry()->Clone();
     int sliceDimension = -1;
     int sliceIndex = -1;
 
     mitk::SegTool2D::DetermineAffectedImageSlice(m_Segmentation, planeGeometry, sliceDimension, sliceIndex);
 
     const auto numSlices = m_Segmentation->GetDimension(sliceDimension);
     mitk::ProgressBar::GetInstance()->AddStepsToDo(numSlices);
 
     std::atomic_uint totalChangedSlices;
 
     // Reuse interpolation algorithm instance for each slice to cache boundary calculations
     auto algorithm = mitk::ShapeBasedInterpolationAlgorithm::New();
 
     // Distribute slice interpolations to multiple threads
     const auto numThreads = std::min(std::thread::hardware_concurrency(), numSlices);
     // const auto numThreads = 1;
     std::vector<std::vector<unsigned int>> sliceIndices(numThreads);
 
     for (std::remove_const_t<decltype(numSlices)> sliceIndex = 0; sliceIndex < numSlices; ++sliceIndex)
       sliceIndices[sliceIndex % numThreads].push_back(sliceIndex);
 
     std::vector<std::thread> threads;
     threads.reserve(numThreads);
 
     // This lambda will be executed by the threads
     auto interpolate = [=, &interpolator = m_Interpolator, &totalChangedSlices](unsigned int threadIndex)
     {
       auto clonedPlaneGeometry = planeGeometry->Clone();
       auto origin = clonedPlaneGeometry->GetOrigin();
 
       //  Go through the sliced indices
       for (auto sliceIndex : sliceIndices[threadIndex])
       {
         slicedGeometry->WorldToIndex(origin, origin);
         origin[sliceDimension] = sliceIndex;
         slicedGeometry->IndexToWorld(origin, origin);
         clonedPlaneGeometry->SetOrigin(origin);
 
         auto interpolation = interpolator->Interpolate(sliceDimension, sliceIndex, clonedPlaneGeometry, timeStep, algorithm);
 
         if (interpolation.IsNotNull())
         {
           // Setting up the reslicing pipeline which allows us to write the interpolation results back into the image volume
           auto reslicer = vtkSmartPointer<mitkVtkImageOverwrite>::New();
 
           // Set overwrite mode to true to write back to the image volume
           reslicer->SetInputSlice(interpolation->GetSliceData()->GetVtkImageAccessor(interpolation)->GetVtkImageData());
           reslicer->SetOverwriteMode(true);
           reslicer->Modified();
 
           auto diffSliceWriter = mitk::ExtractSliceFilter::New(reslicer);
 
           diffSliceWriter->SetInput(diffImage);
           diffSliceWriter->SetTimeStep(0);
           diffSliceWriter->SetWorldGeometry(clonedPlaneGeometry);
           diffSliceWriter->SetVtkOutputRequest(true);
           diffSliceWriter->SetResliceTransformByGeometry(diffImage->GetTimeGeometry()->GetGeometryForTimeStep(0));
           diffSliceWriter->Modified();
           diffSliceWriter->Update();
 
           ++totalChangedSlices;
         }
 
         mitk::ProgressBar::GetInstance()->Progress();
       }
     };
     m_Interpolator->EnableSliceImageCache();
 
     //  Do the interpolation here.
     for (size_t threadIndex = 0; threadIndex < numThreads; ++threadIndex)
     {
       interpolate(threadIndex);
     }
 
     m_Interpolator->DisableSliceImageCache();
 
     const mitk::Label::PixelType newDestinationLabel = dynamic_cast<mitk::LabelSetImage *>(m_Segmentation)->GetActiveLabelSet()->GetActiveLabel()->GetValue();
 
     //  Do and Undo Operations
     if (totalChangedSlices > 0)
     {
       // Create do/undo operations
       auto* doOp = new mitk::ApplyDiffImageOperation(mitk::OpTEST, m_Segmentation, diffImage, timeStep);
 
       auto* undoOp = new mitk::ApplyDiffImageOperation(mitk::OpTEST, m_Segmentation, diffImage, timeStep);
       undoOp->SetFactor(-1.0);
 
       auto comment = "Confirm all interpolations (" + std::to_string(totalChangedSlices) + ")";
       auto* undoStackItem = new mitk::OperationEvent(mitk::DiffImageApplier::GetInstanceForUndo(), doOp, undoOp, comment);
 
       mitk::OperationEvent::IncCurrGroupEventId();
       mitk::OperationEvent::IncCurrObjectEventId();
       mitk::UndoController::GetCurrentUndoModel()->SetOperationEvent(undoStackItem);
       mitk::DiffImageApplier::GetInstanceForUndo()->SetDestinationLabel(newDestinationLabel);
       // Apply the changes to the original image
       mitk::DiffImageApplier::GetInstanceForUndo()->ExecuteOperation(doOp);
     }
     m_FeedbackNode->SetData(nullptr);
   }
 
   mitk::RenderingManager::GetInstance()->RequestUpdateAll();
 }
 
 void QmitkSlicesInterpolator::FinishInterpolation(mitk::SliceNavigationController *slicer)
 {
   // this redirect is for calling from outside
   if (slicer == nullptr)
     OnAcceptAllInterpolationsClicked();
   else
     AcceptAllInterpolations(slicer);
 }
 
 void QmitkSlicesInterpolator::OnAcceptAllInterpolationsClicked()
 {
   QMenu orientationPopup(this);
   for (auto it = m_ActionToSlicer.begin(); it != m_ActionToSlicer.end(); ++it)
     orientationPopup.addAction(it->first);
 
   connect(&orientationPopup, SIGNAL(triggered(QAction *)), this, SLOT(OnAcceptAllPopupActivated(QAction *)));
   orientationPopup.exec(QCursor::pos());
 }
 
 void QmitkSlicesInterpolator::OnAccept3DInterpolationClicked()
 {
   auto referenceImage = GetData<mitk::Image>(m_ToolManager->GetReferenceData(0));
 
   auto* segmentationDataNode = m_ToolManager->GetWorkingData(0);
 
   auto labelSetImage = dynamic_cast<mitk::LabelSetImage*>(segmentationDataNode->GetData());
   auto activeLabelColor = labelSetImage->GetActiveLabelSet()->GetActiveLabel()->GetColor();
   std::string activeLabelName = labelSetImage->GetActiveLabelSet()->GetActiveLabel()->GetName();
 
   auto segmentation = GetData<mitk::Image>(segmentationDataNode);
 
   if (referenceImage.IsNull() || segmentation.IsNull())
     return;
 
   const auto* segmentationGeometry = segmentation->GetTimeGeometry();
   const auto timePoint = m_LastSNC->GetSelectedTimePoint();
 
   if (!referenceImage->GetTimeGeometry()->IsValidTimePoint(timePoint) ||
       !segmentationGeometry->IsValidTimePoint(timePoint))
   {
     MITK_WARN << "Cannot accept interpolation. Current time point is not within the time bounds of the patient image and segmentation.";
     return;
   }
 
   auto interpolatedSurface = GetData<mitk::Surface>(m_InterpolatedSurfaceNode);
 
   if (interpolatedSurface.IsNull())
     return;
 
   auto surfaceToImageFilter = mitk::SurfaceToImageFilter::New();
 
   surfaceToImageFilter->SetImage(referenceImage);
   surfaceToImageFilter->SetMakeOutputBinary(true);
   surfaceToImageFilter->SetUShortBinaryPixelType(itk::IOComponentEnum::USHORT == segmentation->GetPixelType().GetComponentType());
   surfaceToImageFilter->SetInput(interpolatedSurface);
   surfaceToImageFilter->Update();
 
   mitk::Image::Pointer interpolatedSegmentation = surfaceToImageFilter->GetOutput();
   auto timeStep = segmentationGeometry->TimePointToTimeStep(timePoint);
   const mitk::Label::PixelType newDestinationLabel = labelSetImage->GetActiveLabelSet()->GetActiveLabel()->GetValue();
 
-  TransferLabelContent(
+  TransferLabelContentAtTimeStep(
     interpolatedSegmentation,
     labelSetImage,
     labelSetImage->GetActiveLabelSet(),
+    timeStep,
     0,
     0,
     false,
     {{1, newDestinationLabel}},
     mitk::MultiLabelSegmentation::MergeStyle::Merge,
-    mitk::MultiLabelSegmentation::OverwriteStyle::RegardLocks,
-    timeStep);
+    mitk::MultiLabelSegmentation::OverwriteStyle::RegardLocks);
 
   // m_CmbInterpolation->setCurrentIndex(0);
   this->Show3DInterpolationResult(false);
 
   std::string name = segmentationDataNode->GetName() + " 3D-interpolation - " + activeLabelName;
   mitk::TimeBounds timeBounds;
 
   if (1 < interpolatedSurface->GetTimeSteps())
   {
     name += "_t" + std::to_string(timeStep);
 
     auto* polyData = vtkPolyData::New();
     polyData->DeepCopy(interpolatedSurface->GetVtkPolyData(timeStep));
 
     auto surface = mitk::Surface::New();
     surface->SetVtkPolyData(polyData);
 
     interpolatedSurface = surface;
     timeBounds = segmentationGeometry->GetTimeBounds(timeStep);
   }
   else
   {
     timeBounds = segmentationGeometry->GetTimeBounds(0);
   }
 
   auto* surfaceGeometry = static_cast<mitk::ProportionalTimeGeometry*>(interpolatedSurface->GetTimeGeometry());
   surfaceGeometry->SetFirstTimePoint(timeBounds[0]);
   surfaceGeometry->SetStepDuration(timeBounds[1] - timeBounds[0]);
 
   // Typical file formats for surfaces do not save any time-related information. As a workaround at least for MITK scene files, we have the
   // possibility to seralize this information as properties.
 
   interpolatedSurface->SetProperty("ProportionalTimeGeometry.FirstTimePoint", mitk::FloatProperty::New(surfaceGeometry->GetFirstTimePoint()));
   interpolatedSurface->SetProperty("ProportionalTimeGeometry.StepDuration", mitk::FloatProperty::New(surfaceGeometry->GetStepDuration()));
 
   auto interpolatedSurfaceDataNode = mitk::DataNode::New();
 
   interpolatedSurfaceDataNode->SetData(interpolatedSurface);
   interpolatedSurfaceDataNode->SetName(name);
   interpolatedSurfaceDataNode->SetOpacity(0.7f);
 
   interpolatedSurfaceDataNode->SetColor(activeLabelColor);
   m_DataStorage->Add(interpolatedSurfaceDataNode, segmentationDataNode);
 }
 
 
 void QmitkSlicesInterpolator::OnReinit3DInterpolation()
 {
   //  Step 1. Load from the isContourPlaneGeometry nodes the contourNodes.
   mitk::NodePredicateProperty::Pointer pred =
     mitk::NodePredicateProperty::New("isContourPlaneGeometry", mitk::BoolProperty::New(true));
   mitk::DataStorage::SetOfObjects::ConstPointer contourNodes =
     m_DataStorage->GetDerivations(m_ToolManager->GetWorkingData(0), pred);
 
   if (contourNodes->Size() != 0)
   {
     std::vector<const mitk::PlaneGeometry*> contourPlanes;
     std::vector<mitk::Surface::Pointer> contourList;
     if (m_ToolManager->GetWorkingData(0) != nullptr)
     {
       try
       {
         auto labelSetImage = dynamic_cast<mitk::LabelSetImage *>(m_ToolManager->GetWorkingData(0)->GetData());
         auto activeLayerID = labelSetImage->GetActiveLayer();
         const auto timePoint = m_LastSNC->GetSelectedTimePoint();
         if (!labelSetImage->GetTimeGeometry()->IsValidTimePoint(timePoint))
         {
           MITK_ERROR << "Invalid time point requested for interpolation pipeline.";
           return;
         }
 
         //  Adding layer, label and timeStep information for the contourNodes.
         for (auto it = contourNodes->Begin(); it != contourNodes->End(); ++it)
         {
           auto contourNode = it->Value();
           auto layerID = dynamic_cast<mitk::UIntProperty *>(contourNode->GetProperty("layerID"))->GetValue();
           auto labelID = dynamic_cast<mitk::UShortProperty *>(contourNode->GetProperty("labelID"))->GetValue();
           auto timeStep = dynamic_cast<mitk::IntProperty *>(contourNode->GetProperty("timeStep"))->GetValue();
 
           auto px = dynamic_cast<mitk::DoubleProperty *>(contourNode->GetProperty("px"))->GetValue();
           auto py = dynamic_cast<mitk::DoubleProperty *>(contourNode->GetProperty("py"))->GetValue();
           auto pz = dynamic_cast<mitk::DoubleProperty *>(contourNode->GetProperty("pz"))->GetValue();
 
           // auto layerImage = labelSetImage->GetLayerImage(layerID);
 
           auto planeGeometry = dynamic_cast<mitk::PlanarFigure *>(contourNode->GetData())->GetPlaneGeometry();
           labelSetImage->SetActiveLayer(layerID);
           auto sliceImage = ExtractSliceFromImage(labelSetImage, planeGeometry, timeStep);
           labelSetImage->SetActiveLayer(activeLayerID);
           mitk::ImageToContourFilter::Pointer contourExtractor = mitk::ImageToContourFilter::New();
           contourExtractor->SetInput(sliceImage);
           contourExtractor->SetContourValue(labelID);
           contourExtractor->Update();
           mitk::Surface::Pointer contour = contourExtractor->GetOutput();
 
           if (contour->GetVtkPolyData()->GetNumberOfPoints() == 0)
             continue;
 
           vtkSmartPointer<vtkIntArray> intArray = vtkSmartPointer<vtkIntArray>::New();
           intArray->InsertNextValue(labelID);
           intArray->InsertNextValue(layerID);
           intArray->InsertNextValue(timeStep);
           contour->GetVtkPolyData()->GetFieldData()->AddArray(intArray);
           vtkSmartPointer<vtkDoubleArray> doubleArray = vtkSmartPointer<vtkDoubleArray>::New();
           doubleArray->InsertNextValue(px);
           doubleArray->InsertNextValue(py);
           doubleArray->InsertNextValue(pz);
           contour->GetVtkPolyData()->GetFieldData()->AddArray(doubleArray);
           contour->DisconnectPipeline();
           contourList.push_back(contour);
           contourPlanes.push_back(planeGeometry);
         }
         labelSetImage->SetActiveLayer(activeLayerID);
         // size_t activeLayer = labelSetImage->GetActiveLayer();
         for (size_t l = 0; l < labelSetImage->GetNumberOfLayers(); ++l)
         {
           this->OnAddLabelSetConnection(l);
         }
         // labelSetImage->SetActiveLayer(activeLayer);
         m_SurfaceInterpolator->CompleteReinitialization(contourList, contourPlanes);
       }
       catch(const std::exception& e)
       {
         MITK_ERROR << "Exception thrown casting toolmanager working data to labelsetImage";
       }
     }
   }
   else
   {
     m_BtnApply3D->setEnabled(false);
     QMessageBox errorInfo;
     errorInfo.setWindowTitle("Reinitialize surface interpolation");
     errorInfo.setIcon(QMessageBox::Information);
     errorInfo.setText("No contours available for the selected segmentation!");
     errorInfo.exec();
   }
 }
 
 void QmitkSlicesInterpolator::OnAcceptAllPopupActivated(QAction *action)
 {
   try
   {
     auto iter = m_ActionToSlicer.find(action);
     if (iter != m_ActionToSlicer.end())
     {
       mitk::SliceNavigationController *slicer = iter->second;
       AcceptAllInterpolations(slicer);
     }
   }
   catch (...)
   {
     /* Showing message box with possible memory error */
     QMessageBox errorInfo;
     errorInfo.setWindowTitle("Interpolation Process");
     errorInfo.setIcon(QMessageBox::Critical);
     errorInfo.setText("An error occurred during interpolation. Possible cause: Not enough memory!");
     errorInfo.exec();
 
     // additional error message on std::cerr
     std::cerr << "Ill construction in " __FILE__ " l. " << __LINE__ << std::endl;
   }
 }
 
 void QmitkSlicesInterpolator::OnInterpolationActivated(bool on)
 {
   m_2DInterpolationEnabled = on;
 
   try
   {
     if (m_DataStorage.IsNotNull())
     {
       if (on && !m_DataStorage->Exists(m_FeedbackNode))
       {
         m_DataStorage->Add(m_FeedbackNode);
       }
     }
   }
   catch (...)
   {
     // don't care (double add/remove)
   }
 
   if (m_ToolManager)
   {
     mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0);
     mitk::DataNode *referenceNode = m_ToolManager->GetReferenceData(0);
     QWidget::setEnabled(workingNode != nullptr);
 
     m_BtnApply2D->setEnabled(on);
     m_FeedbackNode->SetVisibility(on);
 
     if (!on)
     {
       mitk::RenderingManager::GetInstance()->RequestUpdateAll();
       return;
     }
 
     if (workingNode)
     {
       mitk::Image *segmentation = dynamic_cast<mitk::Image *>(workingNode->GetData());
 
       mitk::Image::Pointer activeLabelImage;
       try
       {
         auto labelSetImage = dynamic_cast<mitk::LabelSetImage *>(workingNode->GetData());
         activeLabelImage = labelSetImage->CreateLabelMask(labelSetImage->GetActiveLabelSet()->GetActiveLabel()->GetValue(), true, 0);
       }
       catch (const std::exception& e)
       {
         MITK_ERROR << e.what() << " | NO LABELSETIMAGE IN WORKING NODE\n";
       }
 
       if (segmentation)
       {
         m_Interpolator->SetSegmentationVolume(activeLabelImage);
 
         if (referenceNode)
         {
           mitk::Image *referenceImage = dynamic_cast<mitk::Image *>(referenceNode->GetData());
           m_Interpolator->SetReferenceVolume(referenceImage); // may be nullptr
         }
       }
     }
   }
   this->UpdateVisibleSuggestion();
 }
 
 void QmitkSlicesInterpolator::Run3DInterpolation()
 {
   m_SurfaceInterpolator->Interpolate();
 }
 
 void QmitkSlicesInterpolator::StartUpdateInterpolationTimer()
 {
   m_Timer->start(500);
 }
 
 void QmitkSlicesInterpolator::StopUpdateInterpolationTimer()
 {
   if(m_ToolManager)
   {
     auto* workingNode = m_ToolManager->GetWorkingData(0);
     auto activeColor = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData())->GetActiveLabelSet()->GetActiveLabel()->GetColor();
     m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(activeColor));
     m_3DContourNode->SetProperty("color", mitk::ColorProperty::New(activeColor));
   }
 
   m_Timer->stop();
 }
 
 void QmitkSlicesInterpolator::ChangeSurfaceColor()
 {
   float currentColor[3];
   m_InterpolatedSurfaceNode->GetColor(currentColor);
 
     m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(SURFACE_COLOR_RGB));
   m_InterpolatedSurfaceNode->Update();
 
   mitk::RenderingManager::GetInstance()->RequestUpdateAll(mitk::RenderingManager::REQUEST_UPDATE_3DWINDOWS);
 }
 
 void QmitkSlicesInterpolator::PrepareInputsFor3DInterpolation()
 {
   if (m_DataStorage.IsNotNull() && m_ToolManager && m_3DInterpolationEnabled)
   {
     auto *workingNode = m_ToolManager->GetWorkingData(0);
     if (workingNode != nullptr)
     {
       int ret = QMessageBox::Yes;
 
       if (m_SurfaceInterpolator->EstimatePortionOfNeededMemory() > 0.5)
       {
         QMessageBox msgBox;
         msgBox.setText("Due to short handed system memory the 3D interpolation may be very slow!");
         msgBox.setInformativeText("Are you sure you want to activate the 3D interpolation?");
         msgBox.setStandardButtons(QMessageBox::No | QMessageBox::Yes);
         ret = msgBox.exec();
       }
 
       auto labelSetImage = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData());
       auto activeLabel = labelSetImage->GetActiveLabelSet()->GetActiveLabel()->GetValue();
 
       m_SurfaceInterpolator->AddActiveLabelContoursForInterpolation(activeLabel);
 
       if (m_Watcher.isRunning())
         m_Watcher.waitForFinished();
 
       if (ret == QMessageBox::Yes)
       {
         //  Maybe set the segmentation node here
         m_Future = QtConcurrent::run(this, &QmitkSlicesInterpolator::Run3DInterpolation);
         m_Watcher.setFuture(m_Future);
       }
       else
       {
         m_CmbInterpolation->setCurrentIndex(0);
       }
     }
     else
     {
       QWidget::setEnabled(false);
       m_ChkShowPositionNodes->setEnabled(m_3DInterpolationEnabled);
     }
 
   }
   if (!m_3DInterpolationEnabled)
   {
     this->Show3DInterpolationResult(false);
     m_BtnApply3D->setEnabled(m_3DInterpolationEnabled);
     // T28261
     // m_BtnSuggestPlane->setEnabled(m_3DInterpolationEnabled);
   }
 
   mitk::RenderingManager::GetInstance()->RequestUpdateAll();
 }
 
 void QmitkSlicesInterpolator::On3DInterpolationActivated(bool on)
 {
   m_3DInterpolationEnabled = on;
   try
   {
     // this->PrepareInputsFor3DInterpolation();
     m_SurfaceInterpolator->Modified();
   }
   catch (...)
   {
     MITK_ERROR << "Error with 3D surface interpolation!";
   }
   mitk::RenderingManager::GetInstance()->RequestUpdateAll();
 }
 
 void QmitkSlicesInterpolator::EnableInterpolation(bool on)
 {
   // only to be called from the outside world
   // just a redirection to OnInterpolationActivated
   OnInterpolationActivated(on);
 }
 
 void QmitkSlicesInterpolator::Enable3DInterpolation(bool on)
 {
   // only to be called from the outside world
   // just a redirection to OnInterpolationActivated
   this->On3DInterpolationActivated(on);
 }
 
 void QmitkSlicesInterpolator::UpdateVisibleSuggestion()
 {
   mitk::RenderingManager::GetInstance()->RequestUpdateAll();
 }
 
 void QmitkSlicesInterpolator::OnInterpolationInfoChanged(const itk::EventObject & /*e*/)
 {
   // something (e.g. undo) changed the interpolation info, we should refresh our display
   this->UpdateVisibleSuggestion();
 }
 
 void QmitkSlicesInterpolator::OnInterpolationAborted(const itk::EventObject& /*e*/)
 {
   m_CmbInterpolation->setCurrentIndex(0);
   m_FeedbackNode->SetData(nullptr);
 }
 
 void QmitkSlicesInterpolator::OnSurfaceInterpolationInfoChanged(const itk::EventObject & /*e*/)
 {
   if (m_Watcher.isRunning())
     m_Watcher.waitForFinished();
 
   if (m_3DInterpolationEnabled)
   {
     m_3DContourNode->SetData(nullptr);
     m_InterpolatedSurfaceNode->SetData(nullptr);
     auto *workingNode = m_ToolManager->GetWorkingData(0);
     auto labelSetImage = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData());
     auto activeLabel = labelSetImage->GetActiveLabelSet()->GetActiveLabel()->GetValue();
     m_SurfaceInterpolator->AddActiveLabelContoursForInterpolation(activeLabel);
     m_Future = QtConcurrent::run(this, &QmitkSlicesInterpolator::Run3DInterpolation);
     m_Watcher.setFuture(m_Future);
   }
 }
 
 void QmitkSlicesInterpolator::SetCurrentContourListID()
 {
   // New ContourList = hide current interpolation
   Show3DInterpolationResult(false);
 
   if (m_DataStorage.IsNotNull() && m_ToolManager && m_LastSNC)
   {
     mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0);
 
     try{
         auto labelSetImage = dynamic_cast<mitk::LabelSetImage *>(workingNode->GetData());
         for (size_t layerID = 0; layerID < labelSetImage->GetNumberOfLayers(); ++layerID)
         {
           this->OnAddLabelSetConnection(layerID);
         }
     }
     catch (std::exception &e)
     {
       MITK_ERROR << e.what() << "\n";
     }
 
     if (workingNode)
     {
       QWidget::setEnabled(true);
 
       const auto timePoint = m_LastSNC->GetSelectedTimePoint();
       // In case the time is not valid use 0 to access the time geometry of the working node
       unsigned int time_position = 0;
       if (!workingNode->GetData()->GetTimeGeometry()->IsValidTimePoint(timePoint))
       {
         MITK_WARN << "Cannot accept interpolation. Time point selected by SliceNavigationController is not within the time bounds of WorkingImage. Time point: " << timePoint;
         return;
       }
 
       //  Sets up the surface interpolator to accept
       time_position = workingNode->GetData()->GetTimeGeometry()->TimePointToTimeStep(timePoint);
 
       mitk::Vector3D spacing = workingNode->GetData()->GetGeometry(time_position)->GetSpacing();
       double minSpacing = 100;
       double maxSpacing = 0;
       for (int i = 0; i < 3; i++)
       {
         if (spacing[i] < minSpacing)
         {
           minSpacing = spacing[i];
         }
         if (spacing[i] > maxSpacing)
         {
           maxSpacing = spacing[i];
         }
       }
 
       m_SurfaceInterpolator->SetMaxSpacing(maxSpacing);
       m_SurfaceInterpolator->SetMinSpacing(minSpacing);
       m_SurfaceInterpolator->SetDistanceImageVolume(50000);
 
       mitk::Image::Pointer segmentationImage;
 
       segmentationImage = dynamic_cast<mitk::Image *>(workingNode->GetData());
       m_SurfaceInterpolator->SetCurrentInterpolationSession(segmentationImage);
       m_SurfaceInterpolator->SetCurrentTimePoint(timePoint);
     }
     else
     {
       QWidget::setEnabled(false);
     }
   }
 }
 
 void QmitkSlicesInterpolator::Show3DInterpolationResult(bool status)
 {
   if (m_InterpolatedSurfaceNode.IsNotNull())
     m_InterpolatedSurfaceNode->SetVisibility(status);
 
   if (m_3DContourNode.IsNotNull())
   {
     auto allRenderWindows = mitk::BaseRenderer::GetAll3DRenderWindows();
     for (auto mapit = allRenderWindows.begin(); mapit != allRenderWindows.end(); ++mapit)
     {
       m_3DContourNode->SetVisibility(status, mapit->second);
     }
   }
 
   mitk::RenderingManager::GetInstance()->RequestUpdateAll();
 }
 
 void QmitkSlicesInterpolator::OnActiveLabelChanged(mitk::Label::PixelType)
 {
   m_3DContourNode->SetData(nullptr);
   m_FeedbackNode->SetData(nullptr);
   m_InterpolatedSurfaceNode->SetData(nullptr);
 
   if (m_Watcher.isRunning())
     m_Watcher.waitForFinished();
 
   if (m_3DInterpolationEnabled)
   {
     m_SurfaceInterpolator->Modified();
   }
 
   if (m_2DInterpolationEnabled)
   {
     m_FeedbackNode->SetData(nullptr);
     this->OnInterpolationActivated(true);
 
     m_LastSNC->SendSlice();
   }
   mitk::RenderingManager::GetInstance()->RequestUpdateAll();
   this->UpdateVisibleSuggestion();
 }
 
 void QmitkSlicesInterpolator::CheckSupportedImageDimension()
 {
   if (m_ToolManager->GetWorkingData(0))
   {
     m_Segmentation = dynamic_cast<mitk::Image *>(m_ToolManager->GetWorkingData(0)->GetData());
 
     if (m_3DInterpolationEnabled && m_Segmentation && ((m_Segmentation->GetDimension() != 3) || (m_Segmentation->GetDimension() != 4)) )
     {
       QMessageBox info;
       info.setWindowTitle("3D Interpolation Process");
       info.setIcon(QMessageBox::Information);
       info.setText("3D Interpolation is only supported for 3D/4D images at the moment!");
       info.exec();
       m_CmbInterpolation->setCurrentIndex(0);
     }
   }
 }
 
 void QmitkSlicesInterpolator::OnSliceNavigationControllerDeleted(const itk::Object *sender,
                                                                  const itk::EventObject & /*e*/)
 {
   // Don't know how to avoid const_cast here?!
   mitk::SliceNavigationController *slicer =
     dynamic_cast<mitk::SliceNavigationController *>(const_cast<itk::Object *>(sender));
   if (slicer)
   {
     m_ControllerToTimeObserverTag.remove(slicer);
     m_ControllerToSliceObserverTag.remove(slicer);
     m_ControllerToDeleteObserverTag.remove(slicer);
   }
 }
 
 void QmitkSlicesInterpolator::WaitForFutures()
 {
   if (m_Watcher.isRunning())
   {
     m_Watcher.waitForFinished();
   }
 
   if (m_PlaneWatcher.isRunning())
   {
     m_PlaneWatcher.waitForFinished();
   }
 }
 
 void QmitkSlicesInterpolator::NodeRemoved(const mitk::DataNode* node)
 {
   if ((m_ToolManager && m_ToolManager->GetWorkingData(0) == node) ||
       node == m_3DContourNode ||
       node == m_FeedbackNode ||
       node == m_InterpolatedSurfaceNode)
   {
     WaitForFutures();
   }
 }
 
 void QmitkSlicesInterpolator::OnAddLabelSetConnection(unsigned int layerID)
 {
   if (m_ToolManager->GetWorkingData(0) != nullptr)
   {
     try
     {
       auto workingImage = dynamic_cast<mitk::LabelSetImage *>(m_ToolManager->GetWorkingData(0)->GetData());
       auto labelSet = workingImage->GetLabelSet(layerID);
-      labelSet->RemoveLabelEvent += mitk::MessageDelegate<QmitkSlicesInterpolator>(
+      labelSet->RemoveLabelEvent += mitk::MessageDelegate1<QmitkSlicesInterpolator, mitk::Label::PixelType>(
         this, &QmitkSlicesInterpolator::OnRemoveLabel);
       labelSet->ActiveLabelEvent += mitk::MessageDelegate1<QmitkSlicesInterpolator,mitk::Label::PixelType>(
             this, &QmitkSlicesInterpolator::OnActiveLabelChanged);
       workingImage->AfterChangeLayerEvent += mitk::MessageDelegate<QmitkSlicesInterpolator>(
         this, &QmitkSlicesInterpolator::OnLayerChanged);
       m_SurfaceInterpolator->AddLabelSetConnection(layerID);
     }
     catch(const std::exception& e)
     {
       MITK_ERROR << e.what() << '\n';
     }
   }
 }
 
 
 
 void QmitkSlicesInterpolator::OnAddLabelSetConnection()
 {
   if (m_ToolManager->GetWorkingData(0) != nullptr)
   {
     try
     {
       auto workingImage = dynamic_cast<mitk::LabelSetImage *>(m_ToolManager->GetWorkingData(0)->GetData());
-      workingImage->GetActiveLabelSet()->RemoveLabelEvent += mitk::MessageDelegate<QmitkSlicesInterpolator>(
+      workingImage->GetActiveLabelSet()->RemoveLabelEvent += mitk::MessageDelegate1<QmitkSlicesInterpolator, mitk::Label::PixelType>(
         this, &QmitkSlicesInterpolator::OnRemoveLabel);
       workingImage->GetActiveLabelSet()->ActiveLabelEvent += mitk::MessageDelegate1<QmitkSlicesInterpolator,mitk::Label::PixelType>(
             this, &QmitkSlicesInterpolator::OnActiveLabelChanged);
       workingImage->AfterChangeLayerEvent += mitk::MessageDelegate<QmitkSlicesInterpolator>(
         this, &QmitkSlicesInterpolator::OnLayerChanged);
       m_SurfaceInterpolator->AddLabelSetConnection();
     }
     catch(const std::exception& e)
     {
       MITK_ERROR << e.what() << '\n';
     }
   }
 }
 
 void QmitkSlicesInterpolator::OnRemoveLabelSetConnection(mitk::LabelSetImage* labelSetImage, unsigned int layerID)
 {
   size_t previousLayerID = labelSetImage->GetActiveLayer();
   labelSetImage->SetActiveLayer(layerID);
-  labelSetImage->GetActiveLabelSet()->RemoveLabelEvent -= mitk::MessageDelegate<QmitkSlicesInterpolator>(
+  labelSetImage->GetActiveLabelSet()->RemoveLabelEvent -= mitk::MessageDelegate1<QmitkSlicesInterpolator, mitk::Label::PixelType>(
         this, &QmitkSlicesInterpolator::OnRemoveLabel);
   labelSetImage->GetActiveLabelSet()->ActiveLabelEvent -= mitk::MessageDelegate1<QmitkSlicesInterpolator,mitk::Label::PixelType>(
             this, &QmitkSlicesInterpolator::OnActiveLabelChanged);
   labelSetImage->AfterChangeLayerEvent -= mitk::MessageDelegate<QmitkSlicesInterpolator>(
         this, &QmitkSlicesInterpolator::OnLayerChanged);
   m_SurfaceInterpolator->RemoveLabelSetConnection(labelSetImage, layerID);
   labelSetImage->SetActiveLayer(previousLayerID);
 }
 
 void QmitkSlicesInterpolator::OnRemoveLabelSetConnection()
 {
   if (m_ToolManager->GetWorkingData(0) != nullptr)
   {
     try
     {
       auto workingImage = dynamic_cast<mitk::LabelSetImage*>(m_ToolManager->GetWorkingData(0)->GetData());
-      workingImage->GetActiveLabelSet()->RemoveLabelEvent -= mitk::MessageDelegate<QmitkSlicesInterpolator>(
+      workingImage->GetActiveLabelSet()->RemoveLabelEvent -= mitk::MessageDelegate1<QmitkSlicesInterpolator, mitk::Label::PixelType>(
         this, &QmitkSlicesInterpolator::OnRemoveLabel);
       workingImage->GetActiveLabelSet()->ActiveLabelEvent -= mitk::MessageDelegate1<QmitkSlicesInterpolator,mitk::Label::PixelType>(
             this, &QmitkSlicesInterpolator::OnActiveLabelChanged);
       workingImage->AfterChangeLayerEvent -= mitk::MessageDelegate<QmitkSlicesInterpolator>(
         this, &QmitkSlicesInterpolator::OnLayerChanged);
     }
     catch(const std::exception& e)
     {
       MITK_ERROR << e.what() << '\n';
     }
   }
 }
 
-void QmitkSlicesInterpolator::OnRemoveLabel()
+void QmitkSlicesInterpolator::OnRemoveLabel(mitk::Label::PixelType /*removedLabelValue*/)
 {
   if (m_ToolManager->GetWorkingData(0) != nullptr)
   {
     try
     {
       auto labelSetImage = dynamic_cast<mitk::LabelSetImage *>(m_ToolManager->GetWorkingData(0)->GetData());
       auto currentLayerID = labelSetImage->GetActiveLayer();
       auto numTimeSteps = labelSetImage->GetTimeGeometry()->CountTimeSteps();
       for (size_t t = 0; t < numTimeSteps; ++t)
       {
         m_SurfaceInterpolator->RemoveContours(m_PreviousActiveLabelValue,t,currentLayerID);
       }
     }
     catch(const std::exception& e)
     {
       MITK_ERROR << "Bad cast error for labelSetImage";
     }
   }
 }
 
 void QmitkSlicesInterpolator::OnModifyLabelChanged(const itk::Object *caller,
                                                       const itk::EventObject & /*event*/)
 {
   auto *tempImage = dynamic_cast<mitk::LabelSetImage *>(const_cast<itk::Object *>(caller) ) ;
   if( tempImage == nullptr)
   {
     MITK_ERROR << "Unable to cast caller to LabelSetImage.";
     return;
   }
 
   ModifyLabelActionTrigerred actionTriggered = ModifyLabelActionTrigerred::Null;
   if(m_ToolManager->GetWorkingData(0) != nullptr)
   {
     auto labelSetImage = dynamic_cast<mitk::LabelSetImage *>(m_ToolManager->GetWorkingData(0)->GetData());
     if (labelSetImage == tempImage)
     {
       const auto timePoint = m_LastSNC->GetSelectedTimePoint();
       if (!labelSetImage->GetTimeGeometry()->IsValidTimePoint(timePoint))
       {
         MITK_ERROR << "Invalid time point requested for interpolation pipeline.";
         return;
       }
       auto timeStep = labelSetImage->GetTimeGeometry()->TimePointToTimeStep(timePoint);
 
       auto numLayersInCurrentSegmentation = m_SurfaceInterpolator->GetNumberOfLayersInCurrentSegmentation();
       //  This handles the add layer or remove layer operation.
       if (labelSetImage->GetNumberOfLayers() != numLayersInCurrentSegmentation)
       {
         bool addLayer = (labelSetImage->GetNumberOfLayers() == (numLayersInCurrentSegmentation +1) );
         bool removeLayer = (labelSetImage->GetNumberOfLayers() == (numLayersInCurrentSegmentation - 1) );
 
         m_SurfaceInterpolator->SetNumberOfLayersInCurrentSegmentation(labelSetImage->GetNumberOfLayers());
 
         if (addLayer)
         {
           m_SurfaceInterpolator->OnAddLayer();
           this->OnAddLabelSetConnection();
         }
         if (removeLayer)
         {
           m_SurfaceInterpolator->OnRemoveLayer();
         }
         return;
       }
 
       //  Get the pixels present in the image.
       //  This portion of the code deals with the merge and erase labels operations.
       auto imageDimension = labelSetImage->GetDimension();
       if (imageDimension == 4)
       {
         actionTriggered = ModifyLabelProcessing<4>(labelSetImage, m_SurfaceInterpolator, timeStep);
       }
       else
       {
         actionTriggered = ModifyLabelProcessing<3>(labelSetImage, m_SurfaceInterpolator, timeStep);
       }
 
       if (actionTriggered == ModifyLabelActionTrigerred::Erase)
       {
         m_InterpolatedSurfaceNode->SetData(nullptr);
       }
 
       auto currentLayerID = labelSetImage->GetActiveLayer();
       if (actionTriggered == ModifyLabelActionTrigerred::Merge)
       {
         this->MergeContours(timeStep, currentLayerID);
         m_SurfaceInterpolator->Modified();
       }
     }
   }
 }
 
 void QmitkSlicesInterpolator::MergeContours(unsigned int timeStep,
                                             unsigned int layerID)
 {
   std::vector<mitk::SurfaceInterpolationController::ContourPositionInformation>& contours =
                                             m_SurfaceInterpolator->GetContours(timeStep,layerID);
   std::this_thread::sleep_for(std::chrono::milliseconds(1000));
 
   for (size_t i = 0; i < contours.size(); ++i)
   {
     for (size_t j = i+1; j < contours.size(); ++j)
     {
       //  And Labels are the same and Layers are the same.
       bool areContoursCoplanar = AreContoursCoplanar(contours[i],contours[j]);
 
       if ( areContoursCoplanar  && (contours[i].LabelValue == contours[j].LabelValue) )
       {
         //  Update the contour by re-extracting the slice from the corresponding plane.
         mitk::Image::Pointer slice = ExtractSliceFromImage(m_Segmentation, contours[i].Plane, timeStep);
         mitk::ImageToContourFilter::Pointer contourExtractor = mitk::ImageToContourFilter::New();
         contourExtractor->SetInput(slice);
         contourExtractor->SetContourValue(contours[i].LabelValue);
         contourExtractor->Update();
         mitk::Surface::Pointer contour = contourExtractor->GetOutput();
         contours[i].Contour = contour;
 
         //  Update the interior point of the contour
         contours[i].ContourPoint = m_SurfaceInterpolator->ComputeInteriorPointOfContour(contours[i],dynamic_cast<mitk::LabelSetImage *>(m_Segmentation));
 
         //  Setting the contour polygon data to an empty vtkPolyData,
         //  as source label is empty after merge operation.
         contours[j].Contour->SetVtkPolyData(vtkSmartPointer<vtkPolyData>::New());
       }
     }
   }
 
   auto segmentationNode = m_SurfaceInterpolator->GetSegmentationImageNode();
 
   if (segmentationNode == nullptr)
   {
     MITK_ERROR << "segmentation Image Node not found\n";
   }
 
   auto isContourPlaneGeometry = mitk::NodePredicateProperty::New("isContourPlaneGeometry", mitk::BoolProperty::New(true));
 
   mitk::DataStorage::SetOfObjects::ConstPointer contourNodes =
     m_DataStorage->GetDerivations(segmentationNode, isContourPlaneGeometry);
 
   //  Remove empty contour nodes.
   auto isContourEmpty = [] (const mitk::SurfaceInterpolationController::ContourPositionInformation& contour)
   {
     return (contour.Contour->GetVtkPolyData()->GetNumberOfPoints() == 0);
   };
 
   auto it = std::remove_if(contours.begin(), contours.end(), isContourEmpty);
   contours.erase(it, contours.end());
 }
\ No newline at end of file
diff --git a/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.h b/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.h
index 5c8efb50e6..fddec42bf3 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.h
+++ b/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.h
@@ -1,425 +1,425 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #ifndef QmitkSlicesInterpolator_h
 #define QmitkSlicesInterpolator_h
 
 #include "mitkDataNode.h"
 #include "mitkDataStorage.h"
 #include "mitkSegmentationInterpolationController.h"
 #include "mitkSliceNavigationController.h"
 #include "mitkSurfaceInterpolationController.h"
 #include "mitkToolManager.h"
 #include <MitkSegmentationUIExports.h>
 
 #include "mitkFeatureBasedEdgeDetectionFilter.h"
 #include "mitkPointCloudScoringFilter.h"
 
 #include <QWidget>
 #include <map>
 
 #include <QCheckBox>
 #include <QComboBox>
 #include <QFrame>
 #include <QGroupBox>
 #include <QRadioButton>
 
 #include "mitkVtkRepresentationProperty.h"
 #include "vtkProperty.h"
 
 // For running 3D interpolation in background
 #include <QFuture>
 #include <QFutureWatcher>
 #include <QTimer>
 #include <QtConcurrentRun>
 
 namespace mitk
 {
   class PlaneGeometry;
   class SliceNavigationController;
 }
 
 class QPushButton;
 class QmitkRenderWindow;
 
 enum ModifyLabelActionTrigerred
 {
   Null,
   Erase,
   Merge
 };
 
 /**
   \brief GUI for slices interpolation.
 
   \ingroup ToolManagerEtAl
   \ingroup Widgets
 
   \sa QmitkInteractiveSegmentation
   \sa mitk::SegmentationInterpolation
 
   While mitk::SegmentationInterpolation does the bookkeeping of interpolation
   (keeping track of which slices contain how much segmentation) and the algorithmic work,
   QmitkSlicesInterpolator is responsible to watch the GUI, to notice, which slice is currently
   visible. It triggers generation of interpolation suggestions and also triggers acception of
   suggestions.
 
   \todo show/hide feedback on demand
 
   Last contributor: $Author: maleike $
 */
 class MITKSEGMENTATIONUI_EXPORT QmitkSlicesInterpolator : public QWidget
 {
   Q_OBJECT
 
 public:
   QmitkSlicesInterpolator(QWidget *parent = nullptr, const char *name = nullptr);
 
   /**
     To be called once before real use.
     */
   void Initialize(mitk::ToolManager *toolManager, const QList<QmitkRenderWindow*>& windows);
 
   /**
    * @brief
    *
    */
   void Uninitialize();
 
   ~QmitkSlicesInterpolator() override;
 
   /**
    * @brief Set the Data Storage object
    *
    * @param storage
    */
   void SetDataStorage(mitk::DataStorage::Pointer storage);
 
   /**
    * @brief Get the Data Storage object
    *
    * @return mitk::DataStorage*
    */
   mitk::DataStorage *GetDataStorage();
 
 
   /**
     Just public because it is called by itk::Commands. You should not need to call this.
   */
   void OnToolManagerWorkingDataModified();
 
   /**
     Just public because it is called by itk::Commands. You should not need to call this.
   */
   void OnToolManagerReferenceDataModified();
 
   /**
    * @brief Reacts to the time changed event.
    *
    * @param sender
    */
   void OnTimeChanged(itk::Object *sender, const itk::EventObject &);
 
   /**
    * @brief Reacts to the slice changed event
    *
    * @param sender
    */
   void OnSliceChanged(itk::Object *sender, const itk::EventObject &);
 
 
   void OnSliceNavigationControllerDeleted(const itk::Object *sender, const itk::EventObject &);
 
   /**
     Just public because it is called by itk::Commands. You should not need to call this.
   */
   void OnInterpolationInfoChanged(const itk::EventObject &);
 
   /**
     Just public because it is called by itk::Commands. You should not need to call this.
   */
   void OnInterpolationAborted(const itk::EventObject &);
 
   /**
     Just public because it is called by itk::Commands. You should not need to call this.
   */
   void OnSurfaceInterpolationInfoChanged(const itk::EventObject &);
 
 
 private:
   /**
    * @brief Set the visibility of the 3d interpolation
    */
   void Show3DInterpolationResult(bool);
 
   /**
    * @brief Function that reacts to a change in the activeLabel of the working segmentation image.
    *
    */
   void OnActiveLabelChanged(mitk::Label::PixelType);
 
   /**
    * @brief Function that reacts to a change in the layer.
    *
    */
   void OnLayerChanged();
 
   /**
    * @brief Function that handles label removal from the segmentation image.
    *
    */
-  void OnRemoveLabel();
+  void OnRemoveLabel(mitk::Label::PixelType removedLabelValue);
 
   /**
    * @brief Function that to changes in the segmentation image. It handles the layer removal, addition, label erasure,
    *
    */
   void OnModifyLabelChanged(const itk::Object *caller,
                               const itk::EventObject & /*event*/);
 
   /**
    * @brief Add the necessary subscribers to the label set image, for UI responsiveness.
    *        It deals with remove label, change active label, layer changes and change in the label.
    *
    */
   void OnAddLabelSetConnection();
 
   /**
    * @brief Add the necessary subscribers to the current LabelSetImage at the layer input, for UI responsiveness.
    *        It deals with remove label, change active label, layer changes and change in the label.
    *
    * @param layerID
    */
   void OnAddLabelSetConnection(unsigned int layerID);
 
   /**
    * @brief Remove the subscribers for the different events to the segmentation image.
    *
    */
   void OnRemoveLabelSetConnection();
 
   /**
    * @brief Merge contours for the current layerID and current timeStep.
    *
    * @param timeStep
    * @param layerID
    */
   void MergeContours(unsigned int timeStep, unsigned int layerID);
 
 
   /**
    * @brief Prepare Inputs for 3D Interpolation.
    *
    */
   void PrepareInputsFor3DInterpolation();
 
 signals:
 
   void SignalRememberContourPositions(bool);
   void SignalShowMarkerNodes(bool);
 
 public slots:
 
   virtual void setEnabled(bool);
   /**
     Call this from the outside to enable/disable interpolation
   */
   void EnableInterpolation(bool);
 
   void Enable3DInterpolation(bool);
 
   /**
     Call this from the outside to accept all interpolations
   */
   void FinishInterpolation(mitk::SliceNavigationController *slicer = nullptr);
 
 protected slots:
 
   /**
     Reaction to button clicks.
   */
   void OnAcceptInterpolationClicked();
 
   /*
     Opens popup to ask about which orientation should be interpolated
   */
   void OnAcceptAllInterpolationsClicked();
 
   /*
    Reaction to button clicks
   */
   void OnAccept3DInterpolationClicked();
 
   /**
    * @brief Reaction to reinit 3D Interpolation. Re-reads the plane geometries of the image
    *         that should have generated the
    *
    */
   void OnReinit3DInterpolation();
 
   /*
    * Will trigger interpolation for all slices in given orientation (called from popup menu of
    * OnAcceptAllInterpolationsClicked)
    */
   void OnAcceptAllPopupActivated(QAction *action);
 
   /**
     Called on activation/deactivation
   */
   void OnInterpolationActivated(bool);
 
   void On3DInterpolationActivated(bool);
 
   void OnInterpolationMethodChanged(int index);
 
   // Enhancement for 3D interpolation
   void On2DInterpolationEnabled(bool);
   void On3DInterpolationEnabled(bool);
   void OnInterpolationDisabled(bool);
   void OnShowMarkers(bool);
 
   void Run3DInterpolation();
 
   /**
    * @brief Function triggers when the surface interpolation thread completes running.
    *        It is responsible for retrieving the data, rendering it in the active color label,
    *        storing the surface information in the feedback node.
    *
    */
   void OnSurfaceInterpolationFinished();
 
   void StartUpdateInterpolationTimer();
 
   void StopUpdateInterpolationTimer();
 
   void ChangeSurfaceColor();
 
   /**
    * @brief Removes all observers to the labelSetImage at the layerID specified.
    *        Is used when changing the segmentation image.
    *
    * @param labelSetImage
    * @param layerID
    */
   void OnRemoveLabelSetConnection(mitk::LabelSetImage* labelSetImage, unsigned int layerID);
 
 protected:
   const std::map<QAction *, mitk::SliceNavigationController *> createActionToSlicer(const QList<QmitkRenderWindow*>& windows);
   std::map<QAction *, mitk::SliceNavigationController *> m_ActionToSlicer;
 
   void AcceptAllInterpolations(mitk::SliceNavigationController *slicer);
 
   /**
     Retrieves the currently selected PlaneGeometry from a SlicedGeometry3D that is generated by a
     SliceNavigationController
     and calls Interpolate to further process this PlaneGeometry into an interpolation.
 
     \param e is a actually a mitk::SliceNavigationController::GeometrySliceEvent, sent by a SliceNavigationController
     \param slicer the SliceNavigationController
         */
   bool TranslateAndInterpolateChangedSlice(const itk::EventObject &e, mitk::SliceNavigationController *slicer);
 
   /**
     Given a PlaneGeometry, this method figures out which slice of the first working image (of the associated
     ToolManager)
     should be interpolated. The actual work is then done by our SegmentationInterpolation object.
    */
   void Interpolate(mitk::PlaneGeometry *plane, mitk::TimePointType timePoint, mitk::SliceNavigationController *slicer);
 
   // void InterpolateSurface();
 
   /**
     Called internally to update the interpolation suggestion. Finds out about the focused render window and requests an
     interpolation.
    */
   void UpdateVisibleSuggestion();
 
   void SetCurrentContourListID();
 
 private:
   void InitializeWindow(QmitkRenderWindow* window);
   void HideAllInterpolationControls();
   void Show2DInterpolationControls(bool show);
   void Show3DInterpolationControls(bool show);
   void CheckSupportedImageDimension();
   void WaitForFutures();
   void NodeRemoved(const mitk::DataNode* node);
 
   mitk::SegmentationInterpolationController::Pointer m_Interpolator;
   mitk::SurfaceInterpolationController::Pointer m_SurfaceInterpolator;
 
   mitk::FeatureBasedEdgeDetectionFilter::Pointer m_EdgeDetector;
   mitk::PointCloudScoringFilter::Pointer m_PointScorer;
 
   mitk::ToolManager::Pointer m_ToolManager;
   bool m_Initialized;
 
   QHash<mitk::SliceNavigationController *, int> m_ControllerToTimeObserverTag;
   QHash<mitk::SliceNavigationController *, int> m_ControllerToSliceObserverTag;
   QHash<mitk::SliceNavigationController *, int> m_ControllerToDeleteObserverTag;
 
   std::map<mitk::LabelSetImage *, unsigned long> m_SegmentationObserverTags;
 
   unsigned int InterpolationInfoChangedObserverTag;
   unsigned int SurfaceInterpolationInfoChangedObserverTag;
   unsigned int InterpolationAbortedObserverTag;
 
   QGroupBox *m_GroupBoxEnableExclusiveInterpolationMode;
   QComboBox *m_CmbInterpolation;
   QPushButton *m_BtnApply2D;
   QPushButton *m_BtnApplyForAllSlices2D;
   QPushButton *m_BtnApply3D;
 
   // T28261
   // QPushButton *m_BtnSuggestPlane;
 
   QCheckBox *m_ChkShowPositionNodes;
   QPushButton *m_BtnReinit3DInterpolation;
 
   mitk::DataNode::Pointer m_FeedbackNode;
   mitk::DataNode::Pointer m_InterpolatedSurfaceNode;
   mitk::DataNode::Pointer m_3DContourNode;
 
   mitk::Image *m_Segmentation;
 
   mitk::SliceNavigationController *m_LastSNC;
   unsigned int m_LastSliceIndex;
 
   QHash<mitk::SliceNavigationController *, mitk::TimePointType> m_TimePoints;
 
   bool m_2DInterpolationEnabled;
   bool m_3DInterpolationEnabled;
 
   unsigned int m_numTimesLabelSetConnectionAdded;
 
   mitk::DataStorage::Pointer m_DataStorage;
 
   QFuture<void> m_Future;
   QFutureWatcher<void> m_Watcher;
 
   QFuture<void> m_ModifyFuture;
   QFutureWatcher<void> m_ModifyWatcher;
 
   QTimer *m_Timer;
 
   QFuture<void> m_PlaneFuture;
   QFutureWatcher<void> m_PlaneWatcher;
 
   mitk::Label::PixelType m_PreviousActiveLabelValue;
   mitk::Label::PixelType m_CurrentActiveLabelValue;
 
   unsigned int m_PreviousLayerIndex;
   unsigned int m_CurrentLayerIndex;
   bool m_FirstRun;
 };
 
 #endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetFolderParser.h b/Modules/SegmentationUI/Qmitk/QmitknnUNetFolderParser.h
index a6e12828c5..0cf1cc66bb 100644
--- a/Modules/SegmentationUI/Qmitk/QmitknnUNetFolderParser.h
+++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetFolderParser.h
@@ -1,259 +1,258 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #ifndef QmitknnUNetFolderParser_h
 #define QmitknnUNetFolderParser_h
 
 #include <QDirIterator>
 #include <memory>
 #include <functional>
 #include <QString>
 #include <vector>
 
 /**
  * @brief Struct to store each (Folder) Node of the hierarchy tree structure.
  *
  */
 struct FolderNode
 {
   QString name;
   QString path; // parent
   std::vector<std::shared_ptr<FolderNode>> subFolders;
 };
 
 /**
  * @brief Class to store and retreive folder hierarchy information
  * of RESULTS_FOLDER. Only Root node is explicitly stored in m_RootNode.
  * No. of sub levels in the hierachry is defined in the LEVEL constant.
  *
  */
 class QmitknnUNetFolderParser
 {
 public:
   /**
    * @brief Construct a new QmitknnUNetFolderParser object
    * Initializes root folder node object pointer calls
    * @param parentFolder
    */
   QmitknnUNetFolderParser(const QString parentFolder);
   /**
    * @brief Destroy the QmitknnUNetFolderParser object
    *
    */
   ~QmitknnUNetFolderParser() = default;
   /**
    * @brief Returns the "Results Folder" string which is parent path of the root node.
    *
    * @return QString
    */
   QString getResultsFolder(); 
 
   /**
    * @brief Returns the Model Names from root node. Template function,
    * type can be any of stl or Qt containers which supports push_back call.
    *
    * @tparam T
    * @return T (any of stl or Qt containers which supports push_back call)
    */
   template <typename T>
   T getModelNames()
   {
     auto models = GetSubFolderNamesFromNode<T>(m_RootNode);
     return models;
   }
 
   /**
    * @brief Returns the task names for a given model. Template function,
    * type can be any of stl or Qt containers which supports push_back call.
    *
    * @tparam T
    * @param modelName
    * @return T (any of stl or Qt containers which supports push_back call)
    */
   template <typename T>
   T getTasksForModel(const QString &modelName)
   {
     std::shared_ptr<FolderNode> modelNode = GetSubNodeMatchingNameCrietria(modelName, m_RootNode);
     auto tasks = GetSubFolderNamesFromNode<T>(modelNode);
     return tasks;
   }
 
   /**
    * @brief Returns the models names for a given task. Template function,
    * type can be any of stl or Qt containers which supports push_back call.
    *
    * @tparam T
    * @param taskName
    * @return T (any of stl or Qt containers which supports push_back call)
    */
   template <typename T>
   T getModelsForTask(const QString &taskName)
   {
     T modelsForTask;
     auto models = GetSubFolderNamesFromNode<T>(m_RootNode);
     foreach (QString model, models)
     {
       QStringList taskList = getTasksForModel<QStringList>(model);
       if (taskList.contains(taskName, Qt::CaseInsensitive))
       {
         modelsForTask << model;
       }
     }
     return modelsForTask;
   }
 
   /**
    * @brief Returns all the task names present in the root node with possible duplicates.
    * Template function, type can be any of stl or Qt containers which supports push_back call.
    *
-   * @param T
-   * @param taskName
+   * @tparam T
    * @return T (any of stl or Qt containers which supports push_back call)
    */
   template <typename T>
   T getAllTasks()
   {
     T allTasks;
     auto models = GetSubFolderNamesFromNode<T>(m_RootNode);
     foreach (QString model, models)
     {
       allTasks << getTasksForModel<QStringList>(model);
     }
     return allTasks;
   }
 
   /**
    * @brief Returns the trainer / planner names for a given task & model. Template function,
    * type can be any of stl or Qt containers which supports push_back call.
    *
    * @tparam T
    * @param taskName
    * @param modelName
    * @return T (any of stl or Qt containers which supports push_back call)
    */
   template <typename T>
   T getTrainerPlannersForTask(const QString &taskName, const QString &modelName)
   {
     std::shared_ptr<FolderNode> modelNode = GetSubNodeMatchingNameCrietria(modelName, m_RootNode);
     std::shared_ptr<FolderNode> taskNode = GetSubNodeMatchingNameCrietria(taskName, modelNode);
     auto tps = GetSubFolderNamesFromNode<T>(taskNode);
     return tps;
   }
 
   /**
    * @brief Returns the Folds names for a given trainer,planner,task & model name. Template function,
    * type can be any of stl or Qt containers which supports push_back call.
    *
    * @tparam T
    * @param trainer
    * @param planner
    * @param taskName
    * @param modelName
    * @return T (any of stl or Qt containers which supports push_back call)
    */
   template <typename T>
   T getFoldsForTrainerPlanner(const QString &trainer,
                               const QString &planner,
                               const QString &taskName,
                               const QString &modelName)
   {
     std::shared_ptr<FolderNode> modelNode = GetSubNodeMatchingNameCrietria(modelName, m_RootNode);
     std::shared_ptr<FolderNode> taskNode = GetSubNodeMatchingNameCrietria(taskName, modelNode);
     QString trainerPlanner = trainer + QString("__") + planner;
     std::shared_ptr<FolderNode> tpNode = GetSubNodeMatchingNameCrietria(trainerPlanner, taskNode);
     auto folds = GetSubFolderNamesFromNode<T>(tpNode);
     return folds;
   }
 
 private:
   const int m_LEVEL = 4;
   std::shared_ptr<FolderNode> m_RootNode;
   
   /**
    * @brief Returns rule function wrapper to check for specific files at given Result_Folder hierarchy level.
    * 
    * @param level 
    * @return std::function<bool(QString)> 
    */
   std::function<bool(QString)> RuleEngine(int level);
 
   /**
    * @brief Iterates through the root node and returns the sub FolderNode object Matching Name Crietria
    *
    * @param queryName
    * @param parentNode
    * @return std::shared_ptr<FolderNode>
    */
   std::shared_ptr<FolderNode> GetSubNodeMatchingNameCrietria(const QString &queryName, std::shared_ptr<FolderNode> parentNode);
 
   /**
    * @brief Returns the sub folder names for a folder node object. Template function,
    * type can be any of stl or Qt containers which supports push_back call.
    *
    * @tparam T
    * @param std::shared_ptr<FolderNode>
    * @return T (any of stl or Qt containers which supports push_back call)
    */
   template <typename T>
   T GetSubFolderNamesFromNode(const std::shared_ptr<FolderNode> parent)
   {
     T folders;
     std::vector<std::shared_ptr<FolderNode>> subNodes = parent->subFolders;
     for (std::shared_ptr<FolderNode> folder : subNodes)
     {
       folders.push_back(folder->name);
     }
     return folders;
   }
 
   /**
    * @brief Iterates through the sub folder hierarchy upto a level provided
    * and create a tree structure.
    *
    * @param parent
    * @param level
    */
   void InitDirs(std::shared_ptr<FolderNode> parent, int level);
 
   /**
    * @brief Iterates through the sub folder hierarchy upto a level provided
    * and clears the sub folder std::vector from each node.
    *
    * @param parent
    * @param level
    */
   void DeleteDirs(std::shared_ptr<FolderNode> parent, int level);
 
   /**
    * @brief Template function to fetch all folders inside a given path.
    * The type can be any of stl or Qt containers which supports push_back call.
    *
    * @tparam T
    * @param path
    * @return T
    */
   template <typename T>
   T FetchFoldersFromDir(const QString &path, std::function<bool(QString)> callback)
   {
     T folders;
     for (QDirIterator it(path, QDir::AllDirs, QDirIterator::NoIteratorFlags); it.hasNext();)
     {
       it.next();
       if (!it.fileName().startsWith('.') && callback(it.filePath()))
       {
         folders.push_back(it.fileName());
       }
     }
     return folders;
   }
 };
 #endif
diff --git a/Modules/SegmentationUI/files.cmake b/Modules/SegmentationUI/files.cmake
index 113bbae7c7..649fc755b7 100644
--- a/Modules/SegmentationUI/files.cmake
+++ b/Modules/SegmentationUI/files.cmake
@@ -1,99 +1,113 @@
 set(CPP_FILES
   Qmitk/QmitkSegWithPreviewToolGUIBase.cpp
   Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.cpp
   Qmitk/QmitkBinaryThresholdToolGUIBase.cpp
   Qmitk/QmitkBinaryThresholdToolGUI.cpp
   Qmitk/QmitkBinaryThresholdULToolGUI.cpp
   Qmitk/QmitkConfirmSegmentationDialog.cpp
   Qmitk/QmitkCopyToClipBoardDialog.cpp
   Qmitk/QmitkDrawPaintbrushToolGUI.cpp
   Qmitk/QmitkErasePaintbrushToolGUI.cpp
   Qmitk/QmitkEditableContourToolGUIBase.cpp
   Qmitk/QmitkGrowCutToolGUI.cpp
   Qmitk/QmitkLiveWireTool2DGUI.cpp
   Qmitk/QmitkLassoToolGUI.cpp
   Qmitk/QmitkOtsuTool3DGUI.cpp
   Qmitk/QmitkPaintbrushToolGUI.cpp
   Qmitk/QmitkPickingToolGUI.cpp
   Qmitk/QmitkSlicesInterpolator.cpp
   Qmitk/QmitkToolGUI.cpp
   Qmitk/QmitkToolGUIArea.cpp
   Qmitk/QmitkToolSelectionBox.cpp
   Qmitk/QmitknnUNetFolderParser.cpp
   Qmitk/QmitknnUNetToolGUI.cpp
   Qmitk/QmitknnUNetWorker.cpp
   Qmitk/QmitknnUNetGPU.cpp
   Qmitk/QmitkSurfaceStampWidget.cpp
   Qmitk/QmitkMaskStampWidget.cpp
   Qmitk/QmitkStaticDynamicSegmentationDialog.cpp
   Qmitk/QmitkSimpleLabelSetListWidget.cpp
   Qmitk/QmitkSegmentationTaskListWidget.cpp
+  Qmitk/QmitkMultiLabelInspector.cpp
+  Qmitk/QmitkMultiLabelManager.cpp
+  Qmitk/QmitkMultiLabelTreeModel.cpp
+  Qmitk/QmitkMultiLabelTreeView.cpp
+  Qmitk/QmitkLabelColorItemDelegate.cpp
+  Qmitk/QmitkLabelToggleItemDelegate.cpp
   SegmentationUtilities/QmitkBooleanOperationsWidget.cpp
   SegmentationUtilities/QmitkContourModelToImageWidget.cpp
   SegmentationUtilities/QmitkImageMaskingWidget.cpp
   SegmentationUtilities/QmitkMorphologicalOperationsWidget.cpp
   SegmentationUtilities/QmitkSurfaceToImageWidget.cpp
   SegmentationUtilities/QmitkSegmentationUtilityWidget.cpp
   SegmentationUtilities/QmitkDataSelectionWidget.cpp
 )
 
 set(MOC_H_FILES
   Qmitk/QmitkSegWithPreviewToolGUIBase.h
   Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.h
   Qmitk/QmitkBinaryThresholdToolGUIBase.h
   Qmitk/QmitkBinaryThresholdToolGUI.h
   Qmitk/QmitkBinaryThresholdULToolGUI.h
   Qmitk/QmitkConfirmSegmentationDialog.h
   Qmitk/QmitkCopyToClipBoardDialog.h
   Qmitk/QmitkDrawPaintbrushToolGUI.h
   Qmitk/QmitkErasePaintbrushToolGUI.h
   Qmitk/QmitkEditableContourToolGUIBase.h
   Qmitk/QmitkGrowCutToolGUI.h
   Qmitk/QmitkLiveWireTool2DGUI.h
   Qmitk/QmitkLassoToolGUI.h
   Qmitk/QmitkOtsuTool3DGUI.h
   Qmitk/QmitkPaintbrushToolGUI.h
   Qmitk/QmitkPickingToolGUI.h
   Qmitk/QmitkSlicesInterpolator.h
   Qmitk/QmitkToolGUI.h
   Qmitk/QmitkToolGUIArea.h
   Qmitk/QmitkToolSelectionBox.h
   Qmitk/QmitknnUNetFolderParser.h
   Qmitk/QmitknnUNetToolGUI.h
   Qmitk/QmitknnUNetGPU.h
   Qmitk/QmitknnUNetWorker.h
   Qmitk/QmitknnUNetEnsembleLayout.h
   Qmitk/QmitkSurfaceStampWidget.h
   Qmitk/QmitkMaskStampWidget.h
   Qmitk/QmitkStaticDynamicSegmentationDialog.h
   Qmitk/QmitkSimpleLabelSetListWidget.h
   Qmitk/QmitkSegmentationTaskListWidget.h
+  Qmitk/QmitkMultiLabelInspector.h
+  Qmitk/QmitkMultiLabelManager.h
+  Qmitk/QmitkMultiLabelTreeModel.h
+  Qmitk/QmitkMultiLabelTreeView.h
+  Qmitk/QmitkLabelColorItemDelegate.h
+  Qmitk/QmitkLabelToggleItemDelegate.h
   SegmentationUtilities/QmitkBooleanOperationsWidget.h
   SegmentationUtilities/QmitkContourModelToImageWidget.h
   SegmentationUtilities/QmitkImageMaskingWidget.h
   SegmentationUtilities/QmitkMorphologicalOperationsWidget.h
   SegmentationUtilities/QmitkSurfaceToImageWidget.h
   SegmentationUtilities/QmitkSegmentationUtilityWidget.h
   SegmentationUtilities/QmitkDataSelectionWidget.h
 )
 
 set(UI_FILES
   Qmitk/QmitkConfirmSegmentationDialog.ui
   Qmitk/QmitkGrowCutToolWidgetControls.ui
   Qmitk/QmitkOtsuToolWidgetControls.ui
   Qmitk/QmitkSurfaceStampWidgetGUIControls.ui
   Qmitk/QmitkMaskStampWidgetGUIControls.ui
   Qmitk/QmitknnUNetToolGUIControls.ui
   Qmitk/QmitkEditableContourToolGUIControls.ui
   Qmitk/QmitkSegmentationTaskListWidget.ui
+  Qmitk/QmitkMultiLabelInspectorControls.ui
+  Qmitk/QmitkMultiLabelManagerControls.ui
   SegmentationUtilities/QmitkBooleanOperationsWidgetControls.ui
   SegmentationUtilities/QmitkContourModelToImageWidgetControls.ui
   SegmentationUtilities/QmitkImageMaskingWidgetControls.ui
   SegmentationUtilities/QmitkMorphologicalOperationsWidgetControls.ui
   SegmentationUtilities/QmitkSurfaceToImageWidgetControls.ui
   SegmentationUtilities/QmitkDataSelectionWidgetControls.ui
 )
 
 set(QRC_FILES
   resources/SegmentationUI.qrc
 )
diff --git a/Modules/SegmentationUI/test/CMakeLists.txt b/Modules/SegmentationUI/test/CMakeLists.txt
new file mode 100644
index 0000000000..153cd81e2e
--- /dev/null
+++ b/Modules/SegmentationUI/test/CMakeLists.txt
@@ -0,0 +1 @@
+MITK_CREATE_MODULE_TESTS()
diff --git a/Modules/SegmentationUI/test/QmitkMultiLabelTreeModelTest.cpp b/Modules/SegmentationUI/test/QmitkMultiLabelTreeModelTest.cpp
new file mode 100644
index 0000000000..68e74dcc52
--- /dev/null
+++ b/Modules/SegmentationUI/test/QmitkMultiLabelTreeModelTest.cpp
@@ -0,0 +1,543 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+All rights reserved.
+
+Use of this source code is governed by a 3-clause BSD license that can be
+found in the LICENSE file.
+
+============================================================================*/
+
+#include <QApplication>
+#include <QColor>
+#include "QmitkMultiLabelTreeModel.h"
+
+#include <mitkTestFixture.h>
+#include <mitkTestingMacros.h>
+
+class QmitkMultiLabelTreeModelTestSuite : public mitk::TestFixture
+{
+  CPPUNIT_TEST_SUITE(QmitkMultiLabelTreeModelTestSuite);
+  MITK_TEST(NullTest);
+  MITK_TEST(GetterSetterTest);
+  MITK_TEST(AddingLabelTest);
+  MITK_TEST(AddingLayerTest);
+  MITK_TEST(RemovingLabelTest);
+  MITK_TEST(RemovingLayerTest);
+  MITK_TEST(ModifyLabelNameTest);
+  MITK_TEST(ModifyLabelTest);
+
+  CPPUNIT_TEST_SUITE_END();
+
+  mitk::LabelSetImage::Pointer m_Segmentation;
+
+  QCoreApplication* m_TestApp;
+
+public:
+
+  mitk::LabelSetImage::Pointer GenerateSegmentation()
+  {
+    // Create a new labelset image
+    auto seg = mitk::LabelSetImage::New();
+    mitk::Image::Pointer regularImage = mitk::Image::New();
+    unsigned int dimensions[3] = { 5, 5, 5 };
+    regularImage->Initialize(mitk::MakeScalarPixelType<char>(), 3, dimensions);
+    seg->Initialize(regularImage);
+    return seg;
+  }
+
+  QColor GetQColor(const mitk::Label* label)
+  {
+    return QColor(label->GetColor().GetRed() * 255, label->GetColor().GetGreen() * 255, label->GetColor().GetBlue() * 255);
+  }
+
+  mitk::Label::Pointer CreateLabel(const std::string& name, mitk::Label::PixelType value)
+  {
+    auto label = mitk::Label::New();
+    label->SetName(name);
+    label->SetValue(value);
+    label->SetColor(mitk::Color(value / 255.));
+    return label;
+  }
+
+  /** Populate a seg with a following setup (in brackets the order of addition).
+  * - Group 1 (1)
+  *  - Label A
+  *   - Instance 1 (1)
+  *   - Instance 5 (2)
+  *   - Instance 10 (8)
+  *  - Label B
+  *   - Instance 4 (3)
+  *  - Label D
+  *   - Instance 2 (7)
+  * - Group 2 (4)
+  * - Group 3 (5)
+  *  - Label B
+  *   - Instance 9 (6)
+  */
+  void PopulateSegmentation(mitk::LabelSetImage* seg)
+  {
+    seg->SetActiveLayer(0);
+    seg->GetActiveLabelSet()->AddLabel(CreateLabel("A", 1));
+    seg->GetActiveLabelSet()->AddLabel(CreateLabel("A", 5));
+    seg->GetActiveLabelSet()->AddLabel(CreateLabel("B", 4));
+    seg->AddLayer();
+    seg->AddLayer();
+    seg->SetActiveLayer(2);
+    seg->GetActiveLabelSet()->AddLabel(CreateLabel("B", 9));
+    seg->SetActiveLayer(0);
+    seg->GetActiveLabelSet()->AddLabel(CreateLabel("D", 2));
+    seg->GetActiveLabelSet()->AddLabel(CreateLabel("A", 10));
+  }
+
+  void setUp() override
+  {
+    m_Segmentation = GenerateSegmentation();
+
+    PopulateSegmentation(m_Segmentation);
+
+    int argc = 0;
+    char** argv = nullptr;
+    m_TestApp = new QCoreApplication(argc, argv);
+  }
+
+  void tearDown() override
+  {
+    delete m_TestApp;
+  }
+
+  QModelIndex GetIndex(const QmitkMultiLabelTreeModel& model, const std::vector<int>& rows, int column = 0) const
+  {
+    QModelIndex testIndex;
+
+    int i = 0;
+    for (auto row : rows)
+    {
+      if (static_cast<std::vector<int>::size_type>(i) + 1 < rows.size())
+      {
+        testIndex = model.index(row, 0, testIndex);
+      }
+      else
+      {
+        testIndex = model.index(row, column, testIndex);
+      }
+      i++;
+    }
+
+    return testIndex;
+  }
+
+  bool CheckModelItem(const QmitkMultiLabelTreeModel& model, const std::vector<int>& rows, const QVariant& reference, int column, const mitk::Label* /*label = nullptr*/) const
+  {
+    QModelIndex testIndex = GetIndex(model, rows, column);
+
+    auto value = model.data(testIndex);
+
+    bool test = value == reference;
+
+    if (!test) std::cerr << std::endl <<"    Model item error. Expected: '" << reference.toString().toStdString() << "'; actual: '" << value.toString().toStdString()  <<"'";
+    return test;
+  }
+
+  bool CheckModelRow(const QmitkMultiLabelTreeModel& model, const std::vector<int>& rows, const std::vector<QVariant> references) const
+  {
+    int column = 0;
+    bool test = true;
+    for (const auto& ref : references)
+    {
+      test = test & CheckModelItem(model, rows, ref, column, nullptr);
+      column++;
+    }
+    return test;
+  }
+
+  void CheckModelGroup0Default(const QmitkMultiLabelTreeModel& model)
+  {
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0 }, { QString("Group 0"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(3, model.rowCount(GetIndex(model, { 0 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,0 }, { QString("A (3 instances)"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(3, model.rowCount(GetIndex(model, { 0,0 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,0,0 }, { QString("A #1"), QVariant(true), QVariant(QColor(1,1,1)), QVariant(true) }));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,0,1 }, { QString("A #5"), QVariant(true), QVariant(QColor(5,5,5)), QVariant(true) }));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,0,2 }, { QString("A #10"), QVariant(true), QVariant(QColor(10,10,10)), QVariant(true) }));
+
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,1 }, { QString("B"), QVariant(true), QVariant(QColor(4,4,4)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,1 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,2 }, { QString("D"), QVariant(true), QVariant(QColor(2,2,2)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,2 })));
+  }
+
+  void CheckModelGroup1Default(const QmitkMultiLabelTreeModel& model)
+  {
+    CPPUNIT_ASSERT(CheckModelRow(model, { 1 }, { QString("Group 1"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 1 })));
+  }
+
+  void CheckModelGroup2Default(const QmitkMultiLabelTreeModel& model)
+  {
+    CPPUNIT_ASSERT(CheckModelRow(model, { 2 }, { QString("Group 2"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(1, model.rowCount(GetIndex(model, { 2 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 2,0 }, { QString("B"), QVariant(true), QVariant(QColor(9,9,9)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 2,0 })));
+  }
+
+  void CheckModelDefault(const QmitkMultiLabelTreeModel& model)
+  {
+    CPPUNIT_ASSERT_EQUAL(3, model.rowCount(QModelIndex()));
+    CheckModelGroup0Default(model);
+    CheckModelGroup1Default(model);
+    CheckModelGroup2Default(model);
+  }
+
+  void NullTest()
+  {
+    QmitkMultiLabelTreeModel model(nullptr);
+
+    CPPUNIT_ASSERT(nullptr == model.GetSegmentation());
+  }
+
+  void GetterSetterTest()
+  {
+    QmitkMultiLabelTreeModel model(nullptr);
+    model.SetSegmentation(m_Segmentation);
+
+    CheckModelDefault(model);
+
+    model.SetSegmentation(nullptr);
+    CPPUNIT_ASSERT(nullptr == model.GetSegmentation());
+    CPPUNIT_ASSERT(false == model.hasChildren(QModelIndex()));
+  }
+
+  void AddingLabelTest()
+  {
+    QmitkMultiLabelTreeModel model(nullptr);
+    model.SetSegmentation(m_Segmentation);
+
+    //Add label instance (not visible) to labelwith multiple instances (at the end)
+    m_Segmentation->SetActiveLayer(0);
+    auto newLabel = CreateLabel("A", 100);
+    newLabel->SetVisible(false);
+    m_Segmentation->GetActiveLabelSet()->AddLabel(newLabel);
+
+    CPPUNIT_ASSERT_EQUAL(3, model.rowCount(QModelIndex()));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0 }, { QString("Group 0"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(3, model.rowCount(GetIndex(model, { 0 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,0 }, { QString("A (4 instances)"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(4, model.rowCount(GetIndex(model, { 0,0 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,0,0 }, { QString("A #1"), QVariant(true), QVariant(QColor(1,1,1)), QVariant(true) }));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,0,1 }, { QString("A #5"), QVariant(true), QVariant(QColor(5,5,5)), QVariant(true) }));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,0,2 }, { QString("A #10"), QVariant(true), QVariant(QColor(10,10,10)), QVariant(true) }));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,0,3 }, { QString("A #100"), QVariant(true), QVariant(QColor(100,100,100)), QVariant(false) }));
+
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,1 }, { QString("B"), QVariant(true), QVariant(QColor(4,4,4)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,1 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,2 }, { QString("D"), QVariant(true), QVariant(QColor(2,2,2)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,2 })));
+
+    CheckModelGroup1Default(model);
+    CheckModelGroup2Default(model);
+
+    //Add label instance (not locked) to label with multiple instances (in between)
+    m_Segmentation->SetActiveLayer(0);
+    newLabel = CreateLabel("A", 7);
+    newLabel->SetLocked(false);
+    m_Segmentation->GetActiveLabelSet()->AddLabel(newLabel);
+
+    CPPUNIT_ASSERT_EQUAL(3, model.rowCount(QModelIndex()));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0 }, { QString("Group 0"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(3, model.rowCount(GetIndex(model, { 0 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,0 }, { QString("A (5 instances)"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(5, model.rowCount(GetIndex(model, { 0,0 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,0,0 }, { QString("A #1"), QVariant(true), QVariant(QColor(1,1,1)), QVariant(true) }));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,0,1 }, { QString("A #5"), QVariant(true), QVariant(QColor(5,5,5)), QVariant(true) }));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,0,2 }, { QString("A #7"), QVariant(false), QVariant(QColor(7,7,7)), QVariant(true) }));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,0,3 }, { QString("A #10"), QVariant(true), QVariant(QColor(10,10,10)), QVariant(true) }));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,0,4 }, { QString("A #100"), QVariant(true), QVariant(QColor(100,100,100)), QVariant(false) }));
+
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,1 }, { QString("B"), QVariant(true), QVariant(QColor(4,4,4)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,1 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,2 }, { QString("D"), QVariant(true), QVariant(QColor(2,2,2)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,2 })));
+
+    CheckModelGroup1Default(model);
+    CheckModelGroup2Default(model);
+
+    //reset everything
+    m_Segmentation = GenerateSegmentation();
+    PopulateSegmentation(m_Segmentation);
+    model.SetSegmentation(m_Segmentation);
+
+    //Add label instance to an empty group
+    m_Segmentation->SetActiveLayer(1);
+    newLabel = CreateLabel("A", 3);
+    m_Segmentation->GetActiveLabelSet()->AddLabel(newLabel);
+
+    CPPUNIT_ASSERT_EQUAL(3, model.rowCount(QModelIndex()));
+    CheckModelGroup0Default(model);
+    CPPUNIT_ASSERT(CheckModelRow(model, { 1 }, { QString("Group 1"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(1, model.rowCount(GetIndex(model, { 1 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 1,0 }, { QString("A"), QVariant(true), QVariant(QColor(3,3,3)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 1,0 })));
+
+    CheckModelGroup2Default(model);
+  }
+
+  void AddingLayerTest()
+  {
+    QmitkMultiLabelTreeModel model(nullptr);
+    model.SetSegmentation(m_Segmentation);
+
+    m_Segmentation->AddLayer();
+
+    CPPUNIT_ASSERT_EQUAL(4, model.rowCount(QModelIndex()));
+    CheckModelGroup0Default(model);
+    CheckModelGroup1Default(model);
+    CheckModelGroup2Default(model);
+    CPPUNIT_ASSERT(CheckModelRow(model, { 3 }, { QString("Group 3"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 3 })));
+  }
+
+  void RemovingLabelTest()
+  {
+    QmitkMultiLabelTreeModel model(nullptr);
+    model.SetSegmentation(m_Segmentation);
+    //remove label instance from label with multiple instances (middel)
+    m_Segmentation->RemoveLabel(5);
+    CPPUNIT_ASSERT_EQUAL(3, model.rowCount(QModelIndex()));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0 }, { QString("Group 0"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(3, model.rowCount(GetIndex(model, { 0 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,0 }, { QString("A (2 instances)"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(2, model.rowCount(GetIndex(model, { 0,0 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,0,0 }, { QString("A #1"), QVariant(true), QVariant(QColor(1,1,1)), QVariant(true) }));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,0,1 }, { QString("A #10"), QVariant(true), QVariant(QColor(10,10,10)), QVariant(true) }));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,1 }, { QString("B"), QVariant(true), QVariant(QColor(4,4,4)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,1 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,2 }, { QString("D"), QVariant(true), QVariant(QColor(2,2,2)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,2 })));
+    CheckModelGroup1Default(model);
+    CheckModelGroup2Default(model);
+
+    //remove label instance from label with multiple instances (first)
+    m_Segmentation->RemoveLabel(1);
+    CPPUNIT_ASSERT_EQUAL(3, model.rowCount(QModelIndex()));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0 }, { QString("Group 0"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(3, model.rowCount(GetIndex(model, { 0 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,0 }, { QString("A"), QVariant(true), QVariant(QColor(10,10,10)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,0 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,1 }, { QString("B"), QVariant(true), QVariant(QColor(4,4,4)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,1 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,2 }, { QString("D"), QVariant(true), QVariant(QColor(2,2,2)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,2 })));
+    CheckModelGroup1Default(model);
+    CheckModelGroup2Default(model);
+
+
+    //reset everything
+    m_Segmentation = GenerateSegmentation();
+    PopulateSegmentation(m_Segmentation);
+    model.SetSegmentation(m_Segmentation);
+
+    //remove label instance from label with multiple instances (at the end)
+    m_Segmentation->RemoveLabel(10);
+    CPPUNIT_ASSERT_EQUAL(3, model.rowCount(QModelIndex()));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0 }, { QString("Group 0"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(3, model.rowCount(GetIndex(model, { 0 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,0 }, { QString("A (2 instances)"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(2, model.rowCount(GetIndex(model, { 0,0 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,0,0 }, { QString("A #1"), QVariant(true), QVariant(QColor(1,1,1)), QVariant(true) }));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,0,1 }, { QString("A #5"), QVariant(true), QVariant(QColor(5,5,5)), QVariant(true) }));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,1 }, { QString("B"), QVariant(true), QVariant(QColor(4,4,4)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,1 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,2 }, { QString("D"), QVariant(true), QVariant(QColor(2,2,2)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,2 })));
+    CheckModelGroup1Default(model);
+    CheckModelGroup2Default(model);
+
+    //reset everything
+    m_Segmentation = GenerateSegmentation();
+    PopulateSegmentation(m_Segmentation);
+    model.SetSegmentation(m_Segmentation);
+
+    //remove label instance from label with only one instance
+    m_Segmentation->RemoveLabel(9);
+    CPPUNIT_ASSERT_EQUAL(3, model.rowCount(QModelIndex()));
+    CheckModelGroup0Default(model);
+    CheckModelGroup1Default(model);
+    CPPUNIT_ASSERT(CheckModelRow(model, { 2 }, { QString("Group 2"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 2 })));
+  }
+
+  void RemovingLayerTest()
+  {
+    QmitkMultiLabelTreeModel model(nullptr);
+    model.SetSegmentation(m_Segmentation);
+    //remove group in the middle
+    m_Segmentation->SetActiveLayer(1);
+    m_Segmentation->RemoveLayer();
+    CPPUNIT_ASSERT_EQUAL(2, model.rowCount(QModelIndex()));
+    CheckModelGroup0Default(model);
+    CPPUNIT_ASSERT(CheckModelRow(model, { 1 }, { QString("Group 1"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(1, model.rowCount(GetIndex(model, { 1 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 1,0 }, { QString("B"), QVariant(true), QVariant(QColor(9,9,9)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 1,0 })));
+
+    //remove groups in the end
+    m_Segmentation->SetActiveLayer(1);
+    m_Segmentation->RemoveLayer();
+    CPPUNIT_ASSERT_EQUAL(1, model.rowCount(QModelIndex()));
+    CheckModelGroup0Default(model);
+
+    //remove all groups
+    m_Segmentation->SetActiveLayer(0);
+    m_Segmentation->RemoveLayer();
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(QModelIndex()));
+
+    //reset everything
+    m_Segmentation = GenerateSegmentation();
+    PopulateSegmentation(m_Segmentation);
+    model.SetSegmentation(m_Segmentation);
+
+    //remove first group
+    m_Segmentation->SetActiveLayer(0);
+    m_Segmentation->RemoveLayer();
+    CPPUNIT_ASSERT_EQUAL(2, model.rowCount(QModelIndex()));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0 }, { QString("Group 0"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 1 }, { QString("Group 1"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(1, model.rowCount(GetIndex(model, { 1 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 1,0 }, { QString("B"), QVariant(true), QVariant(QColor(9,9,9)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 1,0 })));
+  }
+
+
+  void ModifyLabelNameTest()
+  {
+    QmitkMultiLabelTreeModel model(nullptr);
+    model.SetSegmentation(m_Segmentation);
+    //move from multiple instance to new label in the middle
+    auto label = m_Segmentation->GetLabel(5,0);
+    label->SetName("C");
+
+    CPPUNIT_ASSERT_EQUAL(3, model.rowCount(QModelIndex()));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0 }, { QString("Group 0"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(4, model.rowCount(GetIndex(model, { 0 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,0 }, { QString("A (2 instances)"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(2, model.rowCount(GetIndex(model, { 0,0 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,0,0 }, { QString("A #1"), QVariant(true), QVariant(QColor(1,1,1)), QVariant(true) }));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,0,1 }, { QString("A #10"), QVariant(true), QVariant(QColor(10,10,10)), QVariant(true) }));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,1 }, { QString("B"), QVariant(true), QVariant(QColor(4,4,4)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,1 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,2 }, { QString("C"), QVariant(true), QVariant(QColor(5,5,5)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,2 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,3 }, { QString("D"), QVariant(true), QVariant(QColor(2,2,2)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,3 })));
+    CheckModelGroup1Default(model);
+    CheckModelGroup2Default(model);
+
+    //move from multiple instance to new label at the end
+    label = m_Segmentation->GetLabel(10, 0);
+    label->SetName("E");
+
+    CPPUNIT_ASSERT_EQUAL(3, model.rowCount(QModelIndex()));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0 }, { QString("Group 0"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(5, model.rowCount(GetIndex(model, { 0 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,0 }, { QString("A"), QVariant(true), QVariant(QColor(1,1,1)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,0 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,1 }, { QString("B"), QVariant(true), QVariant(QColor(4,4,4)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,1 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,2 }, { QString("C"), QVariant(true), QVariant(QColor(5,5,5)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,2 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,3 }, { QString("D"), QVariant(true), QVariant(QColor(2,2,2)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,3 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,4 }, { QString("E"), QVariant(true), QVariant(QColor(10,10,10)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,4 })));
+    CheckModelGroup1Default(model);
+    CheckModelGroup2Default(model);
+
+    //move last instance to new label
+    label = m_Segmentation->GetLabel(10, 0);
+    label->SetName("F");
+
+    CPPUNIT_ASSERT_EQUAL(3, model.rowCount(QModelIndex()));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0 }, { QString("Group 0"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(5, model.rowCount(GetIndex(model, { 0 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,0 }, { QString("A"), QVariant(true), QVariant(QColor(1,1,1)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,0 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,1 }, { QString("B"), QVariant(true), QVariant(QColor(4,4,4)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,1 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,2 }, { QString("C"), QVariant(true), QVariant(QColor(5,5,5)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,2 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,3 }, { QString("D"), QVariant(true), QVariant(QColor(2,2,2)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,3 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,4 }, { QString("F"), QVariant(true), QVariant(QColor(10,10,10)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,4 })));
+    CheckModelGroup1Default(model);
+    CheckModelGroup2Default(model);
+
+    //move last instance to an existing label
+    label->SetName("B");
+
+    CPPUNIT_ASSERT_EQUAL(3, model.rowCount(QModelIndex()));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0 }, { QString("Group 0"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(4, model.rowCount(GetIndex(model, { 0 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,0 }, { QString("A"), QVariant(true), QVariant(QColor(1,1,1)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,0 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,1 }, { QString("B (2 instances)"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(2, model.rowCount(GetIndex(model, { 0,1 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,1,0 }, { QString("B #4"), QVariant(true), QVariant(QColor(4,4,4)), QVariant(true) }));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,1,1 }, { QString("B #10"), QVariant(true), QVariant(QColor(10,10,10)), QVariant(true) }));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,2 }, { QString("C"), QVariant(true), QVariant(QColor(5,5,5)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,2 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 0,3 }, { QString("D"), QVariant(true), QVariant(QColor(2,2,2)), QVariant(true) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 0,3 })));
+    CheckModelGroup1Default(model);
+    CheckModelGroup2Default(model);
+  }
+
+  void ModifyLabelTest()
+  {
+    QmitkMultiLabelTreeModel model(nullptr);
+    model.SetSegmentation(m_Segmentation);
+
+    auto label = m_Segmentation->GetLabel(9, 2);
+
+    //check single instance modifications
+    label->SetVisible(false);
+    CPPUNIT_ASSERT_EQUAL(3, model.rowCount(QModelIndex()));
+    CheckModelGroup0Default(model);
+    CheckModelGroup1Default(model);
+    CPPUNIT_ASSERT(CheckModelRow(model, { 2 }, { QString("Group 2"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(1, model.rowCount(GetIndex(model, { 2 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 2,0 }, { QString("B"), QVariant(true), QVariant(QColor(9,9,9)), QVariant(false) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 2,0 })));
+
+    label->SetLocked(false);
+    label->SetColor(mitk::Color(22 / 255.));
+    CPPUNIT_ASSERT_EQUAL(3, model.rowCount(QModelIndex()));
+    CheckModelGroup0Default(model);
+    CheckModelGroup1Default(model);
+    CPPUNIT_ASSERT(CheckModelRow(model, { 2 }, { QString("Group 2"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(1, model.rowCount(GetIndex(model, { 2 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 2,0 }, { QString("B"), QVariant(false), QVariant(QColor(22,22,22)), QVariant(false) }));
+    CPPUNIT_ASSERT_EQUAL(0, model.rowCount(GetIndex(model, { 2,0 })));
+
+    //check instance modifications with multi instance label
+    m_Segmentation->SetActiveLayer(2);
+    m_Segmentation->GetActiveLabelSet()->AddLabel(CreateLabel("B", 33));
+    label->SetVisible(true);
+    CPPUNIT_ASSERT_EQUAL(3, model.rowCount(QModelIndex()));
+    CheckModelGroup0Default(model);
+    CheckModelGroup1Default(model);
+    CPPUNIT_ASSERT(CheckModelRow(model, { 2 }, { QString("Group 2"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(1, model.rowCount(GetIndex(model, { 2 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 2,0 }, { QString("B (2 instances)"), QVariant(), QVariant(), QVariant() }));
+    CPPUNIT_ASSERT_EQUAL(2, model.rowCount(GetIndex(model, { 2,0 })));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 2,0,0 }, { QString("B #9"), QVariant(false), QVariant(QColor(22,22,22)), QVariant(true) }));
+    CPPUNIT_ASSERT(CheckModelRow(model, { 2,0,1 }, { QString("B #33"), QVariant(true), QVariant(QColor(33,33,33)), QVariant(true) }));
+  }
+
+};
+
+MITK_TEST_SUITE_REGISTRATION(QmitkMultiLabelTreeModel)
diff --git a/Modules/SegmentationUI/test/files.cmake b/Modules/SegmentationUI/test/files.cmake
new file mode 100644
index 0000000000..f7a7e63e96
--- /dev/null
+++ b/Modules/SegmentationUI/test/files.cmake
@@ -0,0 +1,3 @@
+set(MODULE_TESTS
+  QmitkMultiLabelTreeModelTest.cpp
+)
diff --git a/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.cpp b/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.cpp
index 4d58d99330..015595cda7 100644
--- a/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.cpp
+++ b/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.cpp
@@ -1,1400 +1,1402 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include <mitkSurfaceInterpolationController.h>
 
 #include <mitkCreateDistanceImageFromSurfaceFilter.h>
 #include <mitkComputeContourSetNormalsFilter.h>
 #include <mitkImageAccessByItk.h>
 #include <mitkImagePixelReadAccessor.h>
 #include <mitkImageTimeSelector.h>
 #include <mitkImageToSurfaceFilter.h>
 #include <mitkLabelSetImage.h>
 #include <mitkMemoryUtilities.h>
 #include <mitkNodePredicateDataUID.h>
 #include <mitkNodePredicateProperty.h>
 #include <mitkPlanarCircle.h>
 #include <mitkPlaneGeometry.h>
 #include <mitkReduceContourSetFilter.h>
 
 #include <vtkFieldData.h>
 #include <vtkMath.h>
 #include <vtkPolygon.h>
 
 // Check whether the given contours are coplanar
 bool ContoursCoplanar(mitk::SurfaceInterpolationController::ContourPositionInformation leftHandSide,
                       mitk::SurfaceInterpolationController::ContourPositionInformation rightHandSide)
 {
   // Here we check two things:
   // 1. Whether the normals of both contours are at least parallel
   // 2. Whether both contours lie in the same plane
 
   // Check for coplanarity:
   // a. Span a vector between two points one from each contour
   // b. Calculate dot product for the vector and one of the normals
   // c. If the dot is zero the two vectors are orthogonal and the contours are coplanar
 
   double vec[3];
   vec[0] = leftHandSide.ContourPoint[0] - rightHandSide.ContourPoint[0];
   vec[1] = leftHandSide.ContourPoint[1] - rightHandSide.ContourPoint[1];
   vec[2] = leftHandSide.ContourPoint[2] - rightHandSide.ContourPoint[2];
   double n[3];
   n[0] = rightHandSide.ContourNormal[0];
   n[1] = rightHandSide.ContourNormal[1];
   n[2] = rightHandSide.ContourNormal[2];
   double dot = vtkMath::Dot(n, vec);
 
   double n2[3];
   n2[0] = leftHandSide.ContourNormal[0];
   n2[1] = leftHandSide.ContourNormal[1];
   n2[2] = leftHandSide.ContourNormal[2];
 
   // The normals of both contours have to be parallel but not of the same orientation
   double lengthLHS = leftHandSide.ContourNormal.GetNorm();
   double lengthRHS = rightHandSide.ContourNormal.GetNorm();
   double dot2 = vtkMath::Dot(n, n2);
   bool contoursParallel = mitk::Equal(fabs(lengthLHS * lengthRHS), fabs(dot2), 0.001);
 
   if (mitk::Equal(dot, 0.0, 0.001) && contoursParallel)
     return true;
   else
     return false;
 }
 
 mitk::SurfaceInterpolationController::ContourPositionInformation CreateContourPositionInformation(
   mitk::Surface::Pointer contour, const mitk::PlaneGeometry* planeGeometry)
 {
   mitk::SurfaceInterpolationController::ContourPositionInformation contourInfo;
   contourInfo.Contour = contour;
   mitk::ScalarType n[3];
   vtkPolygon::ComputeNormal(contour->GetVtkPolyData()->GetPoints(), n);
   contourInfo.ContourNormal = n;
   contourInfo.Pos = -1;
   contourInfo.TimeStep = std::numeric_limits<long unsigned int>::max();
   contourInfo.Plane = const_cast<mitk::PlaneGeometry *>(planeGeometry);
 
   auto contourIntArray = vtkIntArray::SafeDownCast( contour->GetVtkPolyData()->GetFieldData()->GetAbstractArray(0) );
 
   if (contourIntArray->GetSize() < 2)
   {
     MITK_ERROR << "In CreateContourPositionInformation. The contourIntArray is empty.";
   }
   contourInfo.LabelValue = contourIntArray->GetValue(0);
   contourInfo.LayerValue = contourIntArray->GetValue(1);
 
   if (contourIntArray->GetSize() >= 3)
   {
     contourInfo.TimeStep = contourIntArray->GetValue(2);
   }
 
   contourInfo.SliceIndex = 0;
 
   return contourInfo;
 };
 
 mitk::SurfaceInterpolationController::SurfaceInterpolationController()
   : m_SelectedSegmentation(nullptr),
     m_CurrentTimePoint(0.),
     m_ContourIndex(0),
     m_ContourPosIndex(0),
     m_NumberOfLayersInCurrentSegmentation(0),
     m_PreviousActiveLabelValue(0),
     m_CurrentActiveLabelValue(0),
     m_PreviousLayerIndex(0),
     m_CurrentLayerIndex(0)
 {
   m_DistanceImageSpacing = 0.0;
   m_ReduceFilter = ReduceContourSetFilter::New();
   m_NormalsFilter = ComputeContourSetNormalsFilter::New();
   m_InterpolateSurfaceFilter = CreateDistanceImageFromSurfaceFilter::New();
   // m_TimeSelector = ImageTimeSelector::New();
 
   m_ReduceFilter->SetUseProgressBar(false);
   //  m_ReduceFilter->SetProgressStepSize(1);
   m_NormalsFilter->SetUseProgressBar(true);
   m_NormalsFilter->SetProgressStepSize(1);
   m_InterpolateSurfaceFilter->SetUseProgressBar(true);
   m_InterpolateSurfaceFilter->SetProgressStepSize(7);
 
   m_Contours = Surface::New();
 
   m_PolyData = vtkSmartPointer<vtkPolyData>::New();
   vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
   m_PolyData->SetPoints(points);
 
   m_NumberOfConnectionsAdded = 0;
 
   m_InterpolationResult = nullptr;
   m_CurrentNumberOfReducedContours = 0;
 }
 
 mitk::SurfaceInterpolationController::~SurfaceInterpolationController()
 {
   // Removing all observers
   this->RemoveObservers();
 }
 
 void mitk::SurfaceInterpolationController::RemoveObservers()
 {
   // Removing all observers
   auto dataIter = m_SegmentationObserverTags.begin();
   for (; dataIter != m_SegmentationObserverTags.end(); ++dataIter)
   {
     (*dataIter).first->RemoveObserver((*dataIter).second);
   }
   m_SegmentationObserverTags.clear();
 }
 
 mitk::SurfaceInterpolationController *mitk::SurfaceInterpolationController::GetInstance()
 {
   static mitk::SurfaceInterpolationController::Pointer m_Instance;
 
   if (m_Instance.IsNull())
   {
     m_Instance = SurfaceInterpolationController::New();
   }
   return m_Instance;
 }
 
 void mitk::SurfaceInterpolationController::AddNewContour(mitk::Surface::Pointer newContour)
 {
   if (newContour->GetVtkPolyData()->GetNumberOfPoints() > 0)
   {
     ContourPositionInformation contourInfo = CreateContourPositionInformation(newContour, nullptr);
     this->AddToInterpolationPipeline(contourInfo);
     this->Modified();
   }
 }
 
 void mitk::SurfaceInterpolationController::AddNewContours(const std::vector<mitk::Surface::Pointer>& newContours,
                                                           std::vector<const mitk::PlaneGeometry*>& contourPlanes,
                                                           bool reinitializationAction)
 {
+  if (nullptr == m_SelectedSegmentation) return;
+
   if (newContours.size() != contourPlanes.size())
   {
     MITK_ERROR << "SurfaceInterpolationController::AddNewContours. contourPlanes and newContours are not of the same size.";
   }
 
   for (size_t i = 0; i < newContours.size(); ++i)
   {
     const auto &newContour = newContours[i];
 
     const mitk::PlaneGeometry * planeGeometry = contourPlanes[i];
     if (newContour->GetVtkPolyData()->GetNumberOfPoints() > 0)
     {
       auto contourInfo = CreateContourPositionInformation(newContour, planeGeometry);
       if (!reinitializationAction)
       {
         contourInfo.ContourPoint = this->ComputeInteriorPointOfContour(contourInfo,
                                               dynamic_cast<mitk::LabelSetImage*>(m_SelectedSegmentation) );
       }
       else
       {
         auto vtkPolyData = contourInfo.Contour->GetVtkPolyData();
         auto pointVtkArray = vtkDoubleArray::SafeDownCast(vtkPolyData->GetFieldData()->GetAbstractArray(1));
         mitk::ScalarType *ptArr = new mitk::ScalarType[3];
         for (int i = 0; i < pointVtkArray->GetSize(); ++i)
           ptArr[i] = pointVtkArray->GetValue(i);
 
         mitk::Point3D pt3D;
         pt3D.FillPoint(ptArr);
         contourInfo.ContourPoint = pt3D;
       }
 
       this->AddToInterpolationPipeline(contourInfo, reinitializationAction);
     }
   }
   this->Modified();
 }
 
 mitk::DataNode* mitk::SurfaceInterpolationController::GetSegmentationImageNode()
 {
     DataNode* segmentationNode = nullptr;
     mitk::NodePredicateDataUID::Pointer dataUIDPredicate = mitk::NodePredicateDataUID::New(m_SelectedSegmentation->GetUID());
     auto dataNodeObjects = m_DataStorage->GetSubset(dataUIDPredicate);
 
     if (dataNodeObjects->Size() != 0)
     {
       for (auto it = dataNodeObjects->Begin(); it != dataNodeObjects->End(); ++it)
       {
         segmentationNode = it->Value();
       }
     }
     else
     {
       MITK_ERROR << "Unable to find the labelSetImage with the desired UID.";
     }
     return segmentationNode;
 }
 
 void mitk::SurfaceInterpolationController::AddPlaneGeometryNodeToDataStorage(const ContourPositionInformation& contourInfo)
 {
   auto planeGeometry = contourInfo.Plane;
   auto planeGeometryData = mitk::PlanarCircle::New();
   planeGeometryData->SetPlaneGeometry(planeGeometry);
   mitk::Point2D p1;
   planeGeometry->Map(planeGeometry->GetCenter(), p1);
   planeGeometryData->PlaceFigure(p1);
   planeGeometryData->SetCurrentControlPoint(p1);
   planeGeometryData->SetProperty("initiallyplaced", mitk::BoolProperty::New(true));
   if (planeGeometry)
   {
     auto segmentationNode = this->GetSegmentationImageNode();
     auto isContourPlaneGeometry = mitk::NodePredicateProperty::New("isContourPlaneGeometry", mitk::BoolProperty::New(true));
 
     mitk::DataStorage::SetOfObjects::ConstPointer contourNodes =
       m_DataStorage->GetDerivations(segmentationNode, isContourPlaneGeometry);
 
     auto contourFound = false;
 
     //  Go through the pre-existing contours and check if the contour position matches them.
     for (auto it = contourNodes->Begin(); it != contourNodes->End(); ++it)
     {
       auto layerID = dynamic_cast<mitk::UIntProperty *>(it->Value()->GetProperty("layerID"))->GetValue();
       auto labelID = dynamic_cast<mitk::UShortProperty *>(it->Value()->GetProperty("labelID"))->GetValue();
       auto posID = dynamic_cast<mitk::IntProperty *>(it->Value()->GetProperty("position"))->GetValue();
       bool sameLayer = (layerID == contourInfo.LayerValue);
       bool sameLabel = (labelID == contourInfo.LabelValue);
       bool samePos = (posID == contourInfo.Pos);
 
       if (samePos & sameLabel & sameLayer)
       {
         contourFound = true;
         it->Value()->SetData(planeGeometryData);
         break;
       }
     }
 
     if (!m_SelectedSegmentation->GetTimeGeometry()->IsValidTimePoint(m_CurrentTimePoint))
     {
       MITK_ERROR << "Invalid time point requested in AddPlaneGeometryNodeToDataStorage.";
       return;
     }
 
     const auto currentTimeStep = m_SelectedSegmentation->GetTimeGeometry()->TimePointToTimeStep(m_CurrentTimePoint);
 
     //  Go through the contourPlaneGeometry Data and add the segmentationNode to it.
     if (!contourFound)
     {
       std::string contourName = "contourPlane " + std::to_string(m_ContourIndex);
 
       auto contourPlaneGeometryDataNode = mitk::DataNode::New();
       contourPlaneGeometryDataNode->SetData(planeGeometryData);
 
       //  No need to change properties
       contourPlaneGeometryDataNode->SetProperty("helper object", mitk::BoolProperty::New(false));
       contourPlaneGeometryDataNode->SetProperty("hidden object", mitk::BoolProperty::New(true));
       contourPlaneGeometryDataNode->SetProperty("isContourPlaneGeometry", mitk::BoolProperty::New(true));
       contourPlaneGeometryDataNode->SetVisibility(false);
 
       //  Need to change properties
       contourPlaneGeometryDataNode->SetProperty("name", mitk::StringProperty::New(contourName) );
       contourPlaneGeometryDataNode->SetProperty("layerID", mitk::UIntProperty::New(contourInfo.LayerValue));
       contourPlaneGeometryDataNode->SetProperty("labelID", mitk::UShortProperty::New(contourInfo.LabelValue));
       contourPlaneGeometryDataNode->SetProperty("position", mitk::IntProperty::New(contourInfo.Pos));
       contourPlaneGeometryDataNode->SetProperty("timeStep", mitk::IntProperty::New(currentTimeStep));
 
       contourPlaneGeometryDataNode->SetProperty("px", mitk::DoubleProperty::New(contourInfo.ContourPoint[0]));
       contourPlaneGeometryDataNode->SetProperty("py", mitk::DoubleProperty::New(contourInfo.ContourPoint[1]));
       contourPlaneGeometryDataNode->SetProperty("pz", mitk::DoubleProperty::New(contourInfo.ContourPoint[2]));
 
       m_DataStorage->Add(contourPlaneGeometryDataNode, segmentationNode);
     }
   }
 }
 
 void mitk::SurfaceInterpolationController::AddToInterpolationPipeline(ContourPositionInformation& contourInfo, bool reinitializationAction)
 {
   if (!m_SelectedSegmentation)
     return;
 
   if (!m_SelectedSegmentation->GetTimeGeometry()->IsValidTimePoint(m_CurrentTimePoint))
   {
     MITK_ERROR << "Invalid time point requested for interpolation pipeline.";
     return;
   }
 
   //  Get current time step either from the
   auto GetCurrentTimeStep = [=](ContourPositionInformation contourInfo)
   {
     if (reinitializationAction)
     {
       return contourInfo.TimeStep;
     }
     return static_cast<size_t>(m_SelectedSegmentation->GetTimeGeometry()->TimePointToTimeStep(m_CurrentTimePoint));
   };
   const auto currentTimeStep = GetCurrentTimeStep(contourInfo);
   auto GetContourLayerID = [=](ContourPositionInformation contourInfo)
   {
     unsigned int currentLayerID;
     if(reinitializationAction)
     {
       if (contourInfo.LayerValue == std::numeric_limits<unsigned int>::max())
       {
         MITK_ERROR << "In mitk::SurfaceInterpolationController::AddToInterpolationPipeline. Problem in finding layerID";
       }
         currentLayerID = contourInfo.LayerValue;
     }
     else
     {
       try
       {
         currentLayerID = dynamic_cast<mitk::LabelSetImage*>(m_SelectedSegmentation)->GetActiveLayer();
       }
       catch (const std::exception& e)
       {
         MITK_ERROR << "Unable to cast image to LabelSetImage. " << e.what() << '\n';
       }
     }
     return currentLayerID;
   };
 
   unsigned int currentLayerID = GetContourLayerID(contourInfo);
 
   ContourPositionInformationVec3D &currentImageContours = m_ListOfContours.at(m_SelectedSegmentation);
   ContourPositionInformationVec2D &currentTimeStepContoursList = currentImageContours.at(currentTimeStep);
   ContourPositionInformationList &currentContourList = currentTimeStepContoursList.at(currentLayerID);
 
   int replacementIndex = -1;
   int pos = -1;
   mitk::Surface* newContour = contourInfo.Contour;
 
   for (size_t i = 0; i < currentContourList.size(); i++)
   {
     auto& contourFromList = currentContourList.at(i);
     bool contoursAreCoplanar = ContoursCoplanar(contourInfo, contourFromList);
     bool contoursHaveSameLabel = contourInfo.LabelValue == contourFromList.LabelValue;
 
     //  Coplanar contours have the same "pos".
     if (contoursAreCoplanar)
     {
       pos = contourFromList.Pos;
       if (contoursHaveSameLabel)
       {
         replacementIndex = i;
       }
     }
   }
   //  The current contour has the same label and position as the current slice and a replacement is done.
   if (replacementIndex != -1)
   {
     contourInfo.Pos = pos;
     m_ListOfContours.at(m_SelectedSegmentation).at(currentTimeStep).at(currentLayerID).at(replacementIndex) = contourInfo;
 
     if (!reinitializationAction)
     {
       this->AddPlaneGeometryNodeToDataStorage(contourInfo);
     }
     return;
   }
 
   //  Case that there is no contour in the current slice with the current label
   if (pos == -1)
     pos = m_ContourPosIndex++;
 
   m_ContourIndex++;
   contourInfo.Pos = pos;
   m_ListOfContours.at(m_SelectedSegmentation).at(currentTimeStep).at(currentLayerID).push_back(contourInfo);
 
   if (contourInfo.Plane == nullptr)
   {
     MITK_ERROR << "contourInfo plane is null.";
   }
   if (!reinitializationAction)
   {
     this->AddPlaneGeometryNodeToDataStorage(contourInfo);
   }
 
   if (newContour->GetVtkPolyData()->GetNumberOfPoints() == 0)
   {
     this->RemoveContour(contourInfo);
     if  (m_ContourIndex > 0)
       m_ContourIndex--;
     if  (m_ContourIndex > 0)
       m_ContourIndex--;
   }
 }
 
 bool mitk::SurfaceInterpolationController::RemoveContour(ContourPositionInformation contourInfo)
 {
   if (!m_SelectedSegmentation)
   {
     return false;
   }
 
   if (!m_SelectedSegmentation->GetTimeGeometry()->IsValidTimePoint(m_CurrentTimePoint))
   {
     return false;
   }
 
   const auto currentTimeStep = m_SelectedSegmentation->GetTimeGeometry()->TimePointToTimeStep(m_CurrentTimePoint);
   unsigned int currentLayerID = 0;
   try
   {
     currentLayerID = dynamic_cast<mitk::LabelSetImage*>(m_SelectedSegmentation)->GetActiveLayer();
   }
   catch (const std::exception& e)
   {
     MITK_ERROR << e.what() << '\n';
   }
 
   auto it = m_ListOfContours.at(m_SelectedSegmentation).at(currentTimeStep).at(currentLayerID).begin();
   while (it != m_ListOfContours.at(m_SelectedSegmentation).at(currentTimeStep).at(currentLayerID).end())
   {
     const ContourPositionInformation &currentContour = (*it);
     if (ContoursCoplanar(currentContour, contourInfo))
     {
       m_ListOfContours.at(m_SelectedSegmentation).at(currentTimeStep).at(currentLayerID).erase(it);
       this->ReinitializeInterpolation();
       return true;
     }
     ++it;
   }
   return false;
 }
 
 const mitk::Surface *mitk::SurfaceInterpolationController::GetContour(const ContourPositionInformation &contourInfo)
 {
   if (!m_SelectedSegmentation)
   {
     return nullptr;
   }
 
   if (!m_SelectedSegmentation->GetTimeGeometry()->IsValidTimePoint(m_CurrentTimePoint))
   {
     return nullptr;
   }
   const auto currentTimeStep = m_SelectedSegmentation->GetTimeGeometry()->TimePointToTimeStep(m_CurrentTimePoint);
   const auto activeLayerID = dynamic_cast<mitk::LabelSetImage*>(m_SelectedSegmentation)->GetActiveLayer();
   const auto &contourList = m_ListOfContours.at(m_SelectedSegmentation).at(currentTimeStep).at(activeLayerID);
 
   for (auto &currentContour : contourList)
   {
     if (ContoursCoplanar(contourInfo, currentContour))
     {
       return currentContour.Contour;
     }
   }
   return nullptr;
 }
 
 unsigned int mitk::SurfaceInterpolationController::GetNumberOfContours()
 {
   if (!m_SelectedSegmentation)
   {
     return -1;
   }
 
   if (!m_SelectedSegmentation->GetTimeGeometry()->IsValidTimePoint(m_CurrentTimePoint))
   {
     return -1;
   }
   const auto currentTimeStep = m_SelectedSegmentation->GetTimeGeometry()->TimePointToTimeStep(m_CurrentTimePoint);
   auto contourDoubleList = m_ListOfContours.at(m_SelectedSegmentation).at(currentTimeStep);
 
   unsigned int numContours = 0;
   for (auto& contourList : contourDoubleList)
   {
 
     numContours += contourList.size();
   }
 
   return numContours;
 }
 
 void mitk::SurfaceInterpolationController::AddActiveLabelContoursForInterpolation(mitk::Label::PixelType activeLabel)
 {
   this->ReinitializeInterpolation();
 
   if (!m_SelectedSegmentation->GetTimeGeometry()->IsValidTimePoint(m_CurrentTimePoint))
   {
     MITK_ERROR << "Invalid time point requested for interpolation pipeline.";
     return;
   }
   const auto currentTimeStep = m_SelectedSegmentation->GetTimeGeometry()->TimePointToTimeStep(m_CurrentTimePoint);
 
   unsigned int currentLayerID = 0;
   try
   {
     currentLayerID = dynamic_cast<mitk::LabelSetImage*>(m_SelectedSegmentation)->GetActiveLayer();
   }
   catch (const std::exception& e)
   {
     MITK_ERROR << e.what() << '\n';
   }
 
   ContourPositionInformationVec3D &currentImageContours = m_ListOfContours.at(m_SelectedSegmentation);
 
   if (currentImageContours.size() <= currentTimeStep)
   {
     MITK_INFO << "Contours for current time step don't exist.";
     return;
   }
   ContourPositionInformationVec2D &currentTimeStepContoursList = currentImageContours.at(currentTimeStep);
 
   if (currentTimeStepContoursList.size() <= currentLayerID)
   {
     MITK_INFO << "Contours for current layer don't exist.";
     return;
   }
   ContourPositionInformationList &currentContours = currentTimeStepContoursList.at(currentLayerID);
 
   for (size_t i = 0; i < currentContours.size(); ++i)
   {
     if (currentContours.at(i).LabelValue == activeLabel)
     {
       m_ListOfInterpolationSessions.at(m_SelectedSegmentation).at(currentTimeStep).push_back(currentContours.at(i));
       m_ReduceFilter->SetInput(m_ListOfInterpolationSessions.at(m_SelectedSegmentation).at(currentTimeStep).size()-1, currentContours.at(i).Contour);
     }
   }
 }
 
 void mitk::SurfaceInterpolationController::Interpolate()
 {
   if (!m_SelectedSegmentation->GetTimeGeometry()->IsValidTimePoint(m_CurrentTimePoint))
   {
     MITK_WARN << "No interpolation possible, currently selected timepoint is not in the time bounds of currently selected segmentation. Time point: " << m_CurrentTimePoint;
     m_InterpolationResult = nullptr;
     return;
   }
   const auto currentTimeStep = m_SelectedSegmentation->GetTimeGeometry()->TimePointToTimeStep(m_CurrentTimePoint);
   m_ReduceFilter->Update();
   m_CurrentNumberOfReducedContours = m_ReduceFilter->GetNumberOfOutputs();
 
   if (m_CurrentNumberOfReducedContours == 1)
   {
     vtkPolyData *tmp = m_ReduceFilter->GetOutput(0)->GetVtkPolyData();
     if (tmp == nullptr)
     {
       m_CurrentNumberOfReducedContours = 0;
     }
   }
 
   //  We use the timeSelector to get the segmentation image for the current segmentation.
   mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New();
   timeSelector->SetInput(m_SelectedSegmentation);
   timeSelector->SetTimeNr(currentTimeStep);
   timeSelector->SetChannelNr(0);
   timeSelector->Update();
 
   mitk::Image::Pointer refSegImage = timeSelector->GetOutput();
   itk::ImageBase<3>::Pointer itkImage = itk::ImageBase<3>::New();
   AccessFixedDimensionByItk_1(refSegImage, GetImageBase, 3, itkImage);
 
   m_NormalsFilter->SetSegmentationBinaryImage(refSegImage);
 
   for (size_t i = 0; i < m_CurrentNumberOfReducedContours; ++i)
   {
     mitk::Surface::Pointer reducedContour = m_ReduceFilter->GetOutput(i);
     reducedContour->DisconnectPipeline();
     m_NormalsFilter->SetInput(i, reducedContour);
     m_InterpolateSurfaceFilter->SetInput(i, m_NormalsFilter->GetOutput(i));
   }
 
   if (m_CurrentNumberOfReducedContours < 2)
   {
     // If no interpolation is possible reset the interpolation result
     MITK_INFO << "Interpolation impossible: not enough contours.";
     m_InterpolationResult = nullptr;
     return;
   }
 
   // Setting up progress bar
   mitk::ProgressBar::GetInstance()->AddStepsToDo(10);
 
   // create a surface from the distance-image
   mitk::ImageToSurfaceFilter::Pointer imageToSurfaceFilter = mitk::ImageToSurfaceFilter::New();
   imageToSurfaceFilter->SetInput(m_InterpolateSurfaceFilter->GetOutput());
   imageToSurfaceFilter->SetThreshold(0);
   imageToSurfaceFilter->SetSmooth(true);
   imageToSurfaceFilter->SetSmoothIteration(1);
   imageToSurfaceFilter->Update();
 
   mitk::Surface::Pointer interpolationResult = mitk::Surface::New();
   interpolationResult->Expand(m_SelectedSegmentation->GetTimeSteps());
 
   auto geometry = m_SelectedSegmentation->GetTimeGeometry()->Clone();
   geometry->ReplaceTimeStepGeometries(mitk::Geometry3D::New());
   interpolationResult->SetTimeGeometry(geometry);
 
   interpolationResult->SetVtkPolyData(imageToSurfaceFilter->GetOutput()->GetVtkPolyData(), currentTimeStep);
   m_InterpolationResult = interpolationResult;
 
   m_DistanceImageSpacing = m_InterpolateSurfaceFilter->GetDistanceImageSpacing();
 
   auto* contoursGeometry = static_cast<mitk::ProportionalTimeGeometry*>(m_Contours->GetTimeGeometry());
   auto timeBounds = geometry->GetTimeBounds(currentTimeStep);
   contoursGeometry->SetFirstTimePoint(timeBounds[0]);
   contoursGeometry->SetStepDuration(timeBounds[1] - timeBounds[0]);
 
   // Last progress step
   mitk::ProgressBar::GetInstance()->Progress(20);
   m_InterpolationResult->DisconnectPipeline();
 }
 
 mitk::Surface::Pointer mitk::SurfaceInterpolationController::GetInterpolationResult()
 {
   return m_InterpolationResult;
 }
 
 mitk::Surface *mitk::SurfaceInterpolationController::GetContoursAsSurface()
 {
   return m_Contours;
 }
 
 
 void mitk::SurfaceInterpolationController::SetDataStorage(DataStorage::Pointer ds)
 {
   m_DataStorage = ds;
 }
 
 void mitk::SurfaceInterpolationController::SetMinSpacing(double minSpacing)
 {
   m_ReduceFilter->SetMinSpacing(minSpacing);
 }
 
 void mitk::SurfaceInterpolationController::SetMaxSpacing(double maxSpacing)
 {
   m_ReduceFilter->SetMaxSpacing(maxSpacing);
   m_NormalsFilter->SetMaxSpacing(maxSpacing);
 }
 
 void mitk::SurfaceInterpolationController::SetDistanceImageVolume(unsigned int distImgVolume)
 {
   m_InterpolateSurfaceFilter->SetDistanceImageVolume(distImgVolume);
 }
 
 mitk::Image::Pointer mitk::SurfaceInterpolationController::GetCurrentSegmentation()
 {
   return m_SelectedSegmentation;
 }
 
 mitk::Image *mitk::SurfaceInterpolationController::GetImage()
 {
   return m_InterpolateSurfaceFilter->GetOutput();
 }
 
 double mitk::SurfaceInterpolationController::EstimatePortionOfNeededMemory()
 {
   double numberOfPointsAfterReduction = m_ReduceFilter->GetNumberOfPointsAfterReduction() * 3;
   double sizeOfPoints = pow(numberOfPointsAfterReduction, 2) * sizeof(double);
   double totalMem = mitk::MemoryUtilities::GetTotalSizeOfPhysicalRam();
   double percentage = sizeOfPoints / totalMem;
   return percentage;
 }
 
 unsigned int mitk::SurfaceInterpolationController::GetNumberOfInterpolationSessions()
 {
   return m_ListOfInterpolationSessions.size();
 }
 
 template <typename TPixel, unsigned int VImageDimension>
 void mitk::SurfaceInterpolationController::GetImageBase(itk::Image<TPixel, VImageDimension> *input,
                                                         itk::ImageBase<3>::Pointer &result)
 {
   result->Graft(input);
 }
 
 void mitk::SurfaceInterpolationController::SetCurrentSegmentationInterpolationList(mitk::Image::Pointer segmentation)
 {
   this->SetCurrentInterpolationSession(segmentation);
 }
 
 void mitk::SurfaceInterpolationController::SetCurrentInterpolationSession(mitk::Image::Pointer currentSegmentationImage)
 {
   if (currentSegmentationImage.GetPointer() == m_SelectedSegmentation)
   {
     return;
   }
 
   if (currentSegmentationImage.IsNull())
   {
     m_SelectedSegmentation = nullptr;
     return;
   }
   m_SelectedSegmentation = currentSegmentationImage.GetPointer();
 
   try
   {
     auto labelSetImage = dynamic_cast<mitk::LabelSetImage *>(m_SelectedSegmentation);
     auto it = m_ListOfContours.find(currentSegmentationImage.GetPointer());
     // If the session does not exist yet create a new ContourPositionPairList otherwise reinitialize the interpolation
     // pipeline
     if (it == m_ListOfContours.end())
     {
       ContourPositionInformationVec3D newList;
 
       auto numTimeSteps = labelSetImage->GetTimeGeometry()->CountTimeSteps();
 
       for (size_t t = 0; t < numTimeSteps; ++t)
       {
         auto twoDList = ContourPositionInformationVec2D();
         auto contourList = ContourPositionInformationList();
         twoDList.push_back(contourList);
         newList.push_back(twoDList);
       }
 
       m_ListOfContours[m_SelectedSegmentation] = newList;
 
       m_InterpolationResult = nullptr;
       m_CurrentNumberOfReducedContours = 0;
 
       auto command = itk::MemberCommand<SurfaceInterpolationController>::New();
       command->SetCallbackFunction(this, &SurfaceInterpolationController::OnSegmentationDeleted);
       m_SegmentationObserverTags[m_SelectedSegmentation] = labelSetImage->AddObserver(itk::DeleteEvent(), command);
 
       m_NumberOfLayersInCurrentSegmentation = labelSetImage->GetNumberOfLayers();
     }
 
     // auto labelSetImage = dynamic_cast<mitk::LabelSetImage*>(m_SelectedSegmentation);
     auto numLayersInSelectedSegmentation = labelSetImage->GetNumberOfLayers();
     //  Maybe this has to change.
     for (size_t layerID = 0; layerID < numLayersInSelectedSegmentation; ++layerID)
     {
       this->AddLabelSetConnection(layerID);
     }
   }
   catch (const std::exception &e)
   {
     MITK_ERROR << "Unable to cast image as LabelSetImage";
   }
 
   auto it2 = m_ListOfInterpolationSessions.find(currentSegmentationImage.GetPointer());
   if (it2 == m_ListOfInterpolationSessions.end())
   {
     ContourPositionInformationVec2D newList;
     m_ListOfInterpolationSessions[m_SelectedSegmentation] = newList;
     m_InterpolationResult = nullptr;
     m_CurrentNumberOfReducedContours = 0;
   }
 
   this->ReinitializeInterpolation();
 }
 
 bool mitk::SurfaceInterpolationController::ReplaceInterpolationSession(mitk::Image::Pointer oldSession,
                                                                        mitk::Image::Pointer newSession)
 {
   if (oldSession.IsNull() || newSession.IsNull())
     return false;
 
   if (oldSession.GetPointer() == newSession.GetPointer())
     return false;
 
   if (!mitk::Equal(*(oldSession->GetGeometry()), *(newSession->GetGeometry()), mitk::eps, false))
     return false;
 
   auto it = m_ListOfInterpolationSessions.find(oldSession.GetPointer());
 
   if (it == m_ListOfInterpolationSessions.end())
     return false;
 
   if (!newSession->GetTimeGeometry()->IsValidTimePoint(m_CurrentTimePoint))
   {
     MITK_WARN << "Interpolation session cannot be replaced. Currently selected timepoint is not in the time bounds of the new session. Time point: " << m_CurrentTimePoint;
     return false;
   }
 
   ContourPositionInformationVec2D oldList = (*it).second;
 
   m_ListOfInterpolationSessions[newSession.GetPointer()] = oldList;
 
   itk::MemberCommand<SurfaceInterpolationController>::Pointer command =
     itk::MemberCommand<SurfaceInterpolationController>::New();
 
   command->SetCallbackFunction(this, &SurfaceInterpolationController::OnSegmentationDeleted);
 
   m_SegmentationObserverTags[newSession] = newSession->AddObserver(itk::DeleteEvent(), command);
 
   if (m_SelectedSegmentation == oldSession)
     m_SelectedSegmentation = newSession;
 
   const auto currentTimeStep = m_SelectedSegmentation->GetTimeGeometry()->TimePointToTimeStep(m_CurrentTimePoint);
 
   mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New();
   timeSelector->SetInput(m_SelectedSegmentation);
   timeSelector->SetTimeNr(currentTimeStep);
   timeSelector->SetChannelNr(0);
   timeSelector->Update();
   mitk::Image::Pointer refSegImage = timeSelector->GetOutput();
 
   m_NormalsFilter->SetSegmentationBinaryImage(refSegImage);
 
   this->RemoveInterpolationSession(oldSession);
   return true;
 }
 
 void mitk::SurfaceInterpolationController::RemoveSegmentationFromContourList(mitk::Image *segmentation)
 {
   this->RemoveInterpolationSession(segmentation);
 }
 
 void mitk::SurfaceInterpolationController::RemoveInterpolationSession(mitk::Image::Pointer segmentationImage)
 {
   if (segmentationImage)
   {
     if (m_SelectedSegmentation == segmentationImage)
     {
       m_NormalsFilter->SetSegmentationBinaryImage(nullptr);
       m_SelectedSegmentation = nullptr;
     }
     m_ListOfInterpolationSessions.erase(segmentationImage);
     m_ListOfContours.erase(segmentationImage);
 
     // Remove observer
     auto pos = m_SegmentationObserverTags.find(segmentationImage);
     if (pos != m_SegmentationObserverTags.end())
     {
       segmentationImage->RemoveObserver((*pos).second);
       m_SegmentationObserverTags.erase(pos);
     }
   }
 }
 
 void mitk::SurfaceInterpolationController::RemoveAllInterpolationSessions()
 {
   // Removing all observers
   auto dataIter = m_SegmentationObserverTags.begin();
   while (dataIter != m_SegmentationObserverTags.end())
   {
     mitk::Image *image = (*dataIter).first;
     image->RemoveObserver((*dataIter).second);
     ++dataIter;
   }
 
   m_SegmentationObserverTags.clear();
   m_SelectedSegmentation = nullptr;
   m_ListOfInterpolationSessions.clear();
   m_ListOfContours.clear();
 }
 
 
 template <unsigned int VImageDimension = 3>
 std::vector<mitk::Label::PixelType> GetPixelValuesPresentInImage(mitk::LabelSetImage* labelSetImage)
 {
   mitk::ImagePixelReadAccessor<mitk::LabelSet::PixelType, VImageDimension> readAccessor(labelSetImage);
   std::vector<mitk::Label::PixelType> pixelsPresent;
 
   std::size_t numberOfPixels = 1;
   for (int dim = 0; dim < static_cast<int>(VImageDimension); ++dim)
     numberOfPixels *= static_cast<std::size_t>(readAccessor.GetDimension(dim));
 
   auto src = readAccessor.GetData();
   for (std::size_t i = 0; i < numberOfPixels; ++i)
   {
     mitk::Label::PixelType pixelVal = *(src + i);
     if ( (std::find(pixelsPresent.begin(), pixelsPresent.end(), pixelVal) == pixelsPresent.end()) && (pixelVal != 0) )
     {
       pixelsPresent.push_back(pixelVal);
     }
   }
   return pixelsPresent;
 }
 
 void mitk::SurfaceInterpolationController::RemoveContours(mitk::Label::PixelType label,
                                                           unsigned int timeStep,
                                                           unsigned int layerID)
 {
   auto isContourEqualToLabelValue = [label] (ContourPositionInformation& contour) -> bool
   {
     return (contour.LabelValue == label);
   };
 
   ContourPositionInformationVec3D &currentImageContours = m_ListOfContours.at(m_SelectedSegmentation);
   ContourPositionInformationList &currentContourList = currentImageContours.at(timeStep).at(layerID);
   unsigned int numContoursBefore = currentContourList.size();
   auto it = std::remove_if(currentContourList.begin(), currentContourList.end(), isContourEqualToLabelValue);
   currentContourList.erase(it, currentContourList.end());
   unsigned int numContoursAfter = currentContourList.size();
   unsigned int numContours = numContoursAfter - numContoursBefore;
   m_ContourIndex -= numContours;
 }
 
 void mitk::SurfaceInterpolationController::OnSegmentationDeleted(const itk::Object *caller,
                                                                  const itk::EventObject & /*event*/)
 {
   auto *tempImage = dynamic_cast<mitk::Image *>(const_cast<itk::Object *>(caller));
   if (tempImage)
   {
     if (m_SelectedSegmentation == tempImage)
     {
       m_NormalsFilter->SetSegmentationBinaryImage(nullptr);
       m_SelectedSegmentation = nullptr;
     }
     m_SegmentationObserverTags.erase(tempImage);
     m_ListOfContours.erase(tempImage);
     m_ListOfInterpolationSessions.erase(tempImage);
   }
 }
 
 void mitk::SurfaceInterpolationController::ReinitializeInterpolation()
 {
   // If session has changed reset the pipeline
   m_ReduceFilter->Reset();
   m_NormalsFilter->Reset();
   m_InterpolateSurfaceFilter->Reset();
 
   //  Empty out the listOfInterpolationSessions
   m_ListOfInterpolationSessions[m_SelectedSegmentation].clear();
 
   itk::ImageBase<3>::Pointer itkImage = itk::ImageBase<3>::New();
 
   if (m_SelectedSegmentation)
   {
     if (!m_SelectedSegmentation->GetTimeGeometry()->IsValidTimePoint(m_CurrentTimePoint))
     {
       MITK_WARN << "Interpolation cannot be reinitialized. Currently selected timepoint is not in the time bounds of the currently selected segmentation. Time point: " << m_CurrentTimePoint;
       return;
     }
 
     const auto currentTimeStep = m_SelectedSegmentation->GetTimeGeometry()->TimePointToTimeStep(m_CurrentTimePoint);
 
     //  Set reference image for interpolation surface filter
     mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New();
     timeSelector->SetInput(m_SelectedSegmentation);
     timeSelector->SetTimeNr(currentTimeStep);
     timeSelector->SetChannelNr(0);
     timeSelector->Update();
     mitk::Image::Pointer refSegImage = timeSelector->GetOutput();
     AccessFixedDimensionByItk_1(refSegImage, GetImageBase, 3, itkImage);
     m_InterpolateSurfaceFilter->SetReferenceImage(itkImage.GetPointer());
 
     //  Resize listofinterpolationsessions and listofcontours to numTimeSteps
     unsigned int numTimeSteps = m_SelectedSegmentation->GetTimeSteps();
     unsigned int size = m_ListOfInterpolationSessions[m_SelectedSegmentation].size();
 
     if (size != numTimeSteps)
     {
       m_ListOfInterpolationSessions.at(m_SelectedSegmentation).resize(numTimeSteps);
     }
   }
 }
 
 void mitk::SurfaceInterpolationController::AddLabelSetConnection(unsigned int layerID)
 {
   if (m_SelectedSegmentation != nullptr)
   {
     try
     {
       auto workingImage = dynamic_cast<mitk::LabelSetImage*>(m_SelectedSegmentation);
       auto previousLayerID = workingImage->GetActiveLayer();
       workingImage->SetActiveLayer(layerID);
       auto activeLabelSet = workingImage->GetLabelSet(layerID);
-      activeLabelSet->RemoveLabelEvent += mitk::MessageDelegate<mitk::SurfaceInterpolationController>(
+      activeLabelSet->RemoveLabelEvent += mitk::MessageDelegate1<mitk::SurfaceInterpolationController, mitk::Label::PixelType>(
         this, &mitk::SurfaceInterpolationController::OnRemoveLabel);
       activeLabelSet->ActiveLabelEvent += mitk::MessageDelegate1<mitk::SurfaceInterpolationController, mitk::Label::PixelType>(
             this, &mitk::SurfaceInterpolationController::OnActiveLabel);
       workingImage->AfterChangeLayerEvent += mitk::MessageDelegate<mitk::SurfaceInterpolationController>(
         this, &mitk::SurfaceInterpolationController::OnLayerChanged);
         m_NumberOfConnectionsAdded += 1;
       workingImage->SetActiveLayer(previousLayerID);
     }
     catch(const std::exception& e)
     {
       MITK_ERROR << e.what() << '\n';
     }
   }
 }
 
 void mitk::SurfaceInterpolationController::AddLabelSetConnection()
 {
   if (m_SelectedSegmentation != nullptr)
   {
     try
     {
       auto workingImage = dynamic_cast<mitk::LabelSetImage*>(m_SelectedSegmentation);
       auto activeLabelSet = workingImage->GetActiveLabelSet();
-      activeLabelSet->RemoveLabelEvent += mitk::MessageDelegate<mitk::SurfaceInterpolationController>(
+      activeLabelSet->RemoveLabelEvent += mitk::MessageDelegate1<mitk::SurfaceInterpolationController, mitk::Label::PixelType>(
         this, &mitk::SurfaceInterpolationController::OnRemoveLabel);
       workingImage->GetActiveLabelSet()->ActiveLabelEvent += mitk::MessageDelegate1<mitk::SurfaceInterpolationController, mitk::Label::PixelType>(
             this, &mitk::SurfaceInterpolationController::OnActiveLabel);
       workingImage->AfterChangeLayerEvent += mitk::MessageDelegate<mitk::SurfaceInterpolationController>(
         this, &mitk::SurfaceInterpolationController::OnLayerChanged);
         m_NumberOfConnectionsAdded += 1;
     }
     catch(const std::exception& e)
     {
       MITK_ERROR << e.what() << '\n';
     }
   }
 }
 
 void mitk::SurfaceInterpolationController::RemoveLabelSetConnection(mitk::LabelSetImage* labelSetImage, unsigned int layerID)
 {
   labelSetImage->SetActiveLayer(layerID);
-  labelSetImage->GetActiveLabelSet()->RemoveLabelEvent -= mitk::MessageDelegate<mitk::SurfaceInterpolationController>(
+  labelSetImage->GetActiveLabelSet()->RemoveLabelEvent -= mitk::MessageDelegate1<mitk::SurfaceInterpolationController, mitk::Label::PixelType>(
     this, &mitk::SurfaceInterpolationController::OnRemoveLabel);
   // labelSetImage->GetActiveLabelSet()->ActiveLabelEvent -= mitk::MessageDelegate1<mitk::SurfaceInterpolationController, mitk::Label::PixelType>(
   //       this, &mitk::SurfaceInterpolationController::OnActiveLabel);
   labelSetImage->AfterChangeLayerEvent -= mitk::MessageDelegate<mitk::SurfaceInterpolationController>(
     this, &mitk::SurfaceInterpolationController::OnLayerChanged);
   m_NumberOfConnectionsAdded -= 1;
 }
 
 void mitk::SurfaceInterpolationController::RemoveLabelSetConnection()
 {
   if (m_SelectedSegmentation != nullptr)
   {
     try
     {
       auto workingImage = dynamic_cast<mitk::LabelSetImage*>(m_SelectedSegmentation);
-      workingImage->GetActiveLabelSet()->RemoveLabelEvent -= mitk::MessageDelegate<mitk::SurfaceInterpolationController>(
+      workingImage->GetActiveLabelSet()->RemoveLabelEvent -= mitk::MessageDelegate1<mitk::SurfaceInterpolationController, mitk::Label::PixelType>(
         this, &mitk::SurfaceInterpolationController::OnRemoveLabel);
       workingImage->GetActiveLabelSet()->ActiveLabelEvent -= mitk::MessageDelegate1<mitk::SurfaceInterpolationController, mitk::Label::PixelType>(
             this, &mitk::SurfaceInterpolationController::OnActiveLabel);
       workingImage->AfterChangeLayerEvent -= mitk::MessageDelegate<mitk::SurfaceInterpolationController>(
         this, &mitk::SurfaceInterpolationController::OnLayerChanged);
     }
     catch (const std::exception& e)
     {
       std::cerr << e.what() << '\n';
     }
   }
 }
 
-void mitk::SurfaceInterpolationController::OnRemoveLabel()
+void mitk::SurfaceInterpolationController::OnRemoveLabel(mitk::Label::PixelType /*removedLabelValue*/)
 {
   if (m_SelectedSegmentation != nullptr)
   {
     auto numTimeSteps = m_SelectedSegmentation->GetTimeGeometry()->CountTimeSteps();
     try
     {
       auto labelSetImage = dynamic_cast<mitk::LabelSetImage *>(m_SelectedSegmentation);
       auto currentLayerID = labelSetImage->GetActiveLayer();
 
       for(unsigned int t = 0; t < numTimeSteps; ++t)
       {
         this->RemoveContours(m_PreviousActiveLabelValue,t,currentLayerID);
       }
 
     }
     catch(const std::exception& e)
     {
       std::cerr << e.what() << '\n';
     }
   }
 }
 
 void mitk::SurfaceInterpolationController::OnActiveLabel(mitk::Label::PixelType newActiveLabelValue)
 {
   m_PreviousActiveLabelValue = m_CurrentActiveLabelValue;
   m_CurrentActiveLabelValue = newActiveLabelValue;
 }
 
 unsigned int mitk::SurfaceInterpolationController::GetNumberOfLayersInCurrentSegmentation() const
 {
   return m_NumberOfLayersInCurrentSegmentation;
 }
 
 void mitk::SurfaceInterpolationController::SetNumberOfLayersInCurrentSegmentation(unsigned int numLayers)
 {
   m_NumberOfLayersInCurrentSegmentation = numLayers;
 }
 
 void mitk::SurfaceInterpolationController::OnAddLayer()
 {
   assert(m_SelectedSegmentation != nullptr);
   auto& contoursForSegmentation = m_ListOfContours.at(m_SelectedSegmentation);
   //  Push an information list for each time step.
   for(size_t t = 0; t < contoursForSegmentation.size(); ++t)
   {
     contoursForSegmentation.at(t).push_back( ContourPositionInformationList() );
   }
 }
 
 void mitk::SurfaceInterpolationController::OnRemoveLayer()
 {
   assert(m_SelectedSegmentation != nullptr);
   auto& contoursForSegmentation = m_ListOfContours.at(m_SelectedSegmentation);
   //  Erase the layers in each of the time steps.
 
   //  The previous layer is removed
   for (size_t t = 0; t < contoursForSegmentation.size(); ++t)
   {
     assert(m_PreviousLayerIndex < contoursForSegmentation.at(t).size());
     auto& contoursAtTimeStep = contoursForSegmentation.at(t);
     for (size_t c = m_CurrentLayerIndex+1; c < contoursAtTimeStep.size(); ++c)
     {
       auto& contoursInCurrentLayer = contoursAtTimeStep.at(c);
       for (auto& contour : contoursInCurrentLayer)
       {
         contour.LayerValue = contour.LayerValue - 1;
       }
     }
   }
 
   for (size_t t = 0; t < contoursForSegmentation.size(); ++t)
   {
     assert (m_CurrentLayerIndex < contoursForSegmentation.at(t).size());
     contoursForSegmentation.at(t).erase(contoursForSegmentation.at(t).begin() + m_PreviousLayerIndex);
   }
 
   this->Modified();
 }
 
 void mitk::SurfaceInterpolationController::OnLayerChanged()
 {
   auto currentLayer = dynamic_cast<mitk::LabelSetImage*>(m_SelectedSegmentation)->GetActiveLayer();
   m_PreviousLayerIndex = m_CurrentLayerIndex;
   m_CurrentLayerIndex = currentLayer;
 }
 
 mitk::SurfaceInterpolationController::ContourPositionInformationList& mitk::SurfaceInterpolationController::GetContours(unsigned int timeStep, unsigned int layerID)
 {
   if (m_SelectedSegmentation == nullptr)
   {
     MITK_ERROR << "Invalid segmentation from mitk::SurfaceInterpolationController::GetContours";
   }
   if (timeStep >= m_ListOfContours.at(m_SelectedSegmentation).size())
   {
     MITK_ERROR << "Invalid timeStep from mitk::SurfaceInterpolationController::GetContours";
   }
   if (layerID >= m_ListOfContours.at(m_SelectedSegmentation).at(timeStep).size())
   {
     MITK_ERROR << "Invalid timeStep from mitk::SurfaceInterpolationController::GetContours";
   }
   return m_ListOfContours.at(m_SelectedSegmentation).at(timeStep).at(layerID);
 }
 
 void mitk::SurfaceInterpolationController::CompleteReinitialization(const std::vector<mitk::Surface::Pointer>& contourList,
                                                                     std::vector<const mitk::PlaneGeometry *>& contourPlanes)
 {
   this->ClearInterpolationSession();
 
   auto labelSetImage = dynamic_cast<mitk::LabelSetImage *>(m_SelectedSegmentation);
   auto numLayers = labelSetImage->GetNumberOfLayers();
 
   //  Add layers to the m_ListOfContours
   for (size_t layer = 0; layer < numLayers; ++layer)
   {
     this->OnAddLayer();
   }
 
   //  Now the layers should be empty and the new layers can be added.
   this->AddNewContours(contourList, contourPlanes, true);
 }
 
 void mitk::SurfaceInterpolationController::ClearInterpolationSession()
 {
   if (m_SelectedSegmentation != nullptr)
   {
     auto it = m_ListOfContours.find(m_SelectedSegmentation);
     if (it != m_ListOfContours.end())
     {
       auto timeSteps = m_ListOfContours[m_SelectedSegmentation].size();
       try
       {
         auto labelSetImage = dynamic_cast<mitk::LabelSetImage *>(m_SelectedSegmentation);
         auto labelSetImageTimeSteps = labelSetImage->GetTimeGeometry()->CountTimeSteps();
 
         if (timeSteps != labelSetImageTimeSteps)
         {
           MITK_ERROR << "Time steps are not the same.";
         }
 
         for (size_t t = 0; t < timeSteps; ++t)
         {
           m_ListOfContours[m_SelectedSegmentation][t].clear();
         }
 
       }
       catch(std::bad_cast& e)
       {
         MITK_ERROR << "Unable to cast m_SelectedSegmentation to labelSetImage in ClearInterpolationSession";
       }
     }
   }
 }
 
 std::vector< mitk::Point3D > mitk::ContourExt::GetBoundingBoxGridPoints(
                                               size_t planeDimension,
                                               double startDim1,
                                               size_t numPointsToSampleDim1,
                                               double deltaDim1,
                                               double startDim2,
                                               size_t numPointsToSampleDim2,
                                               double deltaDim2,
                                               double valuePlaneDim)
 {
   std::vector< mitk::Point3D > gridPoints;
   for (size_t i = 0; i < numPointsToSampleDim1; ++i)
   {
     for (size_t j = 0; j < numPointsToSampleDim2; ++j)
     {
       mitk::ScalarType *ptVec = new mitk::ScalarType[3];
 
       if (planeDimension == 0)
       {
         ptVec[0] = valuePlaneDim;
         ptVec[1] = startDim1 + deltaDim1 * i;
         ptVec[2] = startDim2 + deltaDim2 * j;
       }
       else if (planeDimension == 1)
       {
         ptVec[0] = startDim1 + deltaDim1 * i;
         ptVec[1] = valuePlaneDim;
         ptVec[2] = startDim2 + deltaDim2 * j;
 
       }
       else if (planeDimension == 2)
       {
         ptVec[0] = startDim1 + deltaDim1 * i;
         ptVec[1] = startDim2 + deltaDim2 * j;
         ptVec[2] = valuePlaneDim;
       }
 
       mitk::Point3D pt3D;
       pt3D.FillPoint(ptVec);
       gridPoints.push_back(pt3D);
     }
   }
 
   return gridPoints;
 }
 
 mitk::Point3D mitk::SurfaceInterpolationController::ComputeInteriorPointOfContour(
                                     const mitk::SurfaceInterpolationController::ContourPositionInformation& contour,
                                     mitk::LabelSetImage * labelSetImage)
 {
   if (labelSetImage->GetDimension() == 4)
   {
     return mitk::ContourExt::ComputeInteriorPointOfContour<4>(contour, labelSetImage, m_CurrentTimePoint);
   }
   else
   {
     return mitk::ContourExt::ComputeInteriorPointOfContour<3>(contour, labelSetImage, m_CurrentTimePoint);
   }
 }
 
 template<unsigned int VImageDimension>
 mitk::Point3D mitk::ContourExt::ComputeInteriorPointOfContour(
                                     const mitk::SurfaceInterpolationController::ContourPositionInformation& contour,
                                     mitk::LabelSetImage * labelSetImage,
                                     mitk::TimePointType currentTimePoint)
 {
   mitk::ImagePixelReadAccessor<mitk::LabelSet::PixelType, VImageDimension> readAccessor(labelSetImage);
 
   if (!labelSetImage->GetTimeGeometry()->IsValidTimePoint(currentTimePoint))
   {
     MITK_ERROR << "Invalid time point requested for interpolation pipeline.";
     mitk::Point3D pt;
     return pt;
   }
 
   std::vector<mitk::Label::PixelType> pixelsPresent;
   const auto currentTimeStep = labelSetImage->GetTimeGeometry()->TimePointToTimeStep(currentTimePoint);
 
   auto polyData = contour.Contour->GetVtkPolyData();
 
   polyData->ComputeCellsBounds();
   mitk::ScalarType cellBounds[6];
   polyData->GetCellsBounds(cellBounds);
 
   size_t numPointsToSample = 10;
   mitk::ScalarType StartX = cellBounds[0];
   mitk::ScalarType StartY = cellBounds[2];
   mitk::ScalarType StartZ = cellBounds[4];
 
   size_t deltaX = (cellBounds[1] - cellBounds[0]) / numPointsToSample;
   size_t deltaY = (cellBounds[3] - cellBounds[2]) / numPointsToSample;
   size_t deltaZ = (cellBounds[5] - cellBounds[4]) / numPointsToSample;
 
   auto planeOrientation = mitk::ContourExt::GetContourOrientation(contour.ContourNormal);
 
   std::vector<mitk::Point3D> points;
   if (planeOrientation == 0)
   {
     points = mitk::ContourExt::GetBoundingBoxGridPoints(planeOrientation,
                                       StartY, numPointsToSample, deltaY,
                                       StartZ, numPointsToSample, deltaZ,
                                       StartX);
   }
   else if (planeOrientation == 1)
   {
     points = mitk::ContourExt::GetBoundingBoxGridPoints(planeOrientation,
                                       StartX, numPointsToSample, deltaX,
                                       StartZ, numPointsToSample, deltaZ,
                                       StartY);
   }
   else if (planeOrientation == 2)
   {
     points = mitk::ContourExt::GetBoundingBoxGridPoints(planeOrientation,
                                       StartX, numPointsToSample, deltaX,
                                       StartY, numPointsToSample, deltaY,
                                       StartZ);
   }
   mitk::Label::PixelType pixelVal;
   mitk::Point3D pt3D;
   std::vector<mitk::Label::PixelType> pixelVals;
   for (size_t i = 0; i < points.size(); ++i)
   {
     pt3D = points[i];
     itk::Index<3> itkIndex;
     labelSetImage->GetGeometry()->WorldToIndex(pt3D, itkIndex);
 
     if (VImageDimension == 4)
     {
       itk::Index<VImageDimension> time3DIndex;
       for (size_t i = 0; i < itkIndex.size(); ++i)
         time3DIndex[i] = itkIndex[i];
       time3DIndex[3] = currentTimeStep;
 
       pixelVal = readAccessor.GetPixelByIndexSafe(time3DIndex);
     }
     else if (VImageDimension == 3)
     {
       itk::Index<VImageDimension> geomIndex;
       for (size_t i=0;i<itkIndex.size();++i)
         geomIndex[i] = itkIndex[i];
 
       pixelVal = readAccessor.GetPixelByIndexSafe(geomIndex);
     }
 
     if (pixelVal == contour.LabelValue)
       break;
   }
   return pt3D;
 }
 
 size_t mitk::ContourExt::GetContourOrientation(const mitk::Vector3D& ContourNormal)
 {
   double n[3];
   n[0] = ContourNormal[0];
   n[1] = ContourNormal[1];
   n[2] = ContourNormal[2];
 
   double XVec[3];
   XVec[0] = 1.0;  XVec[1] = 0.0;  XVec[2] = 0.0;
   double dotX = vtkMath::Dot(n, XVec);
 
   double YVec[3];
   YVec[0] = 0.0;  YVec[1] = 1.0;  YVec[2] = 0.0;
   double dotY = vtkMath::Dot(n, YVec);
 
   double ZVec[3];
   ZVec[0] = 0.0;  ZVec[1] = 0.0;  ZVec[2] = 1.0;
   double dotZ = vtkMath::Dot(n, ZVec);
 
   size_t planeOrientation = 0;
   if (fabs(dotZ) > mitk::eps)
   {
     planeOrientation = 2;
   }
   else if (fabs(dotY) > mitk::eps)
   {
     planeOrientation = 1;
   }
   else if(fabs(dotX) > mitk::eps)
   {
     planeOrientation = 0;
   }
   return planeOrientation;
 }
diff --git a/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.h b/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.h
index df44ed8488..fac3d0385d 100644
--- a/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.h
+++ b/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.h
@@ -1,469 +1,469 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #ifndef mitkSurfaceInterpolationController_h
 #define mitkSurfaceInterpolationController_h
 
 #include <mitkDataStorage.h>
 #include <mitkImage.h>
 #include <mitkLabel.h>
 #include <mitkSurface.h>
 
 #include <MitkSurfaceInterpolationExports.h>
 
 namespace mitk
 {
   class ComputeContourSetNormalsFilter;
   class CreateDistanceImageFromSurfaceFilter;
   class LabelSetImage;
   class ReduceContourSetFilter;
 
   class MITKSURFACEINTERPOLATION_EXPORT SurfaceInterpolationController : public itk::Object
   {
   public:
     mitkClassMacroItkParent(SurfaceInterpolationController, itk::Object);
     itkFactorylessNewMacro(Self);
     itkCloneMacro(Self);
     itkGetMacro(DistanceImageSpacing, double);
 
     struct MITKSURFACEINTERPOLATION_EXPORT ContourPositionInformation
     {
       int Pos;
       unsigned int SliceIndex;
       Surface::Pointer Contour;
       Vector3D ContourNormal;
       Point3D ContourPoint;
       mitk::PlaneGeometry* Plane;
       mitk::Label::PixelType LabelValue;
       unsigned int LayerValue;
       size_t TimeStep;
 
       ContourPositionInformation()
         : Pos(-1),
           SliceIndex(0),
           Plane(nullptr),
           LabelValue(std::numeric_limits<mitk::Label::PixelType>::max()),
           LayerValue(std::numeric_limits<unsigned int>::max()),
           TimeStep(std::numeric_limits<size_t>::max())
       {
       }
     };
 
     typedef std::vector<ContourPositionInformation> ContourPositionInformationList;
     typedef std::vector<ContourPositionInformationList> ContourPositionInformationVec2D;
 
     //  first index is the current time step. second index is the layerID. third index is the contour index.
     typedef std::vector<ContourPositionInformationVec2D> ContourPositionInformationVec3D;
 
     typedef std::map<mitk::Image *, ContourPositionInformationVec3D> ContourListMap;
     typedef std::map<mitk::Image *, ContourPositionInformationVec2D> ContourContainer;
 
     static SurfaceInterpolationController *GetInstance();
 
     void SetCurrentTimePoint(TimePointType tp)
     {
       if (m_CurrentTimePoint != tp)
       {
         m_CurrentTimePoint = tp;
 
         if (m_SelectedSegmentation)
         {
           this->ReinitializeInterpolation();
         }
       }
     };
 
     TimePointType GetCurrentTimePoint() const { return m_CurrentTimePoint; };
 
     /**
      * @brief Adds a new extracted contour to the list
      * @param newContour the contour to be added. If a contour at that position
      *        already exists the related contour will be updated
      */
     void AddNewContour(Surface::Pointer newContour);
 
     /**
      * @brief Adds new extracted contours to the list. If one or more contours at a given position
      *        already exist they will be updated respectively
      * @param newContours the list of the contours
      */
     void AddNewContours(const std::vector<Surface::Pointer>& newContours, std::vector<const mitk::PlaneGeometry*>& contourPlanes, bool reinitializeAction = false);
 
     /**
     * @brief Returns the contour for a given plane for the current selected segmenation
     * @param contourInfo the contour which should be returned
     * @return the contour as an mitk::Surface. If no contour is available at the give position nullptr is returned
     */
     const mitk::Surface *GetContour(const ContourPositionInformation& contourInfo);
 
     /**
      * @brief Computes an interior point of the input contour. It's used to detect merge and erase operations.
      *
      * @param contour Contour for which to compute the contour
      * @param labelSetImage LabelSetImage used input to check contour Label.
      * @return mitk::Point3D 3D Interior point of the contour returned.
      */
     mitk::Point3D ComputeInteriorPointOfContour(const ContourPositionInformation& contour,
                                                  mitk::LabelSetImage * labelSetImage);
 
     /**
      * @brief Make the surface interpolator responsive to the segmentation image by subscribing to events from the image.
      *
      */
     void AddLabelSetConnection();
 
     /**
      * @brief Make the surface interpolator responsive to the segmentation image by stopping subscription to events from the image.
      *
      */
     void RemoveLabelSetConnection();
 
     void RemoveLabelSetConnection(mitk::LabelSetImage* labelSetImage, unsigned int layerID);
 
 
     /**
      * @brief Resets the pipeline for interpolation. The various filters used are reset.
      *
      */
     void ReinitializeInterpolation();
 
     void RemoveObservers();
 
     void AddLabelSetConnection(unsigned int layerID);
 
     void UnsetSelectedImage()
     {
       m_SelectedSegmentation = nullptr;
     }
 
     /**
      * @brief Returns the number of layers in the current segmentation image.
      *
      */
     unsigned int GetNumberOfLayersInCurrentSegmentation() const;
 
     /**
      * @brief Set the number of layers in the current segmentation image.
      *
      */
     void SetNumberOfLayersInCurrentSegmentation(unsigned int);
 
     /**
      * @brief Function that does the data management when a layer is removed.
      *
      */
     void OnRemoveLayer();
 
     /**
      * @brief  Function that does the data management when a layer is added.
      *
      */
     void OnAddLayer();
 
     /**
     * @brief Returns the number of available contours for the current selected segmentation
     * @return the number of contours
     */
     unsigned int GetNumberOfContours();
 
     /**
      * @brief Performs the interpolation.
      *
      */
     void Interpolate();
 
     /**
      * @brief Get the Result of the interpolation operation.
      *
      * @return mitk::Surface::Pointer
      */
     mitk::Surface::Pointer GetInterpolationResult();
 
     /**
      * @brief Sets the minimum spacing of the current selected segmentation
      * This is needed since the contour points we reduced before they are used to interpolate the surface.
      *
      * @param minSpacing Paramter to set
      */
     void SetMinSpacing(double minSpacing);
 
     /**
      * @brief Sets the minimum spacing of the current selected segmentation
      * This is needed since the contour points we reduced before they are used to interpolate the surface
      * @param maxSpacing Set the max Spacing for interpolation
      */
     void SetMaxSpacing(double maxSpacing);
 
     /**
      * Sets the volume i.e. the number of pixels that the distance image should have
      * By evaluation we found out that 50.000 pixel delivers a good result
      */
     void SetDistanceImageVolume(unsigned int distImageVolume);
 
     /**
      * @brief Get the current selected segmentation for which the interpolation is performed
      * @return the current segmentation image
      */
     mitk::Image::Pointer GetCurrentSegmentation();
 
     Surface *GetContoursAsSurface();
 
     void SetDataStorage(DataStorage::Pointer ds);
 
     /**
      * Sets the current list of contourpoints which is used for the surface interpolation
      * @param segmentation The current selected segmentation
      * \deprecatedSince{2014_03}
      */
     DEPRECATED(void SetCurrentSegmentationInterpolationList(mitk::Image::Pointer segmentation));
 
     /**
      * Sets the current list of contourpoints which is used for the surface interpolation
      * @param currentSegmentationImage The current selected segmentation
      */
     void SetCurrentInterpolationSession(mitk::Image::Pointer currentSegmentationImage);
 
     /**
      * Removes the segmentation and all its contours from the list
      * @param segmentation The segmentation to be removed
      * \deprecatedSince{2014_03}
      */
     DEPRECATED(void RemoveSegmentationFromContourList(mitk::Image *segmentation));
 
     /**
      * @brief Remove interpolation session
      * @param segmentationImage the session to be removed
      */
     void RemoveInterpolationSession(mitk::Image::Pointer segmentationImage);
 
     /**
      * Replaces the current interpolation session with a new one. All contours form the old
      * session will be applied to the new session. This only works if the two images have the
      * geometry
      * @param oldSession the session which should be replaced
      * @param newSession the new session which replaces the old one
      * @return true it the the replacement was successful, false if not (e.g. the image's geometry differs)
      */
     bool ReplaceInterpolationSession(mitk::Image::Pointer oldSession, mitk::Image::Pointer newSession);
 
     /**
      * @brief Removes all sessions
      */
     void RemoveAllInterpolationSessions();
 
     mitk::Image *GetImage();
 
     /**
      * @brief Get the Contours at a certain timeStep and layerID.
      *
      * @param timeStep Time Step from which to get the contours.
      * @param layerID Layer from which to get the contours.
      * @return std::vector<ContourPositionInformation> Returns contours.
      */
     ContourPositionInformationList& GetContours(unsigned int timeStep, unsigned int layerID);
 
     /**
      * @brief Trigerred with the "Reinit Interpolation" action. The contours are used to repopulate the
      *        surfaceInterpolator data structures so that interpolation can be performed after reloading data.
      *
      * @param contourList List of contours extracted
      * @param contourPlanes List of planes at which the contours were extracted
      */
     void CompleteReinitialization(const std::vector<mitk::Surface::Pointer>& contourList,
                                            std::vector<const mitk::PlaneGeometry *>& contourPlanes);
 
     /**
      * @brief Removes contours of a particular label, at a given time step and layerID.
      *
      * @param label Label of contour to remove.
      * @param timeStep Time step in which to remove the contours.
      * @param layerID Layer in which the contour should be removed.
      */
     void RemoveContours(mitk::Label::PixelType label, unsigned int timeStep, unsigned int layerID);
 
     /**
      * Estimates the memory which is needed to build up the equationsystem for the interpolation.
      * \returns The percentage of the real memory which will be used by the interpolation
      */
     double EstimatePortionOfNeededMemory();
 
     /**
      * Adds Contours from the active Label to the interpolation pipeline
      */
     void AddActiveLabelContoursForInterpolation(mitk::Label::PixelType activeLabel);
 
     unsigned int GetNumberOfInterpolationSessions();
 
     /**
      * @brief Removes the contour for a given plane for the current selected segmenation
      * @param contourInfo the contour which should be removed
      * @return true if a contour was found and removed, false if no contour was found
      */
     bool RemoveContour(ContourPositionInformation contourInfo);
 
     /**
      * @brief Get the Segmentation Image Node object
      *
      * @return DataNode* returns the DataNode containing the segmentation image.
      */
     mitk::DataNode* GetSegmentationImageNode();
 
 
 
   protected:
     SurfaceInterpolationController();
 
     ~SurfaceInterpolationController() override;
 
     template <typename TPixel, unsigned int VImageDimension>
     void GetImageBase(itk::Image<TPixel, VImageDimension> *input, itk::ImageBase<3>::Pointer &result);
 
   private:
 
     /**
      * @brief
      *
      * @param caller
      * @param event
      */
     void OnSegmentationDeleted(const itk::Object *caller, const itk::EventObject &event);
 
     /**
      * @brief Function that removes contours of a particular label when the "Remove Label" event is trigerred in the labelSetImage.
      *
      */
-    void OnRemoveLabel();
+    void OnRemoveLabel(mitk::Label::PixelType removedLabelValue);
 
     /**
      * @brief When a new contour is added to the pipeline or an existing contour is replaced,
      *        the plane geometry information of that contour is added as a child node to the
      *        current node of the segmentation image. This is useful in the retrieval of contour information
      *        when data is reloaded after saving.
      *
      * @param contourInfo contourInfo struct to add to data storage.
      */
     void AddPlaneGeometryNodeToDataStorage(const ContourPositionInformation& contourInfo);
 
     /**
      * @brief Function that toggles active label, when the active label is changed.
      *
      */
     void OnActiveLabel(mitk::Label::PixelType);
 
     /**
      * @brief Clears the interpolation data structures. Called from CompleteReinitialization().
      *
      */
     void ClearInterpolationSession();
 
     /**
      * @brief Add contour to the interpolation pipeline
      *
      * @param contourInfo Contour information to be added
      * @param reinitializationAction If the contour is coming from a reinitialization process or not
      */
     void AddToInterpolationPipeline(ContourPositionInformation& contourInfo, bool reinitializationAction = false);
 
     /**
      * @brief Function to respond to layer changed
      *
      */
     void OnLayerChanged();
 
     itk::SmartPointer<ReduceContourSetFilter> m_ReduceFilter;
     itk::SmartPointer<ComputeContourSetNormalsFilter> m_NormalsFilter;
     itk::SmartPointer<CreateDistanceImageFromSurfaceFilter> m_InterpolateSurfaceFilter;
 
     mitk::Surface::Pointer m_Contours;
 
     double m_DistanceImageSpacing;
 
     vtkSmartPointer<vtkPolyData> m_PolyData;
 
     mitk::DataStorage::Pointer m_DataStorage;
 
     ContourContainer m_ListOfInterpolationSessions;
     ContourListMap m_ListOfContours;
 
     mitk::Surface::Pointer m_InterpolationResult;
 
     unsigned int m_CurrentNumberOfReducedContours;
     unsigned int m_NumberOfConnectionsAdded;
 
     mitk::Image *m_SelectedSegmentation;
 
     std::map<mitk::Image *, unsigned long> m_SegmentationObserverTags;
 
     mitk::TimePointType m_CurrentTimePoint;
 
     unsigned int m_ContourIndex;
     unsigned int m_ContourPosIndex;
     unsigned int m_NumberOfLayersInCurrentSegmentation;
 
     mitk::Label::PixelType m_PreviousActiveLabelValue;
     mitk::Label::PixelType m_CurrentActiveLabelValue;
 
     unsigned int m_PreviousLayerIndex;
     unsigned int m_CurrentLayerIndex;
   };
 
   namespace ContourExt
   {
     /**
      * @brief Returns the plane the contour belongs to.
      *
      * @param ContourNormal
      * @return size_t
      */
     size_t GetContourOrientation(const mitk::Vector3D& ContourNormal);
 
     /**
      * @brief Function used to compute an interior point of the contour.
      *        Used to react to the merge label and erase label actions.
      *
      *
      * @tparam VImageDimension Dimension of the image
      * @param contour Contour for which to compute the interior point
      * @param labelSetImage Label Set Image For which to find the contour
      * @param currentTimePoint Current Time Point of the Image
      * @return mitk::Point3D The returned point in the interior of the contour.s
      */
     template<unsigned int VImageDimension>
     mitk::Point3D ComputeInteriorPointOfContour(const mitk::SurfaceInterpolationController::ContourPositionInformation& contour,
                                                  mitk::LabelSetImage * labelSetImage,
                                                  mitk::TimePointType currentTimePoint);
     /**
      * @brief Get a Grid points within the bounding box of the contour at a certain spacing.
      *
      * @param planeDimension  Plane orientation (Sagittal, Coronal, Axial)
      * @param startDim1 Starting coordinate along dimension 1 to start the grid point sampling from
      * @param numPointsToSampleDim1 Number of points to sample along dimension 1
      * @param deltaDim1 Spacing for dimension 1 at which points should be sampled
      * @param startDim2 Starting coordinate along dimension 2 to start the grid point sampling from
      * @param numPointsToSampleDim2 Number of points to sample along dimension 2
      * @param deltaDim2 Spacing for dimension 1 at which points should be sampled
      * @param valuePlaneDim Slice index of the plane in the volume
      * @return std::vector< mitk::Point3D > The computed grid points are returned by the function.
      */
     std::vector< mitk::Point3D > GetBoundingBoxGridPoints(size_t planeDimension,
                                             double startDim1,
                                             size_t numPointsToSampleDim1,
                                             double deltaDim1,
                                             double startDim2,
                                             size_t numPointsToSampleDim2,
                                             double deltaDim2,
                                             double valuePlaneDim);
   };
 
 }
 
 #endif
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkLabelSetWidget.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkLabelSetWidget.cpp
index 3571708f92..74607e8b82 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkLabelSetWidget.cpp
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkLabelSetWidget.cpp
@@ -1,1156 +1,1166 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "QmitkLabelSetWidget.h"
 
 // mitk
 #include <mitkAutoCropImageFilter.h>
 #include <mitkCoreObjectFactory.h>
 #include <mitkIOUtil.h>
 #include <mitkLabelSetImage.h>
 #include <mitkLabelSetImageToSurfaceThreadedFilter.h>
 #include <mitkRenderingManager.h>
 #include <mitkShowSegmentationAsSurface.h>
 #include <mitkSliceBasedInterpolationController.h>
 #include <mitkStatusBar.h>
 #include <mitkToolManagerProvider.h>
 
 // Qmitk
 #include <QmitkDataStorageComboBox.h>
 #include <QmitkNewSegmentationDialog.h>
 #include <QmitkStyleManager.h>
 
 // Qt
 #include <QColorDialog>
 #include <QCompleter>
 #include <QDateTime>
 #include <QFileDialog>
 #include <QMenu>
 #include <QMessageBox>
 #include <QPushButton>
 #include <QShortcut>
 #include <QStringListModel>
 #include <QWidgetAction>
 
 // itk
 #include <itksys/SystemTools.hxx>
 
 QmitkLabelSetWidget::QmitkLabelSetWidget(QWidget *parent)
   : QWidget(parent), m_DataStorage(nullptr), m_Completer(nullptr), m_ToolManager(nullptr), m_ProcessingManualSelection(false)
 {
   m_Controls.setupUi(this);
 
   m_ColorSequenceRainbow.GoToBegin();
 
   m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager();
 
   m_Controls.m_LabelSearchBox->setAlwaysShowClearIcon(true);
   m_Controls.m_LabelSearchBox->setShowSearchIcon(true);
 
   QStringList completionList;
   completionList << "";
   m_Completer = new QCompleter(completionList, this);
   m_Completer->setCaseSensitivity(Qt::CaseInsensitive);
   m_Controls.m_LabelSearchBox->setCompleter(m_Completer);
 
   connect(m_Controls.m_LabelSearchBox, SIGNAL(returnPressed()), this, SLOT(OnSearchLabel()));
 
   auto* renameLabelShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key::Key_L, Qt::CTRL | Qt::Key::Key_R), this);
   connect(renameLabelShortcut, &QShortcut::activated, this, &QmitkLabelSetWidget::OnRenameLabelShortcutActivated);
 
   QStringListModel *completeModel = static_cast<QStringListModel *>(m_Completer->model());
   completeModel->setStringList(GetLabelStringList());
 
   m_Controls.m_LabelSearchBox->setEnabled(false);
 
   m_Controls.m_lblCaption->setText("");
 
   InitializeTableWidget();
 }
 
 QmitkLabelSetWidget::~QmitkLabelSetWidget() {}
 
 void QmitkLabelSetWidget::OnTableViewContextMenuRequested(const QPoint & /*pos*/)
 {
   int pixelValue = GetPixelValueOfSelectedItem();
 
   if (-1 == pixelValue)
     return;
 
   QMenu *menu = new QMenu(m_Controls.m_LabelSetTableWidget);
 
   if (m_Controls.m_LabelSetTableWidget->selectedItems().size() > 1)
   {
     QAction *mergeAction = new QAction(QIcon(":/Qmitk/MergeLabels.png"), "Merge selection on current label", this);
     mergeAction->setEnabled(true);
     QObject::connect(mergeAction, SIGNAL(triggered(bool)), this, SLOT(OnMergeLabels(bool)));
     menu->addAction(mergeAction);
 
     QAction *removeLabelsAction = new QAction(QIcon(":/Qmitk/RemoveLabel.png"), "Remove selected labels", this);
     removeLabelsAction->setEnabled(true);
     QObject::connect(removeLabelsAction, SIGNAL(triggered(bool)), this, SLOT(OnRemoveLabels(bool)));
     menu->addAction(removeLabelsAction);
 
     QAction *eraseLabelsAction = new QAction(QIcon(":/Qmitk/EraseLabel.png"), "Erase selected labels", this);
     eraseLabelsAction->setEnabled(true);
     QObject::connect(eraseLabelsAction, SIGNAL(triggered(bool)), this, SLOT(OnEraseLabels(bool)));
     menu->addAction(eraseLabelsAction);
   }
   else
   {
     QAction *renameAction = new QAction(QIcon(":/Qmitk/RenameLabel.png"), "Rename...", this);
     renameAction->setEnabled(true);
     QObject::connect(renameAction, SIGNAL(triggered(bool)), this, SLOT(OnRenameLabel(bool)));
     menu->addAction(renameAction);
 
     QAction *removeAction = new QAction(QIcon(":/Qmitk/RemoveLabel.png"), "Remove...", this);
     removeAction->setEnabled(true);
     QObject::connect(removeAction, SIGNAL(triggered(bool)), this, SLOT(OnRemoveLabel(bool)));
     menu->addAction(removeAction);
 
     QAction *eraseAction = new QAction(QIcon(":/Qmitk/EraseLabel.png"), "Erase...", this);
     eraseAction->setEnabled(true);
     QObject::connect(eraseAction, SIGNAL(triggered(bool)), this, SLOT(OnEraseLabel(bool)));
     menu->addAction(eraseAction);
 
     QAction *randomColorAction = new QAction(QIcon(":/Qmitk/RandomColor.png"), "Random color", this);
     randomColorAction->setEnabled(true);
     QObject::connect(randomColorAction, SIGNAL(triggered(bool)), this, SLOT(OnRandomColor(bool)));
     menu->addAction(randomColorAction);
 
     QAction *viewOnlyAction = new QAction(QIcon(":/Qmitk/visible.png"), "View only", this);
     viewOnlyAction->setEnabled(true);
     QObject::connect(viewOnlyAction, SIGNAL(triggered(bool)), this, SLOT(OnSetOnlyActiveLabelVisible(bool)));
     menu->addAction(viewOnlyAction);
 
     QAction *viewAllAction = new QAction(QIcon(":/Qmitk/visible.png"), "View all", this);
     viewAllAction->setEnabled(true);
     QObject::connect(viewAllAction, SIGNAL(triggered(bool)), this, SLOT(OnSetAllLabelsVisible(bool)));
     menu->addAction(viewAllAction);
 
     QAction *hideAllAction = new QAction(QIcon(":/Qmitk/invisible.png"), "Hide all", this);
     hideAllAction->setEnabled(true);
     QObject::connect(hideAllAction, SIGNAL(triggered(bool)), this, SLOT(OnSetAllLabelsInvisible(bool)));
     menu->addAction(hideAllAction);
 
     QAction *lockAllAction = new QAction(QIcon(":/Qmitk/lock.png"), "Lock all", this);
     lockAllAction->setEnabled(true);
     QObject::connect(lockAllAction, SIGNAL(triggered(bool)), this, SLOT(OnLockAllLabels(bool)));
     menu->addAction(lockAllAction);
 
     QAction *unlockAllAction = new QAction(QIcon(":/Qmitk/unlock.png"), "Unlock all", this);
     unlockAllAction->setEnabled(true);
     QObject::connect(unlockAllAction, SIGNAL(triggered(bool)), this, SLOT(OnUnlockAllLabels(bool)));
     menu->addAction(unlockAllAction);
 
     QAction *createSurfaceAction = new QAction(QIcon(":/Qmitk/CreateSurface.png"), "Create surface", this);
     createSurfaceAction->setEnabled(true);
     createSurfaceAction->setMenu(new QMenu());
 
     QAction *tmp1 = createSurfaceAction->menu()->addAction(QString("Detailed"));
     QAction *tmp2 = createSurfaceAction->menu()->addAction(QString("Smoothed"));
 
     QObject::connect(tmp1, SIGNAL(triggered(bool)), this, SLOT(OnCreateDetailedSurface(bool)));
     QObject::connect(tmp2, SIGNAL(triggered(bool)), this, SLOT(OnCreateSmoothedSurface(bool)));
 
     menu->addAction(createSurfaceAction);
 
     QAction *createMaskAction = new QAction(QIcon(":/Qmitk/CreateMask.png"), "Create mask", this);
     createMaskAction->setEnabled(true);
     QObject::connect(createMaskAction, SIGNAL(triggered(bool)), this, SLOT(OnCreateMask(bool)));
 
     menu->addAction(createMaskAction);
 
     QAction *createCroppedMaskAction = new QAction(QIcon(":/Qmitk/CreateMask.png"), "Create cropped mask", this);
     createCroppedMaskAction->setEnabled(true);
     QObject::connect(createCroppedMaskAction, SIGNAL(triggered(bool)), this, SLOT(OnCreateCroppedMask(bool)));
 
     menu->addAction(createCroppedMaskAction);
 
     QSlider *opacitySlider = new QSlider;
     opacitySlider->setMinimum(0);
     opacitySlider->setMaximum(100);
     opacitySlider->setOrientation(Qt::Horizontal);
     QObject::connect(opacitySlider, SIGNAL(valueChanged(int)), this, SLOT(OnOpacityChanged(int)));
 
     QLabel *_OpacityLabel = new QLabel("Opacity: ");
     QVBoxLayout *_OpacityWidgetLayout = new QVBoxLayout;
     _OpacityWidgetLayout->setContentsMargins(4, 4, 4, 4);
     _OpacityWidgetLayout->addWidget(_OpacityLabel);
     _OpacityWidgetLayout->addWidget(opacitySlider);
     QWidget *_OpacityWidget = new QWidget;
     _OpacityWidget->setLayout(_OpacityWidgetLayout);
 
     QWidgetAction *OpacityAction = new QWidgetAction(this);
     OpacityAction->setDefaultWidget(_OpacityWidget);
     //  QObject::connect( m_OpacityAction, SIGNAL( changed() ), this, SLOT( OpacityActionChanged() ) );
     auto workingImage = this->GetWorkingImage();
     auto activeLayer = workingImage->GetActiveLayer();
     auto label = workingImage->GetLabel(pixelValue, activeLayer);
 
     if (nullptr != label)
     {
       auto opacity = label->GetOpacity();
       opacitySlider->setValue(static_cast<int>(opacity * 100));
     }
 
     menu->addAction(OpacityAction);
   }
   menu->popup(QCursor::pos());
 }
 
 void QmitkLabelSetWidget::OnUnlockAllLabels(bool /*value*/)
 {
   GetWorkingImage()->GetActiveLabelSet()->SetAllLabelsLocked(false);
   mitk::RenderingManager::GetInstance()->RequestUpdateAll();
 }
 
 void QmitkLabelSetWidget::OnLockAllLabels(bool /*value*/)
 {
   GetWorkingImage()->GetActiveLabelSet()->SetAllLabelsLocked(true);
   mitk::RenderingManager::GetInstance()->RequestUpdateAll();
 }
 
 void QmitkLabelSetWidget::OnSetAllLabelsVisible(bool /*value*/)
 {
   GetWorkingImage()->GetActiveLabelSet()->SetAllLabelsVisible(true);
   UpdateAllTableWidgetItems();
 }
 
 void QmitkLabelSetWidget::OnSetAllLabelsInvisible(bool /*value*/)
 {
   GetWorkingImage()->GetActiveLabelSet()->SetAllLabelsVisible(false);
   UpdateAllTableWidgetItems();
 }
 
 void QmitkLabelSetWidget::OnSetOnlyActiveLabelVisible(bool /*value*/)
 {
   mitk::LabelSetImage *workingImage = GetWorkingImage();
   int pixelValue = GetPixelValueOfSelectedItem();
 
   workingImage->GetActiveLabelSet()->SetAllLabelsVisible(false);
   workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->SetVisible(true);
 
   workingImage->GetActiveLabelSet()->UpdateLookupTable(pixelValue);
 
   this->WaitCursorOn();
 
   const mitk::Point3D &pos =
     workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetCenterOfMassCoordinates();
   this->WaitCursorOff();
   if (pos.GetVnlVector().max_value() > 0.0)
   {
     emit goToLabel(pos);
   }
 
   UpdateAllTableWidgetItems();
 }
 
 void QmitkLabelSetWidget::OnEraseLabel(bool /*value*/)
 {
   int pixelValue = GetPixelValueOfSelectedItem();
   QString question = "Do you really want to erase the contents of label \"";
   question.append(
     QString::fromStdString(GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetName()));
   question.append("\"?");
 
   QMessageBox::StandardButton answerButton =
     QMessageBox::question(this, "Erase label", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
 
   if (answerButton == QMessageBox::Yes)
   {
     this->WaitCursorOn();
     GetWorkingImage()->EraseLabel(pixelValue);
     this->WaitCursorOff();
     mitk::RenderingManager::GetInstance()->RequestUpdateAll();
   }
 }
 
 void QmitkLabelSetWidget::OnRemoveLabel(bool /*value*/)
 {
   int pixelValue = GetPixelValueOfSelectedItem();
   QString question = "Do you really want to remove label \"";
   question.append(
     QString::fromStdString(GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetName()));
   question.append("\"?");
 
   QMessageBox::StandardButton answerButton =
     QMessageBox::question(this, "Remove label", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
 
   if (answerButton == QMessageBox::Yes)
   {
     this->WaitCursorOn();
-    GetWorkingImage()->RemoveLabel(pixelValue, GetWorkingImage()->GetActiveLayer());
+    GetWorkingImage()->RemoveLabel(pixelValue);
     this->WaitCursorOff();
   }
 
   ResetAllTableWidgetItems();
 }
 
 void QmitkLabelSetWidget::OnRenameLabel(bool /*value*/)
 {
   int pixelValue = GetPixelValueOfSelectedItem();
   QmitkNewSegmentationDialog dialog(this, this->GetWorkingImage(), QmitkNewSegmentationDialog::RenameLabel);
   dialog.SetColor(GetWorkingImage()->GetActiveLabelSet()->GetLabel(pixelValue)->GetColor());
   dialog.SetName(QString::fromStdString(GetWorkingImage()->GetActiveLabelSet()->GetLabel(pixelValue)->GetName()));
 
   if (dialog.exec() == QDialog::Rejected)
   {
     return;
   }
   QString segmentationName = dialog.GetName();
   if (segmentationName.isEmpty())
   {
     segmentationName = "Unnamed";
   }
 
   GetWorkingImage()->GetActiveLabelSet()->RenameLabel(pixelValue, segmentationName.toStdString(), dialog.GetColor());
   GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue);
 
   UpdateAllTableWidgetItems();
 }
 
 void QmitkLabelSetWidget::OnRenameLabelShortcutActivated()
 {
   if (m_Controls.m_LabelSetTableWidget->selectedItems().size() == 1)
     this->OnRenameLabel(true);
 }
 
 void QmitkLabelSetWidget::OnCombineAndCreateMask(bool /*value*/)
 {
   m_Controls.m_LabelSetTableWidget->selectedRanges();
   // ...to do... //
 }
 
 void QmitkLabelSetWidget::OnCreateMasks(bool /*value*/)
 {
   m_Controls.m_LabelSetTableWidget->selectedRanges();
   // ..to do.. //
 }
 
 void QmitkLabelSetWidget::OnCombineAndCreateSurface(bool /*value*/)
 {
   m_Controls.m_LabelSetTableWidget->selectedRanges();
   // ..to do.. //
 }
 
 void QmitkLabelSetWidget::OnEraseLabels(bool /*value*/)
 {
   QString question = "Do you really want to erase the selected labels?";
 
   QMessageBox::StandardButton answerButton = QMessageBox::question(
     this, "Erase selected labels", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
 
   if (answerButton == QMessageBox::Yes)
   {
     QList<QTableWidgetSelectionRange> ranges = m_Controls.m_LabelSetTableWidget->selectedRanges();
     if (ranges.isEmpty())
       return;
 
     std::vector<mitk::Label::PixelType> VectorOfLablePixelValues;
     foreach (QTableWidgetSelectionRange a, ranges)
       for (int i = a.topRow(); i <= a.bottomRow(); i++)
         VectorOfLablePixelValues.push_back(m_Controls.m_LabelSetTableWidget->item(i, 0)->data(Qt::UserRole).toInt());
 
     this->WaitCursorOn();
     GetWorkingImage()->EraseLabels(VectorOfLablePixelValues);
     this->WaitCursorOff();
     mitk::RenderingManager::GetInstance()->RequestUpdateAll();
   }
 }
 
 void QmitkLabelSetWidget::OnRemoveLabels(bool /*value*/)
 {
   QString question = "Do you really want to remove the selected labels?";
   QMessageBox::StandardButton answerButton = QMessageBox::question(
     this, "Remove selected labels", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
 
   if (answerButton == QMessageBox::Yes)
   {
     QList<QTableWidgetSelectionRange> ranges = m_Controls.m_LabelSetTableWidget->selectedRanges();
     if (ranges.isEmpty())
     {
       return;
     }
 
     std::vector<mitk::Label::PixelType> VectorOfLablePixelValues;
     foreach (QTableWidgetSelectionRange a, ranges)
     {
       for (int i = a.topRow(); i <= a.bottomRow(); ++i)
       {
         VectorOfLablePixelValues.push_back(m_Controls.m_LabelSetTableWidget->item(i, 0)->data(Qt::UserRole).toInt());
       }
     }
 
     this->WaitCursorOn();
-    GetWorkingImage()->RemoveLabels(VectorOfLablePixelValues, GetWorkingImage()->GetActiveLayer());
+    GetWorkingImage()->RemoveLabels(VectorOfLablePixelValues);
     this->WaitCursorOff();
   }
 
   ResetAllTableWidgetItems();
 }
 
 void QmitkLabelSetWidget::OnMergeLabels(bool /*value*/)
 {
   int pixelValue = GetPixelValueOfSelectedItem();
   QString question = "Do you really want to merge selected labels into \"";
   question.append(
     QString::fromStdString(GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetName()));
   question.append("\"?");
 
   QMessageBox::StandardButton answerButton = QMessageBox::question(
     this, "Merge selected label", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
 
   if (answerButton == QMessageBox::Yes)
   {
     QList<QTableWidgetSelectionRange> ranges = m_Controls.m_LabelSetTableWidget->selectedRanges();
     if (ranges.isEmpty())
     {
       return;
     }
 
     std::vector<mitk::Label::PixelType> vectorOfSourcePixelValues;
     foreach (QTableWidgetSelectionRange a, ranges)
     {
       for (int i = a.topRow(); i <= a.bottomRow(); ++i)
       {
         vectorOfSourcePixelValues.push_back(m_Controls.m_LabelSetTableWidget->item(i, 0)->data(Qt::UserRole).toInt());
       }
     }
 
     this->WaitCursorOn();
     GetWorkingImage()->MergeLabels(pixelValue, vectorOfSourcePixelValues, GetWorkingImage()->GetActiveLayer());
     this->WaitCursorOff();
 
     mitk::RenderingManager::GetInstance()->RequestUpdateAll();
   }
 }
 
 void QmitkLabelSetWidget::OnLockedButtonClicked()
 {
   int row = -1;
   for (int i = 0; i < m_Controls.m_LabelSetTableWidget->rowCount(); ++i)
   {
     if (sender() == m_Controls.m_LabelSetTableWidget->cellWidget(i, LOCKED_COL))
     {
       row = i;
     }
   }
   if (row >= 0 && row < m_Controls.m_LabelSetTableWidget->rowCount())
   {
     int pixelValue = m_Controls.m_LabelSetTableWidget->item(row, 0)->data(Qt::UserRole).toInt();
     GetWorkingImage()
       ->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())
       ->SetLocked(!GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetLocked());
   }
 }
 
 void QmitkLabelSetWidget::OnVisibleButtonClicked()
 {
   int row = -1;
   for (int i = 0; i < m_Controls.m_LabelSetTableWidget->rowCount(); ++i)
   {
     if (sender() == m_Controls.m_LabelSetTableWidget->cellWidget(i, VISIBLE_COL))
     {
       row = i;
       break;
     }
   }
 
   if (row >= 0 && row < m_Controls.m_LabelSetTableWidget->rowCount())
   {
     QTableWidgetItem *item = m_Controls.m_LabelSetTableWidget->item(row, 0);
     int pixelValue = item->data(Qt::UserRole).toInt();
     GetWorkingImage()
       ->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())
       ->SetVisible(!GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetVisible());
     GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue);
   }
 
   mitk::RenderingManager::GetInstance()->RequestUpdateAll();
 }
 
 void QmitkLabelSetWidget::OnColorButtonClicked()
 {
   int row = -1;
   for (int i = 0; i < m_Controls.m_LabelSetTableWidget->rowCount(); ++i)
   {
     if (sender() == m_Controls.m_LabelSetTableWidget->cellWidget(i, COLOR_COL))
     {
       row = i;
     }
   }
 
   if (row >= 0 && row < m_Controls.m_LabelSetTableWidget->rowCount())
   {
     int pixelValue = m_Controls.m_LabelSetTableWidget->item(row, 0)->data(Qt::UserRole).toInt();
     const mitk::Color &color = GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetColor();
     QColor initial(color.GetRed() * 255, color.GetGreen() * 255, color.GetBlue() * 255);
     QColor qcolor = QColorDialog::getColor(initial, nullptr, QString("Change color"));
     if (!qcolor.isValid())
     {
       return;
     }
 
     QPushButton *button = static_cast<QPushButton *>(m_Controls.m_LabelSetTableWidget->cellWidget(row, COLOR_COL));
     if (!button)
     {
       return;
     }
 
     button->setAutoFillBackground(true);
 
     QString styleSheet = "background-color:rgb(";
     styleSheet.append(QString::number(qcolor.red()));
     styleSheet.append(",");
     styleSheet.append(QString::number(qcolor.green()));
     styleSheet.append(",");
     styleSheet.append(QString::number(qcolor.blue()));
     styleSheet.append("); border: 0;");
     button->setStyleSheet(styleSheet);
 
     mitk::Color newColor;
     newColor.SetRed(qcolor.red() / 255.0);
     newColor.SetGreen(qcolor.green() / 255.0);
     newColor.SetBlue(qcolor.blue() / 255.0);
 
     GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->SetColor(newColor);
 
     GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue);
   }
 }
 
 void QmitkLabelSetWidget::OnRandomColor(bool /*value*/)
 {
   int pixelValue = GetPixelValueOfSelectedItem();
   GetWorkingImage()
     ->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())
     ->SetColor(m_ColorSequenceRainbow.GetNextColor());
   GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue);
   UpdateAllTableWidgetItems();
 }
 
 void QmitkLabelSetWidget::OnActiveLabelChanged(int pixelValue)
 {
   mitk::LabelSetImage *workingImage = GetWorkingImage();
   assert(workingImage);
   workingImage->GetActiveLabelSet()->SetActiveLabel(pixelValue);
   // MITK_INFO << "Active Label set to << " << pixelValue;
 
   workingImage->Modified();
   mitk::RenderingManager::GetInstance()->RequestUpdateAll();
 }
 
 void QmitkLabelSetWidget::OnItemClicked(QTableWidgetItem *item)
 {
   if (!item)
     return;
 
   int pixelValue = item->data(Qt::UserRole).toInt();
 
   QList<QTableWidgetSelectionRange> ranges = m_Controls.m_LabelSetTableWidget->selectedRanges();
   if (!ranges.empty() && ranges.back().rowCount() == 1)
   {
     m_ProcessingManualSelection = true;
     OnActiveLabelChanged(pixelValue);
     m_ProcessingManualSelection = false;
     mitk::RenderingManager::GetInstance()->RequestUpdateAll();
   }
 }
 
 void QmitkLabelSetWidget::OnItemDoubleClicked(QTableWidgetItem *item)
 {
   if (!item)
     return;
 
   if (QApplication::queryKeyboardModifiers().testFlag(Qt::AltModifier))
   {
     this->OnRenameLabelShortcutActivated();
     return;
   }
 
   int pixelValue = item->data(Qt::UserRole).toInt();
   // OnItemClicked(item); <<-- Double click first call OnItemClicked
   WaitCursorOn();
   mitk::LabelSetImage *workingImage = GetWorkingImage();
   workingImage->UpdateCenterOfMass(pixelValue, workingImage->GetActiveLayer());
   const mitk::Point3D &pos =
     workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetCenterOfMassCoordinates();
   WaitCursorOff();
   if (pos.GetVnlVector().max_value() > 0.0)
   {
     emit goToLabel(pos);
   }
 
   workingImage->Modified();
   mitk::RenderingManager::GetInstance()->RequestUpdateAll();
 }
 
 void QmitkLabelSetWidget::SelectLabelByPixelValue(mitk::Label::PixelType pixelValue)
 {
   if (m_ProcessingManualSelection || !GetWorkingImage()->ExistLabel(pixelValue))
     return;
 
   for (int row = 0; row < m_Controls.m_LabelSetTableWidget->rowCount(); row++)
   {
     if (m_Controls.m_LabelSetTableWidget->item(row, 0)->data(Qt::UserRole).toInt() == pixelValue)
     {
       m_Controls.m_LabelSetTableWidget->clearSelection();
       m_Controls.m_LabelSetTableWidget->selectRow(row);
       m_Controls.m_LabelSetTableWidget->scrollToItem(m_Controls.m_LabelSetTableWidget->item(row, 0));
       return;
     }
   }
 }
 
 void QmitkLabelSetWidget::InsertTableWidgetItem(mitk::Label *label)
 {
   const mitk::Color &color = label->GetColor();
 
   QString styleSheet = "background-color:rgb(";
   styleSheet.append(QString::number(color[0] * 255));
   styleSheet.append(",");
   styleSheet.append(QString::number(color[1] * 255));
   styleSheet.append(",");
   styleSheet.append(QString::number(color[2] * 255));
   styleSheet.append("); border: 0;");
 
   QTableWidget *tableWidget = m_Controls.m_LabelSetTableWidget;
   int colWidth = (tableWidget->columnWidth(NAME_COL) < 180) ? 180 : tableWidget->columnWidth(NAME_COL) - 2;
   QString text = fontMetrics().elidedText(label->GetName().c_str(), Qt::ElideMiddle, colWidth);
   QTableWidgetItem *nameItem = new QTableWidgetItem(text);
   nameItem->setTextAlignment(Qt::AlignCenter | Qt::AlignLeft);
   // ---!---
   // IMPORTANT: ADD PIXELVALUE TO TABLEWIDGETITEM.DATA
   nameItem->setData(Qt::UserRole, QVariant(label->GetValue()));
   // ---!---
 
   QPushButton *pbColor = new QPushButton(tableWidget);
   pbColor->setFixedSize(24, 24);
   pbColor->setCheckable(false);
   pbColor->setAutoFillBackground(true);
   pbColor->setToolTip("Change label color");
   pbColor->setStyleSheet(styleSheet);
 
   connect(pbColor, SIGNAL(clicked()), this, SLOT(OnColorButtonClicked()));
 
   QString transparentStyleSheet = QLatin1String("background-color: transparent; border: 0;");
 
   QPushButton *pbLocked = new QPushButton(tableWidget);
   pbLocked->setFixedSize(24, 24);
   QIcon *iconLocked = new QIcon();
   auto lockIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/lock.svg"));
   auto unlockIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/unlock.svg"));
   iconLocked->addPixmap(lockIcon.pixmap(64), QIcon::Normal, QIcon::Off);
   iconLocked->addPixmap(unlockIcon.pixmap(64), QIcon::Normal, QIcon::On);
   pbLocked->setIcon(*iconLocked);
   pbLocked->setIconSize(QSize(24, 24));
   pbLocked->setCheckable(true);
   pbLocked->setToolTip("Lock/unlock label");
   pbLocked->setChecked(!label->GetLocked());
   pbLocked->setStyleSheet(transparentStyleSheet);
 
   connect(pbLocked, SIGNAL(clicked()), this, SLOT(OnLockedButtonClicked()));
 
   QPushButton *pbVisible = new QPushButton(tableWidget);
   pbVisible->setFixedSize(24, 24);
   pbVisible->setAutoRepeat(false);
   QIcon *iconVisible = new QIcon();
   auto visibleIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/visible.svg"));
   auto invisibleIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/invisible.svg"));
   iconVisible->addPixmap(visibleIcon.pixmap(64), QIcon::Normal, QIcon::Off);
   iconVisible->addPixmap(invisibleIcon.pixmap(64), QIcon::Normal, QIcon::On);
   pbVisible->setIcon(*iconVisible);
   pbVisible->setIconSize(QSize(24, 24));
   pbVisible->setCheckable(true);
   pbVisible->setToolTip("Show/hide label");
   pbVisible->setChecked(!label->GetVisible());
   pbVisible->setStyleSheet(transparentStyleSheet);
 
   connect(pbVisible, SIGNAL(clicked()), this, SLOT(OnVisibleButtonClicked()));
 
   int row = tableWidget->rowCount();
   tableWidget->insertRow(row);
   tableWidget->setRowHeight(row, 24);
   tableWidget->setItem(row, 0, nameItem);
   tableWidget->setCellWidget(row, 1, pbLocked);
   tableWidget->setCellWidget(row, 2, pbColor);
   tableWidget->setCellWidget(row, 3, pbVisible);
   tableWidget->selectRow(row);
 
   // m_LabelSetImage->SetActiveLabel(label->GetPixelValue());
   // m_ToolManager->WorkingDataModified.Send();
   // emit activeLabelChanged(label->GetPixelValue());
 
   if (row == 0)
   {
     tableWidget->hideRow(row); // hide exterior label
   }
 }
 
+void QmitkLabelSetWidget::UpdateAllTableWidgetItems(mitk::Label::PixelType /*lv*/)
+{
+  this->UpdateAllTableWidgetItems();
+}
+
 void QmitkLabelSetWidget::UpdateAllTableWidgetItems()
 {
   mitk::LabelSetImage *workingImage = GetWorkingImage();
   if (!workingImage)
     return;
 
   // add all labels
   QTableWidget *tableWidget = m_Controls.m_LabelSetTableWidget;
   m_LabelStringList.clear();
   for (int i = 0; i < tableWidget->rowCount(); ++i)
   {
     UpdateTableWidgetItem(tableWidget->item(i, 0));
     m_LabelStringList.append(tableWidget->item(i, 0)->text());
   }
 
   OnLabelListModified(m_LabelStringList);
 
   mitk::RenderingManager::GetInstance()->RequestUpdateAll();
 }
 
 void QmitkLabelSetWidget::UpdateTableWidgetItem(QTableWidgetItem *item)
 {
   mitk::LabelSetImage *workingImage = GetWorkingImage();
   mitk::Label *label = workingImage->GetLabel(item->data(Qt::UserRole).toInt(), workingImage->GetActiveLayer());
 
   const mitk::Color &color = label->GetColor();
 
   QString styleSheet = "background-color:rgb(";
   styleSheet.append(QString::number(color[0] * 255));
   styleSheet.append(",");
   styleSheet.append(QString::number(color[1] * 255));
   styleSheet.append(",");
   styleSheet.append(QString::number(color[2] * 255));
   styleSheet.append("); border: 0;");
 
   QTableWidget *tableWidget = m_Controls.m_LabelSetTableWidget;
   int colWidth = (tableWidget->columnWidth(NAME_COL) < 180) ? 180 : tableWidget->columnWidth(NAME_COL) - 2;
   QString text = fontMetrics().elidedText(label->GetName().c_str(), Qt::ElideMiddle, colWidth);
   item->setText(text);
 
   QPushButton *pbLocked = dynamic_cast<QPushButton *>(tableWidget->cellWidget(item->row(), 1));
   pbLocked->setChecked(!label->GetLocked());
 
   QPushButton *pbColor = dynamic_cast<QPushButton *>(tableWidget->cellWidget(item->row(), 2));
   pbColor->setStyleSheet(styleSheet);
 
   QPushButton *pbVisible = dynamic_cast<QPushButton *>(tableWidget->cellWidget(item->row(), 3));
   pbVisible->setChecked(!label->GetVisible());
 
   if (item->row() == 0)
   {
     tableWidget->hideRow(item->row()); // hide exterior label
   }
 }
 
+void QmitkLabelSetWidget::ResetAllTableWidgetItems(mitk::Label::PixelType /*lv*/)
+{
+  this->ResetAllTableWidgetItems();
+}
+
 void QmitkLabelSetWidget::ResetAllTableWidgetItems()
 {
   QTableWidget *tableWidget = m_Controls.m_LabelSetTableWidget;
   // remove all rows
   while (tableWidget->rowCount())
   {
     tableWidget->removeRow(0);
   }
 
   mitk::DataNode * workingNode = GetWorkingNode();
   auto workingImage = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData());
   if (nullptr == workingImage)
   {
     return;
   }
 
   // add all labels
   m_LabelStringList.clear();
 
   mitk::LabelSet::LabelContainerConstIteratorType it = workingImage->GetActiveLabelSet()->IteratorConstBegin();
   mitk::LabelSet::LabelContainerConstIteratorType end = workingImage->GetActiveLabelSet()->IteratorConstEnd();
 
   int pixelValue = -1;
   while (it != end)
   {
     InsertTableWidgetItem(it->second);
     if (workingImage->GetActiveLabel(workingImage->GetActiveLayer()) == it->second) // get active
       pixelValue = it->first;
     m_LabelStringList.append(QString(it->second->GetName().c_str()));
     it++;
   }
 
   SelectLabelByPixelValue(pixelValue);
 
   OnLabelListModified(m_LabelStringList);
 
   std::stringstream captionText;
   captionText << "Number of labels: " << workingImage->GetNumberOfLabels(workingImage->GetActiveLayer()) - 1;
   m_Controls.m_lblCaption->setText(captionText.str().c_str());
 
   mitk::RenderingManager::GetInstance()->RequestUpdateAll();
 
   emit LabelSetWidgetReset();
 }
 
 int QmitkLabelSetWidget::GetPixelValueOfSelectedItem()
 {
   if (m_Controls.m_LabelSetTableWidget->currentItem())
   {
     return m_Controls.m_LabelSetTableWidget->currentItem()->data(Qt::UserRole).toInt();
   }
   return -1;
 }
 
 QStringList &QmitkLabelSetWidget::GetLabelStringList()
 {
   return m_LabelStringList;
 }
 
 void QmitkLabelSetWidget::InitializeTableWidget()
 {
   QTableWidget *tableWidget = m_Controls.m_LabelSetTableWidget;
 
   tableWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
   tableWidget->setTabKeyNavigation(false);
   tableWidget->setAlternatingRowColors(false);
   tableWidget->setFocusPolicy(Qt::NoFocus);
   tableWidget->setColumnCount(4);
   tableWidget->resizeColumnToContents(NAME_COL);
   tableWidget->setColumnWidth(LOCKED_COL, 25);
   tableWidget->setColumnWidth(COLOR_COL, 25);
   tableWidget->setColumnWidth(VISIBLE_COL, 25);
   tableWidget->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
   tableWidget->setContextMenuPolicy(Qt::CustomContextMenu);
   tableWidget->horizontalHeader()->hide();
   tableWidget->setSortingEnabled(false);
   tableWidget->verticalHeader()->hide();
   tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
 
   connect(tableWidget, SIGNAL(itemClicked(QTableWidgetItem *)), this, SLOT(OnItemClicked(QTableWidgetItem *)));
   connect(
     tableWidget, SIGNAL(itemDoubleClicked(QTableWidgetItem *)), this, SLOT(OnItemDoubleClicked(QTableWidgetItem *)));
   connect(tableWidget,
           SIGNAL(customContextMenuRequested(const QPoint &)),
           this,
           SLOT(OnTableViewContextMenuRequested(const QPoint &)));
 }
 
 void QmitkLabelSetWidget::OnOpacityChanged(int value)
 {
   int pixelValue = GetPixelValueOfSelectedItem();
   float opacity = static_cast<float>(value) / 100.0f;
   GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->SetOpacity(opacity);
   GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue);
 }
 
 void QmitkLabelSetWidget::setEnabled(bool enabled)
 {
   QWidget::setEnabled(enabled);
   UpdateControls();
 }
 
 void QmitkLabelSetWidget::SetDataStorage(mitk::DataStorage *storage)
 {
   m_DataStorage = storage;
 }
 
 void QmitkLabelSetWidget::OnSearchLabel()
 {
   std::string text = m_Controls.m_LabelSearchBox->text().toStdString();
   int pixelValue = -1;
   int row = -1;
   for (int i = 0; i < m_Controls.m_LabelSetTableWidget->rowCount(); ++i)
   {
     if (m_Controls.m_LabelSetTableWidget->item(i, 0)->text().toStdString().compare(text) == 0)
     {
       pixelValue = m_Controls.m_LabelSetTableWidget->item(i, 0)->data(Qt::UserRole).toInt();
       row = i;
       break;
     }
   }
   if (pixelValue == -1)
   {
     return;
   }
 
   GetWorkingImage()->GetActiveLabelSet()->SetActiveLabel(pixelValue);
 
   QTableWidgetItem *nameItem = m_Controls.m_LabelSetTableWidget->item(row, NAME_COL);
   if (!nameItem)
   {
     return;
   }
 
   m_Controls.m_LabelSetTableWidget->clearSelection();
   m_Controls.m_LabelSetTableWidget->selectRow(row);
   m_Controls.m_LabelSetTableWidget->scrollToItem(nameItem);
 
   GetWorkingImage()->GetActiveLabelSet()->SetActiveLabel(pixelValue);
 
   this->WaitCursorOn();
   mitk::Point3D pos =
     GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetCenterOfMassCoordinates();
 
   m_ToolManager->WorkingDataChanged();
 
   if (pos.GetVnlVector().max_value() > 0.0)
   {
     emit goToLabel(pos);
   }
   else
   {
     GetWorkingImage()->UpdateCenterOfMass(pixelValue, GetWorkingImage()->GetActiveLayer());
     mitk::Point3D pos =
       GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetCenterOfMassCoordinates();
     emit goToLabel(pos);
   }
 
   this->WaitCursorOff();
 }
 
 void QmitkLabelSetWidget::OnLabelListModified(const QStringList &list)
 {
   QStringListModel *completeModel = static_cast<QStringListModel *>(m_Completer->model());
   completeModel->setStringList(list);
 }
 
 mitk::LabelSetImage *QmitkLabelSetWidget::GetWorkingImage()
 {
   mitk::DataNode *workingNode = GetWorkingNode();
   mitk::LabelSetImage *workingImage = dynamic_cast<mitk::LabelSetImage *>(workingNode->GetData());
   assert(workingImage);
   return workingImage;
 }
 
 mitk::DataNode *QmitkLabelSetWidget::GetWorkingNode()
 {
   mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0);
   assert(workingNode);
   return workingNode;
 }
 
 void QmitkLabelSetWidget::UpdateControls()
 {
   mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0);
   bool hasWorkingData = (workingNode != nullptr);
 
   m_Controls.m_LabelSetTableWidget->setEnabled(hasWorkingData);
   m_Controls.m_LabelSearchBox->setEnabled(hasWorkingData);
 
   if (!hasWorkingData)
     return;
 
   QStringListModel *completeModel = static_cast<QStringListModel *>(m_Completer->model());
   completeModel->setStringList(GetLabelStringList());
 }
 
 void QmitkLabelSetWidget::OnCreateCroppedMask(bool)
 {
   m_ToolManager->ActivateTool(-1);
 
   mitk::LabelSetImage *workingImage = GetWorkingImage();
   mitk::Image::Pointer maskImage;
   int pixelValue = GetPixelValueOfSelectedItem();
   try
   {
     this->WaitCursorOn();
 
     mitk::AutoCropImageFilter::Pointer cropFilter = mitk::AutoCropImageFilter::New();
     cropFilter->SetInput(workingImage->CreateLabelMask(pixelValue));
     cropFilter->SetBackgroundValue(0);
     cropFilter->SetMarginFactor(1.15);
     cropFilter->Update();
 
     maskImage = cropFilter->GetOutput();
 
     this->WaitCursorOff();
   }
   catch (mitk::Exception &e)
   {
     this->WaitCursorOff();
     MITK_ERROR << "Exception caught: " << e.GetDescription();
     QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n");
     return;
   }
 
   if (maskImage.IsNull())
   {
     QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n");
     return;
   }
 
   mitk::DataNode::Pointer maskNode = mitk::DataNode::New();
   std::string name = workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetName();
   name += "-mask";
   maskNode->SetName(name);
   maskNode->SetData(maskImage);
   maskNode->SetBoolProperty("binary", true);
   maskNode->SetBoolProperty("outline binary", true);
   maskNode->SetBoolProperty("outline binary shadow", true);
   maskNode->SetFloatProperty("outline width", 2.0);
   maskNode->SetColor(workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetColor());
   maskNode->SetOpacity(1.0);
 
   m_DataStorage->Add(maskNode, GetWorkingNode());
 }
 
 void QmitkLabelSetWidget::OnCreateMask(bool /*triggered*/)
 {
   m_ToolManager->ActivateTool(-1);
 
   mitk::LabelSetImage *workingImage = GetWorkingImage();
   mitk::Image::Pointer maskImage;
   int pixelValue = GetPixelValueOfSelectedItem();
   try
   {
     this->WaitCursorOn();
     maskImage = workingImage->CreateLabelMask(pixelValue);
     this->WaitCursorOff();
   }
   catch (mitk::Exception &e)
   {
     this->WaitCursorOff();
     MITK_ERROR << "Exception caught: " << e.GetDescription();
     QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n");
     return;
   }
 
   if (maskImage.IsNull())
   {
     QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n");
     return;
   }
 
   mitk::DataNode::Pointer maskNode = mitk::DataNode::New();
   std::string name = workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetName();
   name += "-mask";
   maskNode->SetName(name);
   maskNode->SetData(maskImage);
   maskNode->SetBoolProperty("binary", true);
   maskNode->SetBoolProperty("outline binary", true);
   maskNode->SetBoolProperty("outline binary shadow", true);
   maskNode->SetFloatProperty("outline width", 2.0);
   maskNode->SetColor(workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetColor());
   maskNode->SetOpacity(1.0);
 
   m_DataStorage->Add(maskNode, GetWorkingNode());
 }
 
 void QmitkLabelSetWidget::OnToggleOutline(bool value)
 {
   mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0);
   assert(workingNode);
 
   workingNode->SetBoolProperty("labelset.contour.active", value);
   workingNode->GetData()->Modified(); // fixme: workaround to force data-type rendering (and not only property-type)
   mitk::RenderingManager::GetInstance()->RequestUpdateAll();
 }
 
 void QmitkLabelSetWidget::OnCreateSmoothedSurface(bool /*triggered*/)
 {
   m_ToolManager->ActivateTool(-1);
 
   mitk::DataNode::Pointer workingNode = GetWorkingNode();
   mitk::LabelSetImage *workingImage = GetWorkingImage();
   int pixelValue = GetPixelValueOfSelectedItem();
 
   mitk::LabelSetImageToSurfaceThreadedFilter::Pointer surfaceFilter = mitk::LabelSetImageToSurfaceThreadedFilter::New();
 
   itk::SimpleMemberCommand<QmitkLabelSetWidget>::Pointer successCommand =
     itk::SimpleMemberCommand<QmitkLabelSetWidget>::New();
   successCommand->SetCallbackFunction(this, &QmitkLabelSetWidget::OnThreadedCalculationDone);
   surfaceFilter->AddObserver(mitk::ResultAvailable(), successCommand);
 
   itk::SimpleMemberCommand<QmitkLabelSetWidget>::Pointer errorCommand =
     itk::SimpleMemberCommand<QmitkLabelSetWidget>::New();
   errorCommand->SetCallbackFunction(this, &QmitkLabelSetWidget::OnThreadedCalculationDone);
   surfaceFilter->AddObserver(mitk::ProcessingError(), errorCommand);
 
   mitk::DataNode::Pointer groupNode = workingNode;
   surfaceFilter->SetPointerParameter("Group node", groupNode);
   surfaceFilter->SetPointerParameter("Input", workingImage);
   surfaceFilter->SetParameter("RequestedLabel", pixelValue);
   surfaceFilter->SetParameter("Smooth", true);
   surfaceFilter->SetDataStorage(*m_DataStorage);
 
   mitk::StatusBar::GetInstance()->DisplayText("Surface creation is running in background...");
 
   try
   {
     surfaceFilter->StartAlgorithm();
   }
   catch (mitk::Exception &e)
   {
     MITK_ERROR << "Exception caught: " << e.GetDescription();
     QMessageBox::information(this,
                              "Create Surface",
                              "Could not create a surface mesh out of the selected label. See error log for details.\n");
   }
 }
 
 void QmitkLabelSetWidget::OnCreateDetailedSurface(bool /*triggered*/)
 {
   m_ToolManager->ActivateTool(-1);
 
   mitk::DataNode::Pointer workingNode = GetWorkingNode();
   mitk::LabelSetImage *workingImage = GetWorkingImage();
   int pixelValue = GetPixelValueOfSelectedItem();
 
   mitk::LabelSetImageToSurfaceThreadedFilter::Pointer surfaceFilter = mitk::LabelSetImageToSurfaceThreadedFilter::New();
 
   itk::SimpleMemberCommand<QmitkLabelSetWidget>::Pointer successCommand =
     itk::SimpleMemberCommand<QmitkLabelSetWidget>::New();
   successCommand->SetCallbackFunction(this, &QmitkLabelSetWidget::OnThreadedCalculationDone);
   surfaceFilter->AddObserver(mitk::ResultAvailable(), successCommand);
 
   itk::SimpleMemberCommand<QmitkLabelSetWidget>::Pointer errorCommand =
     itk::SimpleMemberCommand<QmitkLabelSetWidget>::New();
   errorCommand->SetCallbackFunction(this, &QmitkLabelSetWidget::OnThreadedCalculationDone);
   surfaceFilter->AddObserver(mitk::ProcessingError(), errorCommand);
 
   mitk::DataNode::Pointer groupNode = workingNode;
   surfaceFilter->SetPointerParameter("Group node", groupNode);
   surfaceFilter->SetPointerParameter("Input", workingImage);
   surfaceFilter->SetParameter("RequestedLabel", pixelValue);
   surfaceFilter->SetParameter("Smooth", false);
   surfaceFilter->SetDataStorage(*m_DataStorage);
 
   mitk::StatusBar::GetInstance()->DisplayText("Surface creation is running in background...");
 
   try
   {
     surfaceFilter->StartAlgorithm();
   }
   catch (mitk::Exception &e)
   {
     MITK_ERROR << "Exception caught: " << e.GetDescription();
     QMessageBox::information(this,
                              "Create Surface",
                              "Could not create a surface mesh out of the selected label. See error log for details.\n");
   }
 }
 
 void QmitkLabelSetWidget::WaitCursorOn()
 {
   QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
 }
 
 void QmitkLabelSetWidget::WaitCursorOff()
 {
   this->RestoreOverrideCursor();
 }
 
 void QmitkLabelSetWidget::RestoreOverrideCursor()
 {
   QApplication::restoreOverrideCursor();
 }
 
 void QmitkLabelSetWidget::OnThreadedCalculationDone()
 {
   mitk::StatusBar::GetInstance()->Clear();
 }
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkLabelSetWidget.h b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkLabelSetWidget.h
index a1d6146b4d..688ed5f9b5 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkLabelSetWidget.h
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkLabelSetWidget.h
@@ -1,166 +1,169 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #ifndef QmitkLabelSetWidget_h
 #define QmitkLabelSetWidget_h
 
 #include <org_mitk_gui_qt_segmentation_Export.h>
 
 #include <mitkColorSequenceRainbow.h>
 #include <mitkLabel.h>
 #include <mitkNumericTypes.h>
 #include <ui_QmitkLabelSetWidgetControls.h>
 
 class QmitkDataStorageComboBox;
 class QCompleter;
 
 namespace mitk
 {
   class LabelSetImage;
   class LabelSet;
   class Label;
   class DataStorage;
   class ToolManager;
   class DataNode;
 }
 
 class MITK_QT_SEGMENTATION QmitkLabelSetWidget : public QWidget
 {
   Q_OBJECT
 
 public:
   explicit QmitkLabelSetWidget(QWidget *parent = nullptr);
   ~QmitkLabelSetWidget() override;
 
   void SetDataStorage(mitk::DataStorage *storage);
 
   void UpdateControls();
 
   virtual void setEnabled(bool enabled);
 
   QStringList &GetLabelStringList();
 
 signals:
 
   /// \brief Send a signal when it was requested to go to a label.
   void goToLabel(const mitk::Point3D &);
   void LabelSetWidgetReset();
 
 public slots:
 
   /**
   * @brief Updates the current labels in the label set widget table. For each label (widget item) the 'UpdateTableWidgetItem' is called.
   *
   *   Updating means setting the color box of the table, setting the column with and fill it with the label name.
   *   Furthermore the two push buttons for locking and showing/hiding the layer are checked/unchecked.
   *   This functions only changes the appearance of the table widget and no render window update is necessary.
   */
   void UpdateAllTableWidgetItems();
+  void UpdateAllTableWidgetItems(mitk::Label::PixelType);
   /**
   * @brief Resets the current labels in the label set widget table. For each label a widget item is inserted into the table.
   *
   *   Resetting means removing all rows of the widget table and inserting new rows (labels) from the active label set (= layer) of the current working node.
   *   The currently active label is selected and 'Number of labels' is set.
   *   As this function is typically used after one label has been removed or the reference node has been changed (e.g.) the render windows have to be updated.
   */
   void ResetAllTableWidgetItems();
+  void ResetAllTableWidgetItems(mitk::Label::PixelType);
+
   void SelectLabelByPixelValue(mitk::Label::PixelType pixelValue);
 
 private slots:
 
   // LabelSet dependent
   void OnOpacityChanged(int);
   void OnUnlockAllLabels(bool);
   void OnLockAllLabels(bool);
   void OnSetAllLabelsVisible(bool);
   void OnSetAllLabelsInvisible(bool);
   void OnSetOnlyActiveLabelVisible(bool);
   void OnRandomColor(bool);
   void OnRemoveLabel(bool);
   void OnRemoveLabels(bool);
   void OnRenameLabel(bool);
   void OnRenameLabelShortcutActivated();
   void OnLockedButtonClicked();
   void OnVisibleButtonClicked();
   void OnColorButtonClicked();
   void OnItemClicked(QTableWidgetItem *item);
   void OnItemDoubleClicked(QTableWidgetItem *item);
   void OnTableViewContextMenuRequested(const QPoint &);
   void InsertTableWidgetItem(mitk::Label *label);
   void UpdateTableWidgetItem(QTableWidgetItem *item);
   // reaction to "returnPressed" signal from ...
   void OnSearchLabel();
   // reaction to the button "Change Label"
   void OnActiveLabelChanged(int pixelValue);
 
   // LabelSetImage Dependet
   void OnCreateDetailedSurface(bool);
   void OnCreateSmoothedSurface(bool);
   // reaction to the signal "createMask" from QmitkLabelSetTableWidget
   void OnCreateMask(bool);
   void OnCreateMasks(bool);
   // reaction to the signal "createCroppedMask" from QmitkLabelSetTableWidget
   void OnCreateCroppedMask(bool);
   void OnCombineAndCreateMask(bool);
   void OnCombineAndCreateSurface(bool);
   void OnEraseLabel(bool);
   void OnEraseLabels(bool);
   void OnMergeLabels(bool);
 
   // reaction to signal "labelListModified" from QmitkLabelSetTableWidget
   void OnLabelListModified(const QStringList &list);
   // reaction to the signal "toggleOutline" from QmitkLabelSetTableWidget
   void OnToggleOutline(bool);
 
 private:
   enum TableColumns
   {
     NAME_COL = 0,
     LOCKED_COL,
     COLOR_COL,
     VISIBLE_COL
   };
 
   void WaitCursorOn();
 
   void WaitCursorOff();
 
   void RestoreOverrideCursor();
 
   void OnThreadedCalculationDone();
 
   void InitializeTableWidget();
 
   int GetPixelValueOfSelectedItem();
 
   mitk::LabelSetImage *GetWorkingImage();
 
   mitk::DataNode *GetWorkingNode();
 
   Ui::QmitkLabelSetWidgetControls m_Controls;
 
   mitk::ColorSequenceRainbow m_ColorSequenceRainbow;
 
   mitk::DataStorage *m_DataStorage;
 
   QCompleter *m_Completer;
 
   mitk::ToolManager *m_ToolManager;
 
   QStringList m_OrganColors;
 
   QStringList m_LabelStringList;
 
   bool m_ProcessingManualSelection;
 };
 
 #endif
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkNewSegmentationDialog.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkNewSegmentationDialog.cpp
index ae8f174479..b7e607cbda 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkNewSegmentationDialog.cpp
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkNewSegmentationDialog.cpp
@@ -1,370 +1,401 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "QmitkNewSegmentationDialog.h"
 #include <ui_QmitkNewSegmentationDialog.h>
 
 #include <mitkAnatomicalStructureColorPresets.h>
 #include <mitkBaseApplication.h>
 #include <mitkLabelSetImage.h>
 
 #include <mitkCoreServices.h>
 #include <mitkIPreferencesService.h>
 #include <mitkIPreferences.h>
 
 #include <algorithm>
 
 #include <vtkNew.h>
 
 #include <QColorDialog>
 #include <QPushButton>
 
 #include <nlohmann/json.hpp>
 
 namespace
 {
   // Get standard label name and color suggestions from embedded XML preset file for anatomical structures.
   QmitkNewSegmentationDialog::SuggestionsType GetStandardSuggestions()
   {
     vtkNew<mitk::AnatomicalStructureColorPresets> presets;
     presets->LoadPreset();
 
     auto colorPresets = presets->GetColorPresets();
 
     QmitkNewSegmentationDialog::SuggestionsType standardSuggestions;
     standardSuggestions.reserve(colorPresets.size());
 
     for (const auto& preset : colorPresets)
     {
       auto name = QString::fromStdString(preset.first);
       auto color = QColor::fromRgbF(preset.second.GetRed(), preset.second.GetGreen(), preset.second.GetBlue());
       standardSuggestions.emplace_back(name, color);
     }
 
     std::sort(begin(standardSuggestions), end(standardSuggestions), [](const auto& lhs, const auto& rhs) {
       return lhs.first < rhs.first;
     });
 
     return standardSuggestions;
   }
 
   // Parse label name and color suggestions from a JSON file. An array of objects is expected, each consisting
   // of a "name" string and an optional "color" string. If present, the "color" string must follow the conventions
   // of QColor::setNamedColor(), i.e., #rrggbb or any SVG color keyword name like CornflowerBlue. Everything else
   // in the JSON file is simply ignored. In case of any error, an empty map is returned.
   QmitkNewSegmentationDialog::SuggestionsType ParseSuggestions(const std::string& filename)
   {
     std::ifstream file(filename);
 
     if (!file.is_open())
     {
       MITK_ERROR << "Could not open \"" << filename << "\"!";
       return {};
     }
 
     auto json = nlohmann::json::parse(file, nullptr, false);
 
     if (json.is_discarded() || !json.is_array())
     {
       MITK_ERROR << "Could not parse \"" << filename << "\" as JSON array!";
       return {};
     }
 
     QmitkNewSegmentationDialog::SuggestionsType parsedSuggestions;
 
     for (const auto& obj : json)
     {
       if (!obj.is_object() || !obj.contains("name"))
         continue;
 
       auto name = QString::fromStdString(obj["name"]);
       QColor color;
 
       if (obj.contains("color"))
         color.setNamedColor(QString::fromStdString(obj["color"]));
 
       auto it = std::find_if(begin(parsedSuggestions), end(parsedSuggestions), [&name](const auto& suggestion) {
         return name == suggestion.first;
       });
 
       // Ignore duplicates...
       if (it != parsedSuggestions.end())
       {
         // unless we can complete the original suggestion with a valid color.
         if (!it->second.isValid() && color.isValid())
         {
           it->second = color;
         }
         else
         {
           MITK_WARN << "Ignoring duplicate of suggestion \"" << name.toStdString() << "\"!";
         }
 
         continue;
       }
 
       parsedSuggestions.emplace_back(name, color);
     }
 
     if (parsedSuggestions.empty())
       MITK_WARN << "Could not parse any suggestions from \"" << filename << "\"!";
 
     return parsedSuggestions;
   }
 
   struct Preferences
   {
     std::string labelSuggestions;
     bool replaceStandardSuggestions;
     bool suggestOnce;
     std::vector<std::byte> geometry;
   };
 
   // Get all relevant preferences and consider command-line arguments overrides.
   Preferences GetPreferences()
   {
     auto* nodePrefs = mitk::CoreServices::GetPreferencesService()->GetSystemPreferences()->Node("/org.mitk.views.segmentation");
 
     Preferences prefs;
     
     prefs.labelSuggestions = mitk::BaseApplication::instance().config().getString(mitk::BaseApplication::ARG_SEGMENTATION_LABEL_SUGGESTIONS.toStdString(), "");
 
     if (prefs.labelSuggestions.empty())
       prefs.labelSuggestions = nodePrefs->Get("label suggestions", "");
 
     prefs.replaceStandardSuggestions = nodePrefs->GetBool("replace standard suggestions", true);
     prefs.suggestOnce = nodePrefs->GetBool("suggest once", true);
     prefs.geometry = nodePrefs->GetByteArray("QmitkNewSegmentationDialog geometry", nullptr, 0);
 
     return prefs;
   }
 
   void SaveGeometry(const QByteArray& geometry)
   {
     auto* nodePrefs = mitk::CoreServices::GetPreferencesService()->GetSystemPreferences()->Node("/org.mitk.views.segmentation");
     nodePrefs->PutByteArray("QmitkNewSegmentationDialog geometry", reinterpret_cast<const std::byte*>(geometry.data()), geometry.size());
   }
 
   // Get names of all labels in all layers of a LabelSetImage.
   QStringList GetExistingLabelNames(mitk::LabelSetImage* labelSetImage)
   {
     QStringList existingLabelNames;
     existingLabelNames.reserve(labelSetImage->GetTotalNumberOfLabels());
 
     const auto numLayers = labelSetImage->GetNumberOfLayers();
     for (std::remove_const_t<decltype(numLayers)> layerIndex = 0; layerIndex < numLayers; ++layerIndex)
     {
       const auto* labelSet = labelSetImage->GetLabelSet(layerIndex);
 
       for (auto labelIter = labelSet->IteratorConstBegin(); labelIter != labelSet->IteratorConstEnd(); ++labelIter)
       {
         if (0 == labelIter->first)
           continue; // Ignore background label
 
         auto name = QString::fromStdString(labelIter->second->GetName());
 
         if (!name.isEmpty()) // Potential duplicates do not matter for our purpose
           existingLabelNames.push_back(name);
       }
     }
 
     return existingLabelNames;
   }
 
   // Remove blacklisted suggestions.
   QmitkNewSegmentationDialog::SuggestionsType FilterSuggestions(const QmitkNewSegmentationDialog::SuggestionsType& suggestions, const QStringList& blacklist)
   {
     QmitkNewSegmentationDialog::SuggestionsType filteredSuggestions;
 
     std::remove_copy_if(begin(suggestions), end(suggestions), std::inserter(filteredSuggestions, end(filteredSuggestions)), [&blacklist](const auto& suggestion) {
       return blacklist.contains(suggestion.first);
     });
 
     return filteredSuggestions;
   }
 }
 
 QmitkNewSegmentationDialog::QmitkNewSegmentationDialog(QWidget *parent, mitk::LabelSetImage* labelSetImage, Mode mode)
   : QDialog(parent),
     m_Ui(new Ui::QmitkNewSegmentationDialog),
     m_SuggestOnce(true),
     m_Color(Qt::red)
 {
   m_Ui->setupUi(this);
 
   if (RenameLabel == mode)
   {
     this->setWindowTitle("Rename Label");
     m_Ui->label->setText("New name and color of the label");
     m_Ui->buttonBox->button(QDialogButtonBox::Ok)->setText("Rename label");
   }
   else
   {
     m_Ui->buttonBox->button(QDialogButtonBox::Ok)->setText("Create label");
   }
 
   m_Ui->nameLineEdit->setFocus();
 
   connect(this, &QDialog::finished, this, &QmitkNewSegmentationDialog::OnFinished);
   connect(m_Ui->colorButton, &QToolButton::clicked, this, &QmitkNewSegmentationDialog::OnColorButtonClicked);
   connect(m_Ui->nameLineEdit, &QLineEdit::textChanged, this, &QmitkNewSegmentationDialog::OnTextChanged);
   connect(m_Ui->nameList, &QListWidget::itemSelectionChanged, this, &QmitkNewSegmentationDialog::OnSuggestionSelected);
   connect(m_Ui->nameList, &QListWidget::itemDoubleClicked, this, &QmitkNewSegmentationDialog::OnAccept);
   connect(m_Ui->buttonBox, &QDialogButtonBox::accepted, this, &QmitkNewSegmentationDialog::OnAccept);
 
   this->UpdateColorButtonBackground();
 
   auto prefs = GetPreferences();
 
   if (!prefs.labelSuggestions.empty())
   {
     auto suggestions = ParseSuggestions(prefs.labelSuggestions);
     this->SetSuggestions(suggestions, prefs.replaceStandardSuggestions && !suggestions.empty());
   }
   else
   {
     this->SetSuggestions(GetStandardSuggestions(), true);
   }
 
   if (nullptr != labelSetImage && prefs.suggestOnce)
   {
     auto existingLabelNames = GetExistingLabelNames(labelSetImage);
     m_Suggestions = FilterSuggestions(m_Suggestions, existingLabelNames);
 
     this->UpdateNameList();
   }
 
   if (!(prefs.geometry.empty()))
     this->restoreGeometry(QByteArray(reinterpret_cast<const char*>(prefs.geometry.data()), prefs.geometry.size()));
 }
 
 QmitkNewSegmentationDialog::~QmitkNewSegmentationDialog()
 {
 }
 
 void QmitkNewSegmentationDialog::OnFinished(int)
 {
   SaveGeometry(this->saveGeometry());
 }
 
 void QmitkNewSegmentationDialog::UpdateColorButtonBackground()
 {
   m_Ui->colorButton->setStyleSheet("background-color:" + m_Color.name());
 }
 
 QString QmitkNewSegmentationDialog::GetName() const
 {
   return m_Name;
 }
 
 mitk::Color QmitkNewSegmentationDialog::GetColor() const
 {
   mitk::Color color;
 
   if (m_Color.isValid())
   {
     color.SetRed(m_Color.redF());
     color.SetGreen(m_Color.greenF());
     color.SetBlue(m_Color.blueF());
   }
   else
   {
     color.Set(1.0f, 0.0f, 0.0f);
   }
 
   return color;
 }
 
 void QmitkNewSegmentationDialog::SetName(const QString& name)
 {
   m_Ui->nameLineEdit->setText(name);
 }
 
 void QmitkNewSegmentationDialog::SetColor(const mitk::Color& color)
 {
   m_Color.setRgbF(color.GetRed(), color.GetGreen(), color.GetBlue());
   this->UpdateColorButtonBackground();
 }
 
 void QmitkNewSegmentationDialog::SetSuggestions(const SuggestionsType& suggestions, bool replaceStandardSuggestions)
 {
   m_Suggestions = suggestions;
 
   if (!replaceStandardSuggestions)
   {
     auto standardSuggestions = GetStandardSuggestions();
 
     // Append all standard suggestions with unique names to the list of custom suggestions
     std::remove_copy_if(begin(standardSuggestions), end(standardSuggestions), std::inserter(m_Suggestions, end(m_Suggestions)), [this](const auto& suggestion) {
       return m_Suggestions.end() != std::find_if(begin(m_Suggestions), end(m_Suggestions), [&suggestion](const auto& otherSuggestion) {
         return suggestion.first == otherSuggestion.first;
       });
     });
   }
 
   this->UpdateNameList();
 }
 
 void QmitkNewSegmentationDialog::UpdateNameList()
 {
   QStringList names;
 
   for (const auto& suggestion : m_Suggestions)
     names << suggestion.first;
 
   m_Ui->nameList->clear();
   m_Ui->nameList->addItems(names);
 }
 
 void QmitkNewSegmentationDialog::OnAccept()
 {
   m_Name = m_Ui->nameLineEdit->text();
   this->accept();
 }
 
 void QmitkNewSegmentationDialog::OnTextChanged(const QString& text)
 {
   auto finding = m_Ui->nameList->findItems(text, Qt::MatchFlag::MatchExactly);
 
   if (!finding.isEmpty())
     m_Ui->nameList->setCurrentItem(finding.first());
 }
 
 void QmitkNewSegmentationDialog::OnColorButtonClicked()
 {
   auto color = QColorDialog::getColor(m_Color);
 
   if (color.isValid())
   {
     m_Color = color;
     this->UpdateColorButtonBackground();
   }
 }
 
 void QmitkNewSegmentationDialog::OnSuggestionSelected()
 {
   const auto* currentItem = m_Ui->nameList->currentItem();
 
   if (currentItem == nullptr)
     return;
 
   auto row = m_Ui->nameList->selectionModel()->selectedIndexes().first().row();
 
   m_Ui->nameLineEdit->setText(m_Suggestions[row].first);
   auto color = m_Suggestions[row].second;
 
   if (color.isValid())
   {
     m_Color = color;
     this->UpdateColorButtonBackground();
   }
 }
+
+bool QmitkNewSegmentationDialog::DoRenameLabel(const mitk::Label* currentLabel, mitk::LabelSetImage* segmentation, QWidget* parent, Mode mode)
+{
+  if (nullptr == currentLabel) mitkThrow() << "Invalid call of QmitkNewSegmentationDialog::RenameLabel. Passed label is null.";
+  if (nullptr != segmentation && !segmentation->IsLabeInGroup(currentLabel->GetValue())) mitkThrow() << "Invalid call of QmitkNewSegmentationDialog::RenameLabel. Passed label value does not exist in segmentation.";
+  QmitkNewSegmentationDialog dialog(parent, segmentation, mode);
+
+  dialog.SetColor(currentLabel->GetColor());
+  dialog.SetName(QString::fromStdString(currentLabel->GetName()));
+
+  if (dialog.exec() == QDialog::Rejected)
+  {
+    return false;
+  }
+
+  QString segmentationName = dialog.GetName();
+  if (segmentationName.isEmpty())
+  {
+    segmentationName = "Unnamed";
+  }
+
+  if (nullptr != segmentation)
+  {
+    auto groupID = segmentation->GetGroupIndexOfLabel(currentLabel->GetValue());
+    auto group = segmentation->GetLabelSet(groupID);
+    group->RenameLabel(currentLabel->GetValue(), segmentationName.toStdString(), dialog.GetColor());
+    group->UpdateLookupTable(currentLabel->GetValue());
+  }
+
+  return true;
+}
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkNewSegmentationDialog.h b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkNewSegmentationDialog.h
index 84793f6f61..e7c51b335f 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkNewSegmentationDialog.h
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkNewSegmentationDialog.h
@@ -1,83 +1,88 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #ifndef QmitkNewSegmentationDialog_h
 #define QmitkNewSegmentationDialog_h
 
 #include <org_mitk_gui_qt_segmentation_Export.h>
 
 #include <mitkColorProperty.h>
 
 #include <vector>
 #include <utility>
 
 #include <QColor>
 #include <QDialog>
 #include <QString>
 
 namespace mitk
 {
   class LabelSetImage;
+  class Label;
 }
 
 namespace Ui
 {
   class QmitkNewSegmentationDialog;
 }
 
 /**
   \brief Dialog for naming labels.
 */
 class MITK_QT_SEGMENTATION QmitkNewSegmentationDialog : public QDialog
 {
   Q_OBJECT
 
 public:
   using SuggestionsType = std::vector<std::pair<QString, QColor>>;
 
   enum Mode
   {
     NewLabel,
     RenameLabel
   };
 
   explicit QmitkNewSegmentationDialog(QWidget *parent = nullptr, mitk::LabelSetImage* labelSetImage = nullptr, Mode mode = NewLabel);
   ~QmitkNewSegmentationDialog() override;
 
   QString GetName() const;
   mitk::Color GetColor() const;
 
   void SetName(const QString& name);
   void SetColor(const mitk::Color& color);
 
+  /**Static helper function to do (re)naming of a label via the dialog class. If mode == NewLabel, the function assumes that the label is not yet
+  added, thus e.g. no lookup table update is done. In rename mode, if a segmentation is provided, the segmentation will directly be updated.*/
+  static bool DoRenameLabel(const mitk::Label* currentLabel, mitk::LabelSetImage* segmentation, QWidget* parent = nullptr, Mode mode = NewLabel);
+
 private:
   void OnAccept();
   void OnFinished(int result);
   void OnSuggestionSelected();
   void OnColorButtonClicked();
   void OnTextChanged(const QString& text);
 
   void SetSuggestions(const SuggestionsType& suggestions, bool replaceStandardSuggestions = false);
   void UpdateColorButtonBackground();
   void UpdateNameList();
 
   Ui::QmitkNewSegmentationDialog* m_Ui;
 
   bool m_SuggestOnce;
 
   QString m_Name;
   QColor m_Color;
 
   SuggestionsType m_Suggestions;
 };
 
 #endif
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkAutocropLabelSetImageAction.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkAutocropLabelSetImageAction.cpp
index f7f5f77362..692a832c4d 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkAutocropLabelSetImageAction.cpp
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkAutocropLabelSetImageAction.cpp
@@ -1,300 +1,300 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "QmitkAutocropLabelSetImageAction.h"
 
 #include <mitkImagePixelReadAccessor.h>
 #include <mitkImageTimeSelector.h>
 #include <mitkLabelSetImage.h>
 #include <mitkRenderingManager.h>
 
 namespace
 {
   // Iterate over all layers, time steps, and dimensions of a LabelSetImage to
   // determine the overall minimum and maximum indices of labeled pixels.
   //
   // Returns false if the input image is empty, minIndex and maxIndex contain
   // valid indices otherwise.
   //
   // Throws an mitk::Exception if read access was denied.
   //
   bool DetermineMinimumAndMaximumIndicesOfNonBackgroundPixels(mitk::LabelSetImage::Pointer labelSetImage, itk::Index<3>& minIndex, itk::Index<3>& maxIndex)
   {
     // We need a time selector to handle 3d+t images. It is not used for 3d images, though.
     auto timeSelector = mitk::ImageTimeSelector::New();
     timeSelector->SetInput(labelSetImage);
 
-    const auto background = labelSetImage->GetExteriorLabel()->GetValue();
+    const auto background = mitk::LabelSetImage::UnlabeledValue;
     const auto numLayers = labelSetImage->GetNumberOfLayers();
     const auto numTimeSteps = labelSetImage->GetTimeSteps();
 
     const itk::Index<3> dim = {
       labelSetImage->GetDimension(0),
       labelSetImage->GetDimension(1),
       labelSetImage->GetDimension(2)
     };
 
     maxIndex = { 0, 0, 0 };
     minIndex = dim;
     itk::Index<3> index;
 
     bool labelSetImageIsEmpty = true;
 
     for (std::remove_const_t<decltype(numLayers)> layer = 0; layer < numLayers; ++layer)
     {
       labelSetImage->SetActiveLayer(layer);
 
       for (std::remove_const_t<decltype(numTimeSteps)> timeStep = 0; timeStep < numTimeSteps; ++timeStep)
       {
         const mitk::Image* image = nullptr;
 
         if (numTimeSteps > 1)
         {
           timeSelector->SetTimeNr(timeStep);
           timeSelector->Update();
           image = timeSelector->GetOutput();
         }
         else
         {
           image = labelSetImage;
         }
 
         mitk::ImagePixelReadAccessor<mitk::LabelSetImage::PixelType, 3> pixelReader(image);
         bool imageIsEmpty = true;
 
         for (index[2] = 0; index[2] < dim[2]; ++index[2])
         {
           for (index[1] = 0; index[1] < dim[1]; ++index[1])
           {
             for (index[0] = 0; index[0] < dim[0]; ++index[0])
             {
               if (background != pixelReader.GetPixelByIndex(index))
               {
                 imageIsEmpty = false;
                 minIndex = {
                   std::min(minIndex[0], index[0]),
                   std::min(minIndex[1], index[1]),
                   std::min(minIndex[2], index[2])
                 };
                 break;
               }
             }
           }
         }
 
         if (imageIsEmpty)
           continue;
 
         maxIndex = {
           std::max(maxIndex[0], minIndex[0]),
           std::max(maxIndex[1], minIndex[1]),
           std::max(maxIndex[2], minIndex[2])
         };
 
         for (index[2] = dim[2] - 1; index[2] >= 0; --index[2])
         {
           for (index[1] = dim[1] - 1; index[1] >= 0; --index[1])
           {
             for (index[0] = dim[0] - 1; index[0] >= 0; --index[0])
             {
               if (background != pixelReader.GetPixelByIndex(index))
               {
                 maxIndex = {
                   std::max(maxIndex[0], index[0]),
                   std::max(maxIndex[1], index[1]),
                   std::max(maxIndex[2], index[2])
                 };
                 break;
               }
             }
           }
         }
 
         if (!imageIsEmpty)
           labelSetImageIsEmpty = false;
       }
     }
 
     return !labelSetImageIsEmpty;
   }
 
   // Crop a LabelSetImage. Labels in the cropped LabelSetImage will still have
   // their original properties like names and colors.
   //
   // Returns a cropped LabelSetImage.
   //
   // Throws an mitk::Exception if read access was denied.
   //
   mitk::LabelSetImage::Pointer Crop(mitk::LabelSetImage::Pointer labelSetImage, const itk::Index<3>& minIndex, const itk::Index<3>& maxIndex)
   {
     // We need a time selector to handle 3d+t images. It is not used for 3d images, though.
     auto timeSelector = mitk::ImageTimeSelector::New();
     timeSelector->SetInput(labelSetImage);
 
     const auto numLayers = labelSetImage->GetNumberOfLayers();
     const auto numTimeSteps = labelSetImage->GetTimeSteps();
 
     const itk::Index<3> croppedDim = {
       1 + maxIndex[0] - minIndex[0],
       1 + maxIndex[1] - minIndex[1],
       1 + maxIndex[2] - minIndex[2]
     };
 
     const auto numPixels = croppedDim[0] * croppedDim[1] * croppedDim[2];
 
     mitk::BaseGeometry::BoundsArrayType croppedBounds;
     croppedBounds[0] = 0;
     croppedBounds[1] = croppedDim[0];
     croppedBounds[2] = 0;
     croppedBounds[3] = croppedDim[1];
     croppedBounds[4] = 0;
     croppedBounds[5] = croppedDim[2];
 
     // Clone and adapt the original TimeGeometry to the cropped region
 
     auto croppedTimeGeometry = labelSetImage->GetTimeGeometry()->Clone();
 
     for (std::remove_const_t<decltype(numTimeSteps)> timeStep = 0; timeStep < numTimeSteps; ++timeStep)
     {
       auto geometry = croppedTimeGeometry->GetGeometryForTimeStep(timeStep);
 
       mitk::Point3D croppedOrigin;
       geometry->IndexToWorld(minIndex, croppedOrigin);
       geometry->SetOrigin(croppedOrigin);
 
       geometry->SetBounds(croppedBounds);
     }
 
     auto croppedLabelSetImage = mitk::LabelSetImage::New();
     croppedLabelSetImage->Initialize(mitk::MakeScalarPixelType<mitk::LabelSetImage::PixelType>(), *croppedTimeGeometry);
 
     // Create cropped image volumes for all time steps in all layers
 
     for (std::remove_const_t<decltype(numLayers)> layer = 0; layer < numLayers; ++layer)
     {
       labelSetImage->SetActiveLayer(layer);
       croppedLabelSetImage->AddLayer();
 
       for (std::remove_const_t<decltype(numTimeSteps)> timeStep = 0; timeStep < numTimeSteps; ++timeStep)
       {
         const mitk::Image* image = nullptr;
 
         if (numTimeSteps > 1)
         {
           timeSelector->SetTimeNr(timeStep);
           timeSelector->Update();
           image = timeSelector->GetOutput();
         }
         else
         {
           image = labelSetImage;
         }
 
         mitk::ImagePixelReadAccessor<mitk::LabelSetImage::PixelType, 3> pixelReader(image);
         auto* croppedVolume = new mitk::LabelSetImage::PixelType[numPixels];
         itk::Index<3> croppedIndex;
         itk::Index<3> index;
 
         for (croppedIndex[2] = 0; croppedIndex[2] < croppedDim[2]; ++croppedIndex[2])
         {
           for (croppedIndex[1] = 0; croppedIndex[1] < croppedDim[1]; ++croppedIndex[1])
           {
             for (croppedIndex[0] = 0; croppedIndex[0] < croppedDim[0]; ++croppedIndex[0])
             {
               index[0] = croppedIndex[0] + minIndex[0];
               index[1] = croppedIndex[1] + minIndex[1];
               index[2] = croppedIndex[2] + minIndex[2];
               const auto& pixel = pixelReader.GetPixelByIndex(index);
 
               croppedVolume[croppedIndex[2] * croppedDim[1] * croppedDim[0] + croppedIndex[1] * croppedDim[0] + croppedIndex[0]] = pixel;
             }
           }
         }
 
         croppedLabelSetImage->SetImportVolume(croppedVolume, timeStep, 0, mitk::Image::ReferenceMemory);
         croppedLabelSetImage->AddLabelSetToLayer(layer, labelSetImage->GetLabelSet(layer));
       }
     }
 
     return croppedLabelSetImage;
   }
 }
 
 QmitkAutocropLabelSetImageAction::QmitkAutocropLabelSetImageAction()
 {
 }
 
 QmitkAutocropLabelSetImageAction::~QmitkAutocropLabelSetImageAction()
 {
 }
 
 void QmitkAutocropLabelSetImageAction::Run(const QList<mitk::DataNode::Pointer>& selectedNodes)
 {
   for (const auto& dataNode : selectedNodes)
   {
     mitk::LabelSetImage::Pointer labelSetImage = dynamic_cast<mitk::LabelSetImage*>(dataNode->GetData());
 
     if (labelSetImage.IsNull())
       continue;
 
     // Backup currently active layer as we need to restore it later
     auto activeLayer = labelSetImage->GetActiveLayer();
 
     mitk::LabelSetImage::Pointer croppedLabelSetImage;
     itk::Index<3> minIndex;
     itk::Index<3> maxIndex;
 
     try
     {
       if (!DetermineMinimumAndMaximumIndicesOfNonBackgroundPixels(labelSetImage, minIndex, maxIndex))
       {
         MITK_WARN << "Autocrop was skipped: Image \"" << dataNode->GetName() << "\" is empty.";
         labelSetImage->SetActiveLayer(activeLayer); // Restore the originally active layer
         return;
       }
 
       croppedLabelSetImage = Crop(labelSetImage, minIndex, maxIndex);
     }
     catch (const mitk::Exception&)
     {
       MITK_ERROR << "Autocrop was aborted: Image read access to \"" << dataNode->GetName() << "\" was denied.";
       labelSetImage->SetActiveLayer(activeLayer); // Restore the originally active layer
       return;
     }
 
     // Restore the originally active layer in the cropped LabelSetImage
     croppedLabelSetImage->SetActiveLayer(activeLayer);
 
     // Override the original LabelSetImage with the cropped LabelSetImage
     dataNode->SetData(croppedLabelSetImage);
 
     // If we cropped a single LabelSetImage, reinit the views to give a visible feedback to the user
     if (1 == selectedNodes.size())
       mitk::RenderingManager::GetInstance()->InitializeViews(croppedLabelSetImage->GetTimeGeometry());
   }
 }
 
 void QmitkAutocropLabelSetImageAction::SetSmoothed(bool)
 {
 }
 
 void QmitkAutocropLabelSetImageAction::SetDecimated(bool)
 {
 }
 
 void QmitkAutocropLabelSetImageAction::SetDataStorage(mitk::DataStorage*)
 {
 }
 
 void QmitkAutocropLabelSetImageAction::SetFunctionality(berry::QtViewPart*)
 {
 }
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkConvertMaskToLabelAction.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkConvertMaskToLabelAction.cpp
index 9485543eb2..d79b525728 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkConvertMaskToLabelAction.cpp
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkConvertMaskToLabelAction.cpp
@@ -1,97 +1,96 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 #include "QmitkConvertMaskToLabelAction.h"
 
 #include "mitkRenderingManager.h"
 #include "mitkLabelSetImage.h"
 #include "mitkToolManagerProvider.h"
 
 //needed for qApp
 #include <qcoreapplication.h>
 
 QmitkConvertMaskToLabelAction::QmitkConvertMaskToLabelAction()
 {
 }
 
 QmitkConvertMaskToLabelAction::~QmitkConvertMaskToLabelAction()
 {
 }
 
 void QmitkConvertMaskToLabelAction::Run( const QList<mitk::DataNode::Pointer> &selectedNodes )
 {
   mitk::ToolManager::Pointer toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager();
 
   mitk::DataNode* workingNode = toolManager->GetWorkingData(0);
   if (!workingNode)
   {
     MITK_INFO << "There is no available segmentation. Please load or create one before using this tool.";
     return;
   }
 
   mitk::LabelSetImage* workingImage = dynamic_cast<mitk::LabelSetImage*>( workingNode->GetData() );
   assert(workingImage);
 
   foreach ( mitk::DataNode::Pointer maskNode, selectedNodes )
   {
     if (maskNode)
     {
       mitk::Image* mask = dynamic_cast<mitk::Image*>(maskNode->GetData() );
       if (!mask) continue;
 
       std::string name = maskNode->GetName();
       mitk::Color color;
       mitk::ColorProperty::Pointer colorProp;
       maskNode->GetProperty(colorProp,"color");
       if (colorProp.IsNull()) continue;
       color = colorProp->GetValue();
       workingImage->GetLabelSet()->AddLabel(name,color);
-      //workingImage->AddLabelEvent.Send();
 
       try
       {
         workingImage->MaskStamp( mask, false );
       }
       catch ( mitk::Exception& e )
       {
         MITK_ERROR << "Exception caught: " << e.GetDescription();
         return;
       }
 
       maskNode->SetVisibility(false);
       mitk::RenderingManager::GetInstance()->RequestUpdateAll();
     }
     else
     {
       MITK_INFO << "   a nullptr node was selected";
     }
   }
 }
 
 void QmitkConvertMaskToLabelAction::SetSmoothed(bool /*smoothed*/)
 {
  //not needed
 }
 
 void QmitkConvertMaskToLabelAction::SetDecimated(bool /*decimated*/)
 {
   //not needed
 }
 
 void QmitkConvertMaskToLabelAction::SetDataStorage(mitk::DataStorage* /*dataStorage*/)
 {
   //not needed
 }
 
 void QmitkConvertMaskToLabelAction::SetFunctionality(berry::QtViewPart* /*functionality*/)
 {
   //not needed
 }
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkConvertSurfaceToLabelAction.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkConvertSurfaceToLabelAction.cpp
index b4311d7105..5caac30973 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkConvertSurfaceToLabelAction.cpp
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkConvertSurfaceToLabelAction.cpp
@@ -1,102 +1,101 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 #include "QmitkConvertSurfaceToLabelAction.h"
 
 #include "mitkRenderingManager.h"
 #include "mitkLabelSetImage.h"
 #include "mitkToolManagerProvider.h"
 #include <mitkSurface.h>
 
 //needed for qApp
 #include <qcoreapplication.h>
 #include <QApplication>
 
 QmitkConvertSurfaceToLabelAction::QmitkConvertSurfaceToLabelAction()
 {
 }
 
 QmitkConvertSurfaceToLabelAction::~QmitkConvertSurfaceToLabelAction()
 {
 }
 
 void QmitkConvertSurfaceToLabelAction::Run( const QList<mitk::DataNode::Pointer> &selectedNodes )
 {
   mitk::ToolManager::Pointer toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager();
 
   mitk::DataNode* workingNode = toolManager->GetWorkingData(0);
   if (!workingNode)
   {
     MITK_INFO << "There is no available segmentation. Please load or create one before using this tool.";
     return;
   }
 
   mitk::LabelSetImage* workingImage = dynamic_cast<mitk::LabelSetImage*>( workingNode->GetData() );
   assert(workingImage);
 
   foreach ( mitk::DataNode::Pointer surfaceNode, selectedNodes )
   {
     if (surfaceNode)
     {
       mitk::Surface* surface = dynamic_cast<mitk::Surface*>(surfaceNode->GetData() );
       if (!surface) continue;
 
       std::string name = surfaceNode->GetName();
       mitk::Color color;
       mitk::ColorProperty::Pointer colorProp;
       surfaceNode->GetProperty(colorProp,"color");
       if (colorProp.IsNull()) continue;
       color = colorProp->GetValue();
       workingImage->GetLabelSet()->AddLabel(name,color);
-      //workingImage->AddLabelEvent.Send();
 
       try
       {
         QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) );
 //        workingImage->SurfaceStamp( surface, false );
         QApplication::restoreOverrideCursor();
       }
       catch ( mitk::Exception& e )
       {
         QApplication::restoreOverrideCursor();
         MITK_ERROR << "Exception caught: " << e.GetDescription();
         return;
       }
 
       surfaceNode->SetVisibility(false);
       mitk::RenderingManager::GetInstance()->RequestUpdateAll();
     }
     else
     {
       MITK_INFO << "   a nullptr node was selected";
     }
   }
 }
 
 void QmitkConvertSurfaceToLabelAction::SetSmoothed(bool /*smoothed*/)
 {
  //not needed
 }
 
 void QmitkConvertSurfaceToLabelAction::SetDecimated(bool /*decimated*/)
 {
   //not needed
 }
 
 void QmitkConvertSurfaceToLabelAction::SetDataStorage(mitk::DataStorage* /*dataStorage*/)
 {
   //not needed
 }
 
 void QmitkConvertSurfaceToLabelAction::SetFunctionality(berry::QtViewPart* /*functionality*/)
 {
   //not needed
 }
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkLoadMultiLabelPresetAction.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkLoadMultiLabelPresetAction.cpp
index b3506ce9de..5c3ff51ac5 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkLoadMultiLabelPresetAction.cpp
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkLoadMultiLabelPresetAction.cpp
@@ -1,56 +1,56 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "QmitkLoadMultiLabelPresetAction.h"
 
 #include <mitkLabelSetImage.h>
-#include <mitkLabelSetIOHelper.h>
+#include <mitkMultiLabelIOHelper.h>
 
 #include <QFileDialog>
 
 void QmitkLoadMultiLabelPresetAction::Run(const QList<mitk::DataNode::Pointer> &selectedNodes)
 {
   const auto filename = QFileDialog::getOpenFileName(nullptr, QStringLiteral("Load Label Set Preset"),
     QString(), QStringLiteral("Label set preset (*.lsetp)")).toStdString();
 
   if (filename.empty())
     return;
 
   for (const auto &node : selectedNodes)
   {
     if (node.IsNull())
       continue;
 
     mitk::LabelSetImage::Pointer image = dynamic_cast<mitk::LabelSetImage*>(node->GetData());
 
     if (image.IsNull())
       continue;
 
-    mitk::LabelSetIOHelper::LoadLabelSetImagePreset(filename, image);
+    mitk::MultiLabelIOHelper::LoadLabelSetImagePreset(filename, image);
   }
 }
 
 void QmitkLoadMultiLabelPresetAction::SetDataStorage(mitk::DataStorage*)
 {
 }
 
 void QmitkLoadMultiLabelPresetAction::SetFunctionality(berry::QtViewPart*)
 {
 }
 
 void QmitkLoadMultiLabelPresetAction::SetSmoothed(bool)
 {
 }
 
 void QmitkLoadMultiLabelPresetAction::SetDecimated(bool)
 {
 }
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSaveMultiLabelPresetAction.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSaveMultiLabelPresetAction.cpp
index 7b6b07ad6f..f108e59951 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSaveMultiLabelPresetAction.cpp
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSaveMultiLabelPresetAction.cpp
@@ -1,63 +1,63 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "QmitkSaveMultiLabelPresetAction.h"
 
 #include <mitkLabelSetImage.h>
-#include <mitkLabelSetIOHelper.h>
+#include <mitkMultiLabelIOHelper.h>
 
 #include <QFileDialog>
 #include <QMessageBox>
 
 void QmitkSaveMultiLabelPresetAction::Run(const QList<mitk::DataNode::Pointer> &selectedNodes)
 {
   for (const auto &node : selectedNodes)
   {
     if (node.IsNull())
       continue;
 
     mitk::LabelSetImage::Pointer image = dynamic_cast<mitk::LabelSetImage*>(node->GetData());
 
     if (image.IsNull())
       continue;
 
     const auto filename = QFileDialog::getSaveFileName(nullptr, QStringLiteral("Save Label Set Preset"),
       QString(), QStringLiteral("Label set preset (*.lsetp)")).toStdString();
 
     if (filename.empty())
       continue;
 
-    if(!mitk::LabelSetIOHelper::SaveLabelSetImagePreset(filename, image))
+    if(!mitk::MultiLabelIOHelper::SaveLabelSetImagePreset(filename, image))
     {
       QMessageBox::critical(nullptr, QStringLiteral("Save Label Set Preset"),
         QString("Could not save \"%1\" as label set preset.").arg(QString::fromStdString(node->GetName())));
       
       continue;
     }
   }
 }
 
 void QmitkSaveMultiLabelPresetAction::SetDataStorage(mitk::DataStorage*)
 {
 }
 
 void QmitkSaveMultiLabelPresetAction::SetFunctionality(berry::QtViewPart*)
 {
 }
 
 void QmitkSaveMultiLabelPresetAction::SetSmoothed(bool)
 {
 }
 
 void QmitkSaveMultiLabelPresetAction::SetDecimated(bool)
 {
 }
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp
index 91267f19ee..982d199410 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp
@@ -1,1083 +1,1031 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "QmitkSegmentationView.h"
 #include "mitkPluginActivator.h"
 
 // blueberry
 #include <berryIWorkbenchPage.h>
 
 // mitk
 #include <mitkApplicationCursor.h>
 #include <mitkBaseApplication.h>
 #include <mitkBaseRendererHelper.h>
 #include <mitkCameraController.h>
 #include <mitkLabelSetImage.h>
 #include <mitkLabelSetImageHelper.h>
-#include <mitkLabelSetIOHelper.h>
+#include <mitkMultiLabelIOHelper.h>
 #include <mitkManualPlacementAnnotationRenderer.h>
 #include <mitkNodePredicateSubGeometry.h>
 #include <mitkSegmentationObjectFactory.h>
 #include <mitkSegTool2D.h>
 #include <mitkStatusBar.h>
 #include <mitkToolManagerProvider.h>
 #include <mitkVtkResliceInterpolationProperty.h>
 #include <mitkWorkbenchUtil.h>
 #include <mitkIPreferences.h>
 
 // Qmitk
 #include <QmitkRenderWindow.h>
 #include <QmitkStaticDynamicSegmentationDialog.h>
 #include <QmitkNewSegmentationDialog.h>
+#include <QmitkMultiLabelManager.h>
 
 // us
 #include <usModuleResource.h>
 #include <usModuleResourceStream.h>
 
 // Qt
 #include <QMessageBox>
 #include <QShortcut>
 #include <QDir>
 
 // vtk
 #include <vtkQImageToImageSource.h>
 
 #include <regex>
 
 namespace
 {
   QList<QmitkRenderWindow*> Get2DWindows(const QList<QmitkRenderWindow*> allWindows)
   {
     QList<QmitkRenderWindow*> all2DWindows;
     for (auto* window : allWindows)
     {
       if (window->GetRenderer()->GetMapperID() == mitk::BaseRenderer::Standard2D)
       {
         all2DWindows.append(window);
       }
     }
     return all2DWindows;
   }
 }
 
 const std::string QmitkSegmentationView::VIEW_ID = "org.mitk.views.segmentation";
 
 QmitkSegmentationView::QmitkSegmentationView()
   : m_Parent(nullptr)
   , m_Controls(nullptr)
   , m_RenderWindowPart(nullptr)
   , m_ToolManager(nullptr)
   , m_ReferenceNode(nullptr)
   , m_WorkingNode(nullptr)
   , m_DrawOutline(true)
   , m_SelectionMode(false)
   , m_MouseCursorSet(false)
   , m_DefaultLabelNaming(true)
   , m_SelectionChangeIsAlreadyBeingHandled(false)
 {
   auto isImage = mitk::TNodePredicateDataType<mitk::Image>::New();
   auto isDwi = mitk::NodePredicateDataType::New("DiffusionImage");
   auto isDti = mitk::NodePredicateDataType::New("TensorImage");
   auto isOdf = mitk::NodePredicateDataType::New("OdfImage");
   auto isSegment = mitk::NodePredicateDataType::New("Segment");
 
   auto validImages = mitk::NodePredicateOr::New();
   validImages->AddPredicate(mitk::NodePredicateAnd::New(isImage, mitk::NodePredicateNot::New(isSegment)));
   validImages->AddPredicate(isDwi);
   validImages->AddPredicate(isDti);
   validImages->AddPredicate(isOdf);
 
   m_SegmentationPredicate = mitk::NodePredicateAnd::New();
   m_SegmentationPredicate->AddPredicate(mitk::TNodePredicateDataType<mitk::LabelSetImage>::New());
   m_SegmentationPredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object")));
   m_SegmentationPredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("hidden object")));
 
   m_ReferencePredicate = mitk::NodePredicateAnd::New();
   m_ReferencePredicate->AddPredicate(validImages);
   m_ReferencePredicate->AddPredicate(mitk::NodePredicateNot::New(m_SegmentationPredicate));
   m_ReferencePredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object")));
   m_ReferencePredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("hidden object")));
 }
 
 QmitkSegmentationView::~QmitkSegmentationView()
 {
   if (nullptr != m_Controls)
   {
-    this->LooseLabelSetConnection();
-
     // deactivate all tools
     m_ToolManager->ActivateTool(-1);
 
     // removing all observers from working data
     for (NodeTagMapType::iterator dataIter = m_WorkingDataObserverTags.begin(); dataIter != m_WorkingDataObserverTags.end(); ++dataIter)
     {
       (*dataIter).first->GetProperty("visible")->RemoveObserver((*dataIter).second);
     }
     m_WorkingDataObserverTags.clear();
 
     // removing all observers from reference data
     for (NodeTagMapType::iterator dataIter = m_ReferenceDataObserverTags.begin(); dataIter != m_ReferenceDataObserverTags.end(); ++dataIter)
     {
       (*dataIter).first->GetProperty("visible")->RemoveObserver((*dataIter).second);
     }
     m_ReferenceDataObserverTags.clear();
 
     mitk::RenderingManager::GetInstance()->RemoveObserver(m_RenderingManagerObserverTag);
 
     ctkPluginContext* context = mitk::PluginActivator::getContext();
     ctkServiceReference ppmRef = context->getServiceReference<mitk::PlanePositionManagerService>();
     mitk::PlanePositionManagerService* service = context->getService<mitk::PlanePositionManagerService>(ppmRef);
     service->RemoveAllPlanePositions();
     context->ungetService(ppmRef);
 
     m_ToolManager->SetReferenceData(nullptr);
     m_ToolManager->SetWorkingData(nullptr);
   }
 
   m_ToolManager->ActiveToolChanged -=
     mitk::MessageDelegate<QmitkSegmentationView>(this, &QmitkSegmentationView::ActiveToolChanged);
 
   delete m_Controls;
 }
 
 /**********************************************************************/
 /* private Q_SLOTS                                                    */
 /**********************************************************************/
 void QmitkSegmentationView::OnReferenceSelectionChanged(QList<mitk::DataNode::Pointer>)
 {
   this->OnAnySelectionChanged();
 }
 
 void QmitkSegmentationView::OnSegmentationSelectionChanged(QList<mitk::DataNode::Pointer>)
 {
   this->OnAnySelectionChanged();
 }
 
 void QmitkSegmentationView::OnAnySelectionChanged()
 {
   // When only a segmentation has been selected and the method is then called by a reference image selection,
   // the already selected segmentation may not match the geometry predicate of the new reference image anymore.
   // This will trigger a recursive call of this method further below. While it would be resolved gracefully, we
   // can spare the extra call with an early-out. The original call of this method will handle the segmentation
   // selection change afterwards anyway.
 
   if (m_SelectionChangeIsAlreadyBeingHandled)
     return;
 
   auto selectedReferenceNode = m_Controls->referenceNodeSelector->GetSelectedNode();
   bool referenceNodeChanged = false;
 
   m_ToolManager->ActivateTool(-1);
 
   if (m_ReferenceNode != selectedReferenceNode)
   {
     referenceNodeChanged = true;
 
     // Remove visibility observer for the current reference node
     if (m_ReferenceDataObserverTags.find(m_ReferenceNode) != m_ReferenceDataObserverTags.end())
     {
       m_ReferenceNode->GetProperty("visible")->RemoveObserver(m_ReferenceDataObserverTags[m_ReferenceNode]);
       m_ReferenceDataObserverTags.erase(m_ReferenceNode);
     }
 
     // Set new reference node
     m_ReferenceNode = selectedReferenceNode;
     m_ToolManager->SetReferenceData(m_ReferenceNode);
 
     // Prepare for a potential recursive call when changing node predicates of the working node selector
     m_SelectionChangeIsAlreadyBeingHandled = true;
 
     if (m_ReferenceNode.IsNull())
     {
       // Without a reference image, allow all segmentations to be selected
       m_Controls->workingNodeSelector->SetNodePredicate(m_SegmentationPredicate);
       m_SelectionChangeIsAlreadyBeingHandled = false;
     }
     else
     {
       // With a reference image, only allow segmentations that fit the geometry of the reference image to be selected.
       m_Controls->workingNodeSelector->SetNodePredicate(mitk::NodePredicateAnd::New(
         mitk::NodePredicateSubGeometry::New(m_ReferenceNode->GetData()->GetGeometry()),
         m_SegmentationPredicate.GetPointer()));
 
       m_SelectionChangeIsAlreadyBeingHandled = false;
 
       this->ApplySelectionModeOnReferenceNode();
 
       // Add visibility observer for the new reference node
       auto command = itk::SimpleMemberCommand<QmitkSegmentationView>::New();
       command->SetCallbackFunction(this, &QmitkSegmentationView::ValidateSelectionInput);
 
       m_ReferenceDataObserverTags[m_ReferenceNode] =
         m_ReferenceNode->GetProperty("visible")->AddObserver(itk::ModifiedEvent(), command);
     }
   }
 
   auto selectedWorkingNode = m_Controls->workingNodeSelector->GetSelectedNode();
   bool workingNodeChanged = false;
 
   if (m_WorkingNode != selectedWorkingNode)
   {
     workingNodeChanged = true;
 
     // Remove visibility observer for the current working node
     if (m_WorkingDataObserverTags.find(m_WorkingNode) != m_WorkingDataObserverTags.end())
     {
       m_WorkingNode->GetProperty("visible")->RemoveObserver(m_WorkingDataObserverTags[m_WorkingNode]);
       m_WorkingDataObserverTags.erase(m_WorkingNode);
     }
 
-    // Disconnect from current label set image
-    this->LooseLabelSetConnection();
-
     // Set new working node
     m_WorkingNode = selectedWorkingNode;
     m_ToolManager->SetWorkingData(m_WorkingNode);
 
     if (m_WorkingNode.IsNotNull())
     {
       this->ApplySelectionModeOnWorkingNode();
 
-      // Connect to new label set image
-      this->EstablishLabelSetConnection();
-      m_Controls->labelSetWidget->ResetAllTableWidgetItems();
-
       // Add visibility observer for the new segmentation node
       auto command = itk::SimpleMemberCommand<QmitkSegmentationView>::New();
       command->SetCallbackFunction(this, &QmitkSegmentationView::ValidateSelectionInput);
 
       m_WorkingDataObserverTags[m_WorkingNode] =
         m_WorkingNode->GetProperty("visible")->AddObserver(itk::ModifiedEvent(), command);
     }
   }
 
   // Reset camera if any selection changed but only if both reference node and working node are set
   if ((referenceNodeChanged || workingNodeChanged) && (m_ReferenceNode.IsNotNull() && m_WorkingNode.IsNotNull()))
   {
     if (nullptr != m_RenderWindowPart)
     {
       m_RenderWindowPart->InitializeViews(m_ReferenceNode->GetData()->GetTimeGeometry(), false);
     }
   }
 
   this->UpdateGUI();
 }
 
 void QmitkSegmentationView::OnVisibilityShortcutActivated()
 {
   if (m_WorkingNode.IsNull())
   {
     return;
   }
 
   bool isVisible = false;
   m_WorkingNode->GetBoolProperty("visible", isVisible);
   m_WorkingNode->SetVisibility(!isVisible);
 
   mitk::RenderingManager::GetInstance()->RequestUpdateAll();
 }
 
 void QmitkSegmentationView::OnLabelToggleShortcutActivated()
 {
   if (m_WorkingNode.IsNull())
   {
     return;
   }
 
   auto workingImage = dynamic_cast<mitk::LabelSetImage*>(m_WorkingNode->GetData());
   if (nullptr == workingImage)
   {
     return;
   }
 
   this->WaitCursorOn();
   workingImage->GetActiveLabelSet()->SetNextActiveLabel();
   workingImage->Modified();
   this->WaitCursorOff();
 
   mitk::RenderingManager::GetInstance()->RequestUpdateAll();
 }
 
 void QmitkSegmentationView::OnNewSegmentation()
 {
   m_ToolManager->ActivateTool(-1);
 
   if (m_ReferenceNode.IsNull())
   {
     MITK_ERROR << "'Create new segmentation' button should never be clickable unless a reference image is selected.";
     return;
   }
 
   mitk::Image::ConstPointer referenceImage = dynamic_cast<mitk::Image*>(m_ReferenceNode->GetData());
   if (referenceImage.IsNull())
   {
     QMessageBox::information(
       m_Parent, "New segmentation", "Please load and select an image before starting some action.");
     return;
   }
 
   if (referenceImage->GetDimension() <= 1)
   {
     QMessageBox::information(
       m_Parent, "New segmentation", "Segmentation is currently not supported for 2D images");
     return;
   }
 
   auto segTemplateImage = referenceImage;
   if (referenceImage->GetDimension() > 3)
   {
     QmitkStaticDynamicSegmentationDialog dialog(m_Parent);
     dialog.SetReferenceImage(referenceImage.GetPointer());
     dialog.exec();
     segTemplateImage = dialog.GetSegmentationTemplate();
   }
 
   mitk::DataNode::Pointer newSegmentationNode;
   try
   {
     this->WaitCursorOn();
     newSegmentationNode = mitk::LabelSetImageHelper::CreateNewSegmentationNode(m_ReferenceNode, segTemplateImage);
     this->WaitCursorOff();
   }
   catch (mitk::Exception& e)
   {
     this->WaitCursorOff();
     MITK_ERROR << "Exception caught: " << e.GetDescription();
     QMessageBox::warning(m_Parent, "New segmentation", "Could not create a new segmentation.");
     return;
   }
 
   auto newLabelSetImage = dynamic_cast<mitk::LabelSetImage*>(newSegmentationNode->GetData());
   if (nullptr == newLabelSetImage)
   {
     // something went wrong
     return;
   }
 
   const auto labelSetPreset = this->GetDefaultLabelSetPreset();
 
-  if (labelSetPreset.empty() || !mitk::LabelSetIOHelper::LoadLabelSetImagePreset(labelSetPreset, newLabelSetImage))
+  if (labelSetPreset.empty() || !mitk::MultiLabelIOHelper::LoadLabelSetImagePreset(labelSetPreset, newLabelSetImage))
   {
     auto newLabel = mitk::LabelSetImageHelper::CreateNewLabel(newLabelSetImage);
 
     if (!m_DefaultLabelNaming)
     {
-      QmitkNewSegmentationDialog dialog(m_Parent);
-      dialog.SetName(QString::fromStdString(newLabel->GetName()));
-      dialog.SetColor(newLabel->GetColor());
-
-      if (QDialog::Rejected == dialog.exec())
-        return;
-
-      auto name = dialog.GetName();
-
-      if (!name.isEmpty())
-        newLabel->SetName(name.toStdString());
-
-      newLabel->SetColor(dialog.GetColor());
+      QmitkNewSegmentationDialog::DoRenameLabel(newLabel,nullptr,m_Parent);
     }
 
     newLabelSetImage->GetActiveLabelSet()->AddLabel(newLabel);
   }
 
   if (!this->GetDataStorage()->Exists(newSegmentationNode))
   {
     this->GetDataStorage()->Add(newSegmentationNode, m_ReferenceNode);
   }
 
   if (m_ToolManager->GetWorkingData(0))
   {
     m_ToolManager->GetWorkingData(0)->SetSelected(false);
   }
 
   newSegmentationNode->SetSelected(true);
   m_Controls->workingNodeSelector->SetCurrentSelectedNode(newSegmentationNode);
 }
 
 std::string QmitkSegmentationView::GetDefaultLabelSetPreset() const
 {
   auto labelSetPreset = mitk::BaseApplication::instance().config().getString(mitk::BaseApplication::ARG_SEGMENTATION_LABELSET_PRESET.toStdString(), "");
 
   if (labelSetPreset.empty())
     labelSetPreset = m_LabelSetPresetPreference.toStdString();
 
   return labelSetPreset;
 }
 
 void QmitkSegmentationView::OnManualTool2DSelected(int id)
 {
   this->ResetMouseCursor();
   mitk::StatusBar::GetInstance()->DisplayText("");
 
   if (id >= 0)
   {
     std::string text = "Active Tool: \"";
     text += m_ToolManager->GetToolById(id)->GetName();
     text += "\"";
     mitk::StatusBar::GetInstance()->DisplayText(text.c_str());
 
     us::ModuleResource resource = m_ToolManager->GetToolById(id)->GetCursorIconResource();
     this->SetMouseCursor(resource, 0, 0);
   }
 }
 
 void QmitkSegmentationView::OnShowMarkerNodes(bool state)
 {
   mitk::SegTool2D::Pointer manualSegmentationTool;
 
   unsigned int numberOfExistingTools = m_ToolManager->GetTools().size();
 
   for (unsigned int i = 0; i < numberOfExistingTools; i++)
   {
     manualSegmentationTool = dynamic_cast<mitk::SegTool2D*>(m_ToolManager->GetToolById(i));
     if (nullptr == manualSegmentationTool)
     {
       continue;
     }
 
     manualSegmentationTool->SetShowMarkerNodes(state);
   }
 }
 
-void QmitkSegmentationView::OnLayersChanged()
+void QmitkSegmentationView::OnCurrentLabelSelectionChanged(QmitkMultiLabelManager::LabelValueVectorType labels)
 {
-  this->EstablishLabelSetConnection();
-  m_Controls->labelSetWidget->ResetAllTableWidgetItems();
-}
+  auto segmentation = this->GetCurrentSegmentation();
 
-void QmitkSegmentationView::OnShowLabelTable(bool value)
-{
-  m_Controls->labelSetWidget->setVisible(value);
+  const auto labelValue = labels.front();
+  const auto groupID = segmentation->GetGroupIndexOfLabel(labelValue);
+  if (groupID != segmentation->GetActiveLayer()) segmentation->SetActiveLayer(groupID);
+  if (labelValue != segmentation->GetActiveLabelSet()->GetActiveLabel()->GetValue()) segmentation->GetActiveLabelSet()->SetActiveLabel(labelValue);
+
+  segmentation->Modified();
+  mitk::RenderingManager::GetInstance()->RequestUpdateAll();
 }
 
-void QmitkSegmentationView::OnGoToLabel(const mitk::Point3D& pos)
+void QmitkSegmentationView::OnGoToLabel(mitk::LabelSetImage::LabelValueType /*label*/, const mitk::Point3D& pos)
 {
   if (m_RenderWindowPart)
   {
     m_RenderWindowPart->SetSelectedPosition(pos);
   }
 }
 
-void QmitkSegmentationView::OnLabelSetWidgetReset()
+void QmitkSegmentationView::OnLabelRenameRequested(mitk::Label* label, bool rename) const
 {
-  this->ValidateSelectionInput();
+  auto segmentation = this->GetCurrentSegmentation();
+
+  if (rename)
+  {
+    QmitkNewSegmentationDialog::DoRenameLabel(label, segmentation, this->m_Parent, QmitkNewSegmentationDialog::Mode::RenameLabel);
+  }
+  else
+  {
+    QmitkNewSegmentationDialog::DoRenameLabel(label, segmentation, this->m_Parent, QmitkNewSegmentationDialog::Mode::NewLabel);
+  }
+}
+
+mitk::LabelSetImage* QmitkSegmentationView::GetCurrentSegmentation() const
+{
+  auto workingNode = m_Controls->workingNodeSelector->GetSelectedNode();
+  if (workingNode.IsNull()) mitkThrow() << "Segmentation view is in an invalid state. Working node is null, but a label selection change has been triggered.";
+
+  auto segmentation = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData());
+  if (nullptr == segmentation) mitkThrow() << "Segmentation view is in an invalid state. Working node contains no segmentation, but a label selection change has been triggered.";
+
+  return segmentation;
 }
 
 /**********************************************************************/
 /* private                                                            */
 /**********************************************************************/
 void QmitkSegmentationView::CreateQtPartControl(QWidget* parent)
 {
    m_Parent = parent;
 
    m_Controls = new Ui::QmitkSegmentationViewControls;
    m_Controls->setupUi(parent);
 
    // *------------------------
    // * SHORTCUTS
    // *------------------------
    QShortcut* visibilityShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key::Key_H), parent);
    connect(visibilityShortcut, &QShortcut::activated, this, &QmitkSegmentationView::OnVisibilityShortcutActivated);
    QShortcut* labelToggleShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key::Key_L, Qt::CTRL | Qt::Key::Key_I), parent);
    connect(labelToggleShortcut, &QShortcut::activated, this, &QmitkSegmentationView::OnLabelToggleShortcutActivated);
 
    // *------------------------
    // * DATA SELECTION WIDGETS
    // *------------------------
    m_Controls->referenceNodeSelector->SetDataStorage(GetDataStorage());
    m_Controls->referenceNodeSelector->SetNodePredicate(m_ReferencePredicate);
    m_Controls->referenceNodeSelector->SetInvalidInfo("Select an image");
    m_Controls->referenceNodeSelector->SetPopUpTitel("Select an image");
    m_Controls->referenceNodeSelector->SetPopUpHint("Select an image that should be used to define the geometry and bounds of the segmentation.");
 
    m_Controls->workingNodeSelector->SetDataStorage(GetDataStorage());
    m_Controls->workingNodeSelector->SetNodePredicate(m_SegmentationPredicate);
    m_Controls->workingNodeSelector->SetInvalidInfo("Select a segmentation");
    m_Controls->workingNodeSelector->SetPopUpTitel("Select a segmentation");
    m_Controls->workingNodeSelector->SetPopUpHint("Select a segmentation that should be modified. Only segmentation with the same geometry and within the bounds of the reference image are selected.");
 
    connect(m_Controls->referenceNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged,
            this, &QmitkSegmentationView::OnReferenceSelectionChanged);
    connect(m_Controls->workingNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged,
            this, &QmitkSegmentationView::OnSegmentationSelectionChanged);
 
    // *------------------------
    // * TOOLMANAGER
    // *------------------------
    m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager();
    m_ToolManager->SetDataStorage(*(this->GetDataStorage()));
    m_ToolManager->InitializeTools();
 
    QString segTools2D = tr("Add Subtract Lasso Fill Erase Close Paint Wipe 'Region Growing' 'Live Wire'");
    QString segTools3D = tr("Threshold 'UL Threshold' Otsu 'Region Growing 3D' Picking GrowCut");
 
 #ifdef __linux__
    segTools3D.append(" nnUNet"); // plugin not enabled for MacOS / Windows
 #endif
    std::regex extSegTool2DRegEx("SegTool2D$");
    std::regex extSegTool3DRegEx("SegTool3D$");
 
    auto tools = m_ToolManager->GetTools();
    for (const auto &tool : tools)
    {
      if (std::regex_search(tool->GetNameOfClass(), extSegTool2DRegEx))
      {
        segTools2D.append(QString(" '%1'").arg(tool->GetName()));
      }
      else if (std::regex_search(tool->GetNameOfClass(), extSegTool3DRegEx))
      {
        segTools3D.append(QString(" '%1'").arg(tool->GetName()));
      }
    }
 
    // setup 2D tools
    m_Controls->toolSelectionBox2D->SetToolManager(*m_ToolManager);
    m_Controls->toolSelectionBox2D->SetGenerateAccelerators(true);
    m_Controls->toolSelectionBox2D->SetToolGUIArea(m_Controls->toolGUIArea2D);
    m_Controls->toolSelectionBox2D->SetDisplayedToolGroups(segTools2D.toStdString());
    m_Controls->toolSelectionBox2D->SetLayoutColumns(3);
    connect(m_Controls->toolSelectionBox2D, &QmitkToolSelectionBox::ToolSelected,
            this, &QmitkSegmentationView::OnManualTool2DSelected);
 
    // setup 3D Tools
    m_Controls->toolSelectionBox3D->SetToolManager(*m_ToolManager);
    m_Controls->toolSelectionBox3D->SetGenerateAccelerators(true);
    m_Controls->toolSelectionBox3D->SetToolGUIArea(m_Controls->toolGUIArea3D);
    m_Controls->toolSelectionBox3D->SetDisplayedToolGroups(segTools3D.toStdString());
    m_Controls->toolSelectionBox3D->SetLayoutColumns(3);
 
    m_Controls->slicesInterpolator->SetDataStorage(this->GetDataStorage());
 
    // create general signal / slot connections
    connect(m_Controls->newSegmentationButton, &QToolButton::clicked, this, &QmitkSegmentationView::OnNewSegmentation);
-   connect(m_Controls->slicesInterpolator, &QmitkSlicesInterpolator::SignalShowMarkerNodes, this, &QmitkSegmentationView::OnShowMarkerNodes);
-
-   connect(m_Controls->layersWidget, &QmitkLayersWidget::LayersChanged, this, &QmitkSegmentationView::OnLayersChanged);
-   connect(m_Controls->labelsWidget, &QmitkLabelsWidget::ShowLabelTable, this, &QmitkSegmentationView::OnShowLabelTable);
 
-   // *------------------------
-   // * LABELSETWIDGET
-   // *------------------------
-   connect(m_Controls->labelSetWidget, &QmitkLabelSetWidget::goToLabel, this, &QmitkSegmentationView::OnGoToLabel);
-   connect(m_Controls->labelSetWidget, &QmitkLabelSetWidget::LabelSetWidgetReset, this, &QmitkSegmentationView::OnLabelSetWidgetReset);
+   connect(m_Controls->slicesInterpolator, &QmitkSlicesInterpolator::SignalShowMarkerNodes, this, &QmitkSegmentationView::OnShowMarkerNodes);
 
-   m_Controls->labelSetWidget->SetDataStorage(this->GetDataStorage());
-   m_Controls->labelSetWidget->hide();
+   connect(m_Controls->multiLabelWidget, &QmitkMultiLabelManager::CurrentSelectionChanged, this, &QmitkSegmentationView::OnCurrentLabelSelectionChanged);
+   connect(m_Controls->multiLabelWidget, &QmitkMultiLabelManager::GoToLabel, this, &QmitkSegmentationView::OnGoToLabel);
+   connect(m_Controls->multiLabelWidget, &QmitkMultiLabelManager::LabelRenameRequested, this, &QmitkSegmentationView::OnLabelRenameRequested);
 
    auto command = itk::SimpleMemberCommand<QmitkSegmentationView>::New();
    command->SetCallbackFunction(this, &QmitkSegmentationView::ValidateSelectionInput);
    m_RenderingManagerObserverTag =
      mitk::RenderingManager::GetInstance()->AddObserver(mitk::RenderingManagerViewsInitializedEvent(), command);
 
    m_RenderWindowPart = this->GetRenderWindowPart();
    if (nullptr != m_RenderWindowPart)
    {
      this->RenderWindowPartActivated(m_RenderWindowPart);
    }
 
    // Make sure the GUI notices if appropriate data is already present on creation.
    // Should be done last, if everything else is configured because it triggers the autoselection of data.
    m_Controls->referenceNodeSelector->SetAutoSelectNewNodes(true);
    m_Controls->workingNodeSelector->SetAutoSelectNewNodes(true);
 
    this->UpdateGUI();
 }
 
 void QmitkSegmentationView::ActiveToolChanged()
 {
   if (nullptr == m_RenderWindowPart)
   {
     return;
   }
 
   mitk::TimeGeometry* interactionReferenceGeometry = nullptr;
   auto activeTool = m_ToolManager->GetActiveTool();
   if (nullptr != activeTool && m_ReferenceNode.IsNotNull())
   {
     mitk::Image::ConstPointer referenceImage = dynamic_cast<mitk::Image *>(m_ReferenceNode->GetData());
     if (referenceImage.IsNotNull())
     {
       // tool activated, reference image available: set reference geometry
       interactionReferenceGeometry = m_ReferenceNode->GetData()->GetTimeGeometry();
     }
   }
 
   // set the interaction reference geometry for the render window part (might be nullptr)
   m_RenderWindowPart->SetInteractionReferenceGeometry(interactionReferenceGeometry);
 }
 
 void QmitkSegmentationView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart)
 {
   if (m_RenderWindowPart != renderWindowPart)
   {
     m_RenderWindowPart = renderWindowPart;
   }
 
   if (nullptr != m_Parent)
   {
     m_Parent->setEnabled(true);
   }
 
   if (nullptr == m_Controls)
   {
     return;
   }
 
   if (nullptr != m_RenderWindowPart)
   {
     auto all2DWindows = Get2DWindows(m_RenderWindowPart->GetQmitkRenderWindows().values());
     m_Controls->slicesInterpolator->Initialize(m_ToolManager, all2DWindows);
 
     if (!m_RenderWindowPart->HasCoupledRenderWindows())
     {
       // react if the active tool changed, only if a render window part with decoupled render windows is used
       m_ToolManager->ActiveToolChanged +=
         mitk::MessageDelegate<QmitkSegmentationView>(this, &QmitkSegmentationView::ActiveToolChanged);
     }
   }
 }
 
 void QmitkSegmentationView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* /*renderWindowPart*/)
 {
   m_RenderWindowPart = nullptr;
   if (nullptr != m_Parent)
   {
     m_Parent->setEnabled(false);
   }
 
   // remove message-connection to make sure no message is processed if no render window part is available
   m_ToolManager->ActiveToolChanged -=
     mitk::MessageDelegate<QmitkSegmentationView>(this, &QmitkSegmentationView::ActiveToolChanged);
 
   m_Controls->slicesInterpolator->Uninitialize();
 }
 
 void QmitkSegmentationView::RenderWindowPartInputChanged(mitk::IRenderWindowPart* /*renderWindowPart*/)
 {
   if (nullptr == m_RenderWindowPart)
   {
     return;
   }
 
   m_Controls->slicesInterpolator->Uninitialize();
   auto all2DWindows = Get2DWindows(m_RenderWindowPart->GetQmitkRenderWindows().values());
   m_Controls->slicesInterpolator->Initialize(m_ToolManager, all2DWindows);
 }
 
 void QmitkSegmentationView::OnPreferencesChanged(const mitk::IPreferences* prefs)
 {
   auto labelSuggestions = mitk::BaseApplication::instance().config().getString(mitk::BaseApplication::ARG_SEGMENTATION_LABEL_SUGGESTIONS.toStdString(), "");
 
   m_DefaultLabelNaming = labelSuggestions.empty()
     ? prefs->GetBool("default label naming", true)
     : false; // No default label naming when label suggestions are enforced via command-line argument
 
   if (nullptr != m_Controls)
   {
-    m_Controls->labelsWidget->SetDefaultLabelNaming(m_DefaultLabelNaming);
+    m_Controls->multiLabelWidget->SetDefaultLabelNaming(m_DefaultLabelNaming);
 
     bool slimView = prefs->GetBool("slim view", false);
     m_Controls->toolSelectionBox2D->SetShowNames(!slimView);
     m_Controls->toolSelectionBox3D->SetShowNames(!slimView);
   }
 
   m_DrawOutline = prefs->GetBool("draw outline", true);
   m_SelectionMode = prefs->GetBool("selection mode", false);
 
   m_LabelSetPresetPreference = QString::fromStdString(prefs->Get("label set preset", ""));
 
   this->ApplyDisplayOptions();
   this->ApplySelectionMode();
 }
 
 void QmitkSegmentationView::NodeAdded(const mitk::DataNode* node)
 {
   if (m_SegmentationPredicate->CheckNode(node))
     this->ApplyDisplayOptions(const_cast<mitk::DataNode*>(node));
 
   this->ApplySelectionMode();
 }
 
 void QmitkSegmentationView::NodeRemoved(const mitk::DataNode* node)
 {
   if (!m_SegmentationPredicate->CheckNode(node))
   {
     return;
   }
 
   // remove all possible contour markers of the segmentation
   mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers = this->GetDataStorage()->GetDerivations(
     node, mitk::NodePredicateProperty::New("isContourMarker", mitk::BoolProperty::New(true)));
 
   ctkPluginContext* context = mitk::PluginActivator::getContext();
   ctkServiceReference ppmRef = context->getServiceReference<mitk::PlanePositionManagerService>();
   mitk::PlanePositionManagerService* service = context->getService<mitk::PlanePositionManagerService>(ppmRef);
 
   for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End(); ++it)
   {
     std::string nodeName = node->GetName();
     unsigned int t = nodeName.find_last_of(" ");
     unsigned int id = atof(nodeName.substr(t + 1).c_str()) - 1;
 
     service->RemovePlanePosition(id);
 
     this->GetDataStorage()->Remove(it->Value());
   }
 
   context->ungetService(ppmRef);
   service = nullptr;
 
   mitk::Image* image = dynamic_cast<mitk::Image*>(node->GetData());
   mitk::SurfaceInterpolationController::GetInstance()->RemoveInterpolationSession(image);
 }
 
-void QmitkSegmentationView::EstablishLabelSetConnection()
-{
-  if (m_WorkingNode.IsNull())
-    return;
-
-  auto workingImage = dynamic_cast<mitk::LabelSetImage*>(m_WorkingNode->GetData());
-  if (nullptr == workingImage)
-    return;
-
-  workingImage->GetActiveLabelSet()->AddLabelEvent += mitk::MessageDelegate<QmitkLabelSetWidget>(
-    m_Controls->labelSetWidget, &QmitkLabelSetWidget::ResetAllTableWidgetItems);
-  workingImage->GetActiveLabelSet()->RemoveLabelEvent += mitk::MessageDelegate<QmitkLabelSetWidget>(
-    m_Controls->labelSetWidget, &QmitkLabelSetWidget::ResetAllTableWidgetItems);
-  workingImage->GetActiveLabelSet()->ModifyLabelEvent += mitk::MessageDelegate<QmitkLabelSetWidget>(
-    m_Controls->labelSetWidget, &QmitkLabelSetWidget::UpdateAllTableWidgetItems);
-  workingImage->GetActiveLabelSet()->AllLabelsModifiedEvent += mitk::MessageDelegate<QmitkLabelSetWidget>(
-    m_Controls->labelSetWidget, &QmitkLabelSetWidget::UpdateAllTableWidgetItems);
-  workingImage->GetActiveLabelSet()->ActiveLabelEvent += mitk::MessageDelegate1<QmitkLabelSetWidget,
-    mitk::Label::PixelType>(m_Controls->labelSetWidget, &QmitkLabelSetWidget::SelectLabelByPixelValue);
-  workingImage->AfterChangeLayerEvent += mitk::MessageDelegate<QmitkSegmentationView>(
-    this, &QmitkSegmentationView::UpdateGUI);
-}
-
-void QmitkSegmentationView::LooseLabelSetConnection()
-{
-  if (m_WorkingNode.IsNull())
-    return;
-
-  auto workingImage = dynamic_cast<mitk::LabelSetImage*>(m_WorkingNode->GetData());
-  if (nullptr == workingImage)
-    return;
-
-  workingImage->GetActiveLabelSet()->AddLabelEvent -= mitk::MessageDelegate<QmitkLabelSetWidget>(
-    m_Controls->labelSetWidget, &QmitkLabelSetWidget::ResetAllTableWidgetItems);
-  workingImage->GetActiveLabelSet()->RemoveLabelEvent -= mitk::MessageDelegate<QmitkLabelSetWidget>(
-    m_Controls->labelSetWidget, &QmitkLabelSetWidget::ResetAllTableWidgetItems);
-  workingImage->GetActiveLabelSet()->ModifyLabelEvent -= mitk::MessageDelegate<QmitkLabelSetWidget>(
-    m_Controls->labelSetWidget, &QmitkLabelSetWidget::UpdateAllTableWidgetItems);
-  workingImage->GetActiveLabelSet()->AllLabelsModifiedEvent -= mitk::MessageDelegate<QmitkLabelSetWidget>(
-    m_Controls->labelSetWidget, &QmitkLabelSetWidget::UpdateAllTableWidgetItems);
-  workingImage->GetActiveLabelSet()->ActiveLabelEvent -= mitk::MessageDelegate1<QmitkLabelSetWidget,
-    mitk::Label::PixelType>(m_Controls->labelSetWidget, &QmitkLabelSetWidget::SelectLabelByPixelValue);
-  workingImage->AfterChangeLayerEvent -= mitk::MessageDelegate<QmitkSegmentationView>(
-    this, &QmitkSegmentationView::UpdateGUI);
-}
-
 void QmitkSegmentationView::ApplyDisplayOptions()
 {
   if (nullptr == m_Parent)
   {
     return;
   }
 
   if (nullptr == m_Controls)
   {
     return; // might happen on initialization (preferences loaded)
   }
 
   mitk::DataStorage::SetOfObjects::ConstPointer allImages = this->GetDataStorage()->GetSubset(m_SegmentationPredicate);
   for (mitk::DataStorage::SetOfObjects::const_iterator iter = allImages->begin(); iter != allImages->end(); ++iter)
   {
     this->ApplyDisplayOptions(*iter);
   }
 
   mitk::RenderingManager::GetInstance()->RequestUpdateAll();
 }
 
 void QmitkSegmentationView::ApplyDisplayOptions(mitk::DataNode* node)
 {
   if (nullptr == node)
   {
     return;
   }
 
   auto labelSetImage = dynamic_cast<mitk::LabelSetImage*>(node->GetData());
   if (nullptr == labelSetImage)
   {
     return;
   }
 
   // the outline property can be set in the segmentation preference page
   node->SetProperty("labelset.contour.active", mitk::BoolProperty::New(m_DrawOutline));
 
   // force render window update to show outline
   mitk::RenderingManager::GetInstance()->RequestUpdateAll();
 }
 
 void QmitkSegmentationView::ApplySelectionMode()
 {
   if (!m_SelectionMode)
     return;
 
   this->ApplySelectionModeOnReferenceNode();
   this->ApplySelectionModeOnWorkingNode();
 }
 
 void QmitkSegmentationView::ApplySelectionModeOnReferenceNode()
 {
   this->ApplySelectionMode(m_ReferenceNode, m_ReferencePredicate);
 }
 
 void QmitkSegmentationView::ApplySelectionModeOnWorkingNode()
 {
   this->ApplySelectionMode(m_WorkingNode, m_SegmentationPredicate);
 }
 
 void QmitkSegmentationView::ApplySelectionMode(mitk::DataNode* node, mitk::NodePredicateBase* predicate)
 {
   if (!m_SelectionMode || node == nullptr || predicate == nullptr)
     return;
 
   auto nodes = this->GetDataStorage()->GetSubset(predicate);
 
   for (auto iter = nodes->begin(); iter != nodes->end(); ++iter)
     (*iter)->SetVisibility(*iter == node);
 }
 
 void QmitkSegmentationView::OnContourMarkerSelected(const mitk::DataNode* node)
 {
   QmitkRenderWindow* selectedRenderWindow = nullptr;
   auto* renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN);
   auto* axialRenderWindow = renderWindowPart->GetQmitkRenderWindow("axial");
   auto* sagittalRenderWindow = renderWindowPart->GetQmitkRenderWindow("sagittal");
   auto* coronalRenderWindow = renderWindowPart->GetQmitkRenderWindow("coronal");
   auto* threeDRenderWindow = renderWindowPart->GetQmitkRenderWindow("3d");
   bool PlanarFigureInitializedWindow = false;
 
   // find initialized renderwindow
   if (node->GetBoolProperty("PlanarFigureInitializedWindow",
     PlanarFigureInitializedWindow, axialRenderWindow->GetRenderer()))
   {
     selectedRenderWindow = axialRenderWindow;
   }
   if (!selectedRenderWindow && node->GetBoolProperty(
     "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow,
     sagittalRenderWindow->GetRenderer()))
   {
     selectedRenderWindow = sagittalRenderWindow;
   }
   if (!selectedRenderWindow && node->GetBoolProperty(
     "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow,
     coronalRenderWindow->GetRenderer()))
   {
     selectedRenderWindow = coronalRenderWindow;
   }
   if (!selectedRenderWindow && node->GetBoolProperty(
     "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow,
     threeDRenderWindow->GetRenderer()))
   {
     selectedRenderWindow = threeDRenderWindow;
   }
 
   // make node visible
   if (nullptr != selectedRenderWindow)
   {
     std::string nodeName = node->GetName();
     unsigned int t = nodeName.find_last_of(" ");
     unsigned int id = atof(nodeName.substr(t + 1).c_str()) - 1;
 
     ctkPluginContext* context = mitk::PluginActivator::getContext();
     ctkServiceReference ppmRef = context->getServiceReference<mitk::PlanePositionManagerService>();
     mitk::PlanePositionManagerService* service = context->getService<mitk::PlanePositionManagerService>(ppmRef);
     selectedRenderWindow->GetSliceNavigationController()->ExecuteOperation(service->GetPlanePosition(id));
     context->ungetService(ppmRef);
 
     selectedRenderWindow->GetRenderer()->GetCameraController()->Fit();
     mitk::RenderingManager::GetInstance()->RequestUpdateAll();
   }
 }
 
 void QmitkSegmentationView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList<mitk::DataNode::Pointer>& nodes)
 {
   if (0 == nodes.size())
   {
     return;
   }
 
   std::string markerName = "Position";
   unsigned int numberOfNodes = nodes.size();
   std::string nodeName = nodes.at(0)->GetName();
   if ((numberOfNodes == 1) && (nodeName.find(markerName) == 0))
   {
     this->OnContourMarkerSelected(nodes.at(0));
     return;
   }
 }
 
 void QmitkSegmentationView::ResetMouseCursor()
 {
   if (m_MouseCursorSet)
   {
     mitk::ApplicationCursor::GetInstance()->PopCursor();
     m_MouseCursorSet = false;
   }
 }
 
 void QmitkSegmentationView::SetMouseCursor(const us::ModuleResource& resource, int hotspotX, int hotspotY)
 {
   // Remove previously set mouse cursor
   if (m_MouseCursorSet)
   {
     this->ResetMouseCursor();
   }
 
   if (resource)
   {
     us::ModuleResourceStream cursor(resource, std::ios::binary);
     mitk::ApplicationCursor::GetInstance()->PushCursor(cursor, hotspotX, hotspotY);
     m_MouseCursorSet = true;
   }
 }
 
 void QmitkSegmentationView::UpdateGUI()
 {
   mitk::DataNode* referenceNode = m_ToolManager->GetReferenceData(0);
   bool hasReferenceNode = referenceNode != nullptr;
 
   mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0);
   bool hasWorkingNode = workingNode != nullptr;
 
   m_Controls->newSegmentationButton->setEnabled(false);
 
   if (hasReferenceNode)
   {
     m_Controls->newSegmentationButton->setEnabled(true);
   }
 
   if (hasWorkingNode && hasReferenceNode)
   {
     int layer = -1;
     referenceNode->GetIntProperty("layer", layer);
     workingNode->SetIntProperty("layer", layer + 1);
   }
 
-  m_Controls->layersWidget->UpdateGUI();
-  m_Controls->labelsWidget->UpdateGUI();
-
   this->ValidateSelectionInput();
 }
 
 void QmitkSegmentationView::ValidateSelectionInput()
 {
   auto referenceNode = m_Controls->referenceNodeSelector->GetSelectedNode();
   auto workingNode = m_Controls->workingNodeSelector->GetSelectedNode();
 
   bool hasReferenceNode = referenceNode.IsNotNull();
   bool hasWorkingNode = workingNode.IsNotNull();
   bool hasBothNodes = hasReferenceNode && hasWorkingNode;
 
   QString warning;
   bool toolSelectionBoxesEnabled = hasReferenceNode && hasWorkingNode;
   unsigned int numberOfLabels = 0;
 
-  m_Controls->layersWidget->setEnabled(hasWorkingNode);
-  m_Controls->labelsWidget->setEnabled(hasWorkingNode);
-  m_Controls->labelSetWidget->setEnabled(hasWorkingNode);
+  m_Controls->multiLabelWidget->setEnabled(hasWorkingNode);
 
   m_Controls->toolSelectionBox2D->setEnabled(hasBothNodes);
   m_Controls->toolSelectionBox3D->setEnabled(hasBothNodes);
 
   m_Controls->slicesInterpolator->setEnabled(false);
   m_Controls->interpolatorWarningLabel->hide();
 
   if (hasReferenceNode)
   {
     if (nullptr != m_RenderWindowPart && m_RenderWindowPart->HasCoupledRenderWindows() && !referenceNode->IsVisible(nullptr))
     {
       warning += tr("The selected reference image is currently not visible!");
       toolSelectionBoxesEnabled = false;
     }
   }
 
   if (hasWorkingNode)
   {
     if (nullptr != m_RenderWindowPart && m_RenderWindowPart->HasCoupledRenderWindows() && !workingNode->IsVisible(nullptr))
     {
       warning += (!warning.isEmpty() ? "<br>" : "") + tr("The selected segmentation is currently not visible!");
       toolSelectionBoxesEnabled = false;
     }
 
     m_ToolManager->SetReferenceData(referenceNode);
     m_ToolManager->SetWorkingData(workingNode);
-    m_Controls->layersWidget->setEnabled(true);
-    m_Controls->labelsWidget->setEnabled(true);
-    m_Controls->labelSetWidget->setEnabled(true);
+    m_Controls->multiLabelWidget->setEnabled(true);
     m_Controls->toolSelectionBox2D->setEnabled(true);
     m_Controls->toolSelectionBox3D->setEnabled(true);
 
     auto labelSetImage = dynamic_cast<mitk::LabelSetImage *>(workingNode->GetData());
     auto activeLayer = labelSetImage->GetActiveLayer();
     numberOfLabels = labelSetImage->GetNumberOfLabels(activeLayer);
 
-    if (numberOfLabels > 1)
+    if (numberOfLabels > 0)
       m_Controls->slicesInterpolator->setEnabled(true);
+
+    m_Controls->multiLabelWidget->SetMultiLabelSegmentation(dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData()));
+  }
+  else
+  {
+    m_Controls->multiLabelWidget->SetMultiLabelSegmentation(nullptr);
   }
 
-  toolSelectionBoxesEnabled &= numberOfLabels > 1;
+  toolSelectionBoxesEnabled &= numberOfLabels > 0;
 
   // Here we need to check whether the geometry of the selected segmentation image (working image geometry)
   // is aligned with the geometry of the 3D render window.
   // It is not allowed to use a geometry different from the working image geometry for segmenting.
   // We only need to this if the tool selection box would be enabled without this check.
   // Additionally this check only has to be performed for render window parts with coupled render windows.
   // For different render window parts the user is given the option to reinitialize each render window individually
   // (see QmitkRenderWindow::ShowOverlayMessage).
   if (toolSelectionBoxesEnabled && nullptr != m_RenderWindowPart && m_RenderWindowPart->HasCoupledRenderWindows())
   {
     const mitk::BaseGeometry* workingNodeGeometry = workingNode->GetData()->GetGeometry();
     const mitk::BaseGeometry* renderWindowGeometry =
       m_RenderWindowPart->GetQmitkRenderWindow("3d")->GetSliceNavigationController()->GetCurrentGeometry3D();
     if (nullptr != workingNodeGeometry && nullptr != renderWindowGeometry)
     {
       if (!mitk::Equal(*workingNodeGeometry->GetBoundingBox(), *renderWindowGeometry->GetBoundingBox(), mitk::eps, true))
       {
         warning += (!warning.isEmpty() ? "<br>" : "") + tr("Please reinitialize the selected segmentation image!");
         toolSelectionBoxesEnabled = false;
       }
     }
   }
 
   m_Controls->toolSelectionBox2D->setEnabled(toolSelectionBoxesEnabled);
   m_Controls->toolSelectionBox3D->setEnabled(toolSelectionBoxesEnabled);
 
   this->UpdateWarningLabel(warning);
 
   m_ToolManager->SetReferenceData(referenceNode);
   m_ToolManager->SetWorkingData(workingNode);
 }
 
 void QmitkSegmentationView::UpdateWarningLabel(QString text)
 {
   if (text.isEmpty())
   {
     m_Controls->selectionWarningLabel->hide();
   }
   else
   {
     m_Controls->selectionWarningLabel->setText("<font color=\"red\">" + text + "</font>");
     m_Controls->selectionWarningLabel->show();
   }
 }
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h
index 2f04a734cd..93d2058441 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h
@@ -1,189 +1,184 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #ifndef QmitkSegmentationView_h
 #define QmitkSegmentationView_h
 
 #include "ui_QmitkSegmentationViewControls.h"
 
 #include <QmitkAbstractView.h>
 #include <mitkIRenderWindowPartListener.h>
 
 /**
 * @brief The segmentation view provides a set of tool to use different segmentation algorithms.
 *        It provides two selection widgets to load an image node and a segmentation node
 *        on which to perform the segmentation. Creating new segmentation nodes is also possible.
 *        The available segmentation tools are grouped into "2D"- and "3D"-tools.
 *
 *        Most segmentation tools / algorithms need some kind of user interaction, where the
 *        user is asked to draw something in the image display or set some seed points / start values.
 *        The tools also often provide additional propeties so that a user can modify the
 *        algorithm's behavior.
 *
 *        This class additionally provides options to work with different layers (create new layers,
 *        switch between layers).
 *        Moreover, a multilabel widget displays all the existing labels of a multilabel segmentation
 *        for the currently active layer.
 *        The multilabel widget allows to control the labels by creatin new one, removing existing ones,
 *        showing / hiding single labels, merging labels, (re-)naming them etc.
 *
 *        Additionally the view provides an option to create "2D"- and "3D"-interpolations between
 *        neighboring segmentation masks on unsegmented slices.
 *        Interpolation for multilabel segmentations is currently not implemented.
 */
 class QmitkSegmentationView : public QmitkAbstractView, public mitk::IRenderWindowPartListener
 {
   Q_OBJECT
 
 public:
 
   static const std::string VIEW_ID;
 
   QmitkSegmentationView();
   ~QmitkSegmentationView() override;
 
 private Q_SLOTS:
 
   // reaction to the selection of a new reference image in the selection widget
   void OnReferenceSelectionChanged(QList<mitk::DataNode::Pointer> nodes);
 
   // reaction to the selection of a new segmentation image in the selection widget
   void OnSegmentationSelectionChanged(QList<mitk::DataNode::Pointer> nodes);
 
   // reaction to the shortcut ("CTRL+H") for toggling the visibility of the working node
   void OnVisibilityShortcutActivated();
 
   // reaction to the shortcut ("CTRL+L") for iterating over all labels
   void OnLabelToggleShortcutActivated();
 
   // reaction to the button "New segmentation"
   void OnNewSegmentation();
 
   void OnManualTool2DSelected(int id);
 
   void OnShowMarkerNodes(bool);
 
-  void OnLayersChanged();
+  void OnCurrentLabelSelectionChanged(QmitkMultiLabelManager::LabelValueVectorType labels);
 
-  void OnShowLabelTable(bool);
-
-  void OnGoToLabel(const mitk::Point3D &pos);
-
-  void OnLabelSetWidgetReset();
+  void OnGoToLabel(mitk::LabelSetImage::LabelValueType label, const mitk::Point3D&);
+  void OnLabelRenameRequested(mitk::Label* label, bool rename) const;
 
 private:
 
   void CreateQtPartControl(QWidget* parent) override;
 
   void SetFocus() override {}
   /**
   * @brief Enable or disable the SegmentationInteractor.
   *
   * The active tool is retrieved from the tool manager.
   * If the active tool is valid, the SegmentationInteractor is enabled
   * to listen to 'SegmentationInteractionEvent's.
   */
   void ActiveToolChanged();
   /**
   * @brief Test whether the geometry of the reference image
   *        fits the world geometry of the respective renderer.
   *
   * The respective renderer is retrieved from the given event, which
   * needs to be a 'SegmentationInteractionEvent'.
   * This event provides not only the sending base renderer but also a
   * bool that indicates whether the mouse cursor entered or left the
   * base renderer.
   * This information is used to compare the renderer's world geometry
   * with the oriented time geometry of the current reference image.
   * If the geometries align, the renderer is not blocked anymore and the
   * view's warning message is removed.
   * If the geometries do not align, 'ShowRenderWindowWarning' is called
   * and a warning message is added to the top of this plugin view.
   *
   * @param event   The observed mitk::SegmentationInteractionEvent.
   */
   void ValidateRendererGeometry(const itk::EventObject& event);
 
   void RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) override;
   void RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart) override;
   void RenderWindowPartInputChanged(mitk::IRenderWindowPart* renderWindowPart) override;
 
   void OnPreferencesChanged(const mitk::IPreferences* prefs) override;
 
   void NodeAdded(const mitk::DataNode* node) override;
 
   void NodeRemoved(const mitk::DataNode* node) override;
 
-  void EstablishLabelSetConnection();
-
-  void LooseLabelSetConnection();
-
   void OnAnySelectionChanged();
 
   // make sure all images / segmentations look according to the user preference settings
   void ApplyDisplayOptions();
 
   // decorates a DataNode according to the user preference settings
   void ApplyDisplayOptions(mitk::DataNode* node);
 
   void ApplySelectionMode();
   void ApplySelectionModeOnReferenceNode();
   void ApplySelectionModeOnWorkingNode();
   void ApplySelectionMode(mitk::DataNode* node, mitk::NodePredicateBase* predicate);
 
   // If a contourmarker is selected, the plane in the related widget will be reoriented according to the marker`s geometry
   void OnContourMarkerSelected(const mitk::DataNode* node);
 
   void OnSelectionChanged(berry::IWorkbenchPart::Pointer part, const QList<mitk::DataNode::Pointer> &nodes) override;
 
   void ResetMouseCursor();
 
   void SetMouseCursor(const us::ModuleResource&, int hotspotX, int hotspotY);
 
   void UpdateGUI();
 
   void ValidateSelectionInput();
 
   void UpdateWarningLabel(QString text);
 
   std::string GetDefaultLabelSetPreset() const;
 
+  mitk::LabelSetImage* GetCurrentSegmentation() const;
+
   QWidget* m_Parent;
 
   Ui::QmitkSegmentationViewControls* m_Controls;
 
   mitk::IRenderWindowPart* m_RenderWindowPart;
 
   mitk::ToolManager* m_ToolManager;
 
   mitk::DataNode::Pointer m_ReferenceNode;
   mitk::DataNode::Pointer m_WorkingNode;
 
   typedef std::map<mitk::DataNode*, unsigned long> NodeTagMapType;
   NodeTagMapType m_WorkingDataObserverTags;
   NodeTagMapType m_ReferenceDataObserverTags;
   unsigned int m_RenderingManagerObserverTag;
 
   mitk::NodePredicateAnd::Pointer m_ReferencePredicate;
   mitk::NodePredicateAnd::Pointer m_SegmentationPredicate;
 
   bool m_DrawOutline;
   bool m_SelectionMode;
   bool m_MouseCursorSet;
 
   QString m_LabelSetPresetPreference;
   bool m_DefaultLabelNaming;
 
   bool m_SelectionChangeIsAlreadyBeingHandled;
 };
 
 #endif
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationViewControls.ui b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationViewControls.ui
index 958d8992b7..54eae24ca6 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationViewControls.ui
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationViewControls.ui
@@ -1,344 +1,312 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <ui version="4.0">
  <class>QmitkSegmentationViewControls</class>
  <widget class="QWidget" name="QmitkSegmentationViewControls">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>300</width>
     <height>600</height>
    </rect>
   </property>
   <property name="sizePolicy">
    <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
     <horstretch>0</horstretch>
     <verstretch>0</verstretch>
    </sizepolicy>
   </property>
   <layout class="QVBoxLayout" name="verticalLayout">
    <item>
     <widget class="QGroupBox" name="groupBox">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
        <horstretch>0</horstretch>
        <verstretch>0</verstretch>
       </sizepolicy>
      </property>
      <property name="title">
       <string>Data selection</string>
      </property>
      <layout class="QGridLayout" name="gridLayout">
       <item row="0" column="0">
        <widget class="QLabel" name="referenceNodeLabel">
         <property name="text">
          <string>Selected image</string>
         </property>
        </widget>
       </item>
       <item row="0" column="1">
        <widget class="QmitkSingleNodeSelectionWidget" name="referenceNodeSelector" native="true">
         <property name="minimumSize">
          <size>
           <width>0</width>
           <height>40</height>
          </size>
         </property>
        </widget>
       </item>
       <item row="1" column="0">
        <widget class="QLabel" name="workingNodeLabel">
         <property name="text">
          <string>Selected segmentation</string>
         </property>
        </widget>
       </item>
       <item row="1" column="1">
        <widget class="QmitkSingleNodeSelectionWidget" name="workingNodeSelector" native="true">
         <property name="minimumSize">
          <size>
           <width>0</width>
           <height>40</height>
          </size>
         </property>
        </widget>
       </item>
       <item row="1" column="2">
        <widget class="QToolButton" name="newSegmentationButton">
         <property name="toolTip">
          <string>Create a new segmentation</string>
         </property>
         <property name="text">
          <string>...</string>
         </property>
         <property name="icon">
          <iconset>
           <normaloff>:/Qmitk/NewSegmentation_48x48.png</normaloff>:/Qmitk/NewSegmentation_48x48.png</iconset>
         </property>
         <property name="iconSize">
          <size>
           <width>28</width>
           <height>28</height>
          </size>
         </property>
         <property name="autoRaise">
          <bool>true</bool>
         </property>
        </widget>
       </item>
       <item row="2" column="0" colspan="3">
        <widget class="QLabel" name="selectionWarningLabel">
         <property name="sizePolicy">
          <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
           <horstretch>0</horstretch>
           <verstretch>0</verstretch>
          </sizepolicy>
         </property>
         <property name="wordWrap">
          <bool>true</bool>
         </property>
        </widget>
       </item>
      </layout>
     </widget>
    </item>
    <item>
-    <widget class="QmitkLayersWidget" name="layersWidget" native="true">
-     <property name="sizePolicy">
-      <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
-       <horstretch>0</horstretch>
-       <verstretch>0</verstretch>
-      </sizepolicy>
-     </property>
-    </widget>
-   </item>
-   <item>
-    <widget class="QmitkLabelsWidget" name="labelsWidget" native="true">
-     <property name="sizePolicy">
-      <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
-       <horstretch>0</horstretch>
-       <verstretch>0</verstretch>
-      </sizepolicy>
-     </property>
-    </widget>
-   </item>
-   <item>
-    <widget class="QmitkLabelSetWidget" name="labelSetWidget" native="true">
+    <widget class="QmitkMultiLabelManager" name="multiLabelWidget" native="true">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
        <horstretch>0</horstretch>
        <verstretch>0</verstretch>
       </sizepolicy>
      </property>
     </widget>
    </item>
    <item>
     <widget class="QTabWidget" name="tabWidgetSegmentationTools">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
        <horstretch>0</horstretch>
        <verstretch>0</verstretch>
       </sizepolicy>
      </property>
      <property name="styleSheet">
       <string notr="true">QTabWidget::tab-bar { alignment: middle; }</string>
      </property>
      <property name="currentIndex">
       <number>0</number>
      </property>
      <widget class="QWidget" name="tab2DTools">
       <attribute name="title">
        <string>2D tools</string>
       </attribute>
       <layout class="QVBoxLayout" name="verticalLayout2D">
        <item>
         <widget class="QmitkToolGUIArea" name="toolGUIArea2D" native="true">
          <property name="sizePolicy">
           <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
            <horstretch>0</horstretch>
            <verstretch>0</verstretch>
           </sizepolicy>
          </property>
          <property name="font">
           <font>
            <weight>50</weight>
            <bold>false</bold>
           </font>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QmitkToolSelectionBox" name="toolSelectionBox2D" native="true">
          <property name="sizePolicy">
           <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
            <horstretch>0</horstretch>
            <verstretch>0</verstretch>
           </sizepolicy>
          </property>
          <property name="font">
           <font>
            <weight>50</weight>
            <bold>false</bold>
           </font>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QLabel" name="interpolatorWarningLabel">
          <property name="sizePolicy">
           <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
            <horstretch>0</horstretch>
            <verstretch>0</verstretch>
           </sizepolicy>
          </property>
          <property name="text">
           <string/>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QmitkSlicesInterpolator" name="slicesInterpolator" native="true">
          <property name="sizePolicy">
           <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
            <horstretch>0</horstretch>
            <verstretch>0</verstretch>
           </sizepolicy>
          </property>
          <property name="font">
           <font>
            <weight>50</weight>
            <bold>false</bold>
           </font>
          </property>
         </widget>
        </item>
        <item>
         <spacer name="verticalSpacer2D">
          <property name="orientation">
           <enum>Qt::Vertical</enum>
          </property>
          <property name="sizeHint" stdset="0">
           <size>
            <width>20</width>
            <height>40</height>
           </size>
          </property>
         </spacer>
        </item>
       </layout>
      </widget>
      <widget class="QWidget" name="tab3DTools">
       <attribute name="title">
        <string>3D tools</string>
       </attribute>
       <layout class="QVBoxLayout" name="verticalLayout3D">
        <item>
         <widget class="QmitkToolGUIArea" name="toolGUIArea3D" native="true">
          <property name="sizePolicy">
           <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
            <horstretch>0</horstretch>
            <verstretch>0</verstretch>
           </sizepolicy>
          </property>
          <property name="font">
           <font>
            <weight>50</weight>
            <bold>false</bold>
           </font>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QmitkToolSelectionBox" name="toolSelectionBox3D" native="true">
          <property name="sizePolicy">
           <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
            <horstretch>0</horstretch>
            <verstretch>0</verstretch>
           </sizepolicy>
          </property>
          <property name="font">
           <font>
            <weight>50</weight>
            <bold>false</bold>
           </font>
          </property>
         </widget>
        </item>
        <item>
         <spacer name="verticalSpacer3D">
          <property name="orientation">
           <enum>Qt::Vertical</enum>
          </property>
          <property name="sizeHint" stdset="0">
           <size>
            <width>20</width>
            <height>40</height>
           </size>
          </property>
         </spacer>
        </item>
       </layout>
      </widget>
     </widget>
    </item>
    <item>
     <spacer name="verticalSpacer">
      <property name="orientation">
       <enum>Qt::Vertical</enum>
      </property>
      <property name="sizeHint" stdset="0">
       <size>
        <width>20</width>
        <height>40</height>
       </size>
      </property>
     </spacer>
    </item>
   </layout>
  </widget>
  <customwidgets>
   <customwidget>
    <class>QmitkSingleNodeSelectionWidget</class>
    <extends>QWidget</extends>
    <header location="global">QmitkSingleNodeSelectionWidget.h</header>
    <container>1</container>
   </customwidget>
-  <customwidget>
-   <class>QmitkLayersWidget</class>
-   <extends>QWidget</extends>
-   <header location="global">internal/Common/QmitkLayersWidget.h</header>
-   <container>1</container>
-  </customwidget>
-  <customwidget>
-   <class>QmitkLabelsWidget</class>
-   <extends>QWidget</extends>
-   <header location="global">internal/Common/QmitkLabelsWidget.h</header>
-   <container>1</container>
-  </customwidget>
-  <customwidget>
-   <class>QmitkLabelSetWidget</class>
-   <extends>QWidget</extends>
-   <header location="global">QmitkLabelSetWidget.h</header>
-   <container>1</container>
-  </customwidget>
   <customwidget>
    <class>QmitkToolSelectionBox</class>
    <extends>QWidget</extends>
    <header location="global">QmitkToolSelectionBox.h</header>
   </customwidget>
   <customwidget>
    <class>QmitkToolGUIArea</class>
    <extends>QWidget</extends>
    <header location="global">QmitkToolGUIArea.h</header>
   </customwidget>
   <customwidget>
    <class>QmitkSlicesInterpolator</class>
    <extends>QWidget</extends>
    <header location="global">QmitkSlicesInterpolator.h</header>
   </customwidget>
+  <customwidget>
+   <class>QmitkMultiLabelManager</class>
+   <extends>QWidget</extends>
+   <header>QmitkMultiLabelManager.h</header>
+   <container>1</container>
+  </customwidget>
  </customwidgets>
  <resources>
   <include location="../../resources/segmentation.qrc"/>
  </resources>
  <connections/>
 </ui>