diff --git a/Modules/Multilabel/autoload/IO/mitkSegmentationTaskIO.cpp b/Modules/Multilabel/autoload/IO/mitkSegmentationTaskIO.cpp
index bb5c3f0ef3..19ce017be3 100644
--- a/Modules/Multilabel/autoload/IO/mitkSegmentationTaskIO.cpp
+++ b/Modules/Multilabel/autoload/IO/mitkSegmentationTaskIO.cpp
@@ -1,238 +1,259 @@
 /*============================================================================
 
 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 "mitkSegmentationTaskIO.h"
 #include "mitkMultilabelIOMimeTypes.h"
 
 #include <mitkSegmentationTask.h>
 
 #include <nlohmann/json.hpp>
 
 #include <filesystem>
 #include <fstream>
 
 namespace mitk
 {
   void to_json(nlohmann::json& json, const SegmentationTask::Subtask& subtask)
   {
-    const auto name = subtask.GetName();
+    if (subtask.HasName())
+      json["Name"] = subtask.GetName();
 
-    if (!name.empty())
-      json["Name"] = name;
+    if (subtask.HasDescription())
+      json["Description"] = subtask.GetDescription();
 
-    const auto description = subtask.GetDescription();
+    if (subtask.HasImage())
+      json["Image"] = subtask.GetImage();
 
-    if (!description.empty())
-      json["Description"] = description;
+    if (subtask.HasSegmentation())
+      json["Segmentation"] = subtask.GetSegmentation();
 
-    const auto image = subtask.GetImage();
+    if (subtask.HasLabelName())
+      json["LabelName"] = subtask.GetLabelName();
 
-    if (!image.empty())
-      json["Image"] = image;
+    if (subtask.HasPreset())
+      json["Preset"] = subtask.GetPreset();
 
-    const auto segmentation = subtask.GetSegmentation();
+    if (subtask.HasResult())
+      json["Result"] = subtask.GetResult();
 
-    if (!segmentation.empty())
-      json["Segmentation"] = segmentation;
+    if (subtask.HasDynamic())
+      json["Dynamic"] = subtask.GetDynamic();
+  }
 
-    const auto labelName = subtask.GetLabelName();
+  void from_json(const nlohmann::json& json, SegmentationTask::Subtask& subtask)
+  {
+    auto iter = json.find("Name");
 
-    if (!labelName.empty())
-      json["LabelName"] = labelName;
+    if (iter != json.end())
+      subtask.SetName(json["Name"].get<std::string>());
 
-    const auto preset = subtask.GetPreset();
+    iter = json.find("Description");
 
-    if (!preset.empty())
-      json["Preset"] = preset;
+    if (iter != json.end())
+      subtask.SetDescription(json["Description"].get<std::string>());
 
-    const auto result = subtask.GetResult();
+    iter = json.find("Image");
 
-    if (!result.empty())
-      json["Result"] = result;
-  }
+    if (iter != json.end())
+      subtask.SetImage(json["Image"].get<std::string>());
 
-  void from_json(const nlohmann::json& json, SegmentationTask::Subtask& subtask)
-  {
-    subtask.SetName(json.value("Name", ""));
-    subtask.SetDescription(json.value("Description", ""));
-    subtask.SetImage(json.value("Image", ""));
-    subtask.SetSegmentation(json.value("Segmentation", ""));
-    subtask.SetLabelName(json.value("LabelName", ""));
-    subtask.SetPreset(json.value("Preset", ""));
-    subtask.SetResult(json.value("Result", ""));
+    iter = json.find("Segmentation");
+
+    if (iter != json.end())
+      subtask.SetSegmentation(json["Segmentation"].get<std::string>());
+
+    iter = json.find("LabelName");
+
+    if (iter != json.end())
+      subtask.SetLabelName(json["LabelName"].get<std::string>());
+
+    iter = json.find("Preset");
+
+    if (iter != json.end())
+      subtask.SetPreset(json["Preset"].get<std::string>());
+
+    iter = json.find("Result");
+
+    if (iter != json.end())
+      subtask.SetResult(json["Result"].get<std::string>());
+
+    iter = json.find("Dynamic");
+
+    if (iter != json.end())
+      subtask.SetDynamic(json["Dynamic"].get<bool>());
   }
 }
 
 mitk::SegmentationTaskIO::SegmentationTaskIO()
   : AbstractFileIO(SegmentationTask::GetStaticNameOfClass(), MitkMultilabelIOMimeTypes::SEGMENTATIONTASK_MIMETYPE(), "MITK Segmentation Task")
 {
   this->RegisterService();
 }
 
 std::vector<mitk::BaseData::Pointer> mitk::SegmentationTaskIO::DoRead()
 {
   auto* stream = this->GetInputStream();
   std::ifstream fileStream;
 
   if (nullptr == stream)
   {
     auto filename = this->GetInputLocation();
 
     if (filename.empty() || !std::filesystem::exists(filename))
       mitkThrow() << "Invalid or nonexistent filename: \"" << filename << "\"!";
 
     fileStream.open(filename);
 
     if (!fileStream.is_open())
       mitkThrow() << "Could not open file \"" << filename << "\" for reading!";
 
     stream = &fileStream;
   }
 
   nlohmann::json json;
 
   try
   {
     json = nlohmann::json::parse(*stream);
   }
   catch (const nlohmann::json::exception& e)
   {
     mitkThrow() << e.what();
   }
 
   if (!json.is_object())
     mitkThrow() << "Unknown file format (expected JSON object as root)!";
 
   if ("MITK Segmentation Task" != json.value("FileFormat", ""))
     mitkThrow() << "Unknown file format (expected \"MITK Segmentation Task\")!";
 
   if (1 != json.value<int>("Version", 0))
     mitkThrow() << "Unknown file format version (expected \"1\")!";
 
   if (!json.contains("Subtasks") || !json["Subtasks"].is_array())
     mitkThrow() << "Subtasks array not found!";
 
   auto segmentationTask = SegmentationTask::New();
 
   if (json.contains("Name"))
     segmentationTask->SetProperty("name", StringProperty::New(json["Name"].get<std::string>()));
 
   try
   {
     if (json.contains("Defaults"))
     {
       segmentationTask->SetDefaults(json["Defaults"].get<SegmentationTask::Subtask>());
 
-      if (!segmentationTask->GetDefaults().GetResult().empty())
+      if (segmentationTask->GetDefaults().HasResult())
         mitkThrow() << "Defaults must not contain \"Result\"!";
     }
 
     for (const auto& subtask : json["Subtasks"])
     {
       auto i = segmentationTask->AddSubtask(subtask.get<SegmentationTask::Subtask>());
 
-      std::filesystem::path imagePath(segmentationTask->GetImage(i));
-
-      if (imagePath.empty())
+      if (!segmentationTask->HasImage(i))
         mitkThrow() << "Subtask " << i << " must contain \"Image\"!";
 
+      std::filesystem::path imagePath(segmentationTask->GetImage(i));
+
       if (imagePath.is_relative())
       {
         auto inputLocation = this->GetInputLocation();
 
         /* If we have access to properties, we are reading from an MITK scene
          * file. In this case, paths are still relative to the original input
          * location, which is preserved in the properties.
          */
 
         const auto* properties = this->GetProperties();
 
         if (properties != nullptr)
           properties->GetStringProperty("MITK.IO.reader.inputlocation", inputLocation);
 
         imagePath = std::filesystem::path(inputLocation).remove_filename() / imagePath;
       }
 
       if (!std::filesystem::exists(imagePath))
         mitkThrow() << "Referenced image \"" << imagePath << "\" in subtask " << i << " does not exist!";
 
