diff --git a/Modules/Multilabel/files.cmake b/Modules/Multilabel/files.cmake
index 0524d4f98c..8723b71a3b 100644
--- a/Modules/Multilabel/files.cmake
+++ b/Modules/Multilabel/files.cmake
@@ -1,21 +1,21 @@
set(CPP_FILES
mitkLabel.cpp
mitkLabelSet.cpp
mitkLabelSetImage.cpp
mitkLabelSetImageConverter.cpp
mitkLabelSetImageSource.cpp
mitkLabelSetImageHelper.cpp
mitkLabelSetImageSurfaceStampFilter.cpp
mitkLabelSetImageToSurfaceFilter.cpp
mitkLabelSetImageToSurfaceThreadedFilter.cpp
mitkLabelSetImageVtkMapper2D.cpp
mitkMultilabelObjectFactory.cpp
- mitkLabelSetIOHelper.cpp
+ mitkMultiLabelIOHelper.cpp
mitkDICOMSegmentationPropertyHelper.cpp
mitkDICOMSegmentationConstants.cpp
mitkSegmentationTaskList.cpp
)
set(RESOURCE_FILES
)
diff --git a/Modules/Multilabel/mitkLabelSetIOHelper.cpp b/Modules/Multilabel/mitkMultiLabelIOHelper.cpp
similarity index 88%
rename from Modules/Multilabel/mitkLabelSetIOHelper.cpp
rename to Modules/Multilabel/mitkMultiLabelIOHelper.cpp
index 75d0ea5999..8593587441 100644
--- a/Modules/Multilabel/mitkLabelSetIOHelper.cpp
+++ b/Modules/Multilabel/mitkMultiLabelIOHelper.cpp
@@ -1,280 +1,280 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
-#include "mitkLabelSetIOHelper.h"
+#include "mitkMultiLabelIOHelper.h"
#include "mitkLabelSetImage.h"
#include
#include
namespace
{
std::string EnsureExtension(const std::string& filename)
{
const std::string extension = ".lsetp";
if (filename.size() < extension.size() || std::string::npos == filename.find(extension, filename.size() - extension.size()))
return filename + extension;
return filename;
}
}
-bool mitk::LabelSetIOHelper::SaveLabelSetImagePreset(const std::string &presetFilename,
+bool mitk::MultiLabelIOHelper::SaveLabelSetImagePreset(const std::string &presetFilename,
const mitk::LabelSetImage *inputImage)
{
const auto filename = EnsureExtension(presetFilename);
tinyxml2::XMLDocument xmlDocument;
xmlDocument.InsertEndChild(xmlDocument.NewDeclaration());
auto *rootElement = xmlDocument.NewElement("LabelSetImagePreset");
rootElement->SetAttribute("layers", inputImage->GetNumberOfLayers());
xmlDocument.InsertEndChild(rootElement);
for (unsigned int layerIndex = 0; layerIndex < inputImage->GetNumberOfLayers(); layerIndex++)
{
auto *layerElement = xmlDocument.NewElement("Layer");
layerElement->SetAttribute("index", layerIndex);
layerElement->SetAttribute("labels", inputImage->GetNumberOfLabels(layerIndex));
rootElement->InsertEndChild(layerElement);
for (unsigned int labelIndex = 0; labelIndex < inputImage->GetNumberOfLabels(layerIndex); labelIndex++)
- layerElement->InsertEndChild(LabelSetIOHelper::GetLabelAsXMLElement(xmlDocument, inputImage->GetLabel(labelIndex, layerIndex)));
+ layerElement->InsertEndChild(MultiLabelIOHelper::GetLabelAsXMLElement(xmlDocument, inputImage->GetLabel(labelIndex, layerIndex)));
}
return tinyxml2::XML_SUCCESS == xmlDocument.SaveFile(filename.c_str());
}
-bool mitk::LabelSetIOHelper::LoadLabelSetImagePreset(const std::string &presetFilename,
+bool mitk::MultiLabelIOHelper::LoadLabelSetImagePreset(const std::string &presetFilename,
mitk::LabelSetImage *inputImage)
{
if (nullptr == inputImage)
return false;
const auto filename = EnsureExtension(presetFilename);
tinyxml2::XMLDocument xmlDocument;
if (tinyxml2::XML_SUCCESS != xmlDocument.LoadFile(filename.c_str()))
{
MITK_WARN << "Label set preset file \"" << filename << "\" does not exist or cannot be opened";
return false;
}
auto *rootElement = xmlDocument.FirstChildElement("LabelSetImagePreset");
if (nullptr == rootElement)
{
MITK_WARN << "Not a valid Label set preset";
return false;
}
auto activeLayerBackup = inputImage->GetActiveLayer();
int numberOfLayers = 0;
rootElement->QueryIntAttribute("layers", &numberOfLayers);
auto* layerElement = rootElement->FirstChildElement("Layer");
if (nullptr == layerElement)
{
MITK_WARN << "Label set preset does not contain any layers";
return false;
}
for (int layerIndex = 0; layerIndex < numberOfLayers; layerIndex++)
{
int numberOfLabels = 0;
layerElement->QueryIntAttribute("labels", &numberOfLabels);
if (nullptr == inputImage->GetLabelSet(layerIndex))
{
inputImage->AddLayer();
}
else
{
inputImage->SetActiveLayer(layerIndex);
}
auto *labelElement = layerElement->FirstChildElement("Label");
if (nullptr == labelElement)
continue;
for (int labelIndex = 0; labelIndex < numberOfLabels; labelIndex++)
{
- auto label = mitk::LabelSetIOHelper::LoadLabelFromXMLDocument(labelElement);
+ auto label = mitk::MultiLabelIOHelper::LoadLabelFromXMLDocument(labelElement);
const auto labelValue = label->GetValue();
if (LabelSetImage::UnlabeledLabelValue != labelValue)
{
auto* labelSet = inputImage->GetLabelSet(layerIndex);
auto* alreadyExistingLabel = labelSet->GetLabel(labelValue);
if (nullptr != alreadyExistingLabel)
{
// Override existing label with label from preset
alreadyExistingLabel->ConcatenatePropertyList(label);
labelSet->UpdateLookupTable(labelValue);
}
else
{
labelSet->AddLabel(label);
}
}
labelElement = labelElement->NextSiblingElement("Label");
if (nullptr == labelElement)
continue;
}
layerElement = layerElement->NextSiblingElement("Layer");
if (nullptr == layerElement)
continue;
}
inputImage->SetActiveLayer(activeLayerBackup);
return true;
}
-tinyxml2::XMLElement *mitk::LabelSetIOHelper::GetLabelAsXMLElement(tinyxml2::XMLDocument &doc, Label *label)
+tinyxml2::XMLElement *mitk::MultiLabelIOHelper::GetLabelAsXMLElement(tinyxml2::XMLDocument &doc, Label *label)
{
auto *labelElem = doc.NewElement("Label");
if (nullptr != label)
{
// add XML contents
const PropertyList::PropertyMap* propmap = label->GetMap();
for (auto iter = propmap->begin(); iter != propmap->end(); ++iter)
{
std::string key = iter->first;
const BaseProperty* property = iter->second;
auto* element = PropertyToXMLElement(doc, key, property);
if (element)
labelElem->InsertEndChild(element);
}
}
return labelElem;
}
-mitk::Label::Pointer mitk::LabelSetIOHelper::LoadLabelFromXMLDocument(const tinyxml2::XMLElement *labelElem)
+mitk::Label::Pointer mitk::MultiLabelIOHelper::LoadLabelFromXMLDocument(const tinyxml2::XMLElement *labelElem)
{
// reread
auto *propElem = labelElem->FirstChildElement("property");
std::string name;
mitk::BaseProperty::Pointer prop;
mitk::Label::Pointer label = mitk::Label::New();
while (propElem)
{
- LabelSetIOHelper::PropertyFromXMLElement(name, prop, propElem);
+ MultiLabelIOHelper::PropertyFromXMLElement(name, prop, propElem);
label->SetProperty(name, prop);
propElem = propElem->NextSiblingElement("property");
}
return label.GetPointer();
}
-tinyxml2::XMLElement *mitk::LabelSetIOHelper::PropertyToXMLElement(tinyxml2::XMLDocument &doc, const std::string &key, const BaseProperty *property)
+tinyxml2::XMLElement *mitk::MultiLabelIOHelper::PropertyToXMLElement(tinyxml2::XMLDocument &doc, const std::string &key, const BaseProperty *property)
{
auto *keyelement = doc.NewElement("property");
keyelement->SetAttribute("key", key.c_str());
keyelement->SetAttribute("type", property->GetNameOfClass());
// construct name of serializer class
std::string serializername(property->GetNameOfClass());
serializername += "Serializer";
std::list allSerializers =
itk::ObjectFactoryBase::CreateAllInstance(serializername.c_str());
if (allSerializers.size() < 1)
MITK_ERROR << "No serializer found for " << property->GetNameOfClass() << ". Skipping object";
if (allSerializers.size() > 1)
MITK_WARN << "Multiple serializers found for " << property->GetNameOfClass() << "Using arbitrarily the first one.";
for (auto iter = allSerializers.begin(); iter != allSerializers.end();
++iter)
{
if (auto *serializer = dynamic_cast(iter->GetPointer()))
{
serializer->SetProperty(property);
try
{
auto *valueelement = serializer->Serialize(doc);
if (valueelement)
keyelement->InsertEndChild(valueelement);
}
catch (std::exception &e)
{
MITK_ERROR << "Serializer " << serializer->GetNameOfClass() << " failed: " << e.what();
}
break;
}
}
return keyelement;
}
-bool mitk::LabelSetIOHelper::PropertyFromXMLElement(std::string &key,
+bool mitk::MultiLabelIOHelper::PropertyFromXMLElement(std::string &key,
mitk::BaseProperty::Pointer &prop,
const tinyxml2::XMLElement *elem)
{
const char* typeC = elem->Attribute("type");
std::string type = nullptr != typeC
? typeC
: "";
const char* keyC = elem->Attribute("key");
key = nullptr != keyC
? keyC
: "";
// construct name of serializer class
std::string serializername(type);
serializername += "Serializer";
std::list allSerializers =
itk::ObjectFactoryBase::CreateAllInstance(serializername.c_str());
if (allSerializers.size() < 1)
MITK_ERROR << "No serializer found for " << type << ". Skipping object";
if (allSerializers.size() > 1)
MITK_WARN << "Multiple deserializers found for " << type << "Using arbitrarily the first one.";
for (auto iter = allSerializers.begin(); iter != allSerializers.end();
++iter)
{
if (auto *serializer = dynamic_cast(iter->GetPointer()))
{
try
{
prop = serializer->Deserialize(elem->FirstChildElement());
}
catch (std::exception &e)
{
MITK_ERROR << "Deserializer " << serializer->GetNameOfClass() << " failed: " << e.what();
return false;
}
break;
}
}
if (prop.IsNull())
return false;
return true;
}
diff --git a/Modules/Multilabel/mitkLabelSetIOHelper.h b/Modules/Multilabel/mitkMultiLabelIOHelper.h
similarity index 93%
rename from Modules/Multilabel/mitkLabelSetIOHelper.h
rename to Modules/Multilabel/mitkMultiLabelIOHelper.h
index a1a6ac8621..f9d3976ed6 100644
--- a/Modules/Multilabel/mitkLabelSetIOHelper.h
+++ b/Modules/Multilabel/mitkMultiLabelIOHelper.h
@@ -1,98 +1,98 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
-#ifndef mitkLabelSetIOHelper_h
-#define mitkLabelSetIOHelper_h
+#ifndef mitkMultiLabelIOHelper_h
+#define mitkMultiLabelIOHelper_h
#include
#include
namespace tinyxml2
{
class XMLDocument;
class XMLElement;
}
namespace mitk
{
class BaseProperty;
class LabelSetImage;
class Label;
/**
- * @brief The LabelSetIOHelper is a static helper class that supports serialization of mitk::LabelSetImage
+ * @brief The MultiLabelIOHelper is a static helper class that supports serialization of mitk::LabelSetImage
*
* This class provides static functions for converting mitk::Label into XML and also allows the serialization
* of mitk::LabelSet as presets
*/
- class MITKMULTILABEL_EXPORT LabelSetIOHelper
+ class MITKMULTILABEL_EXPORT MultiLabelIOHelper
{
public:
/**
* @brief Saves the mitk::LabelSet configuration of inputImage to presetFilename.
* The preset is stored as "*.lsetp"
* @param presetFilename the filename including the filesystem path
* @param inputImage the input image from which the preset should be generated
* @return true if the serialization was successful and false otherwise
*/
static bool SaveLabelSetImagePreset(const std::string &presetFilename,
const mitk::LabelSetImage *inputImage);
/**
* @brief Loads an existing preset for a mitk::LabelSetImage from presetFilename and applies it to inputImage
* @param presetFilename the filename of the preset including the filesystem path
* @param inputImage the image to which the loaded preset will be applied
* @return true if the deserilization was successful and false otherwise
*/
static bool LoadLabelSetImagePreset(const std::string &presetFilename,
mitk::LabelSetImage *inputImage);
/**
* @brief Creates a mitk::Label from an XML element
* @param labelElem the xml element from which a mitk::Label will be created
* @return the created mitk::Label
*/
static itk::SmartPointer LoadLabelFromXMLDocument(const tinyxml2::XMLElement *labelElem);
/**
* @brief Creates an XML element from a mitk::Label
* @param doc
* @param label the mitk::Label from which the xml element will be created
* @return the created XML element
*/
static tinyxml2::XMLElement *GetLabelAsXMLElement(tinyxml2::XMLDocument &doc, Label *label);
/**
* @brief Since a mitk::Label is basically a mitk::PropertyList this function coverts the label's properties into
* XML
* @param doc
* @param key the property's key which will be used in the XML element
* @param property the mitk::BaseProperty that should be converted
* @return the created XML element
*/
static tinyxml2::XMLElement *PropertyToXMLElement(tinyxml2::XMLDocument& doc, const std::string &key, const BaseProperty *property);
/**
* @brief Since a mitk::Label is basically a mitk::PropertyList this function coverts a XML element into a property
* @param key the property's key
* @param prop the mitk::BaseProperty that will be created
* @param elem the XML elem from which the property will be created
* @return true if the conversion was successful and false otherwise
*/
static bool PropertyFromXMLElement(std::string &key, itk::SmartPointer &prop, const tinyxml2::XMLElement *elem);
private:
- LabelSetIOHelper();
+ MultiLabelIOHelper();
};
}
#endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitkSegmentationTaskListWidget.cpp b/Modules/SegmentationUI/Qmitk/QmitkSegmentationTaskListWidget.cpp
index 9705dbd1cd..37ea6adefc 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkSegmentationTaskListWidget.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitkSegmentationTaskListWidget.cpp
@@ -1,981 +1,981 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "QmitkSegmentationTaskListWidget.h"
#include
#include
#include
#include
#include
-#include
+#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace
{
mitk::IPreferences* GetSegmentationPreferences()
{
return mitk::CoreServices::GetPreferencesService()->GetSystemPreferences()->Node("/org.mitk.views.segmentation");
}
std::filesystem::path GetInputLocation(const mitk::BaseData* data)
{
std::string result;
if (data != nullptr)
data->GetPropertyList()->GetStringProperty("MITK.IO.reader.inputlocation", result);
return result;
}
QString ColorString(const QString& string, const QColor& color, const QColor& backgroundColor = QColor::Invalid)
{
if (!color.isValid() && !backgroundColor.isValid())
return string;
auto result = QStringLiteral("%1").arg(string);
return result;
}
mitk::DataStorage::SetOfObjects::ConstPointer GetSubset(const mitk::DataStorage* dataStorage, const mitk::NodePredicateBase* condition, const mitk::DataNode* removedDataNode)
{
auto subset = dataStorage->GetSubset(condition);
if (nullptr != removedDataNode)
{
auto actualSubset = mitk::DataStorage::SetOfObjects::New();
for (auto node : *subset)
{
if (node != removedDataNode)
actualSubset->push_back(node);
}
return actualSubset;
}
return subset;
}
}
/* This constructor has three objectives:
* 1. Do widget initialization that cannot be done in the .ui file
* 2. Connect signals and slots
* 3. Explicitly trigger a reset to a valid initial widget state
*/
QmitkSegmentationTaskListWidget::QmitkSegmentationTaskListWidget(QWidget* parent)
: QWidget(parent),
m_Ui(new Ui::QmitkSegmentationTaskListWidget),
m_FileSystemWatcher(new QFileSystemWatcher(this)),
m_DataStorage(nullptr),
m_UnsavedChanges(false)
{
m_Ui->setupUi(this);
m_Ui->selectionWidget->SetNodePredicate(mitk::TNodePredicateDataType::New());
m_Ui->progressBar->setStyleSheet(QString("QProgressBar::chunk { background-color: %1; }").arg(QmitkStyleManager::GetIconAccentColor()));
m_Ui->storeButton->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/document-save.svg")));
using Self = QmitkSegmentationTaskListWidget;
connect(m_Ui->selectionWidget, &QmitkSingleNodeSelectionWidget::CurrentSelectionChanged, this, &Self::OnSelectionChanged);
connect(m_Ui->previousButton, &QToolButton::clicked, this, &Self::OnPreviousButtonClicked);
connect(m_Ui->nextButton, &QToolButton::clicked, this, &Self::OnNextButtonClicked);
connect(m_Ui->loadButton, &QPushButton::clicked, this, &Self::OnLoadButtonClicked);
connect(m_Ui->storeButton, &QPushButton::clicked, this, &Self::OnStoreButtonClicked);
connect(m_Ui->acceptButton, &QPushButton::clicked, this, &Self::OnAcceptButtonClicked);
connect(m_FileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, &Self::OnResultDirectoryChanged);
auto* prevShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key::Key_P), this);
connect(prevShortcut, &QShortcut::activated, this, &Self::OnPreviousTaskShortcutActivated);
auto* prevUndoneShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key::Key_P), this);
connect(prevUndoneShortcut, &QShortcut::activated, this, &Self::OnPreviousTaskShortcutActivated);
auto* nextShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key::Key_N), this);
connect(nextShortcut, &QShortcut::activated, this, &Self::OnNextTaskShortcutActivated);
auto* nextUndoneShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key::Key_N), this);
connect(nextUndoneShortcut, &QShortcut::activated, this, &Self::OnNextTaskShortcutActivated);
auto* loadShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key::Key_L), this);
connect(loadShortcut, &QShortcut::activated, this, &Self::OnLoadTaskShortcutActivated);
auto* storeShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key::Key_S), parent);
connect(storeShortcut, &QShortcut::activated, this, &Self::OnStoreInterimResultShortcutActivated);
auto* acceptShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key::Key_A), parent);
connect(acceptShortcut, &QShortcut::activated, this, &Self::OnAcceptSegmentationShortcutActivated);
this->ResetControls();
this->CheckDataStorage();
}
QmitkSegmentationTaskListWidget::~QmitkSegmentationTaskListWidget()
{
}
void QmitkSegmentationTaskListWidget::SetDataStorage(mitk::DataStorage* dataStorage)
{
m_DataStorage = dataStorage;
m_Ui->selectionWidget->SetDataStorage(dataStorage); // Triggers OnSelectionChanged()
m_Ui->selectionWidget->SetAutoSelectNewNodes(true);
this->CheckDataStorage();
}
void QmitkSegmentationTaskListWidget::CheckDataStorage(const mitk::DataNode* removedNode)
{
QString warning;
if (nullptr == m_DataStorage)
{
warning = QStringLiteral(
"Developer warning
Call SetDataStorage()
to fully initialize "
"this instance of QmitkSegmentationTaskListWidget
.
");
}
else
{
auto isTaskList = mitk::TNodePredicateDataType::New();
auto taskListNodes = GetSubset(m_DataStorage, isTaskList, removedNode);
if (taskListNodes->empty())
{
warning = QStringLiteral(
"No segmentation task list found
Load a segmentation task list to use "
"this plugin.
");
}
else if (taskListNodes->Size() > 1)
{
warning = QStringLiteral(
"More than one segmentation task list found
Unload everything but a "
"single segmentation task list to use this plugin.
");
}
else
{
const auto* taskListNode = (*taskListNodes)[0].GetPointer();
auto isTaskListNode = mitk::NodePredicateFunction::New([taskListNode](const mitk::DataNode* node) {
return node == taskListNode;
});
auto isChildOfTaskListNode = mitk::NodePredicateFunction::New([this, isTaskListNode](const mitk::DataNode* node) {
return !m_DataStorage->GetSources(node, isTaskListNode, false)->empty();
});
auto isHelperObject = mitk::NodePredicateProperty::New("helper object");
auto isUndesiredNode = mitk::NodePredicateNot::New(mitk::NodePredicateOr::New(
isTaskListNode,
isChildOfTaskListNode,
isHelperObject));
if (!GetSubset(m_DataStorage, isUndesiredNode, removedNode)->empty())
{
warning = QStringLiteral(
"Unrelated data found
Unload everything but a single segmentation task "
"list to use this plugin.
");
}
}
}
m_Ui->label->setText("" + warning + "");
m_Ui->label->setVisible(!warning.isEmpty());
m_Ui->widget->setVisible(warning.isEmpty());
}
void QmitkSegmentationTaskListWidget::OnUnsavedChangesSaved()
{
if (m_UnsavedChanges)
{
m_UnsavedChanges = false;
if (this->ActiveTaskIsShown())
this->UpdateDetailsLabel();
}
}
/* Make sure that the widget transitions into a valid state whenever the
* selection changes.
*/
void QmitkSegmentationTaskListWidget::OnSelectionChanged(const QmitkSingleNodeSelectionWidget::NodeList& nodes)
{
this->UnloadTasks();
this->ResetControls();
if (!nodes.empty())
{
m_TaskListNode = nodes.front();
auto taskList = dynamic_cast(m_TaskListNode->GetData());
if (taskList != nullptr)
{
this->OnTaskListChanged(taskList);
return;
}
}
this->SetTaskList(nullptr);
m_TaskListNode = nullptr;
}
/* Reset all controls to a default state as a common basis for further
* adjustments.
*/
void QmitkSegmentationTaskListWidget::ResetControls()
{
m_Ui->progressBar->setEnabled(false);
m_Ui->progressBar->setFormat("");
m_Ui->progressBar->setValue(0);
m_Ui->progressBar->setMaximum(1);
m_Ui->previousButton->setEnabled(false);
m_Ui->nextButton->setEnabled(false);
this->UpdateLoadButton();
this->UpdateDetailsLabel();
this->UpdateStoreAndAcceptButtons();
}
/* If the segmentation task changed, reset all member variables to expected
* default values and reset the file system watcher.
*/
void QmitkSegmentationTaskListWidget::SetTaskList(mitk::SegmentationTaskList* taskList)
{
if (m_TaskList != taskList)
{
m_TaskList = taskList;
if (taskList != nullptr)
{
this->SetCurrentTaskIndex(0);
}
else
{
this->SetCurrentTaskIndex(std::nullopt);
}
this->ResetFileSystemWatcher();
}
}
void QmitkSegmentationTaskListWidget::ResetFileSystemWatcher()
{
auto paths = m_FileSystemWatcher->directories();
if (!paths.empty())
m_FileSystemWatcher->removePaths(paths);
if (m_TaskList.IsNotNull())
{
for (const auto& task : *m_TaskList)
{
auto resultPath = m_TaskList->GetAbsolutePath(task.GetResult()).remove_filename();
if (!std::filesystem::exists(resultPath))
{
try
{
std::filesystem::create_directories(resultPath);
}
catch (const std::filesystem::filesystem_error& e)
{
MITK_ERROR << e.what();
}
}
if (std::filesystem::exists(resultPath))
m_FileSystemWatcher->addPath(QString::fromStdString(resultPath.string()));
}
}
}
void QmitkSegmentationTaskListWidget::OnResultDirectoryChanged(const QString&)
{
// TODO: If a segmentation was modified ("Unsaved changes"), saved ("Done"), and then the file is deleted, the status should be "Unsaved changes" instead of "Not done".
this->UpdateProgressBar();
this->UpdateDetailsLabel();
}
void QmitkSegmentationTaskListWidget::UpdateProgressBar()
{
int progress = 0;
for (size_t i = 0; i < m_TaskList->GetNumberOfTasks(); ++i)
{
if (m_TaskList->IsDone(i))
++progress;
}
m_Ui->progressBar->setValue(progress);
}
/* Provided that a valid segmentation task list is currently selected and the
* widget is in its default state, update all controls accordingly.
* TODO: Then, load the first unfinished task, if any.
*/
void QmitkSegmentationTaskListWidget::OnTaskListChanged(mitk::SegmentationTaskList* taskList)
{
this->SetTaskList(taskList);
const auto numTasks = taskList->GetNumberOfTasks();
m_Ui->progressBar->setMaximum(numTasks);
m_Ui->progressBar->setFormat(QStringLiteral("%v/%m Task(s) done"));
m_Ui->progressBar->setEnabled(true);
this->UpdateProgressBar();
m_Ui->loadButton->setEnabled(true);
if (numTasks > 1)
m_Ui->nextButton->setEnabled(true);
// TODO: This line should be enough but it is happening too early even before
// the RenderingManager has any registered render windows, resulting in mismatching
// renderer and data geometries.
// this->LoadNextUnfinishedTask();
}
/* If possible, change the currently displayed task to the previous task.
* Enable/disable navigation buttons according to the task's position.
*/
void QmitkSegmentationTaskListWidget::OnPreviousButtonClicked()
{
auto current = m_CurrentTaskIndex.value();
// If the shift modifier key is pressed, look for the previous undone task.
if (QApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier))
{
if (current > 0)
{
for (decltype(current) i = current; i > 0; --i)
{
if (!m_TaskList->IsDone(i - 1))
{
this->SetCurrentTaskIndex(i - 1);
break;
}
}
}
}
else
{
if (current != 0)
this->SetCurrentTaskIndex(current - 1);
}
this->UpdateNavigationButtons();
}
/* If possible, change the currently displayed task to the next task.
* Enable/disable navigation buttons according to the task's position.
*/
void QmitkSegmentationTaskListWidget::OnNextButtonClicked()
{
const auto numTasks = m_TaskList->GetNumberOfTasks();
auto current = m_CurrentTaskIndex.value();
// If the shift modifier key is pressed, look for the next undone task.
if (QApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier))
{
for (std::remove_const_t i = current + 1; i < numTasks; ++i)
{
if (!m_TaskList->IsDone(i))
{
this->SetCurrentTaskIndex(i);
break;
}
}
}
else
{
if (current < numTasks - 1)
this->SetCurrentTaskIndex(current + 1);
}
this->UpdateNavigationButtons();
}
void QmitkSegmentationTaskListWidget::UpdateNavigationButtons()
{
if (m_TaskList.IsNull() || m_TaskList->GetNumberOfTasks() == 0)
{
m_Ui->previousButton->setEnabled(false);
m_Ui->nextButton->setEnabled(false);
return;
}
const auto maxIndex = m_TaskList->GetNumberOfTasks() - 1;
const auto current = m_CurrentTaskIndex.value();
m_Ui->previousButton->setEnabled(current != 0);
m_Ui->nextButton->setEnabled(current != maxIndex);
}
/* Update affected controls when the currently displayed task changed.
*/
void QmitkSegmentationTaskListWidget::OnCurrentTaskChanged()
{
this->UpdateLoadButton();
this->UpdateNavigationButtons();
this->UpdateDetailsLabel();
this->UpdateStoreAndAcceptButtons();
}
/* Update the load button according to the currently displayed task.
*/
void QmitkSegmentationTaskListWidget::UpdateLoadButton()
{
auto text = !this->ActiveTaskIsShown()
? QStringLiteral("Load task")
: QStringLiteral("Task");
if (m_CurrentTaskIndex.has_value())
{
const auto current = m_CurrentTaskIndex.value();
if (m_TaskList.IsNotNull())
{
text += QString(" %1/%2").arg(current + 1).arg(m_TaskList->GetNumberOfTasks());
if (m_TaskList->HasName(current))
text += QStringLiteral(":\n") + QString::fromStdString(m_TaskList->GetName(current));
}
m_Ui->loadButton->setDisabled(this->ActiveTaskIsShown());
}
else
{
m_Ui->loadButton->setEnabled(false);
}
m_Ui->loadButton->setText(text);
}
/* Update the details label according to the currently display task.
* The text is composed of the status of the task and a variable number
* of text blocks according to the optional values provided by the task.
*/
void QmitkSegmentationTaskListWidget::UpdateDetailsLabel()
{
if (!m_CurrentTaskIndex.has_value())
{
m_Ui->detailsLabel->clear();
return;
}
const auto current = m_CurrentTaskIndex.value();
bool isDone = m_TaskList->IsDone(current);
auto details = QString("Status: %1 / ").arg(this->ActiveTaskIsShown()
? ColorString("Active", Qt::white, QColor(Qt::green).darker())
: ColorString("Inactive", Qt::white, QColor(Qt::red).darker()));
if (m_UnsavedChanges && this->ActiveTaskIsShown())
{
details += QString("%1
").arg(ColorString("Unsaved changes", Qt::white, QColor(Qt::red).darker()));
}
else
{
details += QString("%1
").arg(isDone
? ColorString("Done", Qt::white, QColor(Qt::green).darker())
: ColorString("Not done", Qt::white, QColor(Qt::red).darker()));
}
if (m_TaskList->HasDescription(current))
details += QString("Description: %1
").arg(QString::fromStdString(m_TaskList->GetDescription(current)));
QStringList stringList;
if (m_TaskList->HasImage(current))
stringList << QString::fromStdString("Image: " + m_TaskList->GetImage(current).string());
if (m_TaskList->HasSegmentation(current))
stringList << QString::fromStdString("Segmentation: " + m_TaskList->GetSegmentation(current).string());
if (m_TaskList->HasLabelName(current))
stringList << QString::fromStdString("Label name: " + m_TaskList->GetLabelName(current));
if (m_TaskList->HasLabelNameSuggestions(current))
stringList << QString::fromStdString("Label name suggestions: " + m_TaskList->GetLabelNameSuggestions(current).string());
if (m_TaskList->HasPreset(current))
stringList << QString::fromStdString("Label set preset: " + m_TaskList->GetPreset(current).string());
if (m_TaskList->HasDynamic(current))
stringList << QString("Segmentation type: %1").arg(m_TaskList->GetDynamic(current) ? "Dynamic" : "Static");
if (!stringList.empty())
details += QString("%1
").arg(stringList.join(QStringLiteral("
")));
m_Ui->detailsLabel->setText(details);
}
void QmitkSegmentationTaskListWidget::UpdateStoreAndAcceptButtons()
{
auto activeTaskIsShown = this->ActiveTaskIsShown();
m_Ui->storeButton->setVisible(activeTaskIsShown);
m_Ui->acceptButton->setEnabled(activeTaskIsShown);
}
/* Load/activate the currently displayed task. Unload all data nodes from
* previously active tasks first, but spare and reuse the image if possible.
*/
void QmitkSegmentationTaskListWidget::OnLoadButtonClicked()
{
if (!this->HandleUnsavedChanges() || m_UnsavedChanges)
return;
m_Ui->loadButton->setEnabled(false);
QApplication::setOverrideCursor(Qt::BusyCursor);
this->LoadTask(this->GetImageDataNode(m_CurrentTaskIndex.value()));
QApplication::restoreOverrideCursor();
}
/* If present, return the image data node for the task with the specified
* index. Otherwise, return nullptr.
*/
mitk::DataNode* QmitkSegmentationTaskListWidget::GetImageDataNode(size_t index) const
{
const auto imagePath = m_TaskList->GetAbsolutePath(m_TaskList->GetImage(index));
auto imageNodes = m_DataStorage->GetDerivations(m_TaskListNode, mitk::NodePredicateFunction::New([imagePath](const mitk::DataNode* node) {
return imagePath == GetInputLocation(node->GetData());
}));
return !imageNodes->empty()
? imageNodes->front()
: nullptr;
}
/* If present, return the segmentation data node for the task with the
* specified index. Otherwise, return nullptr.
*/
mitk::DataNode* QmitkSegmentationTaskListWidget::GetSegmentationDataNode(size_t index) const
{
const auto* imageNode = this->GetImageDataNode(index);
if (imageNode != nullptr)
{
auto segmentations = m_DataStorage->GetDerivations(imageNode, mitk::TNodePredicateDataType::New());
if (!segmentations->empty())
return segmentations->front();
}
return nullptr;
}
/* Unload all task data nodes but spare the passed image data node.
*/
void QmitkSegmentationTaskListWidget::UnloadTasks(const mitk::DataNode* skip)
{
this->UnsubscribeFromActiveSegmentation();
if (m_TaskListNode.IsNotNull())
{
auto imageNodes = m_DataStorage->GetDerivations(m_TaskListNode, mitk::TNodePredicateDataType::New());
for (auto imageNode : *imageNodes)
{
m_DataStorage->Remove(m_DataStorage->GetDerivations(imageNode, nullptr, false));
if (imageNode != skip)
m_DataStorage->Remove(imageNode);
}
}
this->SetActiveTaskIndex(std::nullopt);
}
void QmitkSegmentationTaskListWidget::LoadNextUnfinishedTask()
{
const auto current = m_CurrentTaskIndex.value();
const auto numTasks = m_TaskList->GetNumberOfTasks();
for (size_t unboundNext = current; unboundNext < current + numTasks; ++unboundNext)
{
auto next = unboundNext % numTasks;
if (!m_TaskList->IsDone(next))
{
this->SetCurrentTaskIndex(next);
this->OnLoadButtonClicked();
break;
}
}
}
/* Load/activate the currently displayed task. The task must specify
* an image. The segmentation is either created from scratch with an optional
* name for the first label, possibly based on a label set preset specified by
* the task, or loaded as specified by the task. If a result file does
* exist, it is chosen as segmentation instead.
*/
void QmitkSegmentationTaskListWidget::LoadTask(mitk::DataNode::Pointer imageNode)
{
this->UnloadTasks(imageNode);
const auto current = m_CurrentTaskIndex.value();
mitk::Image::Pointer image;
mitk::LabelSetImage::Pointer segmentation;
try
{
if (imageNode.IsNull())
{
const auto path = m_TaskList->GetAbsolutePath(m_TaskList->GetImage(current));
image = mitk::IOUtil::Load(path.string());
}
const auto path = m_TaskList->GetAbsolutePath(m_TaskList->GetResult(current));
const auto interimPath = m_TaskList->GetInterimPath(path);
if (std::filesystem::exists(path))
{
segmentation = mitk::IOUtil::Load(path.string());
}
else if (std::filesystem::exists(interimPath))
{
segmentation = mitk::IOUtil::Load(interimPath.string());
}
else if (m_TaskList->HasSegmentation(current))
{
const auto path = m_TaskList->GetAbsolutePath(m_TaskList->GetSegmentation(current));
segmentation = mitk::IOUtil::Load(path.string());
}
}
catch (const mitk::Exception&)
{
return;
}
if (imageNode.IsNull())
{
imageNode = mitk::DataNode::New();
imageNode->SetData(image);
m_DataStorage->Add(imageNode, m_TaskListNode);
mitk::RenderingManager::GetInstance()->InitializeViews(image->GetTimeGeometry());
}
else
{
image = static_cast(imageNode->GetData());
}
auto name = "Task " + std::to_string(current + 1);
imageNode->SetName(name);
if (segmentation.IsNull())
{
mitk::Image::ConstPointer templateImage = image;
if (templateImage->GetDimension() > 3)
{
if (m_TaskList->HasDynamic(current))
{
if (!m_TaskList->GetDynamic(current))
templateImage = mitk::SegmentationHelper::GetStaticSegmentationTemplate(image);
}
else
{
QmitkStaticDynamicSegmentationDialog dialog(this);
dialog.SetReferenceImage(templateImage);
dialog.exec();
templateImage = dialog.GetSegmentationTemplate();
}
}
auto segmentationNode = mitk::LabelSetImageHelper::CreateNewSegmentationNode(imageNode, templateImage, name);
segmentation = static_cast(segmentationNode->GetData());
if (m_TaskList->HasPreset(current))
{
const auto path = m_TaskList->GetAbsolutePath(m_TaskList->GetPreset(current));
- mitk::LabelSetIOHelper::LoadLabelSetImagePreset(path.string(), segmentation);
+ mitk::MultiLabelIOHelper::LoadLabelSetImagePreset(path.string(), segmentation);
}
else
{
auto label = mitk::LabelSetImageHelper::CreateNewLabel(segmentation);
if (m_TaskList->HasLabelName(current))
label->SetName(m_TaskList->GetLabelName(current));
segmentation->GetActiveLabelSet()->AddLabel(label);
}
m_DataStorage->Add(segmentationNode, imageNode);
}
else
{
auto segmentationNode = mitk::DataNode::New();
segmentationNode->SetName(name);
segmentationNode->SetData(segmentation);
m_DataStorage->Add(segmentationNode, imageNode);
}
// Workaround for T29431. Remove when T26953 is fixed.
mitk::DICOMQIPropertyHelper::DeriveDICOMSourceProperties(image, segmentation);
auto prefs = GetSegmentationPreferences();
if (prefs != nullptr)
{
if (m_TaskList->HasLabelNameSuggestions(current))
{
auto path = m_TaskList->GetAbsolutePath(m_TaskList->GetLabelNameSuggestions(current));
prefs->PutBool("default label naming", false);
prefs->Put("label suggestions", path.string());
prefs->PutBool("replace standard suggestions", true);
prefs->PutBool("suggest once", true);
}
else
{
prefs->PutBool("default label naming", true);
prefs->Put("label suggestions", "");
}
}
m_UnsavedChanges = false;
this->SetActiveTaskIndex(current);
this->SubscribeToActiveSegmentation();
this->OnCurrentTaskChanged();
}
void QmitkSegmentationTaskListWidget::SubscribeToActiveSegmentation()
{
if (m_ActiveTaskIndex.has_value())
{
auto segmentationNode = this->GetSegmentationDataNode(m_ActiveTaskIndex.value());
if (segmentationNode != nullptr)
{
auto segmentation = static_cast(segmentationNode->GetData());
auto command = itk::SimpleMemberCommand::New();
command->SetCallbackFunction(this, &QmitkSegmentationTaskListWidget::OnSegmentationModified);
m_SegmentationModifiedObserverTag = segmentation->AddObserver(itk::ModifiedEvent(), command);
}
}
}
void QmitkSegmentationTaskListWidget::UnsubscribeFromActiveSegmentation()
{
if (m_ActiveTaskIndex.has_value() && m_SegmentationModifiedObserverTag.has_value())
{
auto segmentationNode = this->GetSegmentationDataNode(m_ActiveTaskIndex.value());
if (segmentationNode != nullptr)
{
auto segmentation = static_cast(segmentationNode->GetData());
segmentation->RemoveObserver(m_SegmentationModifiedObserverTag.value());
}
m_SegmentationModifiedObserverTag.reset();
}
}
void QmitkSegmentationTaskListWidget::OnSegmentationModified()
{
if (!m_UnsavedChanges)
{
m_UnsavedChanges = true;
if (m_ActiveTaskIndex.value() == m_CurrentTaskIndex)
this->UpdateDetailsLabel();
}
}
void QmitkSegmentationTaskListWidget::SetActiveTaskIndex(const std::optional& index)
{
if (m_ActiveTaskIndex != index)
{
m_ActiveTaskIndex = index;
this->UpdateStoreAndAcceptButtons();
}
}
void QmitkSegmentationTaskListWidget::SetCurrentTaskIndex(const std::optional& index)
{
if (m_CurrentTaskIndex != index)
{
m_CurrentTaskIndex = index;
this->OnCurrentTaskChanged();
}
}
bool QmitkSegmentationTaskListWidget::ActiveTaskIsShown() const
{
return m_ActiveTaskIndex.has_value() && m_CurrentTaskIndex.has_value() && m_ActiveTaskIndex == m_CurrentTaskIndex;
}
bool QmitkSegmentationTaskListWidget::HandleUnsavedChanges(const QString& alternativeTitle)
{
if (m_UnsavedChanges)
{
const auto active = m_ActiveTaskIndex.value();
const auto current = m_CurrentTaskIndex.value();
QString title;
if (alternativeTitle.isEmpty())
{
title = QString("Load task %1").arg(current + 1);
if (m_TaskList->HasName(current))
title += ": " + QString::fromStdString(m_TaskList->GetName(current));
}
else
{
title = alternativeTitle;
}
auto text = QString("The currently active task %1 ").arg(active + 1);
if (m_TaskList->HasName(active))
text += "(" + QString::fromStdString(m_TaskList->GetName(active)) + ") ";
text += "has unsaved changes.";
auto reply = QMessageBox::question(this, title, text, QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Cancel);
switch (reply)
{
case QMessageBox::Save:
this->SaveActiveTask(!std::filesystem::exists(m_TaskList->GetResult(active)));
break;
case QMessageBox::Discard:
m_UnsavedChanges = false;
break;
default:
return false;
}
}
return true;
}
void QmitkSegmentationTaskListWidget::SaveActiveTask(bool saveAsIntermediateResult)
{
if (!m_ActiveTaskIndex.has_value())
return;
QApplication::setOverrideCursor(Qt::BusyCursor);
try
{
const auto active = m_ActiveTaskIndex.value();
m_TaskList->SaveTask(active, this->GetSegmentationDataNode(active)->GetData(), saveAsIntermediateResult);
this->OnUnsavedChangesSaved();
}
catch (const mitk::Exception& e)
{
MITK_ERROR << e;
}
QApplication::restoreOverrideCursor();
}
bool QmitkSegmentationTaskListWidget::OnPreShutdown()
{
return this->HandleUnsavedChanges(QStringLiteral("Application shutdown"));
}
void QmitkSegmentationTaskListWidget::OnPreviousTaskShortcutActivated()
{
m_Ui->previousButton->click();
}
void QmitkSegmentationTaskListWidget::OnNextTaskShortcutActivated()
{
m_Ui->nextButton->click();
}
void QmitkSegmentationTaskListWidget::OnLoadTaskShortcutActivated()
{
m_Ui->loadButton->click();
}
void QmitkSegmentationTaskListWidget::OnStoreInterimResultShortcutActivated()
{
m_Ui->storeButton->click();
}
void QmitkSegmentationTaskListWidget::OnAcceptSegmentationShortcutActivated()
{
m_Ui->acceptButton->click();
}
void QmitkSegmentationTaskListWidget::OnStoreButtonClicked()
{
this->SaveActiveTask(true);
}
void QmitkSegmentationTaskListWidget::OnAcceptButtonClicked()
{
auto* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager();
int activeToolId = -1;
if (toolManager != nullptr)
activeToolId = toolManager->GetActiveToolID();
this->SaveActiveTask();
this->LoadNextUnfinishedTask();
if (toolManager != nullptr)
toolManager->ActivateTool(activeToolId);
}
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkLoadMultiLabelPresetAction.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkLoadMultiLabelPresetAction.cpp
index b3506ce9de..5c3ff51ac5 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkLoadMultiLabelPresetAction.cpp
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkLoadMultiLabelPresetAction.cpp
@@ -1,56 +1,56 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "QmitkLoadMultiLabelPresetAction.h"
#include
-#include
+#include
#include
void QmitkLoadMultiLabelPresetAction::Run(const QList &selectedNodes)
{
const auto filename = QFileDialog::getOpenFileName(nullptr, QStringLiteral("Load Label Set Preset"),
QString(), QStringLiteral("Label set preset (*.lsetp)")).toStdString();
if (filename.empty())
return;
for (const auto &node : selectedNodes)
{
if (node.IsNull())
continue;
mitk::LabelSetImage::Pointer image = dynamic_cast(node->GetData());
if (image.IsNull())
continue;
- mitk::LabelSetIOHelper::LoadLabelSetImagePreset(filename, image);
+ mitk::MultiLabelIOHelper::LoadLabelSetImagePreset(filename, image);
}
}
void QmitkLoadMultiLabelPresetAction::SetDataStorage(mitk::DataStorage*)
{
}
void QmitkLoadMultiLabelPresetAction::SetFunctionality(berry::QtViewPart*)
{
}
void QmitkLoadMultiLabelPresetAction::SetSmoothed(bool)
{
}
void QmitkLoadMultiLabelPresetAction::SetDecimated(bool)
{
}
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSaveMultiLabelPresetAction.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSaveMultiLabelPresetAction.cpp
index 7b6b07ad6f..f108e59951 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSaveMultiLabelPresetAction.cpp
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSaveMultiLabelPresetAction.cpp
@@ -1,63 +1,63 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "QmitkSaveMultiLabelPresetAction.h"
#include
-#include
+#include
#include
#include
void QmitkSaveMultiLabelPresetAction::Run(const QList &selectedNodes)
{
for (const auto &node : selectedNodes)
{
if (node.IsNull())
continue;
mitk::LabelSetImage::Pointer image = dynamic_cast(node->GetData());
if (image.IsNull())
continue;
const auto filename = QFileDialog::getSaveFileName(nullptr, QStringLiteral("Save Label Set Preset"),
QString(), QStringLiteral("Label set preset (*.lsetp)")).toStdString();
if (filename.empty())
continue;
- if(!mitk::LabelSetIOHelper::SaveLabelSetImagePreset(filename, image))
+ if(!mitk::MultiLabelIOHelper::SaveLabelSetImagePreset(filename, image))
{
QMessageBox::critical(nullptr, QStringLiteral("Save Label Set Preset"),
QString("Could not save \"%1\" as label set preset.").arg(QString::fromStdString(node->GetName())));
continue;
}
}
}
void QmitkSaveMultiLabelPresetAction::SetDataStorage(mitk::DataStorage*)
{
}
void QmitkSaveMultiLabelPresetAction::SetFunctionality(berry::QtViewPart*)
{
}
void QmitkSaveMultiLabelPresetAction::SetSmoothed(bool)
{
}
void QmitkSaveMultiLabelPresetAction::SetDecimated(bool)
{
}
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp
index 14c3f23bb6..9fe283dfac 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp
@@ -1,1039 +1,1039 @@
/*============================================================================
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
// mitk
#include
#include
#include
#include
#include
#include
-#include
+#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// Qmitk
#include
#include
#include
#include
// us
#include
#include
// Qt
#include
#include
#include
// vtk
#include
#include
namespace
{
QList Get2DWindows(const QList allWindows)
{
QList 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::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::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();
// 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* service = context->getService(ppmRef);
service->RemoveAllPlanePositions();
context->ungetService(ppmRef);
m_ToolManager->SetReferenceData(nullptr);
m_ToolManager->SetWorkingData(nullptr);
}
m_ToolManager->ActiveToolChanged -=
mitk::MessageDelegate(this, &QmitkSegmentationView::ActiveToolChanged);
delete m_Controls;
}
/**********************************************************************/
/* private Q_SLOTS */
/**********************************************************************/
void QmitkSegmentationView::OnReferenceSelectionChanged(QList)
{
this->OnAnySelectionChanged();
}
void QmitkSegmentationView::OnSegmentationSelectionChanged(QList)
{
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::New();
command->SetCallbackFunction(this, &QmitkSegmentationView::ValidateSelectionInput);
m_ReferenceDataObserverTags[m_ReferenceNode] =
m_ReferenceNode->GetProperty("visible")->AddObserver(itk::ModifiedEvent(), command);
}
}
auto selectedWorkingNode = m_Controls->workingNodeSelector->GetSelectedNode();
bool workingNodeChanged = false;
if (m_WorkingNode != selectedWorkingNode)
{
workingNodeChanged = true;
// Remove visibility observer for the current working node
if (m_WorkingDataObserverTags.find(m_WorkingNode) != m_WorkingDataObserverTags.end())
{
m_WorkingNode->GetProperty("visible")->RemoveObserver(m_WorkingDataObserverTags[m_WorkingNode]);
m_WorkingDataObserverTags.erase(m_WorkingNode);
}
// 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::New();
command->SetCallbackFunction(this, &QmitkSegmentationView::ValidateSelectionInput);
m_WorkingDataObserverTags[m_WorkingNode] =
m_WorkingNode->GetProperty("visible")->AddObserver(itk::ModifiedEvent(), command);
}
}
// Reset camera if any selection changed but only if both reference node and working node are set
if ((referenceNodeChanged || workingNodeChanged) && (m_ReferenceNode.IsNotNull() && m_WorkingNode.IsNotNull()))
{
if (nullptr != m_RenderWindowPart)
{
m_RenderWindowPart->InitializeViews(m_ReferenceNode->GetData()->GetTimeGeometry(), false);
}
}
this->UpdateGUI();
}
void QmitkSegmentationView::OnVisibilityShortcutActivated()
{
if (m_WorkingNode.IsNull())
{
return;
}
bool isVisible = false;
m_WorkingNode->GetBoolProperty("visible", isVisible);
m_WorkingNode->SetVisibility(!isVisible);
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkSegmentationView::OnLabelToggleShortcutActivated()
{
if (m_WorkingNode.IsNull())
{
return;
}
auto workingImage = dynamic_cast(m_WorkingNode->GetData());
if (nullptr == workingImage)
{
return;
}
this->WaitCursorOn();
workingImage->GetActiveLabelSet()->SetNextActiveLabel();
workingImage->Modified();
this->WaitCursorOff();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkSegmentationView::OnNewSegmentation()
{
m_ToolManager->ActivateTool(-1);
if (m_ReferenceNode.IsNull())
{
MITK_ERROR << "'Create new segmentation' button should never be clickable unless a reference image is selected.";
return;
}
mitk::Image::ConstPointer referenceImage = dynamic_cast(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(newSegmentationNode->GetData());
if (nullptr == newLabelSetImage)
{
// something went wrong
return;
}
const auto labelSetPreset = this->GetDefaultLabelSetPreset();
- if (labelSetPreset.empty() || !mitk::LabelSetIOHelper::LoadLabelSetImagePreset(labelSetPreset, newLabelSetImage))
+ if (labelSetPreset.empty() || !mitk::MultiLabelIOHelper::LoadLabelSetImagePreset(labelSetPreset, newLabelSetImage))
{
auto newLabel = mitk::LabelSetImageHelper::CreateNewLabel(newLabelSetImage);
if (!m_DefaultLabelNaming)
{
QmitkNewSegmentationDialog::DoRenameLabel(newLabel,nullptr,m_Parent);
}
newLabelSetImage->GetActiveLabelSet()->AddLabel(newLabel);
}
if (!this->GetDataStorage()->Exists(newSegmentationNode))
{
this->GetDataStorage()->Add(newSegmentationNode, m_ReferenceNode);
}
if (m_ToolManager->GetWorkingData(0))
{
m_ToolManager->GetWorkingData(0)->SetSelected(false);
}
newSegmentationNode->SetSelected(true);
m_Controls->workingNodeSelector->SetCurrentSelectedNode(newSegmentationNode);
}
std::string QmitkSegmentationView::GetDefaultLabelSetPreset() const
{
auto labelSetPreset = mitk::BaseApplication::instance().config().getString(mitk::BaseApplication::ARG_SEGMENTATION_LABELSET_PRESET.toStdString(), "");
if (labelSetPreset.empty())
labelSetPreset = m_LabelSetPresetPreference.toStdString();
return labelSetPreset;
}
void QmitkSegmentationView::OnManualTool2DSelected(int id)
{
this->ResetMouseCursor();
mitk::StatusBar::GetInstance()->DisplayText("");
if (id >= 0)
{
std::string text = "Active Tool: \"";
text += m_ToolManager->GetToolById(id)->GetName();
text += "\"";
mitk::StatusBar::GetInstance()->DisplayText(text.c_str());
us::ModuleResource resource = m_ToolManager->GetToolById(id)->GetCursorIconResource();
this->SetMouseCursor(resource, 0, 0);
}
}
void QmitkSegmentationView::OnShowMarkerNodes(bool state)
{
mitk::SegTool2D::Pointer manualSegmentationTool;
unsigned int numberOfExistingTools = m_ToolManager->GetTools().size();
for (unsigned int i = 0; i < numberOfExistingTools; i++)
{
manualSegmentationTool = dynamic_cast(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();
const auto groupID = segmentation->GetGroupIndexOfLabel(labelValue);
if (groupID != segmentation->GetActiveLayer()) segmentation->SetActiveLayer(groupID);
if (labelValue != segmentation->GetActiveLabelSet()->GetActiveLabel()->GetValue()) segmentation->GetActiveLabelSet()->SetActiveLabel(labelValue);
segmentation->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkSegmentationView::OnGoToLabel(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);
}
else
{
QmitkNewSegmentationDialog::DoRenameLabel(label, segmentation, this->m_Parent, QmitkNewSegmentationDialog::Mode::NewLabel);
}
}
mitk::LabelSetImage* QmitkSegmentationView::GetCurrentSegmentation() const
{
auto workingNode = m_Controls->workingNodeSelector->GetSelectedNode();
if (workingNode.IsNull()) mitkThrow() << "Segmentation view is in an invalid state. Working node is null, but a label selection change has been triggered.";
auto segmentation = dynamic_cast(workingNode->GetData());
if (nullptr == segmentation) mitkThrow() << "Segmentation view is in an invalid state. Working node contains no segmentation, but a label selection change has been triggered.";
return segmentation;
}
/**********************************************************************/
/* private */
/**********************************************************************/
void QmitkSegmentationView::CreateQtPartControl(QWidget* parent)
{
m_Parent = parent;
m_Controls = new Ui::QmitkSegmentationViewControls;
m_Controls->setupUi(parent);
// *------------------------
// * SHORTCUTS
// *------------------------
QShortcut* visibilityShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key::Key_H), parent);
connect(visibilityShortcut, &QShortcut::activated, this, &QmitkSegmentationView::OnVisibilityShortcutActivated);
QShortcut* labelToggleShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key::Key_L, Qt::CTRL | Qt::Key::Key_I), parent);
connect(labelToggleShortcut, &QShortcut::activated, this, &QmitkSegmentationView::OnLabelToggleShortcutActivated);
// *------------------------
// * DATA SELECTION WIDGETS
// *------------------------
m_Controls->referenceNodeSelector->SetDataStorage(GetDataStorage());
m_Controls->referenceNodeSelector->SetNodePredicate(m_ReferencePredicate);
m_Controls->referenceNodeSelector->SetInvalidInfo("Select an image");
m_Controls->referenceNodeSelector->SetPopUpTitel("Select an image");
m_Controls->referenceNodeSelector->SetPopUpHint("Select an image that should be used to define the geometry and bounds of the segmentation.");
m_Controls->workingNodeSelector->SetDataStorage(GetDataStorage());
m_Controls->workingNodeSelector->SetNodePredicate(m_SegmentationPredicate);
m_Controls->workingNodeSelector->SetInvalidInfo("Select a segmentation");
m_Controls->workingNodeSelector->SetPopUpTitel("Select a segmentation");
m_Controls->workingNodeSelector->SetPopUpHint("Select a segmentation that should be modified. Only segmentation with the same geometry and within the bounds of the reference image are selected.");
connect(m_Controls->referenceNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged,
this, &QmitkSegmentationView::OnReferenceSelectionChanged);
connect(m_Controls->workingNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged,
this, &QmitkSegmentationView::OnSegmentationSelectionChanged);
// *------------------------
// * TOOLMANAGER
// *------------------------
m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager();
m_ToolManager->SetDataStorage(*(this->GetDataStorage()));
m_ToolManager->InitializeTools();
QString segTools2D = tr("Add Subtract Lasso Fill Erase Close Paint Wipe 'Region Growing' 'Live Wire'");
QString segTools3D = tr("Threshold 'UL Threshold' Otsu 'Region Growing 3D' Picking GrowCut");
#ifdef __linux__
segTools3D.append(" nnUNet"); // plugin not enabled for MacOS / Windows
#endif
std::regex extSegTool2DRegEx("SegTool2D$");
std::regex extSegTool3DRegEx("SegTool3D$");
auto tools = m_ToolManager->GetTools();
for (const auto &tool : tools)
{
if (std::regex_search(tool->GetNameOfClass(), extSegTool2DRegEx))
{
segTools2D.append(QString(" '%1'").arg(tool->GetName()));
}
else if (std::regex_search(tool->GetNameOfClass(), extSegTool3DRegEx))
{
segTools3D.append(QString(" '%1'").arg(tool->GetName()));
}
}
// setup 2D tools
m_Controls->toolSelectionBox2D->SetToolManager(*m_ToolManager);
m_Controls->toolSelectionBox2D->SetGenerateAccelerators(true);
m_Controls->toolSelectionBox2D->SetToolGUIArea(m_Controls->toolGUIArea2D);
m_Controls->toolSelectionBox2D->SetDisplayedToolGroups(segTools2D.toStdString());
m_Controls->toolSelectionBox2D->SetLayoutColumns(3);
connect(m_Controls->toolSelectionBox2D, &QmitkToolSelectionBox::ToolSelected,
this, &QmitkSegmentationView::OnManualTool2DSelected);
// setup 3D Tools
m_Controls->toolSelectionBox3D->SetToolManager(*m_ToolManager);
m_Controls->toolSelectionBox3D->SetGenerateAccelerators(true);
m_Controls->toolSelectionBox3D->SetToolGUIArea(m_Controls->toolGUIArea3D);
m_Controls->toolSelectionBox3D->SetDisplayedToolGroups(segTools3D.toStdString());
m_Controls->toolSelectionBox3D->SetLayoutColumns(3);
//TODO temporary interpolator deactivation
//m_Controls->slicesInterpolator->SetDataStorage(this->GetDataStorage());
//TODO END temporary interpolator deactivation
// create general signal / slot connections
connect(m_Controls->newSegmentationButton, &QToolButton::clicked, this, &QmitkSegmentationView::OnNewSegmentation);
//TODO temporary interpolator deactivation
// connect(m_Controls->slicesInterpolator, &QmitkSlicesInterpolator::SignalShowMarkerNodes, this, &QmitkSegmentationView::OnShowMarkerNodes);
//TODO END temporary interpolator deactivation
connect(m_Controls->multiLabelWidget, &QmitkMultiLabelManager::CurrentSelectionChanged, this, &QmitkSegmentationView::OnCurrentLabelSelectionChanged);
connect(m_Controls->multiLabelWidget, &QmitkMultiLabelManager::GoToLabel, this, &QmitkSegmentationView::OnGoToLabel);
connect(m_Controls->multiLabelWidget, &QmitkMultiLabelManager::LabelRenameRequested, this, &QmitkSegmentationView::OnLabelRenameRequested);
auto command = itk::SimpleMemberCommand::New();
command->SetCallbackFunction(this, &QmitkSegmentationView::ValidateSelectionInput);
m_RenderingManagerObserverTag =
mitk::RenderingManager::GetInstance()->AddObserver(mitk::RenderingManagerViewsInitializedEvent(), command);
m_RenderWindowPart = this->GetRenderWindowPart();
if (nullptr != m_RenderWindowPart)
{
this->RenderWindowPartActivated(m_RenderWindowPart);
}
// Make sure the GUI notices if appropriate data is already present on creation.
// Should be done last, if everything else is configured because it triggers the autoselection of data.
m_Controls->referenceNodeSelector->SetAutoSelectNewNodes(true);
m_Controls->workingNodeSelector->SetAutoSelectNewNodes(true);
this->UpdateGUI();
}
void QmitkSegmentationView::ActiveToolChanged()
{
if (nullptr == m_RenderWindowPart)
{
return;
}
mitk::TimeGeometry* interactionReferenceGeometry = nullptr;
auto activeTool = m_ToolManager->GetActiveTool();
if (nullptr != activeTool && m_ReferenceNode.IsNotNull())
{
mitk::Image::ConstPointer referenceImage = dynamic_cast(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(this, &QmitkSegmentationView::ActiveToolChanged);
}
}
}
void QmitkSegmentationView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* /*renderWindowPart*/)
{
m_RenderWindowPart = nullptr;
if (nullptr != m_Parent)
{
m_Parent->setEnabled(false);
}
// remove message-connection to make sure no message is processed if no render window part is available
m_ToolManager->ActiveToolChanged -=
mitk::MessageDelegate(this, &QmitkSegmentationView::ActiveToolChanged);
m_Controls->slicesInterpolator->Uninitialize();
}
void QmitkSegmentationView::RenderWindowPartInputChanged(mitk::IRenderWindowPart* /*renderWindowPart*/)
{
if (nullptr == m_RenderWindowPart)
{
return;
}
m_Controls->slicesInterpolator->Uninitialize();
auto all2DWindows = Get2DWindows(m_RenderWindowPart->GetQmitkRenderWindows().values());
m_Controls->slicesInterpolator->Initialize(m_ToolManager, all2DWindows);
}
void QmitkSegmentationView::OnPreferencesChanged(const mitk::IPreferences* prefs)
{
auto labelSuggestions = mitk::BaseApplication::instance().config().getString(mitk::BaseApplication::ARG_SEGMENTATION_LABEL_SUGGESTIONS.toStdString(), "");
m_DefaultLabelNaming = labelSuggestions.empty()
? prefs->GetBool("default label naming", true)
: false; // No default label naming when label suggestions are enforced via command-line argument
if (nullptr != m_Controls)
{
m_Controls->multiLabelWidget->SetDefaultLabelNaming(m_DefaultLabelNaming);
bool slimView = prefs->GetBool("slim view", false);
m_Controls->toolSelectionBox2D->SetShowNames(!slimView);
m_Controls->toolSelectionBox3D->SetShowNames(!slimView);
}
m_DrawOutline = prefs->GetBool("draw outline", true);
m_SelectionMode = prefs->GetBool("selection mode", false);
m_LabelSetPresetPreference = QString::fromStdString(prefs->Get("label set preset", ""));
this->ApplyDisplayOptions();
this->ApplySelectionMode();
}
void QmitkSegmentationView::NodeAdded(const mitk::DataNode* node)
{
if (m_SegmentationPredicate->CheckNode(node))
this->ApplyDisplayOptions(const_cast(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* service = context->getService(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;
//TODO temporary interpolator deactivation
//mitk::Image* image = dynamic_cast(node->GetData());
//mitk::SurfaceInterpolationController::GetInstance()->RemoveInterpolationSession(image);
//TODO END temporary interpolator deactivation
}
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(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* service = context->getService(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& 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() ? "
" : "") + 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(workingNode->GetData());
auto activeLayer = labelSetImage->GetActiveLayer();
numberOfLabels = labelSetImage->GetNumberOfLabels(activeLayer);
//TODO temporary interpolator deactivation
//if (numberOfLabels > 1)
// m_Controls->slicesInterpolator->setEnabled(true);
//TODO END temporary interpolator deactivation
m_Controls->multiLabelWidget->SetMultiLabelSegmentation(dynamic_cast(workingNode->GetData()));
}
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() ? "
" : "") + 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("" + text + "");
m_Controls->selectionWarningLabel->show();
}
}