diff --git a/Modules/Multilabel/mitkLabelSetImage.cpp b/Modules/Multilabel/mitkLabelSetImage.cpp
index 97a6d995f1..ddc161f4af 100644
--- a/Modules/Multilabel/mitkLabelSetImage.cpp
+++ b/Modules/Multilabel/mitkLabelSetImage.cpp
@@ -1,1672 +1,1672 @@
 /*============================================================================
 
 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 <itkCommand.h>
 
 #include <itkBinaryFunctorImageFilter.h>
 
 
 namespace mitk
 {
   template <typename ImageType>
   void ClearBufferProcessing(ImageType* itkImage)
   {
     itkImage->FillBuffer(0);
   }
 
   void ClearImageBuffer(mitk::Image* image)
   {
     if (image->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(image, ClearBufferProcessing, 4);
     }
     else
     {
       AccessByItk(image, ClearBufferProcessing);
     }
   }
 }
 
 mitk::LabelSetImage::LabelSetImage()
   : mitk::Image(), m_UnlabeledLabelLock(false), m_ActiveLayer(0), m_activeLayerInvalid(false), m_ActiveLabelValue(0)
 {
   m_LookupTable = mitk::LookupTable::New();
   m_LookupTable->SetType(mitk::LookupTable::MULTILABEL);
 
   // 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_LookupTable(other.m_LookupTable->Clone()),
     m_ActiveLabelValue(other.m_ActiveLabelValue)
 {
   GroupIndexType i = 0;
   for (auto groupImage : other.m_LayerContainer)
   {
     this->AddLayer(groupImage->Clone(), other.GetConstLabelsByValue(other.GetLabelValuesByGroup(i)));
     i++;
   }
   m_Groups = other.m_Groups;
 
   // Add some DICOM Tags as properties to segmentation image
   DICOMSegmentationPropertyHelper::DeriveDICOMSegmentationProperties(this);
 }
 
 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
   ClearImageBuffer(this);
 
   // 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 [value, label] : m_LabelMap)
   {
     this->ReleaseLabel(label);
   }
   m_LabelMap.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
 {
   if (m_LayerContainer.size() == 0) mitkThrow() << "Cannot return active layer index. No layer is available.";
 
   return m_ActiveLayer;
 }
 
 unsigned int mitk::LabelSetImage::GetNumberOfLayers() const
 {
   return m_LayerContainer.size();
 }
 
 void mitk::LabelSetImage::RemoveGroup(GroupIndexType indexToDelete)
 {
   if (!this->ExistGroup(indexToDelete)) mitkThrow() << "Cannot remove group. Group does not exist. Invalid group index: "<<indexToDelete;
 
   const auto activeIndex = GetActiveLayer();
 
   auto newActiveIndex = activeIndex;
   auto newActiveIndexBeforeDeletion = activeIndex;
   //determin new active group index (afte the group will be removed);
   if (indexToDelete < activeIndex)
   { //lower the index because position in m_LayerContainer etc has changed
     newActiveIndex = activeIndex-1;
   }
   else if (indexToDelete == activeIndex)
   {
     if (this->GetNumberOfLayers() == 1)
     { //last layer is about to be deleted
       newActiveIndex = 0;
     }
     else
     {
       //we have to add/substract one more because we have not removed the layer yet, thus the group count is to 1 high.
       newActiveIndex = indexToDelete+1 < GetNumberOfLayers() ? indexToDelete : GetNumberOfLayers() - 2;
       newActiveIndexBeforeDeletion = indexToDelete + 1 < GetNumberOfLayers() ? indexToDelete+1 : indexToDelete -1;
     }
   }
 
   if (activeIndex == indexToDelete)
   {
     // we are deleting the active layer, it should not be copied back into the vector
     m_activeLayerInvalid = true;
     //copy the image content of the upcoming new active layer; 
     SetActiveLayer(newActiveIndexBeforeDeletion);
   }
 
   auto relevantLabels = m_GroupToLabelMap[indexToDelete];
 
   // remove labels of group
   for (auto labelValue : relevantLabels)
   {
     auto label = m_LabelMap[labelValue];
     this->ReleaseLabel(label);
     m_LabelToGroupMap.erase(labelValue);
     m_LabelMap.erase(labelValue);
-    this->m_LabelRemovedMessage.Send(labelValue);
+    this->InvokeEvent(LabelRemovedEvent(labelValue));
   }
   // remove the group entries in the maps and the image.
   m_Groups.erase(m_Groups.begin() + indexToDelete);
   m_GroupToLabelMap.erase(m_GroupToLabelMap.begin() + indexToDelete);
   m_LayerContainer.erase(m_LayerContainer.begin() + indexToDelete);
 
   //update old indeces in m_GroupToLabelMap to new layer indices
   for (auto& element : m_LabelToGroupMap)
   {
     if (element.second > indexToDelete) element.second = element.second -1;
   }
 
   //correct active layer index
   m_ActiveLayer = newActiveIndex;
 
-  this->m_LabelsChangedMessage.Send(relevantLabels);
-  this->m_GroupRemovedMessage.Send(indexToDelete);
+  this->InvokeEvent(LabelsChangedEvent(relevantLabels));
+  this->InvokeEvent(GroupRemovedEvent(indexToDelete));
   this->Modified();
 }
 
 mitk::LabelSetImage::LabelValueVectorType mitk::LabelSetImage::ExtractLabelValuesFromLabelVector(const LabelVectorType& labels)
 {
   LabelValueVectorType result;
 
   for (auto label : labels)
   {
     result.emplace_back(label->GetValue());
   }
   return result;
 }
 
 mitk::LabelSetImage::LabelValueVectorType mitk::LabelSetImage::ExtractLabelValuesFromLabelVector(const ConstLabelVectorType& labels)
 {
   LabelValueVectorType result;
 
   for (auto label : labels)
   {
     result.emplace_back(label->GetValue());
   }
   return result;
 }
 
 mitk::LabelSetImage::ConstLabelVectorType mitk::LabelSetImage::ConvertLabelVectorConst(const LabelVectorType& labels)
 {
   ConstLabelVectorType result(labels.begin(), labels.end());
   return result;
 };
 
 const mitk::LabelSetImage::LabelValueVectorType mitk::LabelSetImage::GetAllLabelValues() const
 {
   LabelValueVectorType result;
 
   for (auto [value, label] : m_LabelMap)
   {
     result.emplace_back(value);
   }
   return result;
 }
 
 mitk::LabelSetImage::LabelValueVectorType mitk::LabelSetImage::GetUsedLabelValues() const
 {
   LabelValueVectorType result = { UNLABELED_VALUE };
 
   for (auto [value, label] : m_LabelMap)
   {
     result.emplace_back(value);
   }
 
   return result;
 }
 
 mitk::LabelSetImage::GroupIndexType mitk::LabelSetImage::AddLayer(ConstLabelVector labels)
 {
   mitk::Image::Pointer newImage = mitk::Image::New();
   newImage->Initialize(this->GetPixelType(),
                        this->GetDimension(),
                        this->GetDimensions(),
                        this->GetImageDescriptor()->GetNumberOfChannels());
   newImage->SetTimeGeometry(this->GetTimeGeometry()->Clone());
 
   ClearImageBuffer(newImage);
 
   return this->AddLayer(newImage, labels);
 }
 
 mitk::LabelSetImage::GroupIndexType mitk::LabelSetImage::AddLayer(mitk::Image::Pointer layerImage, ConstLabelVector labels)
 {
   GroupIndexType newGroupID = m_Groups.size();
 
   // push a new working image for the new layer
   m_LayerContainer.push_back(layerImage);
 
   m_Groups.push_back("");
   m_GroupToLabelMap.push_back({});
 
   for (auto label : labels)
   {
     if (m_LabelMap.end() != m_LabelMap.find(label->GetValue()))
     {
       mitkThrow() << "Cannot add layer. Labels that should be added with layer use at least one label value that is already in use. Conflicted label value: " << label->GetValue();
     }
 
     auto labelClone = label->Clone();
 
     DICOMSegmentationPropertyHelper::SetDICOMSegmentProperties(labelClone);
     this->AddLabelToMap(labelClone->GetValue(), labelClone, newGroupID);
     this->RegisterLabel(labelClone);
   }
 
   this->Modified();
-  this->m_GroupAddedMessage.Send(newGroupID);
+  this->InvokeEvent(GroupAddedEvent(newGroupID));
 
   return newGroupID;
 }
 
 void mitk::LabelSetImage::ReplaceGroupLabels(const GroupIndexType groupID, const ConstLabelVectorType& labelSet)
 {
   if (m_LayerContainer.size() <= groupID)
   {
     mitkThrow() << "Trying to replace labels of non-exising group. Invalid group id: "<<groupID;
   }
 
   //remove old group labels
   auto oldLabels = this->m_GroupToLabelMap[groupID];
   for (auto labelID : oldLabels)
   {
     this->RemoveLabelFromMap(labelID);
-    this->m_LabelRemovedMessage.Send(labelID);
+    this->InvokeEvent(LabelRemovedEvent(labelID));
 
   }
-  this->m_LabelsChangedMessage.Send(oldLabels);
-  this->m_GroupModifiedMessage.Send(groupID);
+  this->InvokeEvent(LabelsChangedEvent(oldLabels));
+  this->InvokeEvent(GroupModifiedEvent(groupID));
 
   //add new labels to group
   for (auto label : labelSet)
   {
     this->AddLabel(label->Clone(), groupID, true, false);
   }
 }
 
 void mitk::LabelSetImage::ReplaceGroupLabels(const GroupIndexType groupID, const LabelVectorType& labelSet)
 {
   return ReplaceGroupLabels(groupID, ConvertLabelVectorConst(labelSet));
 }
 
 mitk::Image* mitk::LabelSetImage::GetGroupImage(GroupIndexType groupID)
 {
   if (!this->ExistGroup(groupID)) mitkThrow() << "Error, cannot return group image. Group ID is invalid. Invalid ID: " << groupID;
 
   return groupID == this->GetActiveLayer() ? this : m_LayerContainer[groupID];
 }
 
 const mitk::Image* mitk::LabelSetImage::GetGroupImage(GroupIndexType groupID) const
 {
   if (!this->ExistGroup(groupID)) mitkThrow() << "Error, cannot return group image. Group ID is invalid. Invalid ID: " << groupID;
 
   return groupID == this->GetActiveLayer() ? this : m_LayerContainer[groupID].GetPointer();
 }
 
 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;
         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;
         AccessByItk_1(this, LayerContainerToImageProcessing, GetActiveLayer());
 
         AfterChangeLayerEvent.Send();
       }
     }
   }
   catch (itk::ExceptionObject &e)
   {
     mitkThrow() << e.GetDescription();
   }
   this->Modified();
 }
 
 void mitk::LabelSetImage::SetActiveLabel(LabelValueType label)
 {
   m_ActiveLabelValue = label;
 
   if (label != UNLABELED_VALUE)
   {
     auto groupID = this->GetGroupIndexOfLabel(label);
     if (groupID!=this->GetActiveLayer()) this->SetActiveLayer(groupID);
   }
   Modified();
 }
 
 void mitk::LabelSetImage::ClearBuffer()
 {
   try
   {
     ClearImageBuffer(this);
     this->Modified();
   }
   catch (itk::ExceptionObject &e)
   {
     mitkThrow() << e.GetDescription();
   }
 }
 
 bool mitk::LabelSetImage::ExistLabelSet(unsigned int layer) const
 {
   return layer < m_LayerContainer.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();
   }
   this->SetActiveLabel(pixelValue);
-  this->m_LabelModifiedMessage.Send(sourcePixelValue);
-  this->m_LabelModifiedMessage.Send(pixelValue);
-  this->m_LabelsChangedMessage.Send({ sourcePixelValue, pixelValue });
+  this->InvokeEvent(LabelModifiedEvent(sourcePixelValue));
+  this->InvokeEvent(LabelModifiedEvent(pixelValue));
+  this->InvokeEvent(LabelsChangedEvent({ sourcePixelValue, pixelValue }));
   Modified();
 }
 
 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]);
+      this->InvokeEvent(LabelModifiedEvent(vectorOfSourcePixelValues[idx]));
     }
   }
   catch (itk::ExceptionObject &e)
   {
     mitkThrow() << e.GetDescription();
   }
   this->SetActiveLabel(pixelValue);
-  this->m_LabelModifiedMessage.Send(pixelValue);
+  this->InvokeEvent(LabelModifiedEvent(pixelValue));
   auto modifiedValues = vectorOfSourcePixelValues;
   modifiedValues.push_back(pixelValue);
-  this->m_LabelsChangedMessage.Send(modifiedValues);
+  this->InvokeEvent(LabelsChangedEvent(modifiedValues));
 
   Modified();
 }
 
 void mitk::LabelSetImage::RemoveLabel(LabelValueType pixelValue)
 {
   if (m_LabelMap.find(pixelValue) == m_LabelMap.end()) return;
 
   auto groupID = this->GetGroupIndexOfLabel(pixelValue);
 
   //first erase the pixel content (also triggers a LabelModified event)
   this->EraseLabel(pixelValue);
   this->RemoveLabelFromMap(pixelValue);
 
 
   if (m_ActiveLabelValue == pixelValue)
   {
     this->SetActiveLabel(0);
   }
 
-  this->m_LabelRemovedMessage.Send(pixelValue);
-  this->m_LabelsChangedMessage.Send({ pixelValue });
-  this->m_GroupModifiedMessage.Send(groupID);
+  this->InvokeEvent(LabelRemovedEvent(pixelValue));
+  this->InvokeEvent(LabelsChangedEvent({ pixelValue }));
+  this->InvokeEvent(GroupModifiedEvent(groupID));
 }
 
 void mitk::LabelSetImage::RemoveLabelFromMap(LabelValueType pixelValue)
 {
   if (m_LabelMap.find(pixelValue) == m_LabelMap.end()) mitkThrow()<<"Invalid state of of instance. RemoveLabelFromMap was called for unkown label id. invalid label id: "<<pixelValue;
 
   auto groupID = this->GetGroupIndexOfLabel(pixelValue);
 
   this->ReleaseLabel(m_LabelMap[pixelValue]);
   //now remove the label entry itself
   m_LabelMap.erase(pixelValue);
   m_LabelToGroupMap.erase(pixelValue);
   auto labelsInGroup = m_GroupToLabelMap[groupID];
   labelsInGroup.erase(std::remove(labelsInGroup.begin(), labelsInGroup.end(), pixelValue), labelsInGroup.end());
   m_GroupToLabelMap[groupID] = labelsInGroup;
 }
 
 void mitk::LabelSetImage::RemoveLabels(const LabelValueVectorType& vectorOfLabelPixelValues)
 {
   for (const auto labelValue : vectorOfLabelPixelValues)
   {
     this->RemoveLabel(labelValue);
   }
-  this->m_LabelsChangedMessage.Send(vectorOfLabelPixelValues);
+  this->InvokeEvent(LabelsChangedEvent(vectorOfLabelPixelValues));
 }
 
 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(groupImage, EraseLabelProcessing, 4, pixelValue);
     }
     else
     {
       AccessByItk_1(groupImage, EraseLabelProcessing, pixelValue);
     }
   }
   catch (const itk::ExceptionObject& e)
   {
     mitkThrow() << e.GetDescription();
   }
 
-  this->m_LabelModifiedMessage.Send(pixelValue);
-  this->m_LabelsChangedMessage.Send({ pixelValue });
+  this->InvokeEvent(LabelModifiedEvent(pixelValue));
+  this->InvokeEvent(LabelsChangedEvent({ pixelValue }));
   Modified();
 }
 
 void mitk::LabelSetImage::EraseLabels(const std::vector<PixelType>& VectorOfLabelPixelValues)
 {
   for (unsigned int idx = 0; idx < VectorOfLabelPixelValues.size(); idx++)
   {
     this->EraseLabel(VectorOfLabelPixelValues[idx]);
   }
 }
 
 mitk::LabelSetImage::LabelValueType mitk::LabelSetImage::GetUnusedLabelValue() const
 {
   auto usedValues = this->GetUsedLabelValues();
   return usedValues.back() + 1;
 }
 
 mitk::Label* mitk::LabelSetImage::AddLabel(mitk::Label* label, GroupIndexType groupID, bool addAsClone, bool correctLabelValue)
 {
   unsigned int max_size = mitk::Label::MAX_LABEL_VALUE + 1;
   if (m_LayerContainer.size() >= max_size)
     return nullptr;
 
   mitk::Label::Pointer newLabel = addAsClone ? label->Clone() : Label::Pointer(label);
 
   auto pixelValue = newLabel->GetValue();
   auto usedValues = this->GetUsedLabelValues();
   auto finding = std::find(usedValues.begin(), usedValues.end(), pixelValue);
 
   if (!usedValues.empty() && usedValues.end() != finding)
   {
     if (correctLabelValue)
     {
       pixelValue = this->GetUnusedLabelValue();
       newLabel->SetValue(pixelValue);
     }
     else
     {
       mitkThrow() << "Cannot add label due to conflicting label value that already exists in the MultiLabelSegmentation. Conflicting label value: " << pixelValue;
     }
   }
 
   // add DICOM information of the label
   DICOMSegmentationPropertyHelper::SetDICOMSegmentProperties(newLabel);
 
   this->AddLabelToMap(pixelValue, newLabel, groupID);
   this->RegisterLabel(newLabel);
 
-  m_LabelAddedMessage.Send(newLabel->GetValue());
+  this->InvokeEvent(LabelAddedEvent(newLabel->GetValue()));
   m_ActiveLabelValue = newLabel->GetValue();
   this->Modified();
 
   return newLabel;
 }
 
 mitk::Label* mitk::LabelSetImage::AddLabel(const std::string& name, const mitk::Color& color, GroupIndexType groupID)
 {
   mitk::Label::Pointer newLabel = mitk::Label::New();
   newLabel->SetName(name);
   newLabel->SetColor(color);
   return AddLabel(newLabel,groupID,false);
 }
 
 void mitk::LabelSetImage::RenameLabel(PixelType pixelValue, const std::string& name, const mitk::Color& color)
 {
   mitk::Label* label = GetLabel(pixelValue);
   label->SetName(name);
   label->SetColor(color);
 
   this->UpdateLookupTable(pixelValue);
   // change DICOM information of the label
   DICOMSegmentationPropertyHelper::SetDICOMSegmentProperties(label);
 }
 
 mitk::Label *mitk::LabelSetImage::GetActiveLabel()
 {
   if (m_ActiveLabelValue == UNLABELED_VALUE) return nullptr;
 
   auto finding = m_LabelMap.find(m_ActiveLabelValue);
   return finding == m_LabelMap.end() ? nullptr : finding->second;
 }
 
 const mitk::Label* mitk::LabelSetImage::GetActiveLabel() const
 {
   if (m_ActiveLabelValue == UNLABELED_VALUE) return nullptr;
 
   auto finding = m_LabelMap.find(m_ActiveLabelValue);
   return finding == m_LabelMap.end() ? nullptr : finding->second;
 }
 
 void mitk::LabelSetImage::UpdateCenterOfMass(PixelType pixelValue)
 {
   if (4 == this->GetDimension())
   {
     AccessFixedDimensionByItk_1(this->GetGroupImage(this->GetGroupIndexOfLabel(pixelValue)), CalculateCenterOfMassProcessing, 4, pixelValue);
   }
   else
   {
     AccessByItk_1(this->GetGroupImage(this->GetGroupIndexOfLabel(pixelValue)), CalculateCenterOfMassProcessing, pixelValue);
   }
 }
 
 void mitk::LabelSetImage::SetLookupTable(mitk::LookupTable* lut)
 {
   m_LookupTable = lut;
   this->Modified();
 }
 
 void mitk::LabelSetImage::UpdateLookupTable(PixelType pixelValue)
 {
   const mitk::Color& color = this->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);
 }
 
 unsigned int mitk::LabelSetImage::GetNumberOfLabels(unsigned int layer) const
 {
   return m_GroupToLabelMap[layer].size();
 }
 
 unsigned int mitk::LabelSetImage::GetTotalNumberOfLabels() const
 {
   return m_LabelMap.size();
 }
 
 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)
 {
   if (!this->ExistLabel(index)) mitkThrow() << "Error, cannot return label mask. Label ID is invalid. Invalid ID: " << index;
 
   auto mask = mitk::Image::New();
 
   // 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());
 
   ClearImageBuffer(mask);
 
   const auto groupID = this->GetGroupIndexOfLabel(index);
 
   auto destinationLabel = this->GetLabel(index)->Clone();
   destinationLabel->SetValue(1);
 
   TransferLabelContent(this->GetGroupImage(groupID), mask.GetPointer(),
     {destinationLabel},
     LabelSetImage::UNLABELED_VALUE, LabelSetImage::UNLABELED_VALUE, false,
     { { index, destinationLabel->GetValue()}}, MultiLabelSegmentation::MergeStyle::Replace, MultiLabelSegmentation::OverwriteStyle::IgnoreLocks);
 
   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 (Exception e)
   {
     mitkReThrow(e) << "Could not intialize by provided labeled image.";
   }
   catch (...)
   {
     mitkThrow() << "Could not intialize by provided labeled image due to unkown error.";
   }
   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())
   {
     const auto originalSourceValue = sourceIter.Get();
     const auto sourceValue = static_cast<PixelType>(originalSourceValue);
 
     if (originalSourceValue > mitk::Label::MAX_LABEL_VALUE)
     {
       mitkThrow() << "Cannot initialize MultiLabelSegmentation by image. Image contains a pixel value that exceeds the label value range. Invalid pixel value:" << originalSourceValue;
     }
 
     targetIter.Set(sourceValue);
 
     if (LabelSetImage::UNLABELED_VALUE!=sourceValue && !this->ExistLabel(sourceValue))
     {
       if (this->GetTotalNumberOfLabels() >= mitk::Label::MAX_LABEL_VALUE)
       {
         mitkThrow() << "Cannot initialize MultiLabelSegmentation by image. Image contains to many labels.";
       }
 
       std::stringstream name;
       name << "object-" << sourceValue;
 
       double rgba[4];
       this->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->AddLabel(label,0,false);
     }
 
     ++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();
 
   const auto activeLabel = this->GetActiveLabel()->GetValue();
 
   while (!sourceIter.IsAtEnd())
   {
     PixelType sourceValue = sourceIter.Get();
     PixelType targetValue = targetIter.Get();
 
     if ((sourceValue != UNLABELED_VALUE) &&
         (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, LabelValueType pixelValue)
 {
   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];
 
   this->GetLabel(pixelValue)->SetCenterOfMassIndex(pos);
   this->GetSlicedGeometry()->IndexToWorld(pos, pos);
   this->GetLabel(pixelValue)->SetCenterOfMassCoordinates(pos);
 }
 
 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::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;
 
   if (!this->ExistGroup(groupID))
     mitkThrow() << "Cannot add label. Defined group is unkown. Invalid group index: " << groupID;
 
   m_LabelMap[labelValue] = label;
   m_LabelToGroupMap[labelValue] = groupID;
   auto groupFinding = std::find(m_GroupToLabelMap[groupID].begin(), m_GroupToLabelMap[groupID].end(), labelValue);
   if (groupFinding == m_GroupToLabelMap[groupID].end())
   {
     m_GroupToLabelMap[groupID].push_back(labelValue);
   }
 }
 
 void mitk::LabelSetImage::RegisterLabel(mitk::Label* label)
 {
   UpdateLookupTable(label->GetValue());
 
   auto command = itk::MemberCommand<LabelSetImage>::New();
   command->SetCallbackFunction(this, &LabelSetImage::OnLabelModified);
   label->AddObserver(itk::ModifiedEvent(), command);
 }
 
 void mitk::LabelSetImage::ReleaseLabel(Label* label)
 {
   if (nullptr == label) mitkThrow() << "Invalid call of ReleaseLabel with a nullptr.";
   label->RemoveAllObservers();
 }
 
 void mitk::LabelSetImage::ApplyToLabels(const LabelValueVectorType& values, std::function<void(Label*)>&& lambda)
 {
   auto labels = this->GetLabelsByValue(values);
   std::for_each(labels.begin(), labels.end(), lambda);
-  m_LabelsChangedMessage.Send(values);
+  this->InvokeEvent(LabelsChangedEvent(values));
 }
 
 void mitk::LabelSetImage::VisitLabels(const LabelValueVectorType& values, std::function<void(const Label*)>&& lambda) const
 {
   auto labels = this->GetConstLabelsByValue(values);
   std::for_each(labels.begin(), labels.end(), lambda);
 }
 
 
 void mitk::LabelSetImage::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.";
 
   Superclass::Modified();
-  this->m_LabelModifiedMessage.Send(label->GetValue());
+  this->InvokeEvent(LabelModifiedEvent(label->GetValue()));
 }
 
 bool mitk::LabelSetImage::ExistLabel(LabelValueType value) const
 {
   auto finding = m_LabelMap.find(value);
   return m_LabelMap.end() != finding;
 }
 
 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_LayerContainer.size();
 }
 
 bool mitk::LabelSetImage::IsLabelInGroup(LabelValueType value) const
 {
   GroupIndexType dummy;
   return this->IsLabelInGroup(value, dummy);
 }
 
 bool mitk::LabelSetImage::IsLabelInGroup(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 == UNLABELED_VALUE)
   {
     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::LabelVectorType mitk::LabelSetImage::GetLabelsByValue(const LabelValueVectorType& labelValues, bool ignoreMissing)
 {
   LabelVectorType result;
   for (const auto& labelValue : labelValues)
   {
     auto* label = this->GetLabel(labelValue);
 
     if (label != nullptr)
     {
       result.emplace_back(label);
     }
     else if (!ignoreMissing) mitkThrow() << "Error cannot get labels by Value. At least one passed value is unknown. Unknown value: " << labelValue;
   }
   return result;
 }
 
 const mitk::LabelSetImage::ConstLabelVectorType mitk::LabelSetImage::GetConstLabelsByValue(const LabelValueVectorType& labelValues, bool ignoreMissing) const
 {
   ConstLabelVectorType result;
   for (const auto& labelValue : labelValues)
   {
     const auto* label = this->GetLabel(labelValue);
 
     if (label != nullptr)
     {
       result.emplace_back(label);
     }
     else if (!ignoreMissing) mitkThrow() << "Error cannot get labels by Value. At least one passed value is unknown. Unknown value: " << labelValue;
   }
   return result;
 }
 
 const mitk::LabelSetImage::LabelValueVectorType mitk::LabelSetImage::GetLabelValuesByGroup(GroupIndexType index) const
 {
   if (!this->ExistGroup(index))
     mitkThrow() << "Cannot get labels of an invalid group. Invalid group index: " << index;
 
   return m_GroupToLabelMap[index];
 }
 
 const mitk::LabelSetImage::LabelValueVectorType mitk::LabelSetImage::GetLabelValuesByName(GroupIndexType index, std::string_view name) const
 {
   LabelValueVectorType result;
 
   auto searchName = [&result, name](const Label* l) { if(l->GetName() == name) result.push_back(l->GetValue()); };
 
   this->VisitLabels(this->GetLabelValuesByGroup(index), searchName);
 
   return result;
 }
 
 std::vector<std::string> mitk::LabelSetImage::GetLabelClassNames() const
 {
   std::set<std::string> names;
   auto searchName = [&names](const Label* l) { names.emplace(l->GetName()); };
   this->VisitLabels(this->GetAllLabelValues(), searchName);
 
   return std::vector<std::string>(names.begin(), names.end());
 }
 
 std::vector<std::string> mitk::LabelSetImage::GetLabelClassNamesByGroup(GroupIndexType index) const
 {
   std::set<std::string> names;
   auto searchName = [&names](const Label* l) { names.emplace(l->GetName()); };
   this->VisitLabels(this->GetLabelValuesByGroup(index), searchName);
 
   return std::vector<std::string>(names.begin(), names.end());
 }
 
 void mitk::LabelSetImage::SetAllLabelsVisible(bool visible)
 {
   auto setVisibility = [visible](Label* l) { l->SetVisible(visible); };
 
   this->ApplyToLabels(this->GetAllLabelValues(), setVisibility);
 }
 
 void mitk::LabelSetImage::SetAllLabelsVisibleByGroup(GroupIndexType group, bool visible)
 {
   auto setVisibility = [visible](Label* l) { l->SetVisible(visible); };
 
   this->ApplyToLabels(this->GetLabelValuesByGroup(group), setVisibility);
 }
 
 void mitk::LabelSetImage::SetAllLabelsVisibleByName(GroupIndexType group, std::string_view name, bool visible)
 {
   auto setVisibility = [visible](Label* l) { l->SetVisible(visible); };
 
   this->ApplyToLabels(this->GetLabelValuesByName(group, name), setVisibility);
 }
 
 void mitk::LabelSetImage::SetAllLabelsLocked(bool locked)
 {
   auto setLock = [locked](Label* l) { l->SetLocked(locked); };
 
   this->ApplyToLabels(this->GetAllLabelValues(), setLock);
 }
 
 void mitk::LabelSetImage::SetAllLabelsLockedByGroup(GroupIndexType group, bool locked)
 {
   auto setLock = [locked](Label* l) { l->SetLocked(locked); };
 
   this->ApplyToLabels(this->GetLabelValuesByGroup(group), setLock);
 }
 
 void mitk::LabelSetImage::SetAllLabelsLockedByName(GroupIndexType group, std::string_view name, bool locked)
 {
   auto setLock = [locked](Label* l) { l->SetLocked(locked); };
 
   this->ApplyToLabels(this->GetLabelValuesByName(group, name), setLock);
 }
 
 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 ---";
 
   // 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;
     ;
   }
 
   // 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;
     }
   }
 
   if (leftHandSide.GetTotalNumberOfLabels() != rightHandSide.GetTotalNumberOfLabels())
   {
     MITK_INFO(verbose) << "Number of labels are 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.GetGroupImage(layerIndex), *rightHandSide.GetGroupImage(layerIndex), eps, verbose);
       if (!returnValue)
       {
         MITK_INFO(verbose) << "Layer image data not equal.";
         return false;
       }
     }
 
     // label data
     auto leftLabelsInGroup = leftHandSide.GetLabelValuesByGroup(layerIndex);
     auto rightLabelsInGroup = rightHandSide.GetLabelValuesByGroup(layerIndex);
 
     if (leftLabelsInGroup.size()!=rightLabelsInGroup.size())
     {
       MITK_INFO(verbose) << "Number of layer labels is not equal. Invalid layer:" <<layerIndex;
       return false;
     }
 
     for (ConstLabelVector::size_type index = 0; index < leftLabelsInGroup.size(); ++index)
     {
       if (!mitk::Equal(*(leftHandSide.GetLabel(leftLabelsInGroup[index])), *(rightHandSide.GetLabel(rightLabelsInGroup[index])),eps,verbose))
       {
         MITK_INFO(verbose) << "At least one label in layer is not equal. Invalid layer:" << layerIndex;
         return false;
       }
     }
   }
 
   return returnValue;
 }
 
 bool mitk::Equal(const mitk::LabelSetImage::ConstLabelVectorType& leftHandSide,
   const mitk::LabelSetImage::ConstLabelVectorType& rightHandSide, ScalarType eps, bool verbose)
 {
   bool returnValue = true;
 
   // container size;
   returnValue = leftHandSide.size() == rightHandSide.size();
   if (!returnValue)
   {
     MITK_INFO(verbose) << "Number of labels not equal.";
     return returnValue;
     ;
   }
 
   // m_LabelContainer;
   auto lhsit = leftHandSide.begin();
   auto rhsit = rightHandSide.begin();
   for (; lhsit != leftHandSide.end(); ++lhsit, ++rhsit)
   {
     returnValue = mitk::Equal(**rhsit, **lhsit,eps,verbose);
     if (!returnValue)
     {
       MITK_INFO(verbose) << "Label in label container not equal.";
       return returnValue;
       ;
     }
   }
 
   return returnValue;
 }
 
 /**Helper function to convert a vector of labels into a label map
  * @pre every label in the vector has a unique value.*/
 using ConstLabelMapType = std::map<mitk::LabelSetImage::LabelValueType, mitk::Label::ConstPointer>;
 ConstLabelMapType ConvertLabelVectorToMap(const mitk::ConstLabelVector& labelV)
 {
   ConstLabelMapType result;
   for (auto label : labelV)
   {
     const auto value = label->GetValue();
     auto finding = result.find(value);
     if (finding != result.end()) mitkThrow() << "Operation failed. Cannot convert label vector into label map, because at least one label value is not unique. Violating label value: " << value;
     result.insert(std::make_pair(value, label));
   }
 
   return result;
 }
 
 
 /** 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 ConstLabelMapType& destinationLabels, 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_DestinationLabels(destinationLabels), 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_DestinationLabels == other.m_DestinationLabels;
   }
 
   LabelTransferFunctor& operator=(const LabelTransferFunctor& other)
   {
     this->m_DestinationLabels = other.m_DestinationLabels;
     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
       {
         if (existingDestinationValue == m_DestinationBackground)
         {
           if (!m_DestinationBackgroundLocked)
           {
             return this->m_NewDestinationLabel;
           }
         }
         else
         {
           auto labelFinding = this->m_DestinationLabels.find(existingDestinationValue);
           if (labelFinding==this->m_DestinationLabels.end() || !labelFinding->second->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:
   ConstLabelMapType m_DestinationLabels;
   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 TransferLabelContentAtTimeStep to allow the templating over different image dimensions in conjunction of AccessFixedPixelTypeByItk_n.*/
 template<unsigned int VImageDimension>
 void TransferLabelContentAtTimeStepHelper(const itk::Image<mitk::Label::PixelType, VImageDimension>* itkSourceImage, mitk::Image* destinationImage,
   const mitk::ConstLabelVector& destinationLabels, 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 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(ConvertLabelVectorToMap(destinationLabels), 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::TransferLabelContentAtTimeStep(
   const Image* sourceImage, Image* destinationImage, const mitk::ConstLabelVector& destinationLabels, const TimeStepType timeStep, mitk::Label::PixelType sourceBackground,
   mitk::Label::PixelType destinationBackground, bool destinationBackgroundLocked, LabelValueMappingVector labelMapping,
   MultiLabelSegmentation::MergeStyle mergeStyle, MultiLabelSegmentation::OverwriteStyle overwriteStlye)
 {
   if (nullptr == sourceImage)
   {
     mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; sourceImage must not be null.";
   }
   if (nullptr == destinationImage)
   {
     mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; destinationImage 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 TransferLabelContentAtTimeStep; sourceImage does not have the requested time step: " << timeStep;
   }
   if (nullptr == destinationImageAtTimeStep)
   {
     mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; destinationImage does not have the requested time step: " << timeStep;
   }
 
   auto destLabelMap = ConvertLabelVectorToMap(destinationLabels);
   for (const auto& [sourceLabel, newDestinationLabel] : labelMapping)
   {
     if (LabelSetImage::UNLABELED_VALUE!=newDestinationLabel && destLabelMap.end() == destLabelMap.find(newDestinationLabel))
     {
       mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep. Defined destination label does not exist in destinationImage. newDestinationLabel: " << newDestinationLabel;
     }
 
     AccessFixedPixelTypeByItk_n(sourceImageAtTimeStep, TransferLabelContentAtTimeStepHelper, (Label::PixelType), (destinationImageAtTimeStep, destinationLabels, sourceBackground, destinationBackground, destinationBackgroundLocked, sourceLabel, newDestinationLabel, mergeStyle, overwriteStlye));
   }
   destinationImage->Modified();
 }
 
 void mitk::TransferLabelContent(
   const Image* sourceImage, Image* destinationImage, const mitk::ConstLabelVector& destinationLabels, mitk::Label::PixelType sourceBackground,
   mitk::Label::PixelType destinationBackground, bool destinationBackgroundLocked, LabelValueMappingVector 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, destinationLabels, i, sourceBackground,
       destinationBackground, destinationBackgroundLocked, labelMapping, mergeStyle, overwriteStlye);
   }
 }
 
 void mitk::TransferLabelContentAtTimeStep(
   const LabelSetImage* sourceImage, LabelSetImage* destinationImage, const TimeStepType timeStep,
   LabelValueMappingVector labelMapping,
   MultiLabelSegmentation::MergeStyle mergeStyle, MultiLabelSegmentation::OverwriteStyle overwriteStlye)
 {
   if (nullptr == sourceImage)
   {
     mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; sourceImage must not be null.";
   }
 
   auto destinationLabels = destinationImage->GetConstLabelsByValue(destinationImage->GetLabelValuesByGroup(destinationImage->GetActiveLayer()));
 
   for (const auto& mappingElement : labelMapping)
   {
     if (LabelSetImage::UNLABELED_VALUE != mappingElement.first && !sourceImage->ExistLabel(mappingElement.first, sourceImage->GetActiveLayer()))
     {
       mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep. Defined source label does not exist in sourceImage. SourceLabel: " << mappingElement.first;
     }
   }
 
   TransferLabelContentAtTimeStep(sourceImage, destinationImage, destinationLabels, timeStep, LabelSetImage::UNLABELED_VALUE, LabelSetImage::UNLABELED_VALUE, destinationImage->GetUnlabeledLabelLock(),
     labelMapping, mergeStyle, overwriteStlye);
 }
 
 void mitk::TransferLabelContent(
   const LabelSetImage* sourceImage, LabelSetImage* destinationImage,
   LabelValueMappingVector 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 6a99f21aa5..17bedb725f 100644
--- a/Modules/Multilabel/mitkLabelSetImage.h
+++ b/Modules/Multilabel/mitkLabelSetImage.h
@@ -1,713 +1,621 @@
 /*============================================================================
 
 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 <mitkLabel.h>
-#include <mitkMessage.h>
 #include <mitkLookupTable.h>
+#include <mitkMultiLabelEvents.h>
+#include <mitkMessage.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
+  /** @brief LabelSetImage class for handling labels and layers in a segmentation session.
+  *
+  * Events that are potentially send by the class in regard to groups or labels:
+  * - LabelAddedEvent is emitted whenever a new label has been added.
+  * - LabelModifiedEvent is emitted whenever a label has been modified.
+  * - LabelRemovedEvent is emitted whenever a label has been removed.
+  * - LabelsChangedEvent 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).
+  * - GroupAddedEvent is emitted whenever a new group has been added.
+  * - GroupModifiedEvent is emitted whenever a group has been modified.
+  * - GroupRemovedEvent is emitted whenever a label has been removed.
+  *
+  * @ingroup Data
+  */
   class MITKMULTILABEL_EXPORT LabelSetImage : public Image
   {
   public:
-    mitkClassMacro(LabelSetImage, Image);
-    itkNewMacro(Self);
-
-    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.
     ///////////////////////////////////////////////////////////////////////////////////
     ///////////////////////////////////////////////////////////////////////////////////
+    mitkClassMacro(LabelSetImage, Image);
+    itkNewMacro(Self);
+
+    typedef mitk::Label::PixelType PixelType;
 
     using GroupIndexType = std::size_t;
     using LabelValueType = mitk::Label::PixelType;
     const static LabelValueType UNLABELED_VALUE = 0;
     using ConstLabelVectorType = ConstLabelVector;
     using LabelVectorType = LabelVector;
     using LabelValueVectorType = std::vector<LabelValueType>;
 
     /** \brief Adds a label instance to a group of the multi label image.
     * @remark By default, if the pixel value of the label is already used in the image, the label
     * will get a new none conflicting value assigned. This can be controlled by correctLabelValue.
     * @param label Instance of an label that should be added or used as template
     * @param groupID The id of the group the label should be added to.
     * @param addAsClone Flag that controls, if the passed instance should be added (false; the image will then take ownership,
     * be aware that e.g. event observers will be added)
     * a clone of the instance (true).
     * @param correctLabelValue Flag that controls, if the value of the passed label should be correct, if this value is already used in
     * the multi label image. True: Conflicting values will be corrected, be assigning a none conflicting value. False: If the value is conflicting
     * an exception will be thrown.
     * @return Instance of the label as it was added to the label set.
     * @pre label must point to a valid instance.
     * @pre If correctLabelValue==false, label value must be non conflicting.
     * @pre groupID must indicate an existing group.
     */
     mitk::Label* AddLabel(mitk::Label* label, GroupIndexType groupID, bool addAsClone = true, bool correctLabelValue = true);
 
     /** \brief Adds a new label to a group of the image by providing name and color.
     * @param name (Class) name of the label instance that should be added.
     * @param color Color of the new label sinstance.
     * @param groupID The id of the group the label should be added to.
     * @return Instance of the label as it was added to the label set.
     * @pre groupID must indicate an existing group.
     */
     mitk::Label* AddLabel(const std::string& name, const Color& color, GroupIndexType groupID);
 
     /** \brief
     */
     void RenameLabel(LabelValueType labelValue, const std::string&, const Color&);
 
     /**
      * @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 UNLABELED_VALUE.
      * @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);
 
     /** \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 IsLabelInGroup(LabelValueType value) const;
     bool IsLabelInGroup(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();
 
     const LabelVectorType GetLabelsByValue(const LabelValueVectorType& labelValues, bool ignoreMissing = true);
     const ConstLabelVectorType GetConstLabelsByValue(const LabelValueVectorType& labelValues, bool ignoreMissing = false) const;
 
     static LabelValueVectorType ExtractLabelValuesFromLabelVector(const ConstLabelVectorType& labels);
     static LabelValueVectorType ExtractLabelValuesFromLabelVector(const LabelVectorType& labels);
 
     static ConstLabelVectorType ConvertLabelVectorConst(const LabelVectorType& labels);
 
     const LabelValueVectorType GetAllLabelValues() const;
 
     /**
      * @brief Returns a vector of all label values 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 label values.
      * @pre group index must exist.
      */
     const LabelValueVectorType GetLabelValuesByGroup(GroupIndexType index) const;
 
     /**
      * @brief Returns a vector of all label values located on the specified group having a certain name.
      * @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.
      * @param name Name of the label instances one is looking for.
      * @return the respective vector of label values.
      * @pre group index must exist.
      */
     const LabelValueVectorType GetLabelValuesByName(GroupIndexType index, std::string_view name) const;
 
     std::vector<std::string> GetLabelClassNames() const;
     std::vector<std::string> GetLabelClassNamesByGroup(GroupIndexType index) const;
 
     /** Helper that returns an unused label value, that could be used e.g. if one wants to define a label externally
     before adding it.
      @return A label value currently not in use.
      @remark is no unused label value can be provided an exception will be thrown.*/
     LabelValueType GetUnusedLabelValue() const;
 
     itkGetConstMacro(UnlabeledLabelLock, bool);
     itkSetMacro(UnlabeledLabelLock, bool);
     itkBooleanMacro(UnlabeledLabelLock);
 
     /** \brief
     */
     void SetAllLabelsVisible(bool visible);
     void SetAllLabelsVisibleByGroup(GroupIndexType group, bool visible);
     void SetAllLabelsVisibleByName(GroupIndexType group, std::string_view name, bool visible);
 
     /** \brief
     */
     void SetAllLabelsLocked(bool locked);
     void SetAllLabelsLockedByGroup(GroupIndexType group, bool locked);
     void SetAllLabelsLockedByName(GroupIndexType group, std::string_view name, bool locked);
 
     /**
     * \brief Replaces the labels of a group with a given vector of labels.
     *
     * @remark The passed label instances will be cloned before added to ensure clear ownership
     * of the new labels.
     * @remark The pixel content of the old labels will not be removed.
     * \param groupID The index of the group that should have its labels replaced
     * \param newLabels The vector of new labels
     * @pre Group that shuold be replaced must exist.
     * Qpre new label values must not be used in other groups.
     */
     void ReplaceGroupLabels(const GroupIndexType groupID, const ConstLabelVectorType& newLabels);
     void ReplaceGroupLabels(const GroupIndexType groupID, const LabelVectorType& newLabels);
 
     /** Returns the pointer to the image that containes the labeling of the indicate group.
      *@pre groupID must reference an existing group.*/
     mitk::Image* GetGroupImage(GroupIndexType groupID);
     /** Returns the pointer to the image that containes the labeling of the indicate group.
      *@pre groupID must reference an existing group.*/
     const mitk::Image* GetGroupImage(GroupIndexType groupID) const;
 
-    ////////////////////////////////////////////////////////////////////
-    //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.
+    itkGetModifiableObjectMacro(LookupTable, mitk::LookupTable);
+    /** \brief
     */
-    mitkNewMessage1Macro(GroupRemoved, GroupIndexType);
+    void SetLookupTable(LookupTable* lut);
+    void UpdateLookupTable(PixelType pixelValue);
 
     protected:
 
       void OnLabelModified(const Object* sender, const itk::EventObject&);
 
       /** Helper to ensure that the maps are correctly populated for a new label instance.*/
       void AddLabelToMap(LabelValueType labelValue, Label* label, GroupIndexType groupID);
       void RemoveLabelFromMap(LabelValueType labelValue);
       /** Helper to ensure label events are correctly connected and lookup table is updated for a new label instance.*/
       void RegisterLabel(Label* label);
       /** Helper to ensure label events are unregistered.*/
       void ReleaseLabel(Label* label);
 
       void ApplyToLabels(const LabelValueVectorType& values, std::function<void(Label*)>&& lambda);
       void VisitLabels(const LabelValueVectorType& values, std::function<void(const Label*)>&& lambda) const;
 
       using LabelMapType = std::map<LabelValueType, Label::Pointer>;
       LabelMapType m_LabelMap;
 
       using GroupNameVectorType = std::vector<std::string>;
       GroupNameVectorType m_Groups;
 
       /**This type is internally used to track which label is currently
        * associated with which layer.*/
       using GroupToLabelMapType = std::vector<LabelValueVectorType>;
       GroupToLabelMapType m_GroupToLabelMap;
       using LabelToGroupMapType = std::map<LabelValueType, GroupIndexType>;
       LabelToGroupMapType m_LabelToGroupMap;
 
       LookupTable::Pointer m_LookupTable;
 
       LabelValueType m_ActiveLabelValue;
 
     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);
-
-      itkGetModifiableObjectMacro(LookupTable, mitk::LookupTable);
-      /** \brief
-      */
-      void SetLookupTable(LookupTable* lut);
-      void UpdateLookupTable(PixelType pixelValue);
-
 
     ///////////////////////////////////////////////////////////////////////////////////
     ///////////////////////////////////////////////////////////////////////////////////
     // END FUTURE MultiLabelSegmentation
     ///////////////////////////////////////////////////////////////////////////////////
     ///////////////////////////////////////////////////////////////////////////////////
+    /**
+      * \brief  */
+      void UpdateCenterOfMass(PixelType pixelValue);
 
     /**
      * @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, const std::vector<PixelType>& vectorOfSourcePixelValues, unsigned int layer = 0);
 
     /**
      * @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(const std::vector<PixelType> &VectorOfLabelPixelValues);
 
     /**
       * \brief  Returns true if the labelset exists*/
     //[[deprecated("Will be removed with T28524")]]
     DEPRECATED(bool ExistLabelSet(unsigned int layer) const);
 
     /**
      * @brief Gets the ID of the currently active layer
      * @return the ID of the active layer
      * @pre at least on group must exist.
      */
     unsigned int GetActiveLayer() const;
 
     /** \brief
 */
     Label* GetActiveLabel();
     /** \brief
     */
     const Label* GetActiveLabel() 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) 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;
 
     /**
       * \brief  */
     mitk::Image::Pointer CreateLabelMask(PixelType index);
 
     /**
      * @brief Initialize a new mitk::LabelSetImage by a 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
      * an excption will be raised.
      * @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);
     void SetActiveLabel(LabelValueType label);
 
     /**
       * \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
      */
     GroupIndexType AddLayer(ConstLabelVector labels = {});
 
     /**
     * \brief Adds a layer based on a provided mitk::Image.
     * \param layerImage is added to the vector of label images
     * \param labels labels that will be cloned and added to the new layer if provided
     * \return the layer ID of the new layer
     */
     GroupIndexType AddLayer(mitk::Image::Pointer layerImage, ConstLabelVector labels = {});
 
     /**
       * \brief  */
     mitk::Image *GetLayerImage(unsigned int layer);
     const mitk::Image *GetLayerImage(unsigned int layer) 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, LabelValueType index);
 
     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.
       returns a sorted list of all labels (including the value for Unlabeled pixels..*/
     LabelValueVectorType GetUsedLabelValues() const;
 
     std::vector<Image::Pointer> m_LayerContainer;
 
     int m_ActiveLayer;
     bool m_activeLayerInvalid;
   };
 
   /**
   * @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);
 
   /**
   * @brief Equal A function comparing two vectors of labels for beeing equal in data
   *
   * @ingroup MITKTestingAPI
   *
   * Following aspects are tested for equality:
   *  - Labels in vector
   *
   * @param rightHandSide An vector of labels to be compared
   * @param leftHandSide An vector of labels 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::LabelSetImage::ConstLabelVectorType& leftHandSide,
     const mitk::LabelSetImage::ConstLabelVectorType& 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 label 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.
     };
   }
 
   using LabelValueMappingVector = std::vector < std::pair<Label::PixelType, Label::PixelType> >;
 
   /**Helper function that transfers pixels of the specified source label from source image to the destination image by using
   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 TransferLabelContentAtTimeStep(const LabelSetImage* sourceImage, LabelSetImage* destinationImage,
     const TimeStepType timeStep, LabelValueMappingVector 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, LabelValueMappingVector 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 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 destinationImage Pointer to the image that should be used as destination for the transfer.
   @param destinationLabelVector Reference to the vector of labels (incl. 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 destinationLabelVector must be valid
   @pre sourceImage and destinationImage must contain the indicated timeStep
   @pre destinationLabelVector must contain all indicated destinationLabels for mapping.*/
   MITKMULTILABEL_EXPORT void TransferLabelContentAtTimeStep(const Image* sourceImage, Image* destinationImage, const mitk::ConstLabelVector& destinationLabelVector,
     const TimeStepType timeStep, mitk::Label::PixelType sourceBackground = LabelSetImage::UNLABELED_VALUE,
     mitk::Label::PixelType destinationBackground = LabelSetImage::UNLABELED_VALUE,
     bool destinationBackgroundLocked = false,
     LabelValueMappingVector 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::ConstLabelVector& destinationLabelVector,
     mitk::Label::PixelType sourceBackground = LabelSetImage::UNLABELED_VALUE,
     mitk::Label::PixelType destinationBackground = LabelSetImage::UNLABELED_VALUE,
     bool destinationBackgroundLocked = false,
     LabelValueMappingVector labelMapping = { {1,1} },
     MultiLabelSegmentation::MergeStyle mergeStyle = MultiLabelSegmentation::MergeStyle::Replace,
     MultiLabelSegmentation::OverwriteStyle overwriteStlye = MultiLabelSegmentation::OverwriteStyle::RegardLocks);
 
 } // namespace mitk
 
 #endif
