diff --git a/Modules/RegistrationOntology/files.cmake b/Modules/RegistrationOntology/files.cmake index 1e696fcf9f..533610f2ca 100644 --- a/Modules/RegistrationOntology/files.cmake +++ b/Modules/RegistrationOntology/files.cmake @@ -1,5 +1,6 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkLesionPropagation.cpp + mitkSegmentationPropagation.cpp ) diff --git a/Modules/RegistrationOntology/include/mitkLesionPropagation.h b/Modules/RegistrationOntology/include/mitkLesionPropagation.h index bacfc61754..3a9a2dfd89 100644 --- a/Modules/RegistrationOntology/include/mitkLesionPropagation.h +++ b/Modules/RegistrationOntology/include/mitkLesionPropagation.h @@ -1,73 +1,72 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKLESIONPROPAGATION_H #define MITKLESIONPROPAGATION_H // registration ontology module #include "MitkRegistrationOntologyExports.h" // mitk core #include // semantic relations module #include #include // matchpoint ontology #include #include namespace mitk { /** * @brief * * */ class MITKREGISTRATIONONTOLOGY_EXPORT LesionPropagation { public: LesionPropagation(DataStorage* dataStorage); /** * @brief Find the closest segmentation mask for the lesion given the target image. * 'Closest' in this case means the following: * 1. retrieve all images where the given lesion is already present. * 2a. filter all images for images that are in the same control point group as the target image * 2b. filter all images for images that are in the same information type group as the target image * 3a. check if a control point image is registered to the target image * 3b. check if an information type image is registered to the target image * 4. return the first found registered image */ BaseData* FindClosestSegmentationMask(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& sourceLesion, DataNode* targetImage); - DataNode::Pointer PropagateSegmentationMask(const SemanticTypes::CaseID& caseID, BaseData* segmentationMask, DataNode* targetImage); private: bool LesionPropagation::CheckControlPoint(const DataNode* lesionImage, const DataNode* targetImage); bool LesionPropagation::CheckInformationType(const DataNode* lesionImage, const DataNode* targetImage); DataNode* LesionPropagation::GetSegmentationFromRelatedImage(const SemanticTypes::Lesion& sourceLesion, const DataNode* lesionImage); bool LesionPropagation::CheckRegistration(const DataNode* lesionImage, const DataNode* targetImage); WeakPointer m_DataStorage; std::unique_ptr m_SemanticRelationsDataStorageAccess; std::unique_ptr m_SemanticRelationsIntegration; }; } // namespace mitk #endif // MITKLESIONPROPAGATION_H diff --git a/Modules/RegistrationOntology/include/mitkSegmentationPropagation.h b/Modules/RegistrationOntology/include/mitkSegmentationPropagation.h new file mode 100644 index 0000000000..753f5b81c2 --- /dev/null +++ b/Modules/RegistrationOntology/include/mitkSegmentationPropagation.h @@ -0,0 +1,50 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef MITKSEGMENTATIONPROPAGATION_H +#define MITKSEGMENTATIONPROPAGATION_H + +// registration ontology module +#include "MitkRegistrationOntologyExports.h" + +// mitk core +#include + +// matchpoint ontology +//#include +//#include + +namespace mitk +{ + /** + * @brief + * + * + */ + class MITKREGISTRATIONONTOLOGY_EXPORT SegmentationPropagation + { + public: + + /** + * @brief + * + */ + DataNode::Pointer PropagateSegmentationMask(BaseData* segmentationMask, DataNode* targetImage); + + }; +} // namespace mitk + +#endif // MITKSEGMENTATIONPROPAGATION_H diff --git a/Modules/RegistrationOntology/src/mitkLesionPropagation.cpp b/Modules/RegistrationOntology/src/mitkLesionPropagation.cpp index 08aa868ab7..e448520dca 100644 --- a/Modules/RegistrationOntology/src/mitkLesionPropagation.cpp +++ b/Modules/RegistrationOntology/src/mitkLesionPropagation.cpp @@ -1,228 +1,177 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkLesionPropagation.h" // semantic relations module #include #include #include -// mitk core -#include -#include - -// DICOMQI module -#include - -// multilabel module -#include - mitk::LesionPropagation::LesionPropagation(DataStorage* dataStorage) - : m_DataStorage(dataStorage) - , m_SemanticRelationsDataStorageAccess(std::make_unique(dataStorage)) + : m_SemanticRelationsDataStorageAccess(std::make_unique(dataStorage)) , m_SemanticRelationsIntegration(std::make_unique()) { // nothing here } mitk::BaseData* mitk::LesionPropagation::FindClosestSegmentationMask(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& sourceLesion, DataNode* targetImage) { auto allImagesOfLesion = m_SemanticRelationsDataStorageAccess->GetAllImagesOfLesion(caseID, sourceLesion); SemanticRelationsDataStorageAccess::DataNodeVector sameControlPointImages; SemanticRelationsDataStorageAccess::DataNodeVector sameInformationTypeImages; SemanticRelationsDataStorageAccess::DataNodeVector noRelation; for (const auto& image : allImagesOfLesion) { bool sameControlPoint = CheckControlPoint(image, targetImage); bool sameInformationType = CheckInformationType(image, targetImage); if(sameControlPoint && !sameInformationType) { // lesion image and target image are in the same control point relation group sameControlPointImages.push_back(image); } if (sameInformationType && !sameControlPoint) { // lesion image and target image are in the same information type relation group sameInformationTypeImages.push_back(image); } if (!sameControlPoint && !sameInformationType) { // lesion image and target image are none of the above relation groups noRelation.push_back(image); } } // sorted all lesion images according to the target image's relation group for (const auto& image : sameControlPointImages) { // check if any image in the same control point relation group is registered to the target image bool registered = CheckRegistration(image, targetImage); if (registered) { auto segmentationNode = GetSegmentationFromRelatedImage(sourceLesion, image); if (nullptr != segmentationNode) { return segmentationNode->GetData(); } } } for (const auto& image : sameInformationTypeImages) { bool registered = CheckRegistration(image, targetImage); if (registered) { // check if any image in the same information type relation group is registered to the target image auto segmentationNode = GetSegmentationFromRelatedImage(sourceLesion, image); if (nullptr != segmentationNode) { return segmentationNode->GetData(); } } } for (const auto& image : noRelation) { bool registered = CheckRegistration(image, targetImage); if (registered) { // need special registration check for non-related images auto segmentationNode = GetSegmentationFromRelatedImage(sourceLesion, image); if (nullptr != segmentationNode) { return segmentationNode->GetData(); } } } return nullptr; } -mitk::DataNode::Pointer mitk::LesionPropagation::PropagateSegmentationMask(const SemanticTypes::CaseID& caseID, BaseData* segmentationMask, DataNode* targetImage) -{ - if (m_DataStorage.IsExpired()) - { - return nullptr; - } - - if (nullptr == targetImage) - { - return nullptr; - } - - BaseData* targetImageData = targetImage->GetData(); - if (nullptr == targetImageData) - { - return nullptr; - } - - auto dataStorage = m_DataStorage.Lock(); - // clone the segmentation image / the base data - Image::Pointer segmentation = dynamic_cast(segmentationMask); - auto labelSetImage = segmentation->Clone(); - - // copy DICOM information from the target image to the new segmentation - DICOMQIPropertyHandler::DeriveDICOMSourceProperties(targetImageData, labelSetImage); - - // DICOM tag is "SeriesInstanceUID" - needs to be removed to define a new tag with UID - std::string nodeIDPropertyKey = GeneratePropertyNameForDICOMTag(0x0020, 0x000e); - labelSetImage->RemoveProperty(nodeIDPropertyKey); - - auto newSegmentationNode = DataNode::New(); - newSegmentationNode->SetData(labelSetImage); - newSegmentationNode->SetProperty("name", StringProperty::New("PropagatedLesion")); - newSegmentationNode->SetProperty("binary", BoolProperty::New(true)); - - dataStorage->Add(newSegmentationNode, targetImage); - - return newSegmentationNode; -} - bool mitk::LesionPropagation::CheckControlPoint(const DataNode* lesionImage, const DataNode* targetImage) { auto lesionImageControlPoint = SemanticRelationsInference::GetControlPointOfImage(lesionImage); auto targetImageControlPoint = SemanticRelationsInference::GetControlPointOfImage(targetImage); if (lesionImageControlPoint.UID == targetImageControlPoint.UID) { return true; } return false; } bool mitk::LesionPropagation::CheckInformationType(const DataNode* lesionImage, const DataNode* targetImage) { auto lesionImageInformationType = SemanticRelationsInference::GetInformationTypeOfImage(lesionImage); auto targetImageInformationType = SemanticRelationsInference::GetInformationTypeOfImage(targetImage); if (lesionImageInformationType == targetImageInformationType) { return true; } return false; } mitk::DataNode* mitk::LesionPropagation::GetSegmentationFromRelatedImage(const SemanticTypes::Lesion& sourceLesion, const DataNode* lesionImage) { // found an image of the source lesion that is registered to the target image // could clone the mask and copy it to the target image // need to find the correct segmentation amongst many segmentations of the source image SemanticRelationsDataStorageAccess::DataNodeVector allSpecificSegmentations; try { allSpecificSegmentations = m_SemanticRelationsDataStorageAccess->GetAllSpecificSegmentations(mitk::GetCaseIDFromDataNode(lesionImage), SemanticRelationsInference::GetControlPointOfImage(lesionImage), SemanticRelationsInference::GetInformationTypeOfImage(lesionImage)); } catch (const SemanticRelationException&) { return nullptr; } auto lambda = [this, &sourceLesion](const DataNode* specificSegmentation) { try { auto representedLesion = SemanticRelationsInference::GetLesionOfSegmentation(specificSegmentation); return sourceLesion.UID == representedLesion.UID; } catch (const SemanticRelationException&) { return false; } }; auto segmentation = std::find_if(allSpecificSegmentations.begin(), allSpecificSegmentations.end(), lambda); // return segmentation as image mask of source lesion if (segmentation != allSpecificSegmentations.end()) { return *segmentation; } return nullptr; } bool mitk::LesionPropagation::CheckRegistration(const DataNode* lesionImage, const DataNode* targetImage) { // #TODO // for now we will always return true // registration ontology is not fully implemented return true; } diff --git a/Modules/RegistrationOntology/src/mitkSegmentationPropagation.cpp b/Modules/RegistrationOntology/src/mitkSegmentationPropagation.cpp new file mode 100644 index 0000000000..1a144b30cc --- /dev/null +++ b/Modules/RegistrationOntology/src/mitkSegmentationPropagation.cpp @@ -0,0 +1,69 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkSegmentationPropagation.h" + +// mitk core +#include +#include + +// DICOMQI module +#include + +// multilabel module +#include + +mitk::DataNode::Pointer mitk::SegmentationPropagation::PropagateSegmentationMask(BaseData* segmentationMask, DataNode* targetImage) +{ + if (nullptr == targetImage) + { + return nullptr; + } + + BaseData* targetImageData = targetImage->GetData(); + if (nullptr == targetImageData) + { + return nullptr; + } + + Image::Pointer segmentation = dynamic_cast(segmentationMask); + if (nullptr == segmentation) + { + return nullptr; + } + + + // clone the segmentation image / the base data + //auto labelSetImage = segmentation->Clone(); + // create new empty segmentation + auto labelSetImage = LabelSetImage::New(); + // copy image data (e.g. pixel information) + labelSetImage->InitializeByLabeledImage(segmentation); + + // copy DICOM information from the target image to the new segmentation + DICOMQIPropertyHandler::DeriveDICOMSourceProperties(targetImageData, labelSetImage); + + // DICOM tag is "SeriesInstanceUID" - needs to be removed to define a new tag with UID + std::string nodeIDPropertyKey = GeneratePropertyNameForDICOMTag(0x0020, 0x000e); + labelSetImage->RemoveProperty(nodeIDPropertyKey); + + auto newSegmentationNode = DataNode::New(); + newSegmentationNode->SetData(labelSetImage); + newSegmentationNode->SetProperty("name", StringProperty::New("PropagatedLesion")); + newSegmentationNode->SetProperty("binary", BoolProperty::New(true)); + + return newSegmentationNode; +} diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp index dde11138a5..a2778e7d89 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp @@ -1,443 +1,446 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // semantic relations plugin #include "QmitkLesionInfoWidget.h" #include "QmitkSemanticRelationsNodeSelectionDialog.h" #include "QmitkDataNodeAddToSemanticRelationsAction.h" // semantic relations UI module #include // semantic relations module #include #include #include #include // registration ontology module #include +#include // qt #include #include #include #include #include QmitkLesionInfoWidget::QmitkLesionInfoWidget(mitk::DataStorage* dataStorage, QWidget* parent /*= nullptr*/) : QWidget(parent) , m_DataStorage(dataStorage) , m_SemanticRelationsDataStorageAccess(std::make_unique(dataStorage)) , m_SemanticRelationsIntegration(std::make_unique()) { Initialize(); } QmitkLesionInfoWidget::~QmitkLesionInfoWidget() { // nothing here } void QmitkLesionInfoWidget::Initialize() { m_Controls.setupUi(this); m_Controls.lesionTreeView->setAlternatingRowColors(true); m_Controls.lesionTreeView->setSelectionMode(QAbstractItemView::SingleSelection); m_Controls.lesionTreeView->setSelectionBehavior(QAbstractItemView::SelectRows); m_Controls.lesionTreeView->setContextMenuPolicy(Qt::CustomContextMenu); m_StorageModel = new QmitkLesionTreeModel(m_Controls.lesionTreeView); if (m_DataStorage.IsExpired()) { return; } auto dataStorage = m_DataStorage.Lock(); m_StorageModel->SetDataStorage(dataStorage); m_Controls.lesionTreeView->setModel(m_StorageModel); SetUpConnections(); } void QmitkLesionInfoWidget::SetUpConnections() { connect(m_StorageModel, &QmitkLesionTreeModel::ModelUpdated, this, &QmitkLesionInfoWidget::OnModelUpdated); // connect buttons to modify semantic relations connect(m_Controls.addLesionPushButton, &QPushButton::clicked, this, &QmitkLesionInfoWidget::OnAddLesionButtonClicked); // connect each list widget with a custom slots connect(m_Controls.lesionTreeView->selectionModel(), &QItemSelectionModel::currentChanged, this, &QmitkLesionInfoWidget::OnSelectionChanged); // connect context menu entries connect(m_Controls.lesionTreeView, &QTreeView::customContextMenuRequested, this, &QmitkLesionInfoWidget::OnLesionListContextMenuRequested); } void QmitkLesionInfoWidget::SetCaseID(const mitk::SemanticTypes::CaseID& caseID) { m_CaseID = caseID; m_StorageModel->SetCaseID(caseID); } void QmitkLesionInfoWidget::SetDataNodeSelection(const QList& dataNodeSelection) { m_StorageModel->SetDataNodeSelection(dataNodeSelection); } ////////////////////////////////////////////////////////////////////////// // Implementation of the QT_SLOTS ////////////////////////////////////////////////////////////////////////// void QmitkLesionInfoWidget::OnModelUpdated() { m_Controls.lesionTreeView->expandAll(); int columns = m_Controls.lesionTreeView->model()->columnCount(); for (int i = 0; i < columns; ++i) { m_Controls.lesionTreeView->resizeColumnToContents(i); } } void QmitkLesionInfoWidget::OnAddLesionButtonClicked() { if (m_CaseID.empty()) { QMessageBox msgBox(QMessageBox::Warning, "No case ID set.", "In order to add a lesion, please specify the current case / patient."); msgBox.exec(); return; } mitk::SemanticTypes::Lesion newLesion = mitk::GenerateNewLesion(); try { m_SemanticRelationsIntegration->AddLesion(m_CaseID, newLesion); } catch (mitk::SemanticRelationException& e) { MITK_INFO << "Could not add a new lesion. " << e; } } void QmitkLesionInfoWidget::OnSelectionChanged(const QModelIndex& current, const QModelIndex& /*previous*/) { // only the UID is needed to identify a representing lesion QVariant data = m_StorageModel->data(current, Qt::UserRole); if (!data.canConvert()) { return; } auto lesion = data.value()->GetData().GetLesion(); if (false == mitk::SemanticRelationsInference::InstanceExists(m_CaseID, lesion)) { // no UID of a existing lesion found; cannot create a lesion return; } // if selected data nodes are set, reset to empty list to // hide "selected data nodes presence background highlighting" in the model if (!m_StorageModel->GetSelectedDataNodes().isEmpty()) { m_StorageModel->SetDataNodeSelection(QList()); } emit LesionSelectionChanged(lesion); } void QmitkLesionInfoWidget::OnLesionListContextMenuRequested(const QPoint& pos) { if (nullptr == m_SemanticRelationsIntegration) { return; } if (m_CaseID.empty()) { QMessageBox msgBox(QMessageBox::Warning, "No case ID set.", "In order to access the context menu entries a case ID has to be set."); msgBox.exec(); return; } QModelIndex index = m_Controls.lesionTreeView->indexAt(pos); if (!index.isValid()) { // no item clicked; cannot retrieve the current lesion return; } QVariant data = m_StorageModel->data(index, Qt::UserRole); mitk::SemanticTypes::Lesion selectedLesion; if (data.canConvert()) { selectedLesion = data.value()->GetData().GetLesion(); } else { return; } QMenu* menu = new QMenu(m_Controls.lesionTreeView); QAction* linkToSegmentation = new QAction("Link to segmentation", this); linkToSegmentation->setEnabled(true); connect(linkToSegmentation, &QAction::triggered, [this, selectedLesion] { OnLinkToSegmentation(selectedLesion); }); menu->addAction(linkToSegmentation); QAction* setLesionName = new QAction("Set lesion name", this); setLesionName->setEnabled(true); connect(setLesionName, &QAction::triggered, [this, selectedLesion] { OnSetLesionName(selectedLesion); }); menu->addAction(setLesionName); QAction* setLesionClass = new QAction("Set lesion class", this); setLesionClass->setEnabled(true); connect(setLesionClass, &QAction::triggered, [this, selectedLesion] { OnSetLesionClass(selectedLesion); }); menu->addAction(setLesionClass); QAction* propageLesionToImage = new QAction("Propagate lesion to image", this); propageLesionToImage->setEnabled(true); connect(propageLesionToImage, &QAction::triggered, [this, selectedLesion] { OnPropagateLesion(selectedLesion); }); menu->addAction(propageLesionToImage); QAction* removeLesion = new QAction("Remove lesion", this); removeLesion->setEnabled(true); connect(removeLesion, &QAction::triggered, [this, selectedLesion] { OnRemoveLesion(selectedLesion); }); menu->addAction(removeLesion); menu->popup(QCursor::pos()); } void QmitkLesionInfoWidget::OnLinkToSegmentation(mitk::SemanticTypes::Lesion selectedLesion) { if (m_DataStorage.IsExpired()) { return; } auto dataStorage = m_DataStorage.Lock(); QmitkSemanticRelationsNodeSelectionDialog* dialog = new QmitkSemanticRelationsNodeSelectionDialog(this, "Select segmentation to link to the selected lesion.", ""); dialog->setWindowTitle("Select segmentation node"); dialog->SetDataStorage(dataStorage); dialog->SetNodePredicate(mitk::NodePredicates::GetSegmentationPredicate()); dialog->SetSelectOnlyVisibleNodes(true); dialog->SetCaseID(m_CaseID); int dialogReturnValue = dialog->exec(); if (QDialog::Rejected == dialogReturnValue) { return; } auto nodes = dialog->GetSelectedNodes(); mitk::DataNode::Pointer selectedDataNode = nullptr; if (!nodes.isEmpty()) { // only single selection allowed selectedDataNode = nodes.front(); } if (nullptr == selectedDataNode || false == mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(selectedDataNode)) { QMessageBox msgBox(QMessageBox::Warning, "No valid segmentation node selected.", "In order to link the selected lesion to a segmentation, please specify a valid segmentation node."); msgBox.exec(); return; } mitk::BaseData* baseData = selectedDataNode->GetData(); if (nullptr == baseData) { QMessageBox msgBox(QMessageBox::Warning, "No valid base data.", "In order to link the selected lesion to a segmentation, please specify a valid segmentation node."); msgBox.exec(); return; } try { m_SemanticRelationsIntegration->LinkSegmentationToLesion(selectedDataNode, selectedLesion); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox(QMessageBox::Warning, "Could not link the selected lesion.", "The program wasn't able to correctly link the selected lesion with the selected segmentation.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.exec(); } } void QmitkLesionInfoWidget::OnSetLesionName(mitk::SemanticTypes::Lesion selectedLesion) { // use the lesion information to set the input text for the dialog QmitkLesionTextDialog* inputDialog = new QmitkLesionTextDialog(this); inputDialog->setWindowTitle("Set lesion name"); inputDialog->SetLineEditText(selectedLesion.name); int dialogReturnValue = inputDialog->exec(); if (QDialog::Rejected == dialogReturnValue) { return; } selectedLesion.name = inputDialog->GetLineEditText().toStdString(); m_SemanticRelationsIntegration->OverwriteLesion(m_CaseID, selectedLesion); } void QmitkLesionInfoWidget::OnSetLesionClass(mitk::SemanticTypes::Lesion selectedLesion) { // use the lesion information to set the input text for the dialog QmitkLesionTextDialog* inputDialog = new QmitkLesionTextDialog(this); inputDialog->setWindowTitle("Set lesion class"); inputDialog->SetLineEditText(selectedLesion.lesionClass.classType); // prepare the completer for the dialogs input text field mitk::LesionClassVector allLesionClasses = mitk::SemanticRelationsInference::GetAllLesionClassesOfCase(m_CaseID); QStringList wordList; for (const auto& lesionClass : allLesionClasses) { wordList << QString::fromStdString(lesionClass.classType); } QCompleter* completer = new QCompleter(wordList, this); completer->setCaseSensitivity(Qt::CaseInsensitive); inputDialog->GetLineEdit()->setCompleter(completer); int dialogReturnValue = inputDialog->exec(); if (QDialog::Rejected == dialogReturnValue) { return; } // retrieve the new input lesion class type and check for an already existing lesion class types std::string newLesionClassType = inputDialog->GetLineEditText().toStdString(); mitk::SemanticTypes::LesionClass existingLesionClass = mitk::FindExistingLesionClass(newLesionClassType, allLesionClasses); if (existingLesionClass.UID.empty()) { // could not find lesion class information for the new lesion class type // create a new lesion class for the selected lesion existingLesionClass = mitk::GenerateNewLesionClass(newLesionClassType); } selectedLesion.lesionClass = existingLesionClass; m_SemanticRelationsIntegration->OverwriteLesion(m_CaseID, selectedLesion); } void QmitkLesionInfoWidget::OnPropagateLesion(mitk::SemanticTypes::Lesion selectedLesion) { if (m_DataStorage.IsExpired()) { return; } auto dataStorage = m_DataStorage.Lock(); QmitkSemanticRelationsNodeSelectionDialog* dialog = new QmitkSemanticRelationsNodeSelectionDialog(this, "Select data node to propagate the selected lesion.", ""); dialog->setWindowTitle("Select image node"); dialog->SetDataStorage(dataStorage); dialog->SetNodePredicate(mitk::NodePredicates::GetImagePredicate()); dialog->SetSelectOnlyVisibleNodes(true); dialog->SetCaseID(m_CaseID); dialog->SetLesion(selectedLesion); int dialogReturnValue = dialog->exec(); if (QDialog::Rejected == dialogReturnValue) { return; } auto nodes = dialog->GetSelectedNodes(); mitk::DataNode::Pointer selectedDataNode = nullptr; if (!nodes.isEmpty()) { // only single selection allowed selectedDataNode = nodes.front(); } if (nullptr == selectedDataNode || false == mitk::NodePredicates::GetImagePredicate()->CheckNode(selectedDataNode)) { QMessageBox msgBox(QMessageBox::Warning, "No valid image node selected.", "In order to propagate the selected lesion to an image, please specify a valid image node."); msgBox.exec(); return; } mitk::BaseData* baseData = selectedDataNode->GetData(); if (nullptr == baseData) { QMessageBox msgBox(QMessageBox::Warning, "No valid base data.", "In order to propagate the selected lesion to an image, please specify a valid image node."); msgBox.exec(); return; } try { mitk::LesionPropagation lesionPropagation(dataStorage); auto segmentationMask = lesionPropagation.FindClosestSegmentationMask(m_CaseID, selectedLesion, selectedDataNode); if (nullptr == segmentationMask) { return; } - auto propagatedSegmentation = lesionPropagation.PropagateSegmentationMask(m_CaseID, segmentationMask, selectedDataNode); + mitk::SegmentationPropagation segmentationPropagation; + auto propagatedSegmentation = segmentationPropagation.PropagateSegmentationMask(segmentationMask, selectedDataNode); + dataStorage->Add(propagatedSegmentation, selectedDataNode); AddToSemanticRelationsAction::Run(m_SemanticRelationsIntegration.get(), dataStorage, propagatedSegmentation); m_SemanticRelationsIntegration->LinkSegmentationToLesion(propagatedSegmentation, selectedLesion); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox(QMessageBox::Warning, "Could not propagate the selected lesion.", "The program wasn't able to correctly propagate the selected lesion to the selected image.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.exec(); } } void QmitkLesionInfoWidget::OnRemoveLesion(mitk::SemanticTypes::Lesion selectedLesion) { try { m_SemanticRelationsIntegration->RemoveLesion(m_CaseID, selectedLesion); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox(QMessageBox::Warning, "Could not remove the selected lesion.", "The program wasn't able to correctly remove the selected lesion from the semantic relations model.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.exec(); } }