-      if (segmentationTask->GetResult(i).empty())
+      if (!segmentationTask->HasResult(i))
         mitkThrow() << "Subtask " << i << " must contain \"Result\"!";
     }
   }
   catch (const nlohmann::json::type_error& e)
   {
     mitkThrow() << e.what();
   }
 
   std::vector<BaseData::Pointer> result;
   result.push_back(segmentationTask.GetPointer());
 
   return result;
 }
 
 void mitk::SegmentationTaskIO::Write()
 {
   auto segmentationTask = dynamic_cast<const SegmentationTask*>(this->GetInput());
 
   if (nullptr == segmentationTask)
     mitkThrow() << "Invalid input for writing!";
 
   if (segmentationTask->GetNumberOfSubtasks() == 0)
     mitkThrow() << "No subtasks found!";
 
   auto* stream = this->GetOutputStream();
   std::ofstream fileStream;
 
   if (nullptr == stream)
   {
     auto filename = this->GetOutputLocation();
 
     if (filename.empty())
       mitkThrow() << "Neither an output stream nor an output filename was specified!";
 
     fileStream.open(filename);
 
     if (!fileStream.is_open())
       mitkThrow() << "Could not open file \"" << filename << "\" for writing!";
 
     stream = &fileStream;
   }
 
   if (!stream->good())
     mitkThrow() << "Stream for writing is not good!";
 
   nlohmann::ordered_json json = {
     { "FileFormat", "MITK Segmentation Task" },
     { "Version", 1 },
     { "Name", segmentationTask->GetProperty("name")->GetValueAsString() }
   };
 
   nlohmann::json defaults = segmentationTask->GetDefaults();
 
   if (!defaults.is_null())
     json["Defaults"] = defaults;
 
   nlohmann::json subtasks;
 
   for (const auto& subtask : *segmentationTask)
     subtasks.push_back(subtask);
 
   json["Subtasks"] = subtasks;
 
   *stream << std::setw(2) << json << std::endl;
 }
 
 mitk::SegmentationTaskIO* mitk::SegmentationTaskIO::IOClone() const
 {
   return new SegmentationTaskIO(*this);
 }
diff --git a/Modules/Multilabel/mitkSegmentationTask.h b/Modules/Multilabel/mitkSegmentationTask.h
index 5178269662..b726aec8d8 100644
--- a/Modules/Multilabel/mitkSegmentationTask.h
+++ b/Modules/Multilabel/mitkSegmentationTask.h
@@ -1,99 +1,102 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #ifndef mitkSegmentationTask_h
 #define mitkSegmentationTask_h
 
 #include <mitkBaseData.h>
 #include <mitkSegmentationTaskMacros.h>
 
 #include <MitkMultilabelExports.h>
 
 #include <filesystem>