diff --git a/Modules/Multilabel/mitkMultiLabelEvents.h b/Modules/Multilabel/mitkMultiLabelEvents.h
index f131ba4d5b..1ad2e95b15 100644
--- a/Modules/Multilabel/mitkMultiLabelEvents.h
+++ b/Modules/Multilabel/mitkMultiLabelEvents.h
@@ -1,139 +1,197 @@
 /*============================================================================
 
 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 mitkMultiLabelEvents_h
 #define mitkMultiLabelEvents_h
 
 #include <itkEventObject.h>
 #include <mitkLabel.h>
 
 #include <MitkMultilabelExports.h>
 
 namespace mitk
 {
 #define mitkMultiLabelEventMacroDeclaration(classname, super, IDType)         \
   /** \class classname */                                  \
   class MITKMULTILABEL_EXPORT classname : public super     \
   {                                                        \
   public:                                                  \
     using Self = classname;                                \
     using Superclass = super;                              \
     classname() = default;                                 \
     classname(IDType value);                               \
     classname(const Self & s);                             \
     virtual ~classname() override;                         \
     virtual const char *                                   \
     GetEventName() const override;                         \
     virtual bool                                           \
     CheckEvent(const itk::EventObject * e) const override; \
     virtual itk::EventObject *                             \
     MakeObject() const override;                           \
                                                            \
   private:                                                 \
     void                                                   \
     operator=(const Self &);                               \
   };                                                       \
   static_assert(true, "Compile time eliminated. Used to require a semi-colon at end of macro.")
 
 #define mitkMultiLabelEventMacroDefinition(classname, super, IDType)         \
   classname::classname(const classname & s)                                  \
     : super(s){};                                                            \
   classname::classname(IDType value): super(value) {}                        \
   classname::~classname() {}                                                 \
   const char * classname::GetEventName() const { return #classname; }        \
   bool         classname::CheckEvent(const itk::EventObject * e) const       \
   {                                                                          \
     if (!super::CheckEvent(e)) return false;                                 \
     return (dynamic_cast<const classname *>(e) != nullptr);                  \
   }                                                                          \
   itk::EventObject * classname::MakeObject() const { return new classname; } \
   static_assert(true, "Compile time eliminated. Used to require a semi-colon at end of macro.")
 
-  /**
+  /** Base event class for all events that are about a label in a MultiLabel class.
+  *
+  * It has a member that indicates the label id the event is refering to.
+  * Use the ANY_LABEL value if you want to define an rvent (e.g. for adding an observer)
+  * that reacts to every label and not just to a special one.
   */
   class MITKMULTILABEL_EXPORT AnyLabelEvent : public itk::ModifiedEvent
   {
   public:
     using Self = AnyLabelEvent;
     using Superclass = itk::ModifiedEvent;
     const static mitk::Label::PixelType ANY_LABEL = std::numeric_limits<mitk::Label::PixelType>::max();
 
     AnyLabelEvent() = default;
     AnyLabelEvent(Label::PixelType labelValue);
     AnyLabelEvent(const Self & s);
     ~AnyLabelEvent() override;
     const char * GetEventName() const override;
     bool CheckEvent(const itk::EventObject * e) const override;
     itk::EventObject * MakeObject() const override;
 
     void SetLabelValue(Label::PixelType labelValue);
     Label::PixelType GetLabelValue() const;
   private:
     void operator=(const Self &);
     Label::PixelType m_LabelValue = std::numeric_limits<mitk::Label::PixelType>::max();
   };
 
+  /** Event class that is used to indicated if a label is added in a MultiLabel class.
+  *
+  * It has a member that indicates the label id the event is refering to.
+  * Use the ANY_LABEL value if you want to define an rvent (e.g. for adding an observer)
+  * that reacts to every label and not just to a special one.
+  */
   mitkMultiLabelEventMacroDeclaration(LabelAddedEvent, AnyLabelEvent, Label::PixelType);
+
+  /** Event class that is used to indicated if a label is modified in a MultiLabel class.
+  *
+  * It has a member that indicates the label id the event is refering to.
+  * Use the ANY_LABEL value if you want to define an rvent (e.g. for adding an observer)
+  * that reacts to every label and not just to a special one.
+  */
   mitkMultiLabelEventMacroDeclaration(LabelModifiedEvent, AnyLabelEvent, Label::PixelType);
+
+  /** Event class that is used to indicated if a label is removed in a MultiLabel class.
+  *
+  * It has a member that indicates the label id the event is refering to.
+  * Use the ANY_LABEL value if you want to define an rvent (e.g. for adding an observer)
+  * that reacts to every label and not just to a special one.
+  */
   mitkMultiLabelEventMacroDeclaration(LabelRemovedEvent, AnyLabelEvent, Label::PixelType);
 
+  /** Event class that is used to indicated if a set of labels is changed in a MultiLabel class.
+  *
+  * In difference to the other label events LabelsChangedEvent 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).
+  * It has a member that indicates the label ids the event is refering to.
+  */
   class MITKMULTILABEL_EXPORT LabelsChangedEvent : public itk::ModifiedEvent
   {
   public:
     using Self = LabelsChangedEvent;
     using Superclass = itk::ModifiedEvent;
 
     LabelsChangedEvent() = default;
     LabelsChangedEvent(std::vector<Label::PixelType> labelValues);
     LabelsChangedEvent(const Self& s);
     ~LabelsChangedEvent() override;
     const char* GetEventName() const override;
     bool CheckEvent(const itk::EventObject* e) const override;
     itk::EventObject* MakeObject() const override;
 
     void SetLabelValues(std::vector<Label::PixelType> labelValues);
     std::vector<Label::PixelType> GetLabelValues() const;
   private:
     void operator=(const Self&);
     std::vector<Label::PixelType> m_LabelValues;
   };
 
+  /** Base event class for all events that are about a group in a MultiLabel class.
+  *
+  * It has a member that indicates the group id the event is refering to.
+  * Use the ANY_GROUP value if you want to define an event (e.g. for adding an observer)
+  * that reacts to every group and not just to a special one.
+  */
   class MITKMULTILABEL_EXPORT AnyGroupEvent : public itk::ModifiedEvent
   {
   public:
     using GroupIndexType = std::size_t;
     using Self = AnyGroupEvent;
     using Superclass = itk::ModifiedEvent;
     const static GroupIndexType ANY_GROUP = std::numeric_limits<GroupIndexType>::max();
 
     AnyGroupEvent() = default;
     AnyGroupEvent(GroupIndexType groupID);
     AnyGroupEvent(const Self& s);
     ~AnyGroupEvent() override;
     const char* GetEventName() const override;
     bool CheckEvent(const itk::EventObject* e) const override;
     itk::EventObject* MakeObject() const override;
 
     void SetGroupID(GroupIndexType groupID);
     GroupIndexType GetGroupID() const;
   private:
     void operator=(const Self&);
     GroupIndexType m_GroupID = std::numeric_limits<GroupIndexType>::max();
   };
 
+  /** Event class that is used to indicated if a group is added in a MultiLabel class.
+  *
+  * It has a member that indicates the group id the event is refering to.
+  * Use the ANY_GROUP value if you want to define an rvent (e.g. for adding an observer)
+  * that reacts to every group and not just to a special one.
+  */
   mitkMultiLabelEventMacroDeclaration(GroupAddedEvent, AnyGroupEvent, AnyGroupEvent::GroupIndexType);
+
+  /** Event class that is used to indicated if a group is modified in a MultiLabel class.
+  *
+  * It has a member that indicates the group id the event is refering to.
+  * Use the ANY_GROUP value if you want to define an rvent (e.g. for adding an observer)
+  * that reacts to every group and not just to a special one.
+  */
   mitkMultiLabelEventMacroDeclaration(GroupModifiedEvent, AnyGroupEvent, AnyGroupEvent::GroupIndexType);
+
+  /** Event class that is used to indicated if a group is removed in a MultiLabel class.
+  *
+  * It has a member that indicates the group id the event is refering to.
+  * Use the ANY_GROUP value if you want to define an rvent (e.g. for adding an observer)
+  * that reacts to every group and not just to a special one.
+  */
   mitkMultiLabelEventMacroDeclaration(GroupRemovedEvent, AnyGroupEvent, AnyGroupEvent::GroupIndexType);
 
 }
 
 #endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManager.cpp b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManager.cpp
