diff --git a/Modules/RegistrationOntology/include/mitkLesionPropagation.h b/Modules/RegistrationOntology/include/mitkLesionPropagation.h index f1c042b4a7..bacfc61754 100644 --- a/Modules/RegistrationOntology/include/mitkLesionPropagation.h +++ b/Modules/RegistrationOntology/include/mitkLesionPropagation.h @@ -1,62 +1,73 @@ /*=================================================================== 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 +#include // matchpoint ontology #include #include namespace mitk { /** - * @brief #TODO no need to use semantic relations; directly use relation storage if it is a namespace file + * @brief * * */ class MITKREGISTRATIONONTOLOGY_EXPORT LesionPropagation { public: LesionPropagation(DataStorage* dataStorage); - - DataNode* FindClosestSegmentationMask(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& sourceLesion, DataNode* targetImage); - void PropagateSegmentationMask(const SemanticTypes::CaseID& caseID, DataNode* segmentationMask, DataNode* targetImage); + /** + * @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_SemanticRelations; + std::unique_ptr m_SemanticRelationsDataStorageAccess; + std::unique_ptr m_SemanticRelationsIntegration; }; } // namespace mitk #endif // MITKLESIONPROPAGATION_H diff --git a/Modules/RegistrationOntology/src/mitkLesionPropagation.cpp b/Modules/RegistrationOntology/src/mitkLesionPropagation.cpp index d0234efdcc..84062ea07d 100644 --- a/Modules/RegistrationOntology/src/mitkLesionPropagation.cpp +++ b/Modules/RegistrationOntology/src/mitkLesionPropagation.cpp @@ -1,185 +1,212 @@ /*=================================================================== 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 "mitkSemanticRelationException.h" +#include +#include +#include + +// mitk core +#include +#include // multilabel module -#include "mitkLabelSetImage.h" +#include mitk::LesionPropagation::LesionPropagation(DataStorage* dataStorage) : m_DataStorage(dataStorage) - , m_SemanticRelations(std::make_unique(dataStorage)) + , m_SemanticRelationsDataStorageAccess(std::make_unique(dataStorage)) + , m_SemanticRelationsIntegration(std::make_unique()) { // nothing here } -mitk::DataNode* mitk::LesionPropagation::FindClosestSegmentationMask(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& sourceLesion, DataNode* targetImage) +mitk::BaseData* mitk::LesionPropagation::FindClosestSegmentationMask(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& sourceLesion, DataNode* targetImage) { - auto allImagesOfLesion = m_SemanticRelations->GetAllImagesOfLesion(caseID, sourceLesion); - SemanticRelations::DataNodeVector sameControlPointImages; - SemanticRelations::DataNodeVector sameInformationTypeImages; + 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 group + // 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; + 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; + 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; } -void mitk::LesionPropagation::PropagateSegmentationMask(const SemanticTypes::CaseID& caseID, DataNode* segmentationMask, DataNode* targetImage) +mitk::DataNode::Pointer mitk::LesionPropagation::PropagateSegmentationMask(const SemanticTypes::CaseID& caseID, BaseData* segmentationMask, DataNode* targetImage) { if (m_DataStorage.IsExpired()) { - return; + return nullptr; } auto dataStorage = m_DataStorage.Lock(); - - // #TODO: check mitkTool::CreateSegmentationNode(...); - - std::cout << "Segmentation mask propagation for case " << caseID - << " segmentation node " << segmentationMask->GetName() - << " to target image " << targetImage->GetName() << std::endl; - - auto labelSetImage = LabelSetImage::New(); - mitk::Image::Pointer segmentation = dynamic_cast(segmentationMask->GetData()); - labelSetImage->Initialize(segmentation); + // clone the segmentation image / the base data + Image::Pointer segmentation = dynamic_cast(segmentationMask); + auto labelSetImage = segmentation->Clone(); + // DICOM tag is "PatientName" + std::string caseIDPropertyKey = mitk::GeneratePropertyNameForDICOMTag(0x0010, 0x0010); + // DICOM tag is "SeriesInstanceUID" + std::string nodeIDPropertyKey = mitk::GeneratePropertyNameForDICOMTag(0x0020, 0x000e); + labelSetImage->RemoveProperty(caseIDPropertyKey); + labelSetImage->RemoveProperty(nodeIDPropertyKey); auto newSegmentationNode = DataNode::New(); newSegmentationNode->SetData(labelSetImage); + newSegmentationNode->SetProperty("binary", BoolProperty::New(true)); dataStorage->Add(newSegmentationNode, targetImage); - return; + return newSegmentationNode; } bool mitk::LesionPropagation::CheckControlPoint(const DataNode* lesionImage, const DataNode* targetImage) { - auto lesionImageControlPoint = m_SemanticRelations->GetControlPointOfData(lesionImage); - auto targetImageControlPoint = m_SemanticRelations->GetControlPointOfData(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 = m_SemanticRelations->GetInformationTypeOfImage(lesionImage); - auto targetImageInformationType = m_SemanticRelations->GetInformationTypeOfImage(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 - SemanticRelations::DataNodeVector allSpecificSegmentations; + SemanticRelationsDataStorageAccess::DataNodeVector allSpecificSegmentations; try { - allSpecificSegmentations = m_SemanticRelations->GetAllSpecificSegmentations(mitk::GetCaseIDFromDataNode(lesionImage), - m_SemanticRelations->GetControlPointOfData(lesionImage), - m_SemanticRelations->GetInformationTypeOfImage(lesionImage)); + allSpecificSegmentations = m_SemanticRelationsDataStorageAccess->GetAllSpecificSegmentations(mitk::GetCaseIDFromDataNode(lesionImage), + SemanticRelationsInference::GetControlPointOfImage(lesionImage), + SemanticRelationsInference::GetInformationTypeOfImage(lesionImage)); } catch (const SemanticRelationException&) { return nullptr; } - auto lambda = [this, &sourceLesion](const mitk::DataNode* specificSegmentation) + auto lambda = [this, &sourceLesion](const DataNode* specificSegmentation) { try { - auto representedLesion = m_SemanticRelations->GetRepresentedLesion(specificSegmentation); + 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/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeAddToSemanticRelationsAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeAddToSemanticRelationsAction.cpp index 5e91cb0ef5..0e980bbea9 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeAddToSemanticRelationsAction.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeAddToSemanticRelationsAction.cpp @@ -1,171 +1,179 @@ /*=================================================================== 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 "QmitkDataNodeAddToSemanticRelationsAction.h" // semantic relations module #include #include #include #include +// mitk core +#include + // mitk gui common plugin #include // qt #include // namespace that contains the concrete action namespace AddToSemanticRelationsAction { void Run(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataStorage* dataStorage, const mitk::DataNode* dataNode) { if (nullptr == dataNode) { return; } if (mitk::NodePredicates::GetImagePredicate()->CheckNode(dataNode)) { AddImage(semanticRelationsIntegration, dataNode); } else if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { AddSegmentation(semanticRelationsIntegration, dataStorage, dataNode); } } void AddImage(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataNode* image) { if (nullptr == image) { return; } try { // add the image to the semantic relations storage semanticRelationsIntegration->AddImage(image); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox; msgBox.setWindowTitle("Could not add the selected image."); msgBox.setText("The program wasn't able to correctly add the selected images.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } } void AddSegmentation(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataStorage* dataStorage, const mitk::DataNode* segmentation) { if (nullptr == segmentation) { return; } mitk::BaseData* baseData = segmentation->GetData(); if (nullptr == baseData) { return; } // continue with valid segmentation data // get parent node of the current segmentation node with the node predicate mitk::DataStorage::SetOfObjects::ConstPointer parentNodes = dataStorage->GetSources(segmentation, mitk::NodePredicates::GetImagePredicate(), false); // check for already existing, identifying base properties - mitk::BaseProperty* caseIDProperty = baseData->GetProperty("DICOM.0010.0010"); - mitk::BaseProperty* nodeIDProperty = baseData->GetProperty("DICOM.0020.000E"); + // DICOM tag is "PatientName" + std::string caseIDPropertyKey = mitk::GeneratePropertyNameForDICOMTag(0x0010, 0x0010); + // DICOM tag is "SeriesInstanceUID" + std::string nodeIDPropertyKey = mitk::GeneratePropertyNameForDICOMTag(0x0020, 0x000e); + + mitk::BaseProperty* caseIDProperty = baseData->GetProperty(caseIDPropertyKey.c_str()); + mitk::BaseProperty* nodeIDProperty = baseData->GetProperty(nodeIDPropertyKey.c_str()); if (nullptr == caseIDProperty || nullptr == nodeIDProperty) { MITK_INFO << "No DICOM tags for case and node identification found. Transferring DICOM tags from the parent node to the selected segmentation node."; mitk::SemanticTypes::CaseID caseID = mitk::GetCaseIDFromDataNode(parentNodes->front()); mitk::SemanticTypes::ID nodeID = mitk::GetIDFromDataNode(parentNodes->front()); // transfer DICOM tags to the segmentation node mitk::StringProperty::Pointer caseIDTag = mitk::StringProperty::New(caseID); - baseData->SetProperty("DICOM.0010.0010", caseIDTag); // DICOM tag is "PatientName" + baseData->SetProperty(caseIDPropertyKey.c_str(), caseIDTag); - // add UID to distinguish between different segmentations of the same parent node + // add UID to distinguish between different segmentations of the same parent node mitk::StringProperty::Pointer nodeIDTag = mitk::StringProperty::New(nodeID + mitk::UIDGeneratorBoost::GenerateUID()); - baseData->SetProperty("DICOM.0020.000E", nodeIDTag); // DICOM tag is "SeriesInstanceUID" + baseData->SetProperty(nodeIDPropertyKey.c_str(), nodeIDTag); } try { // add the segmentation with its parent image to the semantic relations storage semanticRelationsIntegration->AddSegmentation(segmentation, parentNodes->front()); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox; msgBox.setWindowTitle("Could not add the selected segmentation."); msgBox.setText("The program wasn't able to correctly add the selected segmentation.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } } } QmitkDataNodeAddToSemanticRelationsAction::QmitkDataNodeAddToSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) : QAction(parent) , QmitkAbstractSemanticRelationsAction(workbenchPartSite) { setText(tr("Add to semantic relations")); InitializeAction(); } QmitkDataNodeAddToSemanticRelationsAction::QmitkDataNodeAddToSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) : QAction(parent) , QmitkAbstractSemanticRelationsAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) { setText(tr("Add to semantic relations")); InitializeAction(); } QmitkDataNodeAddToSemanticRelationsAction::~QmitkDataNodeAddToSemanticRelationsAction() { // nothing here } void QmitkDataNodeAddToSemanticRelationsAction::InitializeAction() { connect(this, &QAction::triggered, this, &QmitkDataNodeAddToSemanticRelationsAction::OnActionTriggered); } void QmitkDataNodeAddToSemanticRelationsAction::OnActionTriggered(bool checked) { if (nullptr == m_SemanticRelationsIntegration) { return; } if (m_DataStorage.IsExpired()) { return; } auto dataNode = GetSelectedNode(); AddToSemanticRelationsAction::Run(m_SemanticRelationsIntegration.get(), m_DataStorage.Lock(), dataNode); } 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 b2f4b7d0cf..615d5e589f 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp @@ -1,440 +1,444 @@ /*=================================================================== 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 -#include // registration ontology module #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; msgBox.setWindowTitle("No case ID set."); msgBox.setText("In order to add a lesion, please specify the current case / patient."); msgBox.setIcon(QMessageBox::Warning); 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) + if (nullptr == segmentationMask) { - lesionPropagation.PropagateSegmentationMask(m_CaseID, segmentationMask, selectedDataNode); + return; } + + auto propagatedSegmentation = lesionPropagation.PropagateSegmentationMask(m_CaseID, segmentationMask, 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(); } }