+#include <optional>
 
 namespace mitk
 {
   class MITKMULTILABEL_EXPORT SegmentationTask : public BaseData
   {
   public:
     class MITKMULTILABEL_EXPORT Subtask
     {
     public:
       Subtask();
       ~Subtask();
 
       void SetDefaults(const Subtask* defaults);
 
-      mitkSubtaskValueMacro(Name)
-      mitkSubtaskValueMacro(Description)
-      mitkSubtaskValueMacro(Image)
-      mitkSubtaskValueMacro(Segmentation)
-      mitkSubtaskValueMacro(LabelName)
-      mitkSubtaskValueMacro(Preset)
-      mitkSubtaskValueMacro(Result)
+      mitkSegmentationSubtaskValueMacro(std::string, Name)
+      mitkSegmentationSubtaskValueMacro(std::string, Description)
+      mitkSegmentationSubtaskValueMacro(std::string, Image)
+      mitkSegmentationSubtaskValueMacro(std::string, Segmentation)
+      mitkSegmentationSubtaskValueMacro(std::string, LabelName)
+      mitkSegmentationSubtaskValueMacro(std::string, Preset)
+      mitkSegmentationSubtaskValueMacro(std::string, Result)
+      mitkSegmentationSubtaskValueMacro(bool, Dynamic)
 
     private:
       const Subtask* m_Defaults;
     };
 
     mitkClassMacro(SegmentationTask, BaseData)
     itkFactorylessNewMacro(Self)
     itkCloneMacro(Self)
 
-    mitkTaskValueMacro(Name)
-    mitkTaskValueMacro(Description)
-    mitkTaskValueMacro(Image)
-    mitkTaskValueMacro(Segmentation)
-    mitkTaskValueMacro(LabelName)
-    mitkTaskValueMacro(Preset)
-    mitkTaskValueMacro(Result)
+    mitkSegmentationTaskValueMacro(std::string, Name)
+    mitkSegmentationTaskValueMacro(std::string, Description)
+    mitkSegmentationTaskValueMacro(std::string, Image)
+    mitkSegmentationTaskValueMacro(std::string, Segmentation)
+    mitkSegmentationTaskValueMacro(std::string, LabelName)
+    mitkSegmentationTaskValueMacro(std::string, Preset)
+    mitkSegmentationTaskValueMacro(std::string, Result)
+    mitkSegmentationTaskValueMacro(bool, Dynamic)
 
     size_t GetNumberOfSubtasks() const;
     size_t AddSubtask(const Subtask& subtask);
     const Subtask* GetSubtask(size_t index) const;
     Subtask* GetSubtask(size_t index);
 
     const Subtask& GetDefaults() const;
     void SetDefaults(const Subtask& defaults);
 
     bool IsDone() const;
     bool IsDone(size_t index) const;
 
     std::filesystem::path GetInputLocation() const;
     std::filesystem::path GetBasePath() const;
     std::filesystem::path GetAbsolutePath(const std::filesystem::path& path) const;
 
     std::vector<Subtask>::const_iterator begin() const;
     std::vector<Subtask>::const_iterator end() const;
 
     std::vector<Subtask>::iterator begin();
     std::vector<Subtask>::iterator end();
 
     void SetRequestedRegionToLargestPossibleRegion() override;
     bool RequestedRegionIsOutsideOfTheBufferedRegion() override;
     bool VerifyRequestedRegion() override;
     void SetRequestedRegion(const itk::DataObject*) override;
 
   protected:
     mitkCloneMacro(Self)
 
     SegmentationTask();
     SegmentationTask(const Self& other);
     ~SegmentationTask() override;
 
   private:
     Subtask m_Defaults;
     std::vector<Subtask> m_Subtasks;
   };
 }
 
 #endif