index 2030759c82..45efcfa8ea 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManager.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManager.cpp
@@ -1,525 +1,536 @@
 /*============================================================================
 
 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>
 #include <QmitkMultiLabelPresetHelper.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->labelSearchBox, SIGNAL(returnPressed()), this, SLOT(OnSearchLabel()));
 
   QStringListModel *completeModel = static_cast<QStringListModel *>(m_Completer->model());
   completeModel->setStringList(GetLabelStringList());
 
   // See T29549
   m_Controls->labelSearchBox->hide();
 
   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")));
   m_Controls->btnAddLabel->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_label_add.svg")));
   m_Controls->btnAddInstance->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_label_add_instance.svg")));
   m_Controls->btnAddGroup->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_group_add.svg")));
   m_Controls->btnRemoveLabel->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_label_delete.svg")));
   m_Controls->btnRemoveInstance->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_label_delete_instance.svg")));
   m_Controls->btnRemoveGroup->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_group_delete.svg")));
 
   connect(m_Controls->btnAddLabel, &QToolButton::clicked, this->m_Controls->labelInspector, &QmitkMultiLabelInspector::AddNewLabel);
   connect(m_Controls->btnRemoveLabel, &QToolButton::clicked, this->m_Controls->labelInspector, &QmitkMultiLabelInspector::DeleteLabel);
   connect(m_Controls->btnAddInstance, &QToolButton::clicked, this->m_Controls->labelInspector, &QmitkMultiLabelInspector::AddNewLabelInstance);
   connect(m_Controls->btnRemoveInstance, &QToolButton::clicked, this->m_Controls->labelInspector, &QmitkMultiLabelInspector::DeleteLabelInstance);
   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->btnLoadPreset, &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::OnSelectedLabelChanged);
   connect(this->m_Controls->labelInspector, &QmitkMultiLabelInspector::ModelUpdated, this, &QmitkMultiLabelManager::OnModelUpdated);
 
   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()
 {
   this->SetMultiLabelSegmentation(nullptr);
   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)
 {
   this->UpdateControls();
   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())
   {
     this->RemoveSegmentationObserver();
     m_Segmentation = segmentation;
     this->AddSegmentationObserver();
     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();
   bool hasMultipleInstances = this->m_Controls->labelInspector->GetLabelInstancesOfSelectedFirstLabel().size() > 1;
   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->btnRemoveInstance->setEnabled(hasWorkingData && !labels.empty() && hasMultipleInstances);
   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()
 {
   QmitkSaveMultiLabelPreset(m_Segmentation);
 }
 
 void QmitkMultiLabelManager::OnLoadPreset()
 {
   QmitkLoadMultiLabelPreset({ m_Segmentation });
 }
 
 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();
 }
 
 void QmitkMultiLabelManager::AddSegmentationObserver()
 {
   if (this->m_Segmentation.IsNotNull())
   {
-    this->m_Segmentation->AddLabelAddedListener(mitk::MessageDelegate1<QmitkMultiLabelManager, mitk::LabelSetImage::LabelValueType>(
-      this, &QmitkMultiLabelManager::OnLabelEvent));
-    this->m_Segmentation->AddLabelModifiedListener(mitk::MessageDelegate1<QmitkMultiLabelManager, mitk::LabelSetImage::LabelValueType>(
-      this, &QmitkMultiLabelManager::OnLabelEvent));
-    this->m_Segmentation->AddLabelRemovedListener(mitk::MessageDelegate1<QmitkMultiLabelManager, mitk::LabelSetImage::LabelValueType>(
-      this, &QmitkMultiLabelManager::OnLabelEvent));
-    this->m_Segmentation->AddGroupAddedListener(mitk::MessageDelegate1<QmitkMultiLabelManager, mitk::LabelSetImage::GroupIndexType>(
-      this, &QmitkMultiLabelManager::OnGroupEvent));
-    this->m_Segmentation->AddGroupModifiedListener(mitk::MessageDelegate1<QmitkMultiLabelManager, mitk::LabelSetImage::GroupIndexType>(
-      this, &QmitkMultiLabelManager::OnGroupEvent));
-    this->m_Segmentation->AddGroupRemovedListener(mitk::MessageDelegate1<QmitkMultiLabelManager, mitk::LabelSetImage::GroupIndexType>(
-      this, &QmitkMultiLabelManager::OnGroupEvent));
+    auto& widget = *this;
+    m_LabelAddedObserver.Reset(m_Segmentation, mitk::LabelAddedEvent(), [&widget](const itk::EventObject& event)
+      {
+        auto labelEvent = dynamic_cast<const mitk::AnyLabelEvent*>(&event);
+        widget.OnLabelEvent(labelEvent->GetLabelValue());
+      });
+    m_LabelModifiedObserver.Reset(m_Segmentation, mitk::LabelModifiedEvent(), [&widget](const itk::EventObject& event)
+      {
+        auto labelEvent = dynamic_cast<const mitk::AnyLabelEvent*>(&event);
+        widget.OnLabelEvent(labelEvent->GetLabelValue());
+      });
+    m_LabelRemovedObserver.Reset(m_Segmentation, mitk::LabelRemovedEvent(), [&widget](const itk::EventObject& event)
+      {
+        auto labelEvent = dynamic_cast<const mitk::AnyLabelEvent*>(&event);
+        widget.OnLabelEvent(labelEvent->GetLabelValue());
+      });
+
+    m_GroupAddedObserver.Reset(m_Segmentation, mitk::GroupAddedEvent(), [&widget](const itk::EventObject& event)
+      {
+        auto groupEvent = dynamic_cast<const mitk::AnyGroupEvent*>(&event);
+        widget.OnGroupEvent(groupEvent->GetGroupID());
+      });
+    m_GroupModifiedObserver.Reset(m_Segmentation, mitk::GroupModifiedEvent(), [&widget](const itk::EventObject& event)
+      {
+        auto groupEvent = dynamic_cast<const mitk::AnyGroupEvent*>(&event);
+        widget.OnGroupEvent(groupEvent->GetGroupID());
+      });
+    m_GroupRemovedObserver.Reset(m_Segmentation, mitk::GroupRemovedEvent(), [&widget](const itk::EventObject& event)
+      {
+        auto groupEvent = dynamic_cast<const mitk::AnyGroupEvent*>(&event);
+        widget.OnGroupEvent(groupEvent->GetGroupID());
+      });
   }
 }
 
 void QmitkMultiLabelManager::RemoveSegmentationObserver()
 {
-  if (this->m_Segmentation.IsNotNull())
-  {
-    this->m_Segmentation->RemoveLabelAddedListener(mitk::MessageDelegate1<QmitkMultiLabelManager, mitk::LabelSetImage::LabelValueType>(
-      this, &QmitkMultiLabelManager::OnLabelEvent));
-    this->m_Segmentation->RemoveLabelModifiedListener(mitk::MessageDelegate1<QmitkMultiLabelManager, mitk::LabelSetImage::LabelValueType>(
-      this, &QmitkMultiLabelManager::OnLabelEvent));
-    this->m_Segmentation->RemoveLabelRemovedListener(mitk::MessageDelegate1<QmitkMultiLabelManager, mitk::LabelSetImage::LabelValueType>(
-      this, &QmitkMultiLabelManager::OnLabelEvent));
-    this->m_Segmentation->RemoveGroupAddedListener(mitk::MessageDelegate1<QmitkMultiLabelManager, mitk::LabelSetImage::GroupIndexType>(
-      this, &QmitkMultiLabelManager::OnGroupEvent));
-    this->m_Segmentation->RemoveGroupModifiedListener(mitk::MessageDelegate1<QmitkMultiLabelManager, mitk::LabelSetImage::GroupIndexType>(
-      this, &QmitkMultiLabelManager::OnGroupEvent));
-    this->m_Segmentation->RemoveGroupRemovedListener(mitk::MessageDelegate1<QmitkMultiLabelManager, mitk::LabelSetImage::GroupIndexType>(
-      this, &QmitkMultiLabelManager::OnGroupEvent));
-  }
+  m_LabelAddedObserver.Reset();
+  m_LabelModifiedObserver.Reset();
+  m_LabelRemovedObserver.Reset();
+  m_GroupAddedObserver.Reset();
+  m_GroupModifiedObserver.Reset();
+  m_GroupRemovedObserver.Reset();
 }
 
 void QmitkMultiLabelManager::OnLabelEvent(mitk::LabelSetImage::LabelValueType /*labelValue*/)
 {
   if (!m_Controls->labelInspector->GetModelManipulationOngoing())
     this->UpdateControls();
 }
 
 void QmitkMultiLabelManager::OnGroupEvent(mitk::LabelSetImage::GroupIndexType /*groupIndex*/)
 {
   if (!m_Controls->labelInspector->GetModelManipulationOngoing())
     this->UpdateControls();
 }
 
 void QmitkMultiLabelManager::OnModelUpdated()
 {
   this->UpdateControls();
 }
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManager.h b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManager.h
index 685ce703d1..4eae48e9f2 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManager.h
+++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManager.h
@@ -1,176 +1,183 @@
 /*============================================================================
 
 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 <mitkITKEventObserverGuard.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;
   void OnModelUpdated();
 
 private:
   enum TableColumns
   {
     NAME_COL = 0,
     LOCKED_COL,
     COLOR_COL,
     VISIBLE_COL
   };
 
   void WaitCursorOn();
 
   void WaitCursorOff();
 
   void RestoreOverrideCursor();
 
   void OnThreadedCalculationDone();
 
   void AddSegmentationObserver();
   void RemoveSegmentationObserver();
 
   void OnLabelEvent(mitk::LabelSetImage::LabelValueType labelValue);
   void OnGroupEvent(mitk::LabelSetImage::GroupIndexType groupIndex);
 
   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;
 
+  mitk::ITKEventObserverGuard m_LabelAddedObserver;
+  mitk::ITKEventObserverGuard m_LabelModifiedObserver;
+  mitk::ITKEventObserverGuard m_LabelRemovedObserver;
+  mitk::ITKEventObserverGuard m_GroupAddedObserver;
+  mitk::ITKEventObserverGuard m_GroupModifiedObserver;
+  mitk::ITKEventObserverGuard m_GroupRemovedObserver;
 };
 
 #endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.cpp b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.cpp
index 9783a7dfbd..1868379275 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.cpp
@@ -1,1021 +1,1032 @@
 /*============================================================================
 
 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 <mitkMultiLabelEvents.h>
+#include <mitkRenderingManager.h>
 
-#include "QmitkStyleManager.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 %1").arg(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(" (%1 instances)").arg(item->m_childItems.size());
 
           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(" [%1]").arg(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 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->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::UNLABELED_VALUE) return QModelIndex();
   auto relevantItem = GetInstanceItem(labelValue, this->m_RootItem.get());
 
   if (nullptr == relevantItem)
     return 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)
   {
     const auto* sibling = searchItem;
 
     while (sibling != nullptr)
     {
       sibling = sibling->NextSibblingItem();
       resultItem = GetFirstInstanceLikeItem(sibling);
 
       if (nullptr != resultItem)
         break;
     }
 
     if (nullptr != resultItem)
       break;
 
     // No next closest label instance on this level -> check for closest before
     sibling = searchItem;
 
     while (sibling != nullptr)
     {
       sibling = sibling->PrevSibblingItem();
       resultItem = GetFirstInstanceLikeItem(sibling);
 
       if (nullptr != resultItem)
         break;
     }
 
     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();
 }
 
 std::vector <QmitkMultiLabelTreeModel::LabelValueType> QmitkMultiLabelTreeModel::GetLabelInstancesOfSameLabelClass(const QModelIndex& currentIndex) const
 {
   const QmitkMultiLabelSegTreeItem* currentItem = nullptr;
 
   if (currentIndex.isValid())
   {
     currentItem = static_cast<const QmitkMultiLabelSegTreeItem*>(currentIndex.internalPointer());
   }
 
   if (!currentItem)
     return {};
 
   if (QmitkMultiLabelSegTreeItem::ItemType::Group == currentItem->m_ItemType)
     return {};
 
   if (QmitkMultiLabelSegTreeItem::ItemType::Instance == currentItem->m_ItemType)
     currentItem = currentItem->ParentItem();
 
   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 labels = m_Segmentation->GetLabelsByValue(m_Segmentation->GetLabelValuesByGroup(groupID));
 
   for (auto& label : labels)
   {
     if (label->GetValue()== mitk::LabelSetImage::UNLABELED_VALUE) continue;
 
     bool newItemCreated = false;
     AddLabelToGroupTree(label, 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()
+void QmitkMultiLabelTreeModel::ITKEventHandler(const itk::EventObject& e)
 {
-  if (this->m_Segmentation.IsNotNull())
+  if (mitk::LabelAddedEvent().CheckEvent(&e))
   {
-    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;
+    auto labelEvent = dynamic_cast<const mitk::AnyLabelEvent*>(&e);
+    this->OnLabelAdded(labelEvent->GetLabelValue());
+  }
+  else if (mitk::LabelModifiedEvent().CheckEvent(&e))
+  {
+    auto labelEvent = dynamic_cast<const mitk::AnyLabelEvent*>(&e);
+    this->OnLabelModified(labelEvent->GetLabelValue());
+  }
+  else if (mitk::LabelRemovedEvent().CheckEvent(&e))
+  {
+    auto labelEvent = dynamic_cast<const mitk::AnyLabelEvent*>(&e);
+    this->OnLabelRemoved(labelEvent->GetLabelValue());
+  }
+  else if (mitk::GroupAddedEvent().CheckEvent(&e))
+  {
+    auto labelEvent = dynamic_cast<const mitk::AnyGroupEvent*>(&e);
+    this->OnGroupAdded(labelEvent->GetGroupID());
+  }
+  else if (mitk::GroupModifiedEvent().CheckEvent(&e))
+  {
+    auto labelEvent = dynamic_cast<const mitk::AnyGroupEvent*>(&e);
+    this->OnGroupModified(labelEvent->GetGroupID());
+  }
+  else if (mitk::GroupRemovedEvent().CheckEvent(&e))
+  {
+    auto labelEvent = dynamic_cast<const mitk::AnyGroupEvent*>(&e);
+    this->OnGroupRemoved(labelEvent->GetGroupID());
   }
 }
 
-void QmitkMultiLabelTreeModel::RemoveObserver()
+void QmitkMultiLabelTreeModel::AddObserver()
 {
+  m_LabelAddedObserver.Reset();
+  m_LabelModifiedObserver.Reset();
+  m_LabelRemovedObserver.Reset();
+  m_GroupAddedObserver.Reset();
+  m_GroupModifiedObserver.Reset();
+  m_GroupRemovedObserver.Reset();
+
   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;
+    auto& model = *this;
+
+    m_LabelAddedObserver.Reset(m_Segmentation, mitk::LabelAddedEvent(), [&model](const itk::EventObject& event){model.ITKEventHandler(event);});
+    m_LabelModifiedObserver.Reset(m_Segmentation, mitk::LabelModifiedEvent(), [&model](const itk::EventObject& event) {model.ITKEventHandler(event); });
+    m_LabelRemovedObserver.Reset(m_Segmentation, mitk::LabelRemovedEvent(), [&model](const itk::EventObject& event) {model.ITKEventHandler(event); });
+    m_GroupAddedObserver.Reset(m_Segmentation, mitk::GroupAddedEvent(), [&model](const itk::EventObject& event) {
+      model.ITKEventHandler(event); });
+    m_GroupModifiedObserver.Reset(m_Segmentation, mitk::GroupModifiedEvent(), [&model](const itk::EventObject& event) {model.ITKEventHandler(event); });
+    m_GroupRemovedObserver.Reset(m_Segmentation, mitk::GroupRemovedEvent(), [&model](const itk::EventObject& event) {model.ITKEventHandler(event); });
+  }
 }
 
 void QmitkMultiLabelTreeModel::OnLabelAdded(LabelValueType labelValue)
 {
   GroupIndexType groupIndex = 0;
   if (m_Segmentation->IsLabelInGroup(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::UNLABELED_VALUE) 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);
         this->beginInsertRows(groupIndex, instanceItem->ParentItem()->Row(), instanceItem->ParentItem()->Row());
         this->endInsertRows();
       }
       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::UNLABELED_VALUE) 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::UNLABELED_VALUE) 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
index 750a62630d..a8b4d671d5 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.h
+++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.h
@@ -1,166 +1,168 @@
 /*============================================================================
 
 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"
+#include <mitkITKEventObserverGuard.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;
 
   /** Returns a vector containing all label values of all label instances that belong to the same label
    * class like the passed index.
    *
    * If index points to a group or invalid, nothing will be returned.
    * @pre currentIndex must be valid and point to a label (class or instance).
    */
   std::vector <LabelValueType> GetLabelInstancesOfSameLabelClass(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 ITKEventHandler(const itk::EventObject& e);
+
   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;
+
+  mitk::ITKEventObserverGuard m_LabelAddedObserver;
+  mitk::ITKEventObserverGuard m_LabelModifiedObserver;
+  mitk::ITKEventObserverGuard m_LabelRemovedObserver;
+  mitk::ITKEventObserverGuard m_GroupAddedObserver;
+  mitk::ITKEventObserverGuard m_GroupModifiedObserver;
+  mitk::ITKEventObserverGuard m_GroupRemovedObserver;
 };
 
 #endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitkSimpleLabelSetListWidget.cpp b/Modules/SegmentationUI/Qmitk/QmitkSimpleLabelSetListWidget.cpp
index 2a72f0e1bb..bd18ca218b 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkSimpleLabelSetListWidget.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitkSimpleLabelSetListWidget.cpp
@@ -1,167 +1,164 @@
 /*============================================================================
 
 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->AfterChangeLayerEvent -= mitk::MessageDelegate<QmitkSimpleLabelSetListWidget>(
       this, &QmitkSimpleLabelSetListWidget::OnLayerChanged);
-    m_LabelSetImage->RemoveLabelAddedListener(mitk::MessageDelegate1<QmitkSimpleLabelSetListWidget, mitk::LabelSetImage::LabelValueType>(
-      this, &QmitkSimpleLabelSetListWidget::OnLabelChanged));
-    m_LabelSetImage->RemoveLabelModifiedListener(mitk::MessageDelegate1<QmitkSimpleLabelSetListWidget, mitk::LabelSetImage::LabelValueType>(
-      this, &QmitkSimpleLabelSetListWidget::OnLabelChanged));
-    m_LabelSetImage->RemoveLabelRemovedListener(mitk::MessageDelegate1<QmitkSimpleLabelSetListWidget, mitk::LabelSetImage::LabelValueType>(
-      this, &QmitkSimpleLabelSetListWidget::OnLabelChanged));
   }
 }
 
 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();
     result.push_back(m_LabelSetImage->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->AfterChangeLayerEvent -= mitk::MessageDelegate<QmitkSimpleLabelSetListWidget>(
-        this, &QmitkSimpleLabelSetListWidget::OnLayerChanged);
-      m_LabelSetImage->RemoveLabelAddedListener(mitk::MessageDelegate1<QmitkSimpleLabelSetListWidget, mitk::LabelSetImage::LabelValueType>(
-        this, &QmitkSimpleLabelSetListWidget::OnLabelChanged));
-      m_LabelSetImage->RemoveLabelModifiedListener(mitk::MessageDelegate1<QmitkSimpleLabelSetListWidget, mitk::LabelSetImage::LabelValueType>(
-        this, &QmitkSimpleLabelSetListWidget::OnLabelChanged));
-      m_LabelSetImage->RemoveLabelRemovedListener(mitk::MessageDelegate1<QmitkSimpleLabelSetListWidget, mitk::LabelSetImage::LabelValueType>(
-        this, &QmitkSimpleLabelSetListWidget::OnLabelChanged));
-    }
+    m_LabelAddedObserver.Reset();
+    m_LabelModifiedObserver.Reset();
+    m_LabelRemovedObserver.Reset();
 
     m_LabelSetImage = image;
 
     if (m_LabelSetImage.IsNotNull())
     {
+      auto& widget = *this;
+      m_LabelAddedObserver.Reset(m_LabelSetImage, mitk::LabelAddedEvent(), [&widget](const itk::EventObject& event)
+        {
+          auto labelEvent = dynamic_cast<const mitk::AnyLabelEvent*>(&event);
+          widget.OnLabelChanged(labelEvent->GetLabelValue());
+        });
+      m_LabelModifiedObserver.Reset(m_LabelSetImage, mitk::LabelModifiedEvent(), [&widget](const itk::EventObject& event)
+        {
+          auto labelEvent = dynamic_cast<const mitk::AnyLabelEvent*>(&event);
+          widget.OnLabelChanged(labelEvent->GetLabelValue());
+        });
+      m_LabelRemovedObserver.Reset(m_LabelSetImage, mitk::LabelRemovedEvent(), [&widget](const itk::EventObject& event)
+        {
+          auto labelEvent = dynamic_cast<const mitk::AnyLabelEvent*>(&event);
+          widget.OnLabelChanged(labelEvent->GetLabelValue());
+        });
+
       m_LabelSetImage->AfterChangeLayerEvent += mitk::MessageDelegate<QmitkSimpleLabelSetListWidget>(
         this, &QmitkSimpleLabelSetListWidget::OnLayerChanged);
-      m_LabelSetImage->AddLabelAddedListener(mitk::MessageDelegate1<QmitkSimpleLabelSetListWidget, mitk::LabelSetImage::LabelValueType>(
-        this, &QmitkSimpleLabelSetListWidget::OnLabelChanged));
-      m_LabelSetImage->AddLabelModifiedListener(mitk::MessageDelegate1<QmitkSimpleLabelSetListWidget, mitk::LabelSetImage::LabelValueType>(
-        this, &QmitkSimpleLabelSetListWidget::OnLabelChanged));
-      m_LabelSetImage->AddLabelRemovedListener(mitk::MessageDelegate1<QmitkSimpleLabelSetListWidget, mitk::LabelSetImage::LabelValueType>(
-        this, &QmitkSimpleLabelSetListWidget::OnLabelChanged));
     }
   }
 }
 
 void QmitkSimpleLabelSetListWidget::OnLayerChanged()
 {
   if (!this->m_Emmiting)
   {
     this->ResetList();
 
     this->m_Emmiting = true;
     emit ActiveLayerChanged();
     emit SelectedLabelsChanged(this->SelectedLabels());
     this->m_Emmiting = false;
   }
 }
 
 void QmitkSimpleLabelSetListWidget::OnLabelChanged(mitk::LabelSetImage::LabelValueType lv)
 {
   if (!this->m_Emmiting && m_LabelSetImage->GetGroupIndexOfLabel(lv)==m_LabelSetImage->GetActiveLayer())
   {
     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 labels = m_LabelSetImage->GetConstLabelsByValue(m_LabelSetImage->GetLabelValuesByGroup(activeLayerID));
 
   for (auto& label : labels)
   {
     auto color = label->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(label->GetName()));
     item->setData(Qt::UserRole, QVariant(label->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 136e900d0b..ffbb7184d7 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkSimpleLabelSetListWidget.h
+++ b/Modules/SegmentationUI/Qmitk/QmitkSimpleLabelSetListWidget.h
@@ -1,61 +1,66 @@
 /*============================================================================
 
 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 <mitkITKEventObserverGuard.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(mitk::LabelSetImage::LabelValueType lv);
 
   void ResetList();
 
   mitk::LabelSetImage::ConstPointer m_LabelSetImage;
   QListWidget* m_LabelList;
   bool m_Emmiting;
+
+  mitk::ITKEventObserverGuard m_LabelAddedObserver;
+  mitk::ITKEventObserverGuard m_LabelModifiedObserver;
+  mitk::ITKEventObserverGuard m_LabelRemovedObserver;
 };
 
 #endif
diff --git a/Modules/SurfaceInterpolation/Testing/mitkSurfaceInterpolationControllerTest.cpp b/Modules/SurfaceInterpolation/Testing/mitkSurfaceInterpolationControllerTest.cpp
index 3f3a6bb78d..3c25a2851d 100644
--- a/Modules/SurfaceInterpolation/Testing/mitkSurfaceInterpolationControllerTest.cpp
+++ b/Modules/SurfaceInterpolation/Testing/mitkSurfaceInterpolationControllerTest.cpp
@@ -1,608 +1,660 @@
 /*============================================================================
 
 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 <mitkTestFixture.h>
 #include <mitkTestingMacros.h>
 
 #include "mitkImageAccessByItk.h"
 #include <mitkImageTimeSelector.h>
 #include <mitkLabelSetImage.h>
 
 #include <vtkDebugLeaks.h>
 #include <vtkDoubleArray.h>
 #include <vtkFieldData.h>
 #include <vtkPolygon.h>
 #include <vtkRegularPolygonSource.h>
 
 template <typename ImageType>
 void ClearBufferProcessing(ImageType* itkImage)
 {
   itkImage->FillBuffer(0);
 }
 
 class mitkSurfaceInterpolationControllerTestSuite : public mitk::TestFixture
 {
   CPPUNIT_TEST_SUITE(mitkSurfaceInterpolationControllerTestSuite);
   MITK_TEST(TestSingleton);
   MITK_TEST(TestSetCurrentInterpolationSession);
   MITK_TEST(TestRemoveAllInterpolationSessions);
   MITK_TEST(TestRemoveInterpolationSession);
   MITK_TEST(TestOnSegmentationDeleted);
+  MITK_TEST(TestOnLabelRemoved);
 
   MITK_TEST(TestSetCurrentInterpolationSession4D);
   MITK_TEST(TestRemoveAllInterpolationSessions4D);
   MITK_TEST(TestRemoveInterpolationSession4D);
   MITK_TEST(TestOnSegmentationDeleted4D);
 
   /// \todo Workaround for memory leak in TestAddNewContour. Bug 18096.
   vtkDebugLeaks::SetExitError(0);
 
   MITK_TEST(TestAddNewContours);
   MITK_TEST(TestRemoveContours);
   CPPUNIT_TEST_SUITE_END();
 
 private:
   mitk::SurfaceInterpolationController::Pointer m_Controller;
 
 public:
   mitk::Image::Pointer createImage(unsigned int *dimensions)
   {
     mitk::Image::Pointer newImage = mitk::Image::New();
     // mitk::LabelSetImage::Pointer newImage = mitk::LabelSetImage::New();
     mitk::PixelType p_type = mitk::MakeScalarPixelType<unsigned char>();
     newImage->Initialize(p_type, 3, dimensions);
     AccessFixedDimensionByItk(newImage, ClearBufferProcessing, 3);
     return newImage;
   }
 
   mitk::LabelSetImage::Pointer createLabelSetImage(unsigned int *dimensions)
   {
     mitk::Image::Pointer image = createImage(dimensions);
     mitk::LabelSetImage::Pointer newImage = mitk::LabelSetImage::New();
     newImage->InitializeByLabeledImage(image);
     return newImage;
   }
 
   mitk::Image::Pointer createImage4D(unsigned int *dimensions)
   {
     mitk::Image::Pointer newImage = mitk::Image::New();
     mitk::PixelType p_type = mitk::MakeScalarPixelType<unsigned char>();
     newImage->Initialize(p_type, 4, dimensions);
     return newImage;
   }
 
   mitk::LabelSetImage::Pointer createLabelSetImage4D(unsigned int *dimensions)
   {
     mitk::Image::Pointer image = createImage4D(dimensions);
     mitk::LabelSetImage::Pointer newImage = mitk::LabelSetImage::New();
     newImage->InitializeByLabeledImage(image);
     return newImage;
   }
 
   void setUp() override
   {
     m_Controller = mitk::SurfaceInterpolationController::GetInstance();
     m_Controller->RemoveAllInterpolationSessions();
 
     vtkSmartPointer<vtkRegularPolygonSource> polygonSource = vtkSmartPointer<vtkRegularPolygonSource>::New();
     polygonSource->SetRadius(100);
     polygonSource->SetNumberOfSides(7);
     polygonSource->Update();
     mitk::Surface::Pointer surface = mitk::Surface::New();
     surface->SetVtkPolyData(polygonSource->GetOutput());
   }
 
   void TestSingleton()
   {
     mitk::SurfaceInterpolationController::Pointer controller2 = mitk::SurfaceInterpolationController::GetInstance();
     CPPUNIT_ASSERT_MESSAGE("SurfaceInterpolationController pointers are not equal!",
                            m_Controller.GetPointer() == controller2.GetPointer());
   }
 
   void TestSetCurrentInterpolationSession()
   {
     // Create image for testing
     unsigned int dimensions1[] = {10, 10, 10};
     mitk::LabelSetImage::Pointer segmentation_1 = createLabelSetImage(dimensions1);
 
     unsigned int dimensions2[] = {20, 10, 30};
     mitk::LabelSetImage::Pointer segmentation_2 = createLabelSetImage(dimensions2);
 
     // Test 1
     m_Controller->SetCurrentInterpolationSession(segmentation_1);
 
     CPPUNIT_ASSERT_MESSAGE("Segmentation images are not equal", m_Controller->GetCurrentSegmentation() == segmentation_1.GetPointer());
     CPPUNIT_ASSERT_MESSAGE("Number of interpolation session not 1",
                            m_Controller->GetNumberOfInterpolationSessions() == 1);
 
     // Test 2
     m_Controller->SetCurrentInterpolationSession(segmentation_2);
     CPPUNIT_ASSERT_MESSAGE("Segmentation images are not equal",
                            m_Controller->GetCurrentSegmentation() == segmentation_2.GetPointer());
     CPPUNIT_ASSERT_MESSAGE("Number of interpolation session not 2",
                            m_Controller->GetNumberOfInterpolationSessions() == 2);
 
     // Test 3
     m_Controller->SetCurrentInterpolationSession(segmentation_1);
     CPPUNIT_ASSERT_MESSAGE("Segmentation images are not equal",
                            m_Controller->GetCurrentSegmentation() == segmentation_1.GetPointer());
     CPPUNIT_ASSERT_MESSAGE("Number of interpolation session not 2",
                            m_Controller->GetNumberOfInterpolationSessions() == 2);
 
     // Test 4
     m_Controller->SetCurrentInterpolationSession(segmentation_1);
     CPPUNIT_ASSERT_MESSAGE("Segmentation images are not equal",
                            m_Controller->GetCurrentSegmentation() == segmentation_1.GetPointer());
     CPPUNIT_ASSERT_MESSAGE("Number of interpolation session not 2",
                            m_Controller->GetNumberOfInterpolationSessions() == 2);
 
     // // Test 5
     m_Controller->SetCurrentInterpolationSession(nullptr);
     CPPUNIT_ASSERT_MESSAGE("Segmentation images are not equal", m_Controller->GetCurrentSegmentation() == nullptr);
     CPPUNIT_ASSERT_MESSAGE("Number of interpolation session not 2",
                            m_Controller->GetNumberOfInterpolationSessions() == 2);
   }
 
   mitk::PlaneGeometry::Pointer CreatePlaneGeometry(mitk::ScalarType zIndex)
   {
     mitk::Point3D origin;
     mitk::Vector3D right, bottom, spacing;
     mitk::ScalarType widthInMM, heightInMM, thicknessInMM;
 
     auto planegeometry = mitk::PlaneGeometry::New();
     widthInMM = 100;
     heightInMM = 200;
     thicknessInMM = 1.0;
     mitk::FillVector3D(origin, 4.5, 7.3, zIndex*thicknessInMM);
     mitk::FillVector3D(right, widthInMM, 0, 0);
     mitk::FillVector3D(bottom, 0, heightInMM, 0);
     mitk::FillVector3D(spacing, 1.0, 1.0, thicknessInMM);
 
     planegeometry->InitializeStandardPlane(right, bottom);
     planegeometry->SetOrigin(origin);
     planegeometry->SetSpacing(spacing);
     return planegeometry;
   }
 
   mitk::Surface::Pointer CreateContour(int numOfSides)
   {
     double center_1[3] = { 1.25f, 3.43f, 4.44f };
     double normal_1[3] = { 0.25f, 1.76f, 0.93f };
     vtkSmartPointer<vtkRegularPolygonSource> p_source = vtkSmartPointer<vtkRegularPolygonSource>::New();
     p_source->SetNumberOfSides(numOfSides*10);
     p_source->SetCenter(center_1);
     p_source->SetRadius(4);
     p_source->SetNormal(normal_1);
     p_source->Update();
     vtkPolyData* poly_1 = p_source->GetOutput();
     mitk::Surface::Pointer surf_1 = mitk::Surface::New();
     surf_1->SetVtkPolyData(poly_1);
     return surf_1;
   }
 
   void TestRemoveAllInterpolationSessions()
   {
     // Create image for testing
     unsigned int dimensions1[] = {10, 10, 10};
     auto segmentation_1 = createLabelSetImage(dimensions1);
 
     unsigned int dimensions2[] = {20, 10, 30};
     auto segmentation_2 = createLabelSetImage(dimensions2);
 
     // Test 1
     m_Controller->SetCurrentInterpolationSession(segmentation_1);
     m_Controller->SetCurrentInterpolationSession(segmentation_2);
     m_Controller->RemoveAllInterpolationSessions();
     CPPUNIT_ASSERT_MESSAGE("Number of interpolation session not 0",
                            m_Controller->GetNumberOfInterpolationSessions() == 0);
   }
 
   void TestRemoveInterpolationSession()
   {
     // Create image for testing
     unsigned int dimensions1[] = {10, 10, 10};
     mitk::LabelSetImage::Pointer segmentation_1 = createLabelSetImage(dimensions1);
 
     unsigned int dimensions2[] = {20, 10, 30};
     mitk::LabelSetImage::Pointer segmentation_2 = createLabelSetImage(dimensions2);
 
     // Test 1
     m_Controller->SetCurrentInterpolationSession(segmentation_1);
     m_Controller->SetCurrentInterpolationSession(segmentation_2);
     CPPUNIT_ASSERT_MESSAGE("Number of interpolation session not 2",
                            m_Controller->GetNumberOfInterpolationSessions() == 2);
 
     // Test current segmentation should not be null if another one was removed
     m_Controller->RemoveInterpolationSession(segmentation_1);
     CPPUNIT_ASSERT_MESSAGE("Number of interpolation session not 1",
                            m_Controller->GetNumberOfInterpolationSessions() == 1);
     CPPUNIT_ASSERT_MESSAGE("Segmentation images are not equal",
                            m_Controller->GetCurrentSegmentation() == segmentation_2.GetPointer());
     CPPUNIT_ASSERT_MESSAGE("Current segmentation is null after another one was removed",
                            m_Controller->GetCurrentSegmentation() != nullptr);
 
     m_Controller->SetCurrentInterpolationSession(segmentation_1);
     CPPUNIT_ASSERT_MESSAGE("Number of interpolation session not 2",
                            m_Controller->GetNumberOfInterpolationSessions() == 2);
 
     // Test current segmentation should not be null if another one was removed
     m_Controller->RemoveInterpolationSession(segmentation_1);
     CPPUNIT_ASSERT_MESSAGE("Number of interpolation session not 1",
                            m_Controller->GetNumberOfInterpolationSessions() == 1);
     CPPUNIT_ASSERT_MESSAGE("Current segmentation is not null after session was removed",
                            m_Controller->GetCurrentSegmentation() == nullptr);
   }
 
   void TestOnSegmentationDeleted()
   {
     // Create image for testing
     unsigned int dimensions1[] = {10, 10, 10};
     auto segmentation_1 = createLabelSetImage(dimensions1);
 
     m_Controller->SetCurrentInterpolationSession(segmentation_1);
 
     segmentation_1 = nullptr;
 
     CPPUNIT_ASSERT_MESSAGE("Number of interpolation session not 0",
                            m_Controller->GetNumberOfInterpolationSessions() == 0);
   }
 
   void TestAddNewContours()
   {
     // Create segmentation image
-    unsigned int dimensions1[] = {10, 10, 10};
-    auto segmentation_1 = createLabelSetImage(dimensions1);
+    unsigned int dimensions1[] = {10, 10, 10, 4};
+    auto segmentation_1 = createLabelSetImage4D(dimensions1);
     m_Controller->SetCurrentInterpolationSession(segmentation_1);
 
     auto surf_1 = CreateContour(3);
     auto surf_2 = CreateContour(3);
     auto surf_3 = CreateContour(3);
     auto surf_3New = CreateContour(3);
 
     auto planeGeometry1 = CreatePlaneGeometry(1);
     auto planeGeometry2 = CreatePlaneGeometry(2);
     auto planeGeometry3 = CreatePlaneGeometry(3);
 
     mitk::SurfaceInterpolationController::CPIVector cpis= { {surf_1, planeGeometry1, 1, 0},
       {surf_2, planeGeometry2, 1, 0}, {surf_3, planeGeometry3, 1, 0}, {surf_3New, planeGeometry3, 1, 0},
       {surf_1, planeGeometry1, 1, 1}, {surf_1, planeGeometry1, 2, 0}, {surf_2, planeGeometry3, 2, 0}
     };
     // Add contours
     m_Controller->AddNewContours(cpis, true);
 
     // Check if all contours are there
     auto contours = m_Controller->GetContours(1, 0);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", contours->size() == 3);
     CPPUNIT_ASSERT_MESSAGE("Contours not equal!",
                            mitk::Equal(*(surf_1->GetVtkPolyData()), *((*contours)[0].Contour->GetVtkPolyData()), 0.000001, true));
     CPPUNIT_ASSERT_MESSAGE("Contours not equal!",
                            mitk::Equal(*(surf_2->GetVtkPolyData()), *((*contours)[1].Contour->GetVtkPolyData()), 0.000001, true));
     CPPUNIT_ASSERT_MESSAGE("Contours not equal!",
                            mitk::Equal(*(surf_3New->GetVtkPolyData()), *((*contours)[2].Contour->GetVtkPolyData()), 0.000001, true));
 
     contours = m_Controller->GetContours(1, 1);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", contours->size() == 1);
     CPPUNIT_ASSERT_MESSAGE("Contours not equal!",
       mitk::Equal(*(surf_1->GetVtkPolyData()), *((*contours)[0].Contour->GetVtkPolyData()), 0.000001, true));
 
     contours = m_Controller->GetContours(2, 0);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", contours->size() == 2);
     CPPUNIT_ASSERT_MESSAGE("Contours not equal!",
       mitk::Equal(*(surf_1->GetVtkPolyData()), *((*contours)[0].Contour->GetVtkPolyData()), 0.000001, true));
     CPPUNIT_ASSERT_MESSAGE("Contours not equal!",
       mitk::Equal(*(surf_2->GetVtkPolyData()), *((*contours)[1].Contour->GetVtkPolyData()), 0.000001, true));
 
     contours = m_Controller->GetContours(2, 1);
     CPPUNIT_ASSERT_MESSAGE("Invalid CPIs exists!", contours == nullptr);
     contours = m_Controller->GetContours(3, 0);
     CPPUNIT_ASSERT_MESSAGE("Invalid CPIs exists!", contours == nullptr);
 
     // Create another segmentation image
-    unsigned int dimensions2[] = {20, 20, 20};
-    mitk::LabelSetImage::Pointer segmentation_2 = createLabelSetImage(dimensions2);
+    unsigned int dimensions2[] = {20, 20, 20, 4};
+    mitk::LabelSetImage::Pointer segmentation_2 = createLabelSetImage4D(dimensions2);
     m_Controller->SetCurrentInterpolationSession(segmentation_2);
 
     auto planeGeometry4 = CreatePlaneGeometry(4);
     auto planeGeometry5 = CreatePlaneGeometry(5);
     auto planeGeometry6 = CreatePlaneGeometry(6);
 
     auto surf_4 = CreateContour(4);
     auto surf_5 = CreateContour(5);
     auto surf_6 = CreateContour(6);
 
     mitk::SurfaceInterpolationController::CPIVector cpis2 = { {surf_1, planeGeometry1, 1, 0},
       {surf_2, planeGeometry2, 1, 0}, {surf_4, planeGeometry4, 1, 0},
       {surf_5, planeGeometry5, 1, 1}, {surf_6, planeGeometry6, 2, 0}
     };
 
     contours = m_Controller->GetContours(1, 0);
     CPPUNIT_ASSERT_MESSAGE("Invalid CPIs exists!", contours == nullptr);
 
     m_Controller->AddNewContours(cpis2);
     // Check if all contours are there
     contours = m_Controller->GetContours(1, 0);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", contours->size() == 3);
     CPPUNIT_ASSERT_MESSAGE("Contours not equal!",
       mitk::Equal(*(surf_1->GetVtkPolyData()), *((*contours)[0].Contour->GetVtkPolyData()), 0.000001, true));
     CPPUNIT_ASSERT_MESSAGE("Contours not equal!",
       mitk::Equal(*(surf_2->GetVtkPolyData()), *((*contours)[1].Contour->GetVtkPolyData()), 0.000001, true));
     CPPUNIT_ASSERT_MESSAGE("Contours not equal!",
       mitk::Equal(*(surf_4->GetVtkPolyData()), *((*contours)[2].Contour->GetVtkPolyData()), 0.000001, true));
 
     contours = m_Controller->GetContours(1, 1);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", contours->size() == 1);
     CPPUNIT_ASSERT_MESSAGE("Contours not equal!",
       mitk::Equal(*(surf_5->GetVtkPolyData()), *((*contours)[0].Contour->GetVtkPolyData()), 0.000001, true));
 
     contours = m_Controller->GetContours(2, 0);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", contours->size() == 1);
     CPPUNIT_ASSERT_MESSAGE("Contours not equal!",
       mitk::Equal(*(surf_6->GetVtkPolyData()), *((*contours)[0].Contour->GetVtkPolyData()), 0.000001, true));
 
     contours = m_Controller->GetContours(2, 1);
     CPPUNIT_ASSERT_MESSAGE("Invalid CPIs exists!", contours == nullptr);
     contours = m_Controller->GetContours(3, 0);
     CPPUNIT_ASSERT_MESSAGE("Invalid CPIs exists!", contours == nullptr);
 
     // Check if all contours of segmentation_1 are still there
     m_Controller->SetCurrentInterpolationSession(segmentation_1);
     contours = m_Controller->GetContours(1, 0);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", contours->size() == 3);
     CPPUNIT_ASSERT_MESSAGE("Contours not equal!",
       mitk::Equal(*(surf_1->GetVtkPolyData()), *((*contours)[0].Contour->GetVtkPolyData()), 0.000001, true));
     CPPUNIT_ASSERT_MESSAGE("Contours not equal!",
       mitk::Equal(*(surf_2->GetVtkPolyData()), *((*contours)[1].Contour->GetVtkPolyData()), 0.000001, true));
     CPPUNIT_ASSERT_MESSAGE("Contours not equal!",
       mitk::Equal(*(surf_3New->GetVtkPolyData()), *((*contours)[2].Contour->GetVtkPolyData()), 0.000001, true));
 
     contours = m_Controller->GetContours(1, 1);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", contours->size() == 1);
     CPPUNIT_ASSERT_MESSAGE("Contours not equal!",
       mitk::Equal(*(surf_1->GetVtkPolyData()), *((*contours)[0].Contour->GetVtkPolyData()), 0.000001, true));
 
     contours = m_Controller->GetContours(2, 0);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", contours->size() == 2);
     CPPUNIT_ASSERT_MESSAGE("Contours not equal!",
       mitk::Equal(*(surf_1->GetVtkPolyData()), *((*contours)[0].Contour->GetVtkPolyData()), 0.000001, true));
     CPPUNIT_ASSERT_MESSAGE("Contours not equal!",
       mitk::Equal(*(surf_2->GetVtkPolyData()), *((*contours)[1].Contour->GetVtkPolyData()), 0.000001, true));
 
     contours = m_Controller->GetContours(2, 1);
     CPPUNIT_ASSERT_MESSAGE("Invalid CPIs exists!", contours == nullptr);
     contours = m_Controller->GetContours(3, 0);
     CPPUNIT_ASSERT_MESSAGE("Invalid CPIs exists!", contours == nullptr);
   }
 
   void TestRemoveContours()
   {
     // Create segmentation image
-    unsigned int dimensions1[] = {12, 12, 12};
-    mitk::LabelSetImage::Pointer segmentation_1 = createLabelSetImage(dimensions1);
+    unsigned int dimensions1[] = {12, 12, 12, 3};
+    mitk::LabelSetImage::Pointer segmentation_1 = createLabelSetImage4D(dimensions1);
     m_Controller->SetCurrentInterpolationSession(segmentation_1);
 
 
     auto surf_1 = CreateContour(3);
     auto surf_2 = CreateContour(3);
     auto surf_3 = CreateContour(3);
 
     auto planeGeometry1 = CreatePlaneGeometry(1);
     auto planeGeometry2 = CreatePlaneGeometry(2);
     auto planeGeometry3 = CreatePlaneGeometry(3);
 
     mitk::SurfaceInterpolationController::CPIVector cpis = { {surf_1, planeGeometry1, 1, 0},
       {surf_2, planeGeometry2, 1, 1}, {surf_3, planeGeometry3, 1, 2},
       {surf_1, planeGeometry1, 2, 0}, {surf_2, planeGeometry3, 2, 0}
     };
     m_Controller->AddNewContours(cpis);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(1, 0)->size() == 1);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(1, 1)->size() == 1);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(1, 2)->size() == 1);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(2, 0)->size() == 2);
 
     //Remove unkown label
-    m_Controller->RemoveContours(9);
+    m_Controller->RemoveContours(segmentation_1, 9);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(1, 0)->size() == 1);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(1, 1)->size() == 1);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(1, 2)->size() == 1);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(2, 0)->size() == 2);
 
     //Remove unkown time step
-    m_Controller->RemoveContours(1,3);
+    m_Controller->RemoveContours(segmentation_1, 1,3);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(1, 0)->size() == 1);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(1, 1)->size() == 1);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(1, 2)->size() == 1);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(2, 0)->size() == 2);
 
 
-    m_Controller->RemoveContours(1, 1);
+    m_Controller->RemoveContours(segmentation_1, 1, 1);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(1, 0)->size() == 1);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(1, 1) == nullptr);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(1, 2)->size() == 1);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(2, 0)->size() == 2);
 
-    m_Controller->RemoveContours(1);
+    m_Controller->RemoveContours(segmentation_1, 1);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(1, 0) == nullptr);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(1, 1) == nullptr);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(1, 2) == nullptr);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(2, 0)->size() == 2);
 
-    m_Controller->RemoveContours(2);
+    m_Controller->RemoveContours(segmentation_1, 2);
     CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(2, 0) == nullptr);
   }
 
+  void TestOnLabelRemoved()
+  {
+    // Create segmentation image
+    unsigned int dimensions[] = { 20, 10, 30, 4 };
+    mitk::LabelSetImage::Pointer segmentation_1 = createLabelSetImage4D(dimensions);
+    segmentation_1->AddLabel(mitk::Label::New(1, "Label1"), 0);
+    segmentation_1->AddLabel(mitk::Label::New(2, "Label2"), 0);
+    mitk::LabelSetImage::Pointer segmentation_2 = createLabelSetImage4D(dimensions);
+    segmentation_2->AddLabel(mitk::Label::New(1, "Label1"), 0);
+    segmentation_2->AddLabel(mitk::Label::New(2, "Label2"), 0);
+
+    auto surf_1 = CreateContour(3);
+    auto surf_2 = CreateContour(3);
+    auto surf_3 = CreateContour(3);
+
+    auto planeGeometry1 = CreatePlaneGeometry(1);
+    auto planeGeometry2 = CreatePlaneGeometry(2);
+    auto planeGeometry3 = CreatePlaneGeometry(3);
+
+    mitk::SurfaceInterpolationController::CPIVector cpis = { {surf_1, planeGeometry1, 1, 0},
+      {surf_2, planeGeometry2, 1, 1}, {surf_3, planeGeometry3, 1, 2},
+      {surf_1, planeGeometry1, 2, 0}, {surf_2, planeGeometry3, 2, 0}
+    };
+
+    m_Controller->SetCurrentInterpolationSession(segmentation_1);
+    m_Controller->AddNewContours(cpis);
+    CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(1, 0)->size() == 1);
+    CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(1, 1)->size() == 1);
+    CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(1, 2)->size() == 1);
+    CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(2, 0)->size() == 2);
+
+    m_Controller->SetCurrentInterpolationSession(segmentation_2);
+    m_Controller->AddNewContours(cpis);
+    CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(1, 0)->size() == 1);
+    CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(1, 1)->size() == 1);
+    CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(1, 2)->size() == 1);
+    CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(2, 0)->size() == 2);
+
+    segmentation_1->RemoveLabel(1);
+    CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(1, 0)->size() == 1);
+    CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(1, 1)->size() == 1);
+    CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(1, 2)->size() == 1);
+    CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(2, 0)->size() == 2);
+    m_Controller->SetCurrentInterpolationSession(segmentation_1);
+    CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(1, 0) == nullptr);
+    CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(1, 1) == nullptr);
+    CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(1, 2) == nullptr);
+    CPPUNIT_ASSERT_MESSAGE("Wrong number of contours!", m_Controller->GetContours(2, 0)->size() == 2);
+  }
+
+
   bool AssertImagesEqual4D(mitk::LabelSetImage *img1, mitk::LabelSetImage *img2)
   {
     auto selector1 = mitk::ImageTimeSelector::New();
     selector1->SetInput(img1);
     selector1->SetChannelNr(0);
     auto selector2 = mitk::ImageTimeSelector::New();
     selector2->SetInput(img2);
     selector2->SetChannelNr(0);
 
     int numTs1 = img1->GetTimeSteps();
     int numTs2 = img2->GetTimeSteps();
     if (numTs1 != numTs2)
     {
       return false;
     }
 
     /*mitk::ImagePixelWriteAccessor<unsigned char,4> accessor( img1 );
     itk::Index<4> ind;
     ind[0] = 5;
     ind[1] = 5;
     ind[2] = 5;
     ind[3] = 2;
     accessor.SetPixelByIndex( ind, 7 );*/
 
     for (int ts = 0; ts < numTs1; ++ts)
     {
       selector1->SetTimeNr(ts);
       selector2->SetTimeNr(ts);
 
       selector1->Update();
       selector2->Update();
 
       mitk::Image::Pointer imgSel1 = selector1->GetOutput();
       mitk::Image::Pointer imgSel2 = selector2->GetOutput();
 
       MITK_ASSERT_EQUAL(imgSel1, imgSel2, "Segmentation images are not equal");
     }
 
     return true;
   }
 
   void TestSetCurrentInterpolationSession4D()
   {
     // Create image for testing
     unsigned int dimensions1[] = {10, 10, 10, 5};
     mitk::LabelSetImage::Pointer segmentation_1 = createLabelSetImage4D(dimensions1);
     // mitk::Image * segmentationImage_1 = dynamic_cast<mitk::Image *>(segmentation_1.GetPointer());
 
     unsigned int dimensions2[] = {20, 10, 30, 4};
     mitk::LabelSetImage::Pointer segmentation_2 = createLabelSetImage4D(dimensions2);
 
     // Test 1
     m_Controller->SetCurrentInterpolationSession(segmentation_1);
     auto currentSegmentation = dynamic_cast<mitk::LabelSetImage *>(m_Controller->GetCurrentSegmentation());
     AssertImagesEqual4D(currentSegmentation, segmentation_1->Clone());
     CPPUNIT_ASSERT_MESSAGE("Segmentation images are not equal",
                            m_Controller->GetCurrentSegmentation() == segmentation_1.GetPointer());
     CPPUNIT_ASSERT_MESSAGE("Number of interpolation session not 1",
                            m_Controller->GetNumberOfInterpolationSessions() == 1);
 
     // Test 2
     m_Controller->SetCurrentInterpolationSession(segmentation_2);
     // MITK_ASSERT_EQUAL(m_Controller->GetCurrentSegmentation(), segmentation_2->Clone(), "Segmentation images are not
     // equal");
     currentSegmentation = dynamic_cast<mitk::LabelSetImage *>(m_Controller->GetCurrentSegmentation());
     // AssertImagesEqual4D(currentSegmentation, segmentation_2->Clone());
     CPPUNIT_ASSERT_MESSAGE("Segmentation images are not equal",
                            currentSegmentation == segmentation_2.GetPointer());
     CPPUNIT_ASSERT_MESSAGE("Number of interpolation session not 2",
                            m_Controller->GetNumberOfInterpolationSessions() == 2);
 
     // Test 3
     m_Controller->SetCurrentInterpolationSession(segmentation_1);
     // MITK_ASSERT_EQUAL(m_Controller->GetCurrentSegmentation(), segmentation_1->Clone(), "Segmentation images are not
     // equal");
     currentSegmentation = dynamic_cast<mitk::LabelSetImage *>(m_Controller->GetCurrentSegmentation());
     AssertImagesEqual4D(currentSegmentation, segmentation_1->Clone());
     CPPUNIT_ASSERT_MESSAGE("Segmentation images are not equal",
                            m_Controller->GetCurrentSegmentation() == segmentation_1.GetPointer());
     CPPUNIT_ASSERT_MESSAGE("Number of interpolation session not 2",
                            m_Controller->GetNumberOfInterpolationSessions() == 2);
 
     // Test 4
     m_Controller->SetCurrentInterpolationSession(segmentation_1);
     // MITK_ASSERT_EQUAL(m_Controller->GetCurrentSegmentation(), segmentation_1->Clone(), "Segmentation images are not
     // equal");
     currentSegmentation = dynamic_cast<mitk::LabelSetImage *>(m_Controller->GetCurrentSegmentation());
     AssertImagesEqual4D(currentSegmentation, segmentation_1->Clone());
     CPPUNIT_ASSERT_MESSAGE("Segmentation images are not equal",
                            m_Controller->GetCurrentSegmentation() == segmentation_1.GetPointer());
     CPPUNIT_ASSERT_MESSAGE("Number of interpolation session not 2",
                            m_Controller->GetNumberOfInterpolationSessions() == 2);
 
     // Test 5
     m_Controller->SetCurrentInterpolationSession(nullptr);
     CPPUNIT_ASSERT_MESSAGE("Segmentation images are not equal", m_Controller->GetCurrentSegmentation()==nullptr);
     CPPUNIT_ASSERT_MESSAGE("Number of interpolation session not 2",
                            m_Controller->GetNumberOfInterpolationSessions() == 2);
   }
 
   void TestRemoveAllInterpolationSessions4D()
   {
     // Create image for testing
     unsigned int dimensions1[] = {10, 10, 10, 4};
     mitk::LabelSetImage::Pointer segmentation_1 = createLabelSetImage4D(dimensions1);
 
     unsigned int dimensions2[] = {20, 10, 30, 5};
     mitk::LabelSetImage::Pointer segmentation_2 = createLabelSetImage4D(dimensions2);
 
     // Test 1
     m_Controller->SetCurrentInterpolationSession(segmentation_1);
     m_Controller->SetCurrentInterpolationSession(segmentation_2);
     m_Controller->RemoveAllInterpolationSessions();
     CPPUNIT_ASSERT_MESSAGE("Number of interpolation session not 0",
                            m_Controller->GetNumberOfInterpolationSessions() == 0);
   }
 
   void TestRemoveInterpolationSession4D()
   {
     // Create image for testing
     unsigned int dimensions1[] = {10, 10, 10, 3};
     mitk::LabelSetImage::Pointer segmentation_1 = createLabelSetImage4D(dimensions1);
 
     unsigned int dimensions2[] = {20, 10, 30, 6};
     mitk::LabelSetImage::Pointer segmentation_2 = createLabelSetImage4D(dimensions2);
 
     // Test 1
     m_Controller->SetCurrentInterpolationSession(segmentation_1);
     m_Controller->SetCurrentInterpolationSession(segmentation_2);
     CPPUNIT_ASSERT_MESSAGE("Number of interpolation session not 2",
                            m_Controller->GetNumberOfInterpolationSessions() == 2);
 
     // Test current segmentation should not be null if another one was removed
     m_Controller->RemoveInterpolationSession(segmentation_1);
     CPPUNIT_ASSERT_MESSAGE("Number of interpolation session not 1",
                            m_Controller->GetNumberOfInterpolationSessions() == 1);
     CPPUNIT_ASSERT_MESSAGE("Segmentation images are not equal",
                            m_Controller->GetCurrentSegmentation() == segmentation_2.GetPointer());
     CPPUNIT_ASSERT_MESSAGE("Current segmentation is null after another one was removed",
                            m_Controller->GetCurrentSegmentation()!=nullptr);
 
     m_Controller->SetCurrentInterpolationSession(segmentation_1);
     CPPUNIT_ASSERT_MESSAGE("Number of interpolation session not 2",
                            m_Controller->GetNumberOfInterpolationSessions() == 2);
 
     // Test current segmentation should not be null if another one was removed
     m_Controller->RemoveInterpolationSession(segmentation_1);
     CPPUNIT_ASSERT_MESSAGE("Number of interpolation session not 1",
                            m_Controller->GetNumberOfInterpolationSessions() == 1);
     CPPUNIT_ASSERT_MESSAGE("Current segmentation is not null after session was removed",
                            m_Controller->GetCurrentSegmentation()==nullptr);
   }
 
   void TestOnSegmentationDeleted4D()
   {
     {
       // Create image for testing
       unsigned int dimensions1[] = {10, 10, 10, 7};
       mitk::LabelSetImage::Pointer segmentation_1 = createLabelSetImage4D(dimensions1);
       m_Controller->SetCurrentInterpolationSession(segmentation_1);
     }
     CPPUNIT_ASSERT_MESSAGE("Number of interpolation session not 0",
                            m_Controller->GetNumberOfInterpolationSessions() == 0);
   }
 };
 MITK_TEST_SUITE_REGISTRATION(mitkSurfaceInterpolationController)