diff --git a/Modules/Multilabel/mitkSegmentationTaskMacros.h b/Modules/Multilabel/mitkSegmentationTaskMacros.h
index 87f445bc85..1d4f456b27 100644
--- a/Modules/Multilabel/mitkSegmentationTaskMacros.h
+++ b/Modules/Multilabel/mitkSegmentationTaskMacros.h
@@ -1,49 +1,61 @@
 /*============================================================================
 
 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 mitkSegmentationTaskMacros_h
 #define mitkSegmentationTaskMacros_h
 
-#define mitkSubtaskGetMacro(x) \
-  std::string Get##x() const { \
-    if (!m_##x.empty()) return m_##x; \
-    if (m_Defaults != nullptr) return m_Defaults->Get##x(); \
-    return ""; \
+#define mitkSegmentationSubtaskHasValueMacro(name) \
+  bool Has##name() const { \
+    return m_##name.has_value(); \
   }
 
-#define mitkSubtaskSetMacro(x) \
-  void Set##x(const std::string& value) { \
-    m_##x = value; \
+#define mitkSegmentationSubtaskGetValueMacro(type, name) \
+  type Get##name() const { \
+    if (m_##name.has_value()) return m_##name.value(); \
+    if (m_Defaults != nullptr && m_Defaults->m_##name.has_value()) return m_Defaults->m_##name.value(); \
+    return type(); \
   }
 
-#define mitkSubtaskValueMacro(x) \
+#define mitkSegmentationSubtaskSetValueMacro(type, name) \
+  void Set##name(const type& value) { \
+    m_##name = value; \
+  }
+
+#define mitkSegmentationSubtaskValueMacro(type, name) \
   public: \
-    mitkSubtaskGetMacro(x) \
-    mitkSubtaskSetMacro(x) \
+    mitkSegmentationSubtaskHasValueMacro(name) \
+    mitkSegmentationSubtaskGetValueMacro(type, name) \
+    mitkSegmentationSubtaskSetValueMacro(type, name) \
   private: \
-    std::string m_##x;
+    std::optional<type> m_##name;
+
+#define mitkSegmentationTaskGetValueMacro(type, name) \
+  type Get##name(size_t index) const { \
+    return index < m_Subtasks.size() ? m_Subtasks[index].Get##name() : type(); \
+  }
 
-#define mitkTaskGetMacro(x) \
-  std::string Get##x(size_t index) const { \
-    return index < m_Subtasks.size() ? m_Subtasks[index].Get##x() : ""; \
+#define mitkSegmentationTaskHasValueMacro(name) \
+  bool Has##name(size_t index) const { \
+    return index < m_Subtasks.size() && (m_Subtasks[index].Has##name() || m_Defaults.Has##name()); \
   }
 
-#define mitkTaskSetDefaultMacro(x) \
-  void SetDefault##x(const std::string& value) { \
-    m_Defaults.Set##x(value); \
+#define mitkSegmentationTaskSetDefaultMacro(type, name) \
+  void SetDefault##name(const type& value) { \
+    m_Defaults.Set##name(value); \
   }
 
-#define mitkTaskValueMacro(x) \
-  mitkTaskGetMacro(x) \
-  mitkTaskSetDefaultMacro(x)
+#define mitkSegmentationTaskValueMacro(type, name) \
+  mitkSegmentationTaskHasValueMacro(name) \
+  mitkSegmentationTaskGetValueMacro(type, name) \
+  mitkSegmentationTaskSetDefaultMacro(type, name)
 
 #endif
diff --git a/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/QmitkSegmentationTaskWidget.cpp b/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/QmitkSegmentationTaskWidget.cpp
index 494d806a1d..2006af2521 100644
--- a/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/QmitkSegmentationTaskWidget.cpp
+++ b/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/QmitkSegmentationTaskWidget.cpp
@@ -1,587 +1,587 @@
 /*============================================================================
 
 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 "QmitkSegmentationTaskWidget.h"
 #include "org_mitk_gui_qt_flow_segmentation_Activator.h"
 
 #include <mitkIDataStorageService.h>
 #include <mitkIOUtil.h>
 #include <mitkLabelSetIOHelper.h>
 #include <mitkLabelSetImageHelper.h>
 #include <mitkNodePredicateDataType.h>
 #include <mitkNodePredicateFunction.h>
 #include <mitkRenderingManager.h>
+#include <mitkSegmentationHelper.h>
 
 #include <QmitkStaticDynamicSegmentationDialog.h>
 #include <QmitkStyleManager.h>
 
 #include <ui_QmitkSegmentationTaskWidget.h>
 
 #include <QFileSystemWatcher>
 
 #include <filesystem>
 
 namespace
 {
   mitk::DataStorage* GetDataStorage()
   {
     auto* pluginContext = org_mitk_gui_qt_flow_segmentation_Activator::GetContext();
     auto dataStorageServiceReference = pluginContext->getServiceReference<mitk::IDataStorageService>();
 
     if (dataStorageServiceReference)
     {
       auto* dataStorageService = pluginContext->getService<mitk::IDataStorageService>(dataStorageServiceReference);
 
       if (dataStorageService != nullptr)
       {
         auto dataStorageReference = dataStorageService->GetDataStorage();
         pluginContext->ungetService(dataStorageServiceReference);
 
         return dataStorageReference->GetDataStorage();
       }
     }
 
     return nullptr;
   }
 
   std::filesystem::path GetInputLocation(const mitk::BaseData* data)
   {
     std::string result;
 
     if (data != nullptr)
       data->GetPropertyList()->GetStringProperty("MITK.IO.reader.inputlocation", result);
 
     return result;
   }
 
   QString ColorString(const QString& string, const QColor& color, const QColor& backgroundColor = QColor::Invalid)
   {
     if (!color.isValid() && !backgroundColor.isValid())
       return string;
 
     auto result = QStringLiteral("<span style=\"");
     QStringList strings;
 
     if (color.isValid())
       strings << QString("color: %1;").arg(color.name());
 
     if (backgroundColor.isValid())
       strings << QString("background-color: %1;").arg(backgroundColor.name());
 
     result += strings.join(' ') + QString("\">%1</span>").arg(string);
 
     return result;
   }
 }
 
 /* This constructor has three objectives:
  *   1. Do widget initialization that cannot be done in the .ui file
  *   2. Connect signals and slots
  *   3. Explicitly trigger a reset to a valid initial widget state
  */
 QmitkSegmentationTaskWidget::QmitkSegmentationTaskWidget(QWidget* parent)
   : QWidget(parent),
     m_Ui(new Ui::QmitkSegmentationTaskWidget),
     m_FileSystemWatcher(new QFileSystemWatcher(this)),
     m_CurrentSubtaskIndex(0)
 {
   m_Ui->setupUi(this);
 
   m_Ui->selectionWidget->SetDataStorage(GetDataStorage());
   m_Ui->selectionWidget->SetSelectionIsOptional(true);
   m_Ui->selectionWidget->SetEmptyInfo(QStringLiteral("Select a segmentation task"));
   m_Ui->selectionWidget->SetAutoSelectNewNodes(true);
   m_Ui->selectionWidget->SetNodePredicate(mitk::TNodePredicateDataType<mitk::SegmentationTask>::New());
 
   m_Ui->progressBar->setStyleSheet(QString("QProgressBar::chunk { background-color: %1; }").arg(QmitkStyleManager::GetIconAccentColor()));
 
   using Self = QmitkSegmentationTaskWidget;
 
   connect(m_Ui->selectionWidget, &QmitkSingleNodeSelectionWidget::CurrentSelectionChanged, this, &Self::OnSelectionChanged);
   connect(m_Ui->previousButton, &QToolButton::clicked, this, &Self::OnPreviousButtonClicked);
   connect(m_Ui->nextButton, &QToolButton::clicked, this, &Self::OnNextButtonClicked);
   connect(m_Ui->loadButton, &QPushButton::clicked, this, &Self::OnLoadButtonClicked);
 
   connect(m_FileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, &Self::OnResultDirectoryChanged);
 
   this->OnSelectionChanged(m_Ui->selectionWidget->GetSelectedNodes());
 }
 
 QmitkSegmentationTaskWidget::~QmitkSegmentationTaskWidget()
 {
 }
 
 mitk::SegmentationTask* QmitkSegmentationTaskWidget::GetTask() const
 {
   return m_Task;
 }
 
 std::optional<size_t> QmitkSegmentationTaskWidget::GetActiveSubtaskIndex() const
 {
   return m_ActiveSubtaskIndex;
 }
 
 size_t QmitkSegmentationTaskWidget::GetCurrentSubtaskIndex() const
 {
   return m_CurrentSubtaskIndex;
 }
 
 /* Make sure that the widget transitions into a valid state whenever the
  * selection changes.
  */
 void QmitkSegmentationTaskWidget::OnSelectionChanged(const QmitkSingleNodeSelectionWidget::NodeList& nodes)
 {
   this->UnloadSubtasks();
   this->ResetControls();
 
   if (!nodes.empty())
   {
     m_TaskNode = nodes.front();
     auto task = dynamic_cast<mitk::SegmentationTask*>(m_TaskNode->GetData());
 
     if (task != nullptr)
     {
       this->OnTaskChanged(task);
       return;
     }
   }
 
   this->SetTask(nullptr);
   m_TaskNode = nullptr;
 }
 
 /* Reset all controls to a default state as a common basis for further
  * adjustments.
  */
 void QmitkSegmentationTaskWidget::ResetControls()
 {
   m_Ui->progressBar->setEnabled(false);
   m_Ui->progressBar->setFormat("");
   m_Ui->progressBar->setValue(0);
   m_Ui->progressBar->setMaximum(1);
 
   m_Ui->previousButton->setEnabled(false);
 
   m_Ui->loadButton->setEnabled(false);
   m_Ui->loadButton->setText(QStringLiteral("Load subtask"));
 
   m_Ui->nextButton->setEnabled(false);
 
   m_Ui->detailsLabel->clear();
 }
 
 /* If the segmentation task changed, reset all member variables to expected
  * default values and reset the file system watcher.
  */
 void QmitkSegmentationTaskWidget::SetTask(mitk::SegmentationTask* task)
 {
   if (m_Task != task)
   {
     m_Task = task;
 
     this->SetCurrentSubtaskIndex(0);
     this->ResetFileSystemWatcher();
   }
 }
 
 void QmitkSegmentationTaskWidget::ResetFileSystemWatcher()
 {
   {
     auto paths = m_FileSystemWatcher->directories();
 
     if (!paths.empty())
       m_FileSystemWatcher->removePaths(paths);
   }
 
   if (m_Task.IsNotNull())
   {
     for (const auto& subtask : *m_Task)
     {
       auto resultPath = m_Task->GetAbsolutePath(subtask.GetResult()).remove_filename();
 
       if (!std::filesystem::exists(resultPath))
       {
         try
         {
           std::filesystem::create_directories(resultPath);
         }
         catch (const std::filesystem::filesystem_error& e)
         {
           MITK_ERROR << e.what();
         }
       }
 
       if (std::filesystem::exists(resultPath))
         m_FileSystemWatcher->addPath(QString::fromStdString(resultPath.string()));
     }
   }
 }
 
 void QmitkSegmentationTaskWidget::OnResultDirectoryChanged(const QString&)
 {
   this->UpdateProgressBar();
   this->UpdateDetailsLabel();
 }
 
 void QmitkSegmentationTaskWidget::UpdateProgressBar()
 {
   int progress = 0;
 
   for (size_t i = 0; i < m_Task->GetNumberOfSubtasks(); ++i)
   {
     if (m_Task->IsDone(i))
       ++progress;
   }
 
   m_Ui->progressBar->setValue(progress);
 }
 
 /* Provided that a valid segmentation task is currently selected and the
  * widget is in its default state, update all controls accordingly.
  */
 void QmitkSegmentationTaskWidget::OnTaskChanged(mitk::SegmentationTask* task)
 {
   this->SetTask(task);
 
   const auto numSubtasks = task->GetNumberOfSubtasks();
 
   m_Ui->progressBar->setMaximum(numSubtasks);
   m_Ui->progressBar->setFormat(QStringLiteral("%v/%m Subtask(s) done"));
   m_Ui->progressBar->setEnabled(true);
 
   this->UpdateProgressBar();
 
   m_Ui->loadButton->setEnabled(true);
 
   if (numSubtasks > 1)
     m_Ui->nextButton->setEnabled(true);
 
   this->OnSubtaskChanged();
 }
 
 /* If possible, change the currently displayed subtask to the previous subtask.
  * Enable/disable navigation buttons according to the subtask's position.
  */
 void QmitkSegmentationTaskWidget::OnPreviousButtonClicked()
 {
   const auto maxIndex = m_Task->GetNumberOfSubtasks() - 1;
 
   if (m_CurrentSubtaskIndex != 0)
   {
     this->SetCurrentSubtaskIndex(m_CurrentSubtaskIndex - 1);
     this->OnSubtaskChanged();
   }
 
   if (m_CurrentSubtaskIndex == 0)
     m_Ui->previousButton->setEnabled(false);
 
   if (m_CurrentSubtaskIndex < maxIndex)
     m_Ui->nextButton->setEnabled(true);
 }
 
 /* If possible, change the currently displayed subtask to the next subtask.
  * Enable/disable navigation buttons according to the subtask's position.
  */
 void QmitkSegmentationTaskWidget::OnNextButtonClicked()
 {
   const auto maxIndex = m_Task->GetNumberOfSubtasks() - 1;
 
   if (m_CurrentSubtaskIndex < maxIndex)
   {
     this->SetCurrentSubtaskIndex(m_CurrentSubtaskIndex + 1);
     this->OnSubtaskChanged();
   }
 
   if (m_CurrentSubtaskIndex != 0)
     m_Ui->previousButton->setEnabled(true);
 
   if (m_CurrentSubtaskIndex >= maxIndex)
     m_Ui->nextButton->setEnabled(false);
 }
 
 /* Update affected controls when the currently displayed subtask changed.
  */
 void QmitkSegmentationTaskWidget::OnSubtaskChanged()
 {
   this->UpdateLoadButton();
   this->UpdateDetailsLabel();
 }
 
 /* Update the load button according to the currently displayed subtask.
  */
 void QmitkSegmentationTaskWidget::UpdateLoadButton()
 {
-  bool active = m_ActiveSubtaskIndex.has_value() && m_ActiveSubtaskIndex.value() == m_CurrentSubtaskIndex;
+  const auto i = m_CurrentSubtaskIndex;
+  bool active = m_ActiveSubtaskIndex.has_value() && m_ActiveSubtaskIndex.value() == i;
 
   auto text = !active
     ? QStringLiteral("Load subtask")
     : QStringLiteral("Subtask");
 
   if (m_Task.IsNotNull())
   {
-    text += QString(" %1/%2").arg(m_CurrentSubtaskIndex + 1).arg(m_Task->GetNumberOfSubtasks());
+    text += QString(" %1/%2").arg(i + 1).arg(m_Task->GetNumberOfSubtasks());
 
-    const auto subtaskName = m_Task->GetName(m_CurrentSubtaskIndex);
-
-    if (!subtaskName.empty())
-      text += QStringLiteral(":\n") + QString::fromStdString(subtaskName);
+    if (m_Task->HasName(i))
+      text += QStringLiteral(":\n") + QString::fromStdString(m_Task->GetName(i));
   }
 
   m_Ui->loadButton->setDisabled(active);
   m_Ui->loadButton->setText(text);
 }
 
 /* Update the details label according to the currently display subtask.
  * The text is composed of the status of the subtask and a variable number
  * of text blocks according to the optional values provided by the subtask.
  */
 void QmitkSegmentationTaskWidget::UpdateDetailsLabel()
 {
-  bool active = m_ActiveSubtaskIndex.has_value() && m_ActiveSubtaskIndex.value() == m_CurrentSubtaskIndex;
-  bool isDone = m_Task->IsDone(m_CurrentSubtaskIndex);
+  const auto i = m_CurrentSubtaskIndex;
+  bool active = m_ActiveSubtaskIndex.has_value() && m_ActiveSubtaskIndex.value() == i;
+  bool isDone = m_Task->IsDone(i);
 
   auto details = QString("<p><b>Status: %1</b> / <b>").arg(active
     ? ColorString("Active", Qt::white, QColor(Qt::green).darker())
     : ColorString("Inactive", Qt::white, QColor(Qt::red).darker()));
 
   details += QString("%1</b></p>").arg(isDone
     ? ColorString("Done", Qt::white, QColor(Qt::green).darker())
     : ColorString("Undone", Qt::white, QColor(Qt::red).darker()));
 
-  const auto description = m_Task->GetDescription(m_CurrentSubtaskIndex);
-
-  if (!description.empty())
-    details += QString("<p><b>Description:</b> %1</p>").arg(QString::fromStdString(description));
+  if (m_Task->HasDescription(i))
+    details += QString("<p><b>Description:</b> %1</p>").arg(QString::fromStdString(m_Task->GetDescription(i)));
 
   QStringList stringList;
 
-  const auto image = m_Task->GetImage(m_CurrentSubtaskIndex);
-
-  if (!image.empty())
-    stringList << QString("<b>Image:</b> %1").arg(QString::fromStdString(image));
-
-  const auto segmentation = m_Task->GetSegmentation(m_CurrentSubtaskIndex);
-
-  if (!segmentation.empty())
-    stringList << QString("<b>Segmentation:</b> %1").arg(QString::fromStdString(segmentation));
+  if (m_Task->HasImage(i))
+    stringList << QString("<b>Image:</b> %1").arg(QString::fromStdString(m_Task->GetImage(i)));
 
-  const auto labelName = m_Task->GetLabelName(m_CurrentSubtaskIndex);
+  if (m_Task->HasSegmentation(i))
+    stringList << QString("<b>Segmentation:</b> %1").arg(QString::fromStdString(m_Task->GetSegmentation(i)));
 
-  if (!labelName.empty())
-    stringList << QString("<b>Label name:</b> %1").arg(QString::fromStdString(labelName));
+  if (m_Task->HasLabelName(i))
+    stringList << QString("<b>Label name:</b> %1").arg(QString::fromStdString(m_Task->GetLabelName(i)));
 
-  const auto preset = m_Task->GetPreset(m_CurrentSubtaskIndex);
+  if (m_Task->HasPreset(i))
+    stringList << QString("<b>Label set preset:</b> %1").arg(QString::fromStdString(m_Task->GetPreset(i)));
 
-  if (!preset.empty())
-    stringList << QString("<b>Label set preset:</b> %1").arg(QString::fromStdString(preset));
+  if (m_Task->HasDynamic(i))
+    stringList << QString("<b>Segmentation type:</b> %1").arg(m_Task->GetDynamic(i) ? "Dynamic" : "Static");
 
   if (!stringList.empty())
     details += QString("<p>%1</p>").arg(stringList.join(QStringLiteral("<br>")));
 
   m_Ui->detailsLabel->setText(details);
 }
 
 /* Load/activate the currently displayed subtask. Unload all data nodes from
  * previously active subtasks first, but spare and reuse the image if possible.
  */
 void QmitkSegmentationTaskWidget::OnLoadButtonClicked()
 {
   m_Ui->loadButton->setEnabled(false);
   QApplication::setOverrideCursor(QCursor(Qt::BusyCursor));
 
   auto* imageNode = this->GetImageDataNode(m_CurrentSubtaskIndex);
 
   this->UnloadSubtasks(imageNode);
   this->LoadSubtask(imageNode);
 
   this->OnSubtaskChanged();
   QApplication::restoreOverrideCursor();
 }
 
 /* If present, return the image data node for the currently displayed subtask.
  * Otherwise, return nullptr.
  */
 mitk::DataNode* QmitkSegmentationTaskWidget::GetImageDataNode(size_t index) const
 {
   const auto imagePath = m_Task->GetAbsolutePath(m_Task->GetImage(index));
 
   auto imageNodes = GetDataStorage()->GetDerivations(m_TaskNode, mitk::NodePredicateFunction::New([imagePath](const mitk::DataNode* node) {
     return imagePath == GetInputLocation(node->GetData());
   }));
 
   return !imageNodes->empty()
     ? imageNodes->front()
     : nullptr;
 }
 
 /* If present, return the segmentation data node for the currently displayed
  * subtask. Otherwise, return nullptr.
  */
 mitk::DataNode* QmitkSegmentationTaskWidget::GetSegmentationDataNode(size_t index) const
 {
   const auto* imageNode = this->GetImageDataNode(index);
 
   if (imageNode != nullptr)
   {
     auto segmentations = GetDataStorage()->GetDerivations(imageNode, mitk::TNodePredicateDataType<mitk::LabelSetImage>::New());
 
     if (!segmentations->empty())
       return segmentations->front();
   }
 
   return nullptr;
 }
 
 /* Unload all subtask data nodes but spare the passed image data node.
  */
 void QmitkSegmentationTaskWidget::UnloadSubtasks(const mitk::DataNode* skip)
 {
   if (m_TaskNode.IsNotNull())
   {
     mitk::DataStorage::Pointer dataStorage = GetDataStorage();
 
     auto imageNodes = dataStorage->GetDerivations(m_TaskNode, mitk::TNodePredicateDataType<mitk::Image>::New());
 
     for (auto imageNode : *imageNodes)
     {
       dataStorage->Remove(dataStorage->GetDerivations(imageNode, nullptr, false));
 
       if (imageNode != skip)
         dataStorage->Remove(imageNode);
     }
   }
 
   this->SetActiveSubtaskIndex(std::nullopt);
 }
 
 /* Load/activate the currently displayed subtask. The subtask must specify
  * an image. The segmentation is either created from scratch with an optional
  * name for the first label, possibly based on a label set preset specified by
  * the subtask, or loaded as specified by the subtask. If a result file does
  * exist, it is chosen as segmentation instead.
  */
 void QmitkSegmentationTaskWidget::LoadSubtask(mitk::DataNode::Pointer imageNode)
 {
-  mitk::DataStorage::Pointer dataStorage = GetDataStorage();
+  const auto i = m_CurrentSubtaskIndex;
 
-  const auto imagePath = m_Task->GetAbsolutePath(m_Task->GetImage(m_CurrentSubtaskIndex));
   mitk::Image::Pointer image;
-
-  const auto resultPath = m_Task->GetAbsolutePath(m_Task->GetResult(m_CurrentSubtaskIndex));
-
-  const auto segmentationPath = m_Task->GetAbsolutePath(m_Task->GetSegmentation(m_CurrentSubtaskIndex));
   mitk::LabelSetImage::Pointer segmentation;
-  mitk::DataNode::Pointer segmentationNode;
-
-  const auto labelName = m_Task->GetLabelName(m_CurrentSubtaskIndex);
-
-  const auto presetPath = m_Task->GetAbsolutePath(m_Task->GetPreset(m_CurrentSubtaskIndex));
 
   try
   {
     if (imageNode.IsNull())
-      image = mitk::IOUtil::Load<mitk::Image>(imagePath.string());
+    {
+      const auto path = m_Task->GetAbsolutePath(m_Task->GetImage(i));
+      image = mitk::IOUtil::Load<mitk::Image>(path.string());
+    }
 
-    if (std::filesystem::exists(resultPath))
+    if (m_Task->HasResult(i))
     {
-      segmentation = mitk::IOUtil::Load<mitk::LabelSetImage>(resultPath.string());
+      const auto path = m_Task->GetAbsolutePath(m_Task->GetResult(i));
+
+      if (std::filesystem::exists(path))
+        segmentation = mitk::IOUtil::Load<mitk::LabelSetImage>(path.string());
     }
-    else if (!segmentationPath.empty())
+    else if (m_Task->HasSegmentation(i))
     {
-      segmentation = mitk::IOUtil::Load<mitk::LabelSetImage>(segmentationPath.string());
+      const auto path = m_Task->GetAbsolutePath(m_Task->GetSegmentation(i));
+      segmentation = mitk::IOUtil::Load<mitk::LabelSetImage>(path.string());
     }
   }
   catch (const mitk::Exception&)
   {
     return;
   }
 
+  auto dataStorage = GetDataStorage();
+
   if (imageNode.IsNull())
   {
     imageNode = mitk::DataNode::New();
     imageNode->SetData(image);
 
     dataStorage->Add(imageNode, m_TaskNode);
 
     mitk::RenderingManager::GetInstance()->InitializeViews(image->GetTimeGeometry());
   }
   else
   {
     image = static_cast<mitk::Image*>(imageNode->GetData());
   }
 
-  auto imageName = "Subtask " + std::to_string(m_CurrentSubtaskIndex + 1);
-  imageNode->SetName(imageName);
-
-  auto segmentationName = imageName;
+  auto name = "Subtask " + std::to_string(i + 1);
+  imageNode->SetName(name);
 
   if (segmentation.IsNull())
   {
     mitk::Image::ConstPointer templateImage = image;
 
     if (templateImage->GetDimension() > 3)
     {
-      QmitkStaticDynamicSegmentationDialog dialog(this);
-      dialog.SetReferenceImage(templateImage);
-      dialog.exec();
+      if (m_Task->HasDynamic(i))
+      {
+        if (!m_Task->GetDynamic(i))
+          templateImage = mitk::SegmentationHelper::GetStaticSegmentationTemplate(image);
+      }
+      else
+      {
+        QmitkStaticDynamicSegmentationDialog dialog(this);
+        dialog.SetReferenceImage(templateImage);
+        dialog.exec();
 
-      templateImage = dialog.GetSegmentationTemplate();
+        templateImage = dialog.GetSegmentationTemplate();
+      }
     }
 
-    segmentationNode = mitk::LabelSetImageHelper::CreateNewSegmentationNode(imageNode, templateImage, segmentationName);
+    auto segmentationNode = mitk::LabelSetImageHelper::CreateNewSegmentationNode(imageNode, templateImage, name);
     segmentation = static_cast<mitk::LabelSetImage*>(segmentationNode->GetData());
 
-    if (!presetPath.empty())
+    if (m_Task->HasPreset(i))
     {
-      mitk::LabelSetIOHelper::LoadLabelSetImagePreset(presetPath.string(), segmentation);
+      const auto path = m_Task->GetAbsolutePath(m_Task->GetPreset(i));
+      mitk::LabelSetIOHelper::LoadLabelSetImagePreset(path.string(), segmentation);
     }
     else
     {
       auto label = mitk::LabelSetImageHelper::CreateNewLabel(segmentation);
 
-      if (!labelName.empty())
-        label->SetName(labelName);
+      if (m_Task->HasLabelName(i))
+        label->SetName(m_Task->GetLabelName(i));
 
       segmentation->GetActiveLabelSet()->AddLabel(label);
     }
 
     dataStorage->Add(segmentationNode, imageNode);
   }
   else
   {
-    segmentationNode = mitk::DataNode::New();
-    segmentationNode->SetName(segmentationName);
+    auto segmentationNode = mitk::DataNode::New();
+    segmentationNode->SetName(name);
     segmentationNode->SetData(segmentation);
 
     dataStorage->Add(segmentationNode, imageNode);
   }
 
-  this->SetActiveSubtaskIndex(m_CurrentSubtaskIndex);
+  this->SetActiveSubtaskIndex(i);
 }
 
 void QmitkSegmentationTaskWidget::SetActiveSubtaskIndex(const std::optional<size_t>& index)
 {
   if (m_ActiveSubtaskIndex != index)
   {
     m_ActiveSubtaskIndex = index;
     emit ActiveSubtaskChanged(m_ActiveSubtaskIndex);
   }
 }
 
 void QmitkSegmentationTaskWidget::SetCurrentSubtaskIndex(size_t index)
 {
   if (m_CurrentSubtaskIndex != index)
   {
     m_CurrentSubtaskIndex = index;
     emit CurrentSubtaskChanged(m_CurrentSubtaskIndex);
   }
 }