diff --git a/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.cpp b/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.cpp
index 3b2c41f166..07e66c19f7 100644
--- a/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.cpp
+++ b/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.cpp
@@ -1,861 +1,883 @@
 /*============================================================================
 
 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 <shared_mutex>
 
 #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 <mitkNodePredicateAnd.h>
 #include <mitkPlanarCircle.h>
 #include <mitkPlaneGeometry.h>
 #include <mitkReduceContourSetFilter.h>
 
 #include <vtkFieldData.h>
 #include <vtkMath.h>
 #include <vtkPolygon.h>
 
 struct CPICache
 {
   mitk::SurfaceInterpolationController::CPIVector cpis;
   itk::TimeStamp cpiTimeStamp;
   mitk::Surface::Pointer cachedSurface;
 };
 
 typedef std::map<mitk::TimeStepType, CPICache> CPITimeStepMap;
 typedef std::map<mitk::LabelSetImage::LabelValueType, CPITimeStepMap> CPITimeStepLabelMap;
 
 typedef std::map<const mitk::LabelSetImage*, CPITimeStepLabelMap> CPITimeStepLabelSegMap;
 
 CPITimeStepLabelSegMap cpiMap;
 std::shared_mutex cpiMutex;
 
 std::map<mitk::LabelSetImage*, unsigned long> segmentationObserverTags;
+std::map<mitk::LabelSetImage*, unsigned long> labelRemovedObserverTags;
 
 mitk::SurfaceInterpolationController::SurfaceInterpolationController()
   : m_DistanceImageVolume(50000),
     m_SelectedSegmentation(nullptr)
 {
 }
 
 mitk::SurfaceInterpolationController::~SurfaceInterpolationController()
 {
   this->RemoveObservers();
 }
 
 void mitk::SurfaceInterpolationController::RemoveObservers()
 {
   // Removing all observers
   while (segmentationObserverTags.size())
   {
     this->RemoveObserversInternal(segmentationObserverTags.begin()->first);
   }
 }
 
 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::AddNewContours(const std::vector<ContourPositionInformation>& newCPIs,
   bool reinitializationAction, bool silent)
 {
   auto selectedSegmentation = m_SelectedSegmentation.Lock();
   if (selectedSegmentation.IsNull()) return;
 
   for (auto cpi : newCPIs)
   {
     if (cpi.Contour->GetVtkPolyData()->GetNumberOfPoints() > 0)
     {
       this->AddToCPIMap(cpi, reinitializationAction);
     }
   }
   if (!silent) this->Modified();
 }
 
 
 mitk::DataNode* GetSegmentationImageNodeInternal(mitk::DataStorage* ds, const mitk::LabelSetImage* seg)
 {
   if (nullptr == ds) return nullptr;
   if (nullptr == seg) return nullptr;
 
   mitk::DataNode* segmentationNode = nullptr;
   mitk::NodePredicateDataUID::Pointer dataUIDPredicate = mitk::NodePredicateDataUID::New(seg->GetUID());
   auto dataNodeObjects = ds->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;
 }
 
 mitk::DataNode* mitk::SurfaceInterpolationController::GetSegmentationImageNode() const
 {
   if (m_DataStorage.IsNull()) return nullptr;
   auto selectedSegmentation = m_SelectedSegmentation.Lock();
   if (selectedSegmentation.IsNull()) return nullptr;
   return GetSegmentationImageNodeInternal(this->m_DataStorage, selectedSegmentation);
 }
 
 mitk::DataStorage::SetOfObjects::ConstPointer mitk::SurfaceInterpolationController::GetPlaneGeometryNodeFromDataStorage(const DataNode* segNode, LabelSetImage::LabelValueType labelValue, TimeStepType timeStep) const
 {
   DataStorage::SetOfObjects::Pointer relevantNodes = DataStorage::SetOfObjects::New();
 
   if (m_DataStorage.IsNotNull())
   {
     //remove relevant plane nodes
     auto nodes = this->GetPlaneGeometryNodeFromDataStorage(segNode, labelValue);
 
     for (auto it = nodes->Begin(); it != nodes->End(); ++it)
     {
       auto aTS = dynamic_cast<mitk::IntProperty*>(it->Value()->GetProperty("timeStep"))->GetValue();
       bool sameTS = (timeStep == aTS);
 
       if (sameTS)
       {
         relevantNodes->push_back(it->Value());
       }
     }
   }
   return relevantNodes;
 }
 
 mitk::DataStorage::SetOfObjects::ConstPointer mitk::SurfaceInterpolationController::GetPlaneGeometryNodeFromDataStorage(const DataNode* segNode, LabelSetImage::LabelValueType labelValue) const
 {
   auto isContourPlaneGeometry = NodePredicateProperty::New("isContourPlaneGeometry", mitk::BoolProperty::New(true));
   auto isCorrectLabel = NodePredicateProperty::New("labelID", mitk::UShortProperty::New(labelValue));
   auto searchPredicate = NodePredicateAnd::New(isContourPlaneGeometry, isCorrectLabel);
 
   mitk::DataStorage::SetOfObjects::ConstPointer result;
   if (m_DataStorage.IsNotNull()) result = m_DataStorage->GetDerivations(segNode, searchPredicate);
   return result;
 }
 
 mitk::DataStorage::SetOfObjects::ConstPointer mitk::SurfaceInterpolationController::GetPlaneGeometryNodeFromDataStorage(const DataNode* segNode) const
 {
   auto isContourPlaneGeometry = NodePredicateProperty::New("isContourPlaneGeometry", mitk::BoolProperty::New(true));
 
   mitk::DataStorage::SetOfObjects::ConstPointer result;
   if (m_DataStorage.IsNotNull()) result = m_DataStorage->GetDerivations(segNode, isContourPlaneGeometry);
   return result;
 }
 void mitk::SurfaceInterpolationController::AddPlaneGeometryNodeToDataStorage(const ContourPositionInformation& contourInfo) const
 {
   auto selectedSegmentation = m_SelectedSegmentation.Lock();
   if (selectedSegmentation.IsNull())
   {
     mitkThrow()<< "Cannot add plane geometries. No valid segmentation selected.";
   }
 
   if (!selectedSegmentation->GetTimeGeometry()->IsValidTimeStep(contourInfo.TimeStep))
   {
     MITK_ERROR << "Invalid time point requested in AddPlaneGeometryNodeToDataStorage.";
     return;
   }
 
   if (m_DataStorage.IsNull())
   {
     MITK_DEBUG << "Cannot add plane geometry nodes. No data storage is set.";
     return;
   }
 
   auto planeGeometry = contourInfo.Plane;
   if (planeGeometry)
   {
     auto segmentationNode = this->GetSegmentationImageNode();
     mitk::DataStorage::SetOfObjects::ConstPointer contourNodes = this->GetPlaneGeometryNodeFromDataStorage(segmentationNode, contourInfo.LabelValue, contourInfo.TimeStep);
 
     mitk::DataNode::Pointer contourPlaneGeometryDataNode;
 
     //  Go through the pre-existing contours and check if the contour position matches them.
     for (auto it = contourNodes->Begin(); it != contourNodes->End(); ++it)
     {
       auto planeData = dynamic_cast<mitk::PlanarFigure*>(it->Value()->GetData());
       if (nullptr == planeData) mitkThrow() << "Invalid ContourPlaneGeometry data node. Does not contion a planar figure as data.";
 
       bool samePlane = contourInfo.Plane->IsOnPlane(planeData->GetPlaneGeometry());
 
       if (samePlane)
       {
         contourPlaneGeometryDataNode = it->Value();
         break;
       }
     }
 
     //  Go through the contourPlaneGeometry Data and add the segmentationNode to it.
     if (contourPlaneGeometryDataNode.IsNull())
     {
       auto planeGeometryData = mitk::PlanarCircle::New();
       planeGeometryData->SetPlaneGeometry(planeGeometry->Clone());
       mitk::Point2D p1;
       planeGeometry->Map(planeGeometry->GetCenter(), p1);
       planeGeometryData->PlaceFigure(p1);
       planeGeometryData->SetCurrentControlPoint(p1);
       planeGeometryData->SetProperty("initiallyplaced", mitk::BoolProperty::New(true));
 
       std::string contourName = "contourPlane L " + std::to_string(contourInfo.LabelValue) + " T " + std::to_string(contourInfo.TimeStep);
 
       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("labelID", mitk::UShortProperty::New(contourInfo.LabelValue));
       contourPlaneGeometryDataNode->SetProperty("timeStep", mitk::IntProperty::New(contourInfo.TimeStep));
 
       contourPlaneGeometryDataNode->SetData(planeGeometryData);
 
       m_DataStorage->Add(contourPlaneGeometryDataNode, segmentationNode);
     }
   }
 }
 
 void mitk::SurfaceInterpolationController::AddToCPIMap(ContourPositionInformation& contourInfo, bool reinitializationAction)
 {
   auto selectedSegmentation = m_SelectedSegmentation.Lock();
   if (selectedSegmentation.IsNull())
     return;
 
   if (!selectedSegmentation->GetTimeGeometry()->IsValidTimeStep(contourInfo.TimeStep))
   {
     MITK_ERROR << "Invalid time step requested for interpolation pipeline.";
     return;
   }
 
   if (contourInfo.Plane == nullptr)
   {
     MITK_ERROR << "contourInfo plane is null.";
     return;
   }
 
   if (contourInfo.Contour->GetVtkPolyData()->GetNumberOfPoints() == 0)
   {
     this->RemoveContour(contourInfo);
     MITK_DEBUG << "contourInfo contour is empty.";
     return;
   }
 
   {
     std::lock_guard<std::shared_mutex> guard(cpiMutex);
     const auto& currentTimeStep = contourInfo.TimeStep;
     const auto& currentLabelValue = contourInfo.LabelValue;
 
     auto& currentImageContours = cpiMap[selectedSegmentation];
     auto& currentLabelContours = currentImageContours[currentLabelValue];
     auto& currentCPICache = currentLabelContours[currentTimeStep];
     auto& currentContourList = currentCPICache.cpis;
 
     auto finding = std::find_if(currentContourList.begin(), currentContourList.end(), [contourInfo](const ContourPositionInformation& element) {return contourInfo.Plane->IsOnPlane(element.Plane); });
 
     if (finding != currentContourList.end())
     {
       MITK_DEBUG << "CPI already exists. CPI is updated. Label: "<< currentLabelValue << "; Time Step: " << currentTimeStep;
       *finding = contourInfo;
     }
     else
     {
       currentContourList.push_back(contourInfo);
     }
     currentCPICache.cpiTimeStamp.Modified();
   }
 
   if (!reinitializationAction)
   {
     this->AddPlaneGeometryNodeToDataStorage(contourInfo);
   }
 }
 
 bool mitk::SurfaceInterpolationController::RemoveContour(ContourPositionInformation contourInfo, bool keepPlaceholderForUndo)
 {
   auto selectedSegmentation = m_SelectedSegmentation.Lock();
   if (selectedSegmentation.IsNull())
   {
     return false;
   }
 
   if (!selectedSegmentation->GetTimeGeometry()->IsValidTimeStep(contourInfo.TimeStep))
   {
     return false;
   }
 
   bool removedIt = false;
 
   {
     std::lock_guard<std::shared_mutex> cpiGuard(cpiMutex);
 
     const auto currentTimeStep = contourInfo.TimeStep;
     const auto currentLabel = contourInfo.LabelValue;
     auto& cpiCache = cpiMap.at(selectedSegmentation).at(currentLabel).at(currentTimeStep);
     auto it = cpiCache.cpis.begin();
 
 
     while (it != cpiCache.cpis.end())
     {
       const ContourPositionInformation& currentContour = (*it);
       if (currentContour.Plane->IsOnPlane(contourInfo.Plane))
       {
         if (keepPlaceholderForUndo)
         {
           it->Contour = nullptr;
         }
         else
         {
           cpiCache.cpis.erase(it);
         }
         cpiCache.cpiTimeStamp.Modified();
         removedIt = true;
 
         if (m_DataStorage.IsNotNull())
         {
           mitk::DataNode::Pointer contourPlaneGeometryDataNode;
 
           auto contourNodes = this->GetPlaneGeometryNodeFromDataStorage(GetSegmentationImageNodeInternal(m_DataStorage, selectedSegmentation), currentLabel, currentTimeStep);
 
           //  Go through the nodes and check if the contour position matches them.
           for (auto it = contourNodes->Begin(); it != contourNodes->End(); ++it)
           {
             auto planeData = dynamic_cast<mitk::PlanarFigure*>(it->Value()->GetData());
             if (nullptr == planeData) mitkThrow() << "Invalid ContourPlaneGeometry data node. Does not contion a planar figure as data.";
 
             bool samePlane = contourInfo.Plane->IsOnPlane(planeData->GetPlaneGeometry());
 
             if (samePlane)
             {
               m_DataStorage->Remove(it->Value());
               break;
             }
           }
         }
         break;
       }
       ++it;
     }
   }
 
   return removedIt;
 }
 
 void mitk::SurfaceInterpolationController::AddActiveLabelContoursForInterpolation(ReduceContourSetFilter* reduceFilter, const LabelSetImage* segmentationImage, LabelSetImage::LabelValueType labelValue, TimeStepType timeStep)
 {
   const auto& currentImageContours = cpiMap.at(segmentationImage);
 
   auto finding = currentImageContours.find(labelValue);
   if (finding == currentImageContours.end())
   {
     MITK_INFO << "Contours for label don't exist. Label value: " << labelValue;
     return;
   }
 
   const auto& currentLabelContoursMap = finding->second;
 
   auto tsfinding = currentLabelContoursMap.find(timeStep);
   if (tsfinding == currentLabelContoursMap.end())
   {
     MITK_INFO << "Contours for current time step don't exist.";
     return;
   }
 
   const auto& currentContours = tsfinding->second.cpis;
 
   unsigned int index = 0;
   for (const auto&  cpi : currentContours)
   {
     if (!cpi.IsPlaceHolder())
     {
       reduceFilter->SetInput(index, cpi.Contour);
       ++index;
     }
   }
 }
 
 bool CPICacheIsOutdated(const mitk::LabelSetImage* segmentationImage, mitk::LabelSetImage::LabelValueType labelValue, mitk::TimeStepType timeStep)
 {
   const auto& currentImageContours = cpiMap.at(segmentationImage);
 
   auto finding = currentImageContours.find(labelValue);
   if (finding == currentImageContours.end())
   {
     return false;
   }
 
   const auto& currentLabelContoursMap = finding->second;
 
   auto tsfinding = currentLabelContoursMap.find(timeStep);
   if (tsfinding == currentLabelContoursMap.end())
   {
     return false;
   }
 
   bool result = tsfinding->second.cachedSurface.IsNull() || tsfinding->second.cachedSurface->GetMTime() < tsfinding->second.cpiTimeStamp.GetMTime();
   return result;
 }
 
 void SetCPICacheSurface(mitk::Surface* surface, const mitk::LabelSetImage* segmentationImage, mitk::LabelSetImage::LabelValueType labelValue, mitk::TimeStepType timeStep)
 {
   const auto& currentImageContours = cpiMap.at(segmentationImage);
 
   auto finding = currentImageContours.find(labelValue);
   if (finding == currentImageContours.end())
   {
     return;
   }
 
   const auto& currentLabelContoursMap = finding->second;
 
   auto tsfinding = currentLabelContoursMap.find(timeStep);
   if (tsfinding == currentLabelContoursMap.end())
   {
     return;
   }
 
   cpiMap[segmentationImage][labelValue][timeStep].cachedSurface = surface;
 }
 
 void mitk::SurfaceInterpolationController::Interpolate(const LabelSetImage* segmentationImage, LabelSetImage::LabelValueType labelValue, TimeStepType timeStep)
 {
   if (nullptr == segmentationImage)
   {
     mitkThrow() << "Cannot interpolate contours. No valid segmentation passed.";
   }
 
   std::lock_guard<std::shared_mutex> guard(cpiMutex);
   auto it = cpiMap.find(segmentationImage);
   if (it == cpiMap.end())
   {
     mitkThrow() << "Cannot interpolate contours. Passed segmentation is not registered at controller.";
   }
 
   if (!segmentationImage->ExistLabel(labelValue))
   {
     mitkThrow() << "Cannot interpolate contours. None existant label request. Invalid label:" << labelValue;
   }
 
   if (!segmentationImage->GetTimeGeometry()->IsValidTimeStep(timeStep))
   {
     mitkThrow() << "Cannot interpolate contours. No valid time step requested. Invalid time step:" << timeStep;
   }
 
   if (!CPICacheIsOutdated(segmentationImage, labelValue, timeStep)) return;
 
   mitk::Surface::Pointer interpolationResult = nullptr;
 
   auto reduceFilter = ReduceContourSetFilter::New();
   auto normalsFilter = ComputeContourSetNormalsFilter::New();
   auto interpolateSurfaceFilter = CreateDistanceImageFromSurfaceFilter::New();
 
   const auto spacing = segmentationImage->GetGeometry(timeStep)->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];
     }
   }
   reduceFilter->SetMinSpacing(minSpacing);
   reduceFilter->SetMaxSpacing(maxSpacing);
   normalsFilter->SetMaxSpacing(maxSpacing);
   interpolateSurfaceFilter->SetDistanceImageVolume(m_DistanceImageVolume);
 
   reduceFilter->SetUseProgressBar(false);
   normalsFilter->SetUseProgressBar(true);
   normalsFilter->SetProgressStepSize(1);
   interpolateSurfaceFilter->SetUseProgressBar(true);
   interpolateSurfaceFilter->SetProgressStepSize(7);
 
   //  Set reference image for interpolation surface filter
   itk::ImageBase<3>::Pointer itkImage = itk::ImageBase<3>::New();
   mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New();
   timeSelector->SetInput(segmentationImage);
   timeSelector->SetTimeNr(timeStep);
   timeSelector->SetChannelNr(0);
   timeSelector->Update();
   mitk::Image::Pointer refSegImage = timeSelector->GetOutput();
   AccessFixedDimensionByItk_1(refSegImage, GetImageBase, 3, itkImage);
   interpolateSurfaceFilter->SetReferenceImage(itkImage.GetPointer());
 
   try
   {
     this->AddActiveLabelContoursForInterpolation(reduceFilter, segmentationImage, labelValue, timeStep);
     reduceFilter->Update();
     auto currentNumberOfReducedContours = reduceFilter->GetNumberOfOutputs();
 
     if (currentNumberOfReducedContours < 2)
     {
       // If no interpolation is possible reset the interpolation result
       MITK_INFO << "Interpolation impossible: not enough contours.";
     }
     else
     {
       normalsFilter->SetSegmentationBinaryImage(refSegImage);
 
       for (size_t i = 0; i < currentNumberOfReducedContours; ++i)
       {
         mitk::Surface::Pointer reducedContour = reduceFilter->GetOutput(i);
         reducedContour->DisconnectPipeline();
         normalsFilter->SetInput(i, reducedContour);
         interpolateSurfaceFilter->SetInput(i, normalsFilter->GetOutput(i));
       }
 
       // Setting up progress bar
       mitk::ProgressBar::GetInstance()->AddStepsToDo(10);
 
       // create a surface from the distance-image
       auto imageToSurfaceFilter = mitk::ImageToSurfaceFilter::New();
       imageToSurfaceFilter->SetInput(interpolateSurfaceFilter->GetOutput());
       imageToSurfaceFilter->SetThreshold(0);
       imageToSurfaceFilter->SetSmooth(true);
       imageToSurfaceFilter->SetSmoothIteration(1);
       imageToSurfaceFilter->Update();
 
       interpolationResult = mitk::Surface::New();
       interpolationResult->Expand(segmentationImage->GetTimeSteps());
 
       auto geometry = segmentationImage->GetTimeGeometry()->Clone();
       geometry->ReplaceTimeStepGeometries(mitk::Geometry3D::New());
       interpolationResult->SetTimeGeometry(geometry);
 
       interpolationResult->SetVtkPolyData(imageToSurfaceFilter->GetOutput()->GetVtkPolyData(), timeStep);
       interpolationResult->DisconnectPipeline();
 
       // Last progress step
       mitk::ProgressBar::GetInstance()->Progress(20);
 
     }
   }
   catch (const Exception& e)
   {
     MITK_ERROR << "Interpolation failed: " << e.what();
     interpolationResult = nullptr;
   }
 
   SetCPICacheSurface(interpolationResult, segmentationImage, labelValue, timeStep);
 }
 
 mitk::Surface::Pointer mitk::SurfaceInterpolationController::GetInterpolationResult(const LabelSetImage* segmentationImage, LabelSetImage::LabelValueType labelValue, TimeStepType timeStep)
 {
   if (nullptr == segmentationImage)
   {
     mitkThrow() << "Cannot interpolate contours. No valid segmentation passed.";
   }
 
   std::shared_lock<std::shared_mutex> guard(cpiMutex);
 
   if (cpiMap.find(segmentationImage) == cpiMap.end())
   {
     mitkThrow() << "Cannot interpolate contours. Passed segmentation is not registered at controller.";
   }
 
   if (!segmentationImage->ExistLabel(labelValue))
   {
     mitkThrow() << "Cannot interpolate contours. None existant label request. Invalid label:" << labelValue;
   }
 
   if (!segmentationImage->GetTimeGeometry()->IsValidTimeStep(timeStep))
   {
     mitkThrow() << "Cannot interpolate contours. No valid time step requested. Invalid time step:" << timeStep;
   }
 
   const auto& currentImageContours = cpiMap.at(segmentationImage);
 
   auto finding = currentImageContours.find(labelValue);
   if (finding == currentImageContours.end())
   {
     return nullptr;
   }
 
   const auto& currentLabelContoursMap = finding->second;
 
   auto tsfinding = currentLabelContoursMap.find(timeStep);
   if (tsfinding == currentLabelContoursMap.end())
   {
     return nullptr;
   }
 
   return tsfinding->second.cachedSurface;
 }
 
 void mitk::SurfaceInterpolationController::SetDataStorage(DataStorage::Pointer ds)
 {
   m_DataStorage = ds;
 }
 
 void mitk::SurfaceInterpolationController::SetDistanceImageVolume(unsigned int distImgVolume)
 {
   m_DistanceImageVolume = distImgVolume;
 }
 
 mitk::LabelSetImage* mitk::SurfaceInterpolationController::GetCurrentSegmentation()
 {
   return m_SelectedSegmentation.Lock();
 }
 
 unsigned int mitk::SurfaceInterpolationController::GetNumberOfInterpolationSessions()
 {
   return cpiMap.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::SetCurrentInterpolationSession(mitk::LabelSetImage* currentSegmentationImage)
 {
   auto selectedSegmentation = m_SelectedSegmentation.Lock();
 
   if (currentSegmentationImage == selectedSegmentation)
   {
     return;
   }
 
   m_SelectedSegmentation = currentSegmentationImage;
   selectedSegmentation = m_SelectedSegmentation.Lock();
 
   if (selectedSegmentation.IsNotNull())
   {
     std::lock_guard<std::shared_mutex> guard(cpiMutex);
 
     auto it = cpiMap.find(selectedSegmentation);
     if (it == cpiMap.end())
     {
       cpiMap[selectedSegmentation] = CPITimeStepLabelMap();
 
       auto command = itk::MemberCommand<SurfaceInterpolationController>::New();
       command->SetCallbackFunction(this, &SurfaceInterpolationController::OnSegmentationDeleted);
       segmentationObserverTags[selectedSegmentation] = selectedSegmentation->AddObserver(itk::DeleteEvent(), command);
-      selectedSegmentation->AddLabelRemovedListener(mitk::MessageDelegate1<SurfaceInterpolationController, mitk::LabelSetImage::LabelValueType>(
-        this, &SurfaceInterpolationController::OnRemoveLabel));
+      auto& controller = *this;
+      const auto sender = selectedSegmentation.GetPointer();
+      labelRemovedObserverTags[selectedSegmentation] = selectedSegmentation->AddObserver(mitk::LabelRemovedEvent(), [&controller, sender](const itk::EventObject& event)
+        {
+          controller.OnRemoveLabel(sender, event);
+        });
     }
   }
 }
 
 void mitk::SurfaceInterpolationController::RemoveInterpolationSession(const mitk::LabelSetImage* segmentationImage)
 {
   if (nullptr != segmentationImage)
   {
     auto selectedSegmentation = m_SelectedSegmentation.Lock();
     if (selectedSegmentation == segmentationImage)
     {
       this->SetCurrentInterpolationSession(nullptr);
     }
 
     {
       std::lock_guard<std::shared_mutex> guard(cpiMutex);
       this->RemoveObserversInternal(segmentationImage);
       cpiMap.erase(segmentationImage);
       if (m_DataStorage.IsNotNull())
       {
         auto nodes = this->GetPlaneGeometryNodeFromDataStorage(GetSegmentationImageNodeInternal(this->m_DataStorage, segmentationImage));
         this->m_DataStorage->Remove(nodes);
       }
     }
 
   }
 }
 
 void mitk::SurfaceInterpolationController::RemoveObserversInternal(const mitk::LabelSetImage* segmentationImage)
 {
   auto pos = segmentationObserverTags.find(const_cast<mitk::LabelSetImage*>(segmentationImage));
   if (pos != segmentationObserverTags.end())
   {
     pos->first->RemoveObserver((*pos).second);
-    segmentationImage->RemoveLabelRemovedListener(mitk::MessageDelegate1<SurfaceInterpolationController, mitk::LabelSetImage::LabelValueType>(
-      this, &SurfaceInterpolationController::OnRemoveLabel));
     segmentationObserverTags.erase(const_cast<mitk::LabelSetImage*>(segmentationImage));
   }
+
+  auto pos2 = labelRemovedObserverTags.find(const_cast<mitk::LabelSetImage*>(segmentationImage));
+  if (pos2 != labelRemovedObserverTags.end())
+  {
+    pos2->first->RemoveObserver((*pos2).second);
+    labelRemovedObserverTags.erase(const_cast<mitk::LabelSetImage*>(segmentationImage));
+  }
 }
 
 void mitk::SurfaceInterpolationController::RemoveAllInterpolationSessions()
 {
   while (!cpiMap.empty())
   {
     this->RemoveInterpolationSession(cpiMap.begin()->first);
   }
 }
 
-void mitk::SurfaceInterpolationController::RemoveContours(mitk::Label::PixelType label,
+void mitk::SurfaceInterpolationController::RemoveContours(const LabelSetImage* segmentationImage,
+  mitk::Label::PixelType label,
   TimeStepType timeStep)
 {
-  auto selectedSegmentation = m_SelectedSegmentation.Lock();
-  if (selectedSegmentation.IsNull())
+  if (nullptr == segmentationImage)
   {
-    mitkThrow() << "Cannot remove contours. No valid segmentation selected.";
+    mitkThrow() << "Cannot remove contours. No valid segmentation passed.";
   }
 
   std::lock_guard<std::shared_mutex> guard(cpiMutex);
 
-  auto& cpiLabelMap = cpiMap[selectedSegmentation];
-  auto finding = cpiLabelMap.find(label);
+  auto segfinding = cpiMap.find(segmentationImage);
 
-  if (finding != cpiLabelMap.end())
+  if (segfinding != cpiMap.end())
   {
-    finding->second.erase(timeStep);
-  }
+    auto& cpiLabelMap = cpiMap[segmentationImage];
+    auto finding = cpiLabelMap.find(label);
 
-  if (m_DataStorage.IsNotNull())
-  {
-    //remove relevant plane nodes
-    auto nodes = this->GetPlaneGeometryNodeFromDataStorage(GetSegmentationImageNodeInternal(this->m_DataStorage, selectedSegmentation), label, timeStep);
+    if (finding != cpiLabelMap.end())
+    {
+      cpiLabelMap[label].erase(timeStep);
+    }
 
-    this->m_DataStorage->Remove(nodes);
+    if (m_DataStorage.IsNotNull())
+    {
+      //remove relevant plane nodes
+      auto nodes = this->GetPlaneGeometryNodeFromDataStorage(GetSegmentationImageNodeInternal(this->m_DataStorage, segmentationImage), label, timeStep);
+
+      this->m_DataStorage->Remove(nodes);
+    }
+    this->Modified();
   }
-  this->Modified();
 }
 
-void mitk::SurfaceInterpolationController::RemoveContours(mitk::Label::PixelType label)
+void mitk::SurfaceInterpolationController::RemoveContours(const LabelSetImage* segmentationImage,
+  mitk::Label::PixelType label)
 {
-  auto selectedSegmentation = m_SelectedSegmentation.Lock();
-  if (selectedSegmentation.IsNull())
+  if (nullptr == segmentationImage)
   {
-    mitkThrow() << "Cannot remove contours. No valid segmentation selected.";
+    mitkThrow() << "Cannot remove contours. No valid segmentation passed.";
   }
 
   std::lock_guard<std::shared_mutex> guard(cpiMutex);
-  cpiMap[selectedSegmentation].erase(label);
 
-  if (m_DataStorage.IsNotNull())
+  auto finding = cpiMap.find(segmentationImage);
+  if (finding != cpiMap.end())
   {
-    //remove relevant plane nodes
-    auto nodes = this->GetPlaneGeometryNodeFromDataStorage(GetSegmentationImageNodeInternal(this->m_DataStorage, selectedSegmentation), label);
-    this->m_DataStorage->Remove(nodes);
+    cpiMap[segmentationImage].erase(label);
+
+    if (m_DataStorage.IsNotNull())
+    {
+      //remove relevant plane nodes
+      auto nodes = this->GetPlaneGeometryNodeFromDataStorage(GetSegmentationImageNodeInternal(this->m_DataStorage, segmentationImage), label);
+      this->m_DataStorage->Remove(nodes);
+    }
+    this->Modified();
   }
-  this->Modified();
 }
 
 
 void mitk::SurfaceInterpolationController::OnSegmentationDeleted(const itk::Object *caller,
                                                                  const itk::EventObject & /*event*/)
 {
   auto tempImage = dynamic_cast<mitk::LabelSetImage *>(const_cast<itk::Object *>(caller));
   if (tempImage)
   {
     this->RemoveInterpolationSession(tempImage);
   }
 }
 
-void mitk::SurfaceInterpolationController::OnRemoveLabel(mitk::Label::PixelType removedLabelValue)
+void mitk::SurfaceInterpolationController::OnRemoveLabel(const itk::Object* caller, const itk::EventObject& event)
 {
-  auto selectedSegmentation = m_SelectedSegmentation.Lock();
+  auto sendingSegmentation = dynamic_cast<const LabelSetImage*>(caller);
 
-  if (selectedSegmentation.IsNotNull())
+  auto removeEvent = dynamic_cast<const mitk::LabelRemovedEvent*>(&event);
+
+  if (nullptr != sendingSegmentation && nullptr != removeEvent)
   {
-    this->RemoveContours(removedLabelValue);
+    this->RemoveContours(sendingSegmentation, removeEvent->GetLabelValue());
   }
 }
 
 mitk::SurfaceInterpolationController::CPIVector* mitk::SurfaceInterpolationController::GetContours(LabelSetImage::LabelValueType labelValue, TimeStepType timeStep)
 {
   auto selectedSegmentation = m_SelectedSegmentation.Lock();
 
   if (selectedSegmentation == nullptr)
     return nullptr;
 
   std::shared_lock<std::shared_mutex> guard(cpiMutex);
 
   auto labelFinding = cpiMap[selectedSegmentation].find(labelValue);
 
   if (labelFinding != cpiMap[selectedSegmentation].end())
   {
     auto tsFinding = labelFinding->second.find(timeStep);
 
     if (tsFinding != labelFinding->second.end())
     {
       return &(tsFinding->second.cpis);
     }
   }
 
   return nullptr;
 }
 
 std::vector<mitk::LabelSetImage::LabelValueType> mitk::SurfaceInterpolationController::GetAffectedLabels(const LabelSetImage* seg, TimeStepType timeStep, const PlaneGeometry* plane) const
 {
   std::lock_guard<std::shared_mutex> guard(cpiMutex);
 
   std::vector<mitk::LabelSetImage::LabelValueType> result;
 
   auto finding = cpiMap.find(seg);
   if (finding == cpiMap.end()) return result;
   const auto& currentImageContours = cpiMap[seg];
 
   for (const auto& [label, contours] : currentImageContours)
   {
     auto tsFinding = contours.find(timeStep);
     if (tsFinding != contours.end())
     {
       const auto& cpis = contours.at(timeStep).cpis;
       auto finding = std::find_if(cpis.begin(), cpis.end(), [plane](const ContourPositionInformation& element) {return plane->IsOnPlane(element.Plane); });
 
       if (finding != cpis.end())
       {
         result.push_back(label);
       }
     }
   }
   return result;
 }
 
 
 void mitk::SurfaceInterpolationController::CompleteReinitialization(const std::vector<ContourPositionInformation>& newCPIs)
 {
   this->ClearInterpolationSession();
 
   //  Now the layers should be empty and the new layers can be added.
   this->AddNewContours(newCPIs, true);
 }
 
 void mitk::SurfaceInterpolationController::ClearInterpolationSession()
 {
   auto selectedSegmentation = m_SelectedSegmentation.Lock();
 
   if (selectedSegmentation != nullptr)
   {
     std::lock_guard<std::shared_mutex> guard(cpiMutex);
     cpiMap[selectedSegmentation].clear();
   }
 }
diff --git a/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.h b/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.h
index 3311bde4aa..5f8bae781e 100644
--- a/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.h
+++ b/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.h
@@ -1,260 +1,260 @@
 /*============================================================================
 
 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 <mitkLabelSetImage.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);
 
     struct MITKSURFACEINTERPOLATION_EXPORT ContourPositionInformation
     {
       Surface::ConstPointer Contour;
       PlaneGeometry::ConstPointer Plane;
       Label::PixelType LabelValue;
       TimeStepType TimeStep;
 
       ContourPositionInformation()
         : Plane(nullptr),
           LabelValue(std::numeric_limits<Label::PixelType>::max()),
           TimeStep(std::numeric_limits<TimeStepType>::max())
       {
       }
 
       ContourPositionInformation(Surface::ConstPointer contour,
         PlaneGeometry::ConstPointer plane,
         Label::PixelType labelValue,
         TimeStepType timeStep)
         :
         Contour(contour),
         Plane(plane),
         LabelValue(labelValue),
         TimeStep(timeStep)
       {
       }
 
       bool IsPlaceHolder() const
       {
         return Contour.IsNull();
       }
     };
 
     typedef std::vector<ContourPositionInformation> CPIVector;
 
     static SurfaceInterpolationController *GetInstance();
 
     /**
      * @brief Adds new extracted contours to the list. If one or more contours at a given position
      *        already exist they will be updated respectively
      */
     void AddNewContours(const std::vector<ContourPositionInformation>& newCPIs, bool reinitializeAction = false, bool silent = false);
 
     /**
      * @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, bool keepPlaceholderForUndo = false);
 
     void RemoveObservers();
 
     /**
      * @brief Performs the interpolation.
      *
      */
     void Interpolate(const LabelSetImage* segmentationImage, LabelSetImage::LabelValueType labelValue, TimeStepType timeStep);
 
     /**
      * @brief Get the Result of the interpolation operation.
      *
      * @return mitk::Surface::Pointer
      */
     mitk::Surface::Pointer GetInterpolationResult(const LabelSetImage* segmentationImage, LabelSetImage::LabelValueType labelValue, TimeStepType timeStep);
 
     /**
      * @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::LabelSetImage* GetCurrentSegmentation();
 
     void SetDataStorage(DataStorage::Pointer ds);
 
     /**
      * Sets the current list of contourpoints which is used for the surface interpolation
      * @param currentSegmentationImage The current selected segmentation
      */
     void SetCurrentInterpolationSession(LabelSetImage* currentSegmentationImage);
 
     /**
      * @brief Remove interpolation session
      * @param segmentationImage the session to be removed
      */
     void RemoveInterpolationSession(const LabelSetImage* segmentationImage);
 
     /**
      * @brief Removes all sessions
      */
     void RemoveAllInterpolationSessions();
 
     /**
      * @brief Get the Contours at a certain timeStep and layerID.
      *
      * @param timeStep Time Step from which to get the contours.
      * @param labelValue label from which to get the contours.
      * @return std::vector<ContourPositionInformation> Returns contours.
      */
     CPIVector* GetContours(LabelSetImage::LabelValueType labelValue, TimeStepType timeStep);
 
     std::vector<LabelSetImage::LabelValueType> GetAffectedLabels(const LabelSetImage* seg, TimeStepType timeStep, const PlaneGeometry* plane) const;
 
     /**
      * @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<ContourPositionInformation>& newCPIs);
 
     /**
      * @brief Removes contours of a particular label and at a given time step for the current session/segmentation.
      *
      * @param label Label of contour to remove.
      * @param timeStep Time step in which to remove the contours.
      * @remark if the label or time step does not exist, nothing happens.
      */
-    void RemoveContours(mitk::Label::PixelType label, TimeStepType timeStep);
+    void RemoveContours(const LabelSetImage* segmentationImage, mitk::Label::PixelType label, TimeStepType timeStep);
 
     /**
      * @brief Removes contours of a particular label and at a given time step for the current session/segmentation.
      *
      * @param label Label of contour to remove.
      * @param timeStep Time step in which to remove the contours.
      * @remark if the label or time step does not exist, nothing happens.
      */
-    void RemoveContours(mitk::Label::PixelType label);
+    void RemoveContours(const LabelSetImage* segmentationImage, mitk::Label::PixelType label);
 
     unsigned int GetNumberOfInterpolationSessions();
 
     /**
      * @brief Get the Segmentation Image Node object
      *
      * @return DataNode* returns the DataNode containing the segmentation image.
      */
     mitk::DataNode* GetSegmentationImageNode() const;
 
   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(mitk::Label::PixelType removedLabelValue);
+    void OnRemoveLabel(const itk::Object* caller, const itk::EventObject& event);
 
     /**
      * @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) const;
 
     DataStorage::SetOfObjects::ConstPointer GetPlaneGeometryNodeFromDataStorage(const DataNode* segNode) const;
     DataStorage::SetOfObjects::ConstPointer GetPlaneGeometryNodeFromDataStorage(const DataNode* segNode, LabelSetImage::LabelValueType labelValue) const;
     DataStorage::SetOfObjects::ConstPointer GetPlaneGeometryNodeFromDataStorage(const DataNode* segNode, LabelSetImage::LabelValueType labelValue, TimeStepType timeStep) const;
 
     /**
      * Adds Contours from the active Label to the interpolation pipeline
      */
     void AddActiveLabelContoursForInterpolation(ReduceContourSetFilter* reduceFilter, const LabelSetImage* segmentationImage, LabelSetImage::LabelValueType labelValue, TimeStepType timeStep);
 
     /**
      * @brief Clears the interpolation data structures. Called from CompleteReinitialization().
      *
      */
     void ClearInterpolationSession();
 
     void RemoveObserversInternal(const mitk::LabelSetImage* segmentationImage);
 
     /**
      * @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 AddToCPIMap(ContourPositionInformation& contourInfo, bool reinitializationAction = false);
 
     unsigned int m_DistanceImageVolume;
     mitk::DataStorage::Pointer m_DataStorage;
 
     WeakPointer<LabelSetImage> m_SelectedSegmentation;
   };
 }
 
 #endif
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 ec09f0e2d7..08afd9849a 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp
@@ -1,1098 +1,1107 @@
 /*============================================================================
 
 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 <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)
   {
     // 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();
 
     this->RemoveObserversFromWorkingImage();
 
     // 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<Self>(this, &Self::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<Self>::New();
       command->SetCallbackFunction(this, &Self::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;
 
     this->RemoveObserversFromWorkingImage();
 
     // 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);
     }
 
     // Set new working node
     m_WorkingNode = selectedWorkingNode;
     m_ToolManager->SetWorkingData(m_WorkingNode);
 
     if (m_WorkingNode.IsNotNull())
     {
       this->ApplySelectionModeOnWorkingNode();
 
       // Add visibility observer for the new segmentation node
       auto command = itk::SimpleMemberCommand<Self>::New();
       command->SetCallbackFunction(this, &Self::ValidateSelectionInput);
 
       m_WorkingDataObserverTags[m_WorkingNode] =
         m_WorkingNode->GetProperty("visible")->AddObserver(itk::ModifiedEvent(), command);
 
       this->AddObserversToWorkingImage();
     }
   }
 
   // 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::OnLabelAdded(mitk::LabelSetImage::LabelValueType)
 {
   this->ValidateSelectionInput();
 }
 
 void QmitkSegmentationView::OnLabelRemoved(mitk::LabelSetImage::LabelValueType)
 {
   this->ValidateSelectionInput();
 }
 
 void QmitkSegmentationView::OnGroupRemoved(mitk::LabelSetImage::GroupIndexType)
 {
   this->ValidateSelectionInput();
 }
 
 mitk::LabelSetImage* QmitkSegmentationView::GetWorkingImage()
 {
   if (m_WorkingNode.IsNull())
     return nullptr;
 
   return dynamic_cast<mitk::LabelSetImage*>(m_WorkingNode->GetData());
 }
 
 void QmitkSegmentationView::AddObserversToWorkingImage()
 {
   auto* workingImage = this->GetWorkingImage();
 
   if (workingImage != nullptr)
   {
-    workingImage->AddLabelAddedListener(mitk::MessageDelegate1<Self, mitk::LabelSetImage::LabelValueType>(this, &Self::OnLabelAdded));
-    workingImage->AddLabelRemovedListener(mitk::MessageDelegate1<Self, mitk::LabelSetImage::LabelValueType>(this, &Self::OnLabelRemoved));
-    workingImage->AddGroupRemovedListener(mitk::MessageDelegate1<Self, mitk::LabelSetImage::GroupIndexType>(this, &Self::OnGroupRemoved));
+    auto& widget = *this;
+    m_LabelAddedObserver.Reset(workingImage, mitk::LabelAddedEvent(), [&widget](const itk::EventObject& event)
+      {
+        auto labelEvent = dynamic_cast<const mitk::AnyLabelEvent*>(&event);
+        widget.OnLabelAdded(labelEvent->GetLabelValue());
+      });
+    m_LabelRemovedObserver.Reset(workingImage, mitk::LabelRemovedEvent(), [&widget](const itk::EventObject& event)
+      {
+        auto labelEvent = dynamic_cast<const mitk::AnyLabelEvent*>(&event);
+        widget.OnLabelRemoved(labelEvent->GetLabelValue());
+      });
+
+    m_GroupRemovedObserver.Reset(workingImage, mitk::GroupRemovedEvent(), [&widget](const itk::EventObject& event)
+      {
+        auto groupEvent = dynamic_cast<const mitk::AnyGroupEvent*>(&event);
+        widget.OnGroupRemoved(groupEvent->GetGroupID());
+      });
   }
 }
 
 void QmitkSegmentationView::RemoveObserversFromWorkingImage()
 {
-  auto* workingImage = this->GetWorkingImage();
-
-  if (workingImage != nullptr)
-  {
-    workingImage->RemoveLabelAddedListener(mitk::MessageDelegate1<Self, mitk::LabelSetImage::LabelValueType>(this, &Self::OnLabelAdded));
-    workingImage->RemoveLabelRemovedListener(mitk::MessageDelegate1<Self, mitk::LabelSetImage::LabelValueType>(this, &Self::OnLabelRemoved));
-    workingImage->RemoveGroupRemovedListener(mitk::MessageDelegate1<Self, mitk::LabelSetImage::GroupIndexType>(this, &Self::OnGroupRemoved));
-  }
+  m_LabelAddedObserver.Reset();
+  m_LabelRemovedObserver.Reset();
+  m_GroupRemovedObserver.Reset();
 }
 
 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();
   auto labels = workingImage->GetLabelValuesByGroup(workingImage->GetActiveLayer());
   auto it = std::find(labels.begin(), labels.end(), workingImage->GetActiveLabel()->GetValue());
 
   if (it != labels.end())
     ++it;
 
   if (it == labels.end())
   {
     it = labels.begin();
   }
 
   workingImage->SetActiveLabel(*it);
   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::MultiLabelIOHelper::LoadLabelSetImagePreset(labelSetPreset, newLabelSetImage))
   {
     auto newLabel = mitk::LabelSetImageHelper::CreateNewLabel(newLabelSetImage);
 
     if (!m_DefaultLabelNaming)
       QmitkNewSegmentationDialog::DoRenameLabel(newLabel, nullptr, m_Parent);
 
     newLabelSetImage->AddLabel(newLabel, newLabelSetImage->GetActiveLayer());
   }
 
   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::OnCurrentLabelSelectionChanged(QmitkMultiLabelManager::LabelValueVectorType labels)
 {
   auto segmentation = this->GetCurrentSegmentation();
 
   const auto labelValue = labels.front();
   if (nullptr == segmentation->GetActiveLabel() || labelValue != segmentation->GetActiveLabel()->GetValue())
   {
     segmentation->SetActiveLabel(labelValue);
     mitk::RenderingManager::GetInstance()->RequestUpdateAll();
   }
   m_Controls->slicesInterpolator->SetActiveLabelValue(labelValue);
 }
 
 void QmitkSegmentationView::OnGoToLabel(mitk::LabelSetImage::LabelValueType /*label*/, const mitk::Point3D& pos)
 {
   if (m_RenderWindowPart)
   {
     m_RenderWindowPart->SetSelectedPosition(pos);
   }
 }
 
 void QmitkSegmentationView::OnLabelRenameRequested(mitk::Label* label, bool rename) const
 {
   auto segmentation = this->GetCurrentSegmentation();
 
   if (rename)
   {
     QmitkNewSegmentationDialog::DoRenameLabel(label, segmentation, this->m_Parent, QmitkNewSegmentationDialog::Mode::RenameLabel);
     return;
   }
 
   QmitkNewSegmentationDialog::DoRenameLabel(label, nullptr, 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, &Self::OnVisibilityShortcutActivated);
    QShortcut* labelToggleShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key::Key_L, Qt::CTRL | Qt::Key::Key_I), parent);
    connect(labelToggleShortcut, &QShortcut::activated, this, &Self::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, &Self::OnReferenceSelectionChanged);
    connect(m_Controls->workingNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged,
            this, &Self::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' 'Segment Anything'");
    QString segTools3D = tr("Threshold 'UL Threshold' Otsu 'Region Growing 3D' Picking GrowCut TotalSegmentator");
 
 #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());
    connect(m_Controls->toolSelectionBox2D, &QmitkToolSelectionBox::ToolSelected,
            this, &Self::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->slicesInterpolator->SetDataStorage(this->GetDataStorage());
 
    // create general signal / slot connections
    connect(m_Controls->newSegmentationButton, &QToolButton::clicked, this, &Self::OnNewSegmentation);
 
    connect(m_Controls->slicesInterpolator, &QmitkSlicesInterpolator::SignalShowMarkerNodes, this, &Self::OnShowMarkerNodes);
 
    connect(m_Controls->multiLabelWidget, &QmitkMultiLabelManager::CurrentSelectionChanged, this, &Self::OnCurrentLabelSelectionChanged);
    connect(m_Controls->multiLabelWidget, &QmitkMultiLabelManager::GoToLabel, this, &Self::OnGoToLabel);
    connect(m_Controls->multiLabelWidget, &QmitkMultiLabelManager::LabelRenameRequested, this, &Self::OnLabelRenameRequested);
 
    auto command = itk::SimpleMemberCommand<Self>::New();
    command->SetCallbackFunction(this, &Self::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<Self>(this, &Self::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<Self>(this, &Self::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->multiLabelWidget->SetDefaultLabelNaming(m_DefaultLabelNaming);
 
     bool compactView = prefs->GetBool("compact view", false);
     int numberOfColumns = compactView ? 6 : 4;
 
     m_Controls->toolSelectionBox2D->SetLayoutColumns(numberOfColumns);
     m_Controls->toolSelectionBox2D->SetShowNames(!compactView);
 
     m_Controls->toolSelectionBox3D->SetLayoutColumns(numberOfColumns);
     m_Controls->toolSelectionBox3D->SetShowNames(!compactView);
   }
 
   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;
 
   auto image = dynamic_cast<mitk::LabelSetImage*>(node->GetData());
   mitk::SurfaceInterpolationController::GetInstance()->RemoveInterpolationSession(image);
 }
 
 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);
   }
 
   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->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->multiLabelWidget->setEnabled(true);
     m_Controls->toolSelectionBox2D->setEnabled(true);
     m_Controls->toolSelectionBox3D->setEnabled(true);
 
     auto labelSetImage = dynamic_cast<mitk::LabelSetImage *>(workingNode->GetData());
     numberOfLabels = labelSetImage->GetTotalNumberOfLabels();
 
     if (numberOfLabels > 0)
       m_Controls->slicesInterpolator->setEnabled(true);
 
     m_Controls->multiLabelWidget->SetMultiLabelSegmentation(dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData()));
 
     if (!m_Controls->multiLabelWidget->GetSelectedLabels().empty())
     {
       m_Controls->slicesInterpolator->SetActiveLabelValue(m_Controls->multiLabelWidget->GetSelectedLabels().front());
     }
   }
   else
   {
     m_Controls->multiLabelWidget->SetMultiLabelSegmentation(nullptr);
   }
 
   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 a4f3ea2571..3f0596941c 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h
@@ -1,175 +1,180 @@
 /*============================================================================
 
 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 <mitkITKEventObserverGuard.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 properties 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 creating new ones, 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 OnCurrentLabelSelectionChanged(QmitkMultiLabelManager::LabelValueVectorType labels);
 
   void OnGoToLabel(mitk::LabelSetImage::LabelValueType label, const mitk::Point3D&);
   void OnLabelRenameRequested(mitk::Label* label, bool rename) const;
 
   void OnLabelAdded(mitk::LabelSetImage::LabelValueType labelValue);
   void OnLabelRemoved(mitk::LabelSetImage::LabelValueType labelValue);
   void OnGroupRemoved(mitk::LabelSetImage::GroupIndexType groupIndex);
 
 private:
 
   using Self = QmitkSegmentationView;
 
   mitk::LabelSetImage* GetWorkingImage();
   void AddObserversToWorkingImage();
   void RemoveObserversFromWorkingImage();
 
   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();
 
   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 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;
+
+  mitk::ITKEventObserverGuard m_LabelAddedObserver;
+  mitk::ITKEventObserverGuard m_LabelRemovedObserver;
+  mitk::ITKEventObserverGuard m_GroupRemovedObserver;
 };
 
 #endif