diff --git a/CMakeExternals/MatchPoint.cmake b/CMakeExternals/MatchPoint.cmake index f23fcf2da2..0ef7624db4 100644 --- a/CMakeExternals/MatchPoint.cmake +++ b/CMakeExternals/MatchPoint.cmake @@ -1,64 +1,66 @@ #----------------------------------------------------------------------------- # MatchPoint #----------------------------------------------------------------------------- if(MITK_USE_MatchPoint) set(MatchPoint_SOURCE_DIR "" CACHE PATH "Location of the MatchPoint source directory") mark_as_advanced(MatchPoint_SOURCE_DIR) # Sanity checks if(DEFINED MatchPoint_DIR AND NOT EXISTS ${MatchPoint_DIR}) message(FATAL_ERROR "MatchPoint_DIR variable is defined but corresponds to non-existing directory") endif() if(NOT MatchPoint_DIR AND MatchPoint_SOURCE_DIR AND NOT EXISTS ${MatchPoint_SOURCE_DIR}) message(FATAL_ERROR "MatchPoint_SOURCE_DIR variable is defined but corresponds to non-existing directory") endif() set(proj MatchPoint) set(proj_DEPENDENCIES ITK) set(MatchPoint_DEPENDS ${proj}) if(NOT MatchPoint_DIR) if(MatchPoint_SOURCE_DIR) set(download_step SOURCE_DIR ${MatchPoint_SOURCE_DIR}) else() set(download_step URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/MatchPoint_rev_24ef6072.tar.gz URL_MD5 b0a0d7d63da5071db8e73dd6e6b4db7c ) endif() ExternalProject_Add(${proj} ${download_step} # INSTALL_COMMAND "${CMAKE_COMMAND} -P cmake_install.cmake" CMAKE_GENERATOR ${gen} CMAKE_ARGS ${ep_common_args} ${additional_cmake_args} -DBUILD_TESTING:BOOL=OFF -DITK_DIR:PATH=${ITK_DIR} #/src/ITK-build -DMAP_USE_SYSTEM_GDCM:BOOL=ON -DMAP_USE_SYSTEM_HDF5:BOOL=ON -DMAP_DISABLE_ITK_IO_FACTORY_AUTO_REGISTER:BOOL=ON -DMAP_WRAP_Plastimatch:BOOL=ON + -DMAP_BUILD_Ontology:BOOL=ON + -DMAP_BUILD_Ontology_simple:BOOL=ON -DGDCM_DIR:PATH=${GDCM_DIR} -DHDF5_DIR:PATH=${HDF5_DIR} CMAKE_CACHE_ARGS ${ep_common_cache_args} CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} DEPENDS ${proj_DEPENDENCIES} ) ExternalProject_Get_Property(${proj} binary_dir) set(${proj}_DIR ${binary_dir}) mitkFunctionInstallExternalCMakeProject(${proj}) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() endif(MITK_USE_MatchPoint) diff --git a/Modules/ModuleList.cmake b/Modules/ModuleList.cmake index 1053573cc2..01baed92b0 100644 --- a/Modules/ModuleList.cmake +++ b/Modules/ModuleList.cmake @@ -1,80 +1,81 @@ # The entries in the mitk_modules list must be # ordered according to their dependencies. set(MITK_MODULES Core CommandLine AppUtil RDF LegacyIO DataTypesExt Annotation LegacyGL AlgorithmsExt MapperExt DICOMReader DICOMReaderServices DICOMTesting SceneSerializationBase PlanarFigure ImageDenoising ImageExtraction SceneSerialization Gizmo GraphAlgorithms Multilabel ImageStatistics ContourModel SurfaceInterpolation Segmentation PlanarFigureSegmentation QtWidgets QtWidgetsExt Chart SegmentationUI MatchPointRegistration MatchPointRegistrationUI Classification GPGPU OpenIGTLink IGTBase IGT CameraCalibration OpenCL OpenCVVideoSupport QtOverlays ToFHardware ToFProcessing ToFUI PhotoacousticsHardware PhotoacousticsAlgorithms PhotoacousticsLib US USUI DicomUI + RegistrationOntology Remeshing Python QtPython Persistence OpenIGTLinkUI IGTUI DicomRT RTUI IOExt XNAT TubeGraph BiophotonicsHardware DiffusionImaging TumorInvasionAnalysis BoundingShape RenderWindowManager RenderWindowManagerUI SemanticRelations SemanticRelationsUI CEST BasicImageProcessing ) if(MITK_ENABLE_PIC_READER) list(APPEND MITK_MODULES IpPicSupportIO) endif() diff --git a/Modules/RegistrationOntology/CMakeLists.txt b/Modules/RegistrationOntology/CMakeLists.txt new file mode 100644 index 0000000000..ad8b813481 --- /dev/null +++ b/Modules/RegistrationOntology/CMakeLists.txt @@ -0,0 +1,4 @@ +MITK_CREATE_MODULE( + DEPENDS MitkCore + PACKAGE_DEPENDS PUBLIC MatchPoint +) diff --git a/Modules/RegistrationOntology/files.cmake b/Modules/RegistrationOntology/files.cmake new file mode 100644 index 0000000000..1e696fcf9f --- /dev/null +++ b/Modules/RegistrationOntology/files.cmake @@ -0,0 +1,5 @@ +file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") + +set(CPP_FILES + mitkLesionPropagation.cpp +) diff --git a/Modules/RegistrationOntology/include/mitkLesionPropagation.h b/Modules/RegistrationOntology/include/mitkLesionPropagation.h new file mode 100644 index 0000000000..ea2a7a7cd9 --- /dev/null +++ b/Modules/RegistrationOntology/include/mitkLesionPropagation.h @@ -0,0 +1,35 @@ +/*=================================================================== + +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 + +// matchpoint ontology +#include +#include + +namespace mitk +{ + MITKREGISTRATIONONTOLOGY_EXPORT void FindClosestSegmentationMask(); +} // namespace mitk + +#endif // MITKLESIONPROPAGATION_H diff --git a/Modules/RegistrationOntology/src/mitkLesionPropagation.cpp b/Modules/RegistrationOntology/src/mitkLesionPropagation.cpp new file mode 100644 index 0000000000..7f73c07a76 --- /dev/null +++ b/Modules/RegistrationOntology/src/mitkLesionPropagation.cpp @@ -0,0 +1,22 @@ +/*=================================================================== + +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" + +void mitk::FindClosestSegmentationMask() +{ + std::cout << "Lesion propagation works and can be called" << std::endl; +} diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/CMakeLists.txt b/Plugins/org.mitk.gui.qt.semanticrelations/CMakeLists.txt index 2ffd63c2e3..595c24082e 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/CMakeLists.txt +++ b/Plugins/org.mitk.gui.qt.semanticrelations/CMakeLists.txt @@ -1,7 +1,7 @@ project(org_mitk_gui_qt_semanticrelations) mitk_create_plugin( EXPORT_DIRECTIVE MITK_GUI_SEMANTICRELATIONS_EXPORT EXPORTED_INCLUDE_SUFFIXES src - MODULE_DEPENDS MitkPersistence MitkSemanticRelationsUI MitkRenderWindowManager + MODULE_DEPENDS MitkPersistence MitkSemanticRelationsUI MitkRenderWindowManager MitkRegistrationOntology ) 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 c952b1eb35..3b20caedf4 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp @@ -1,479 +1,483 @@ /*=================================================================== 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" // semantic relations UI module #include // semantic relations module #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_SemanticRelations(std::make_unique(dataStorage)) { 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(this); 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_SemanticRelations->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) { if (nullptr == m_SemanticRelations && current.isValid()) { return; } // only the UID is needed to identify a representing lesion QVariant data = m_StorageModel->data(current, Qt::UserRole); if (data.canConvert()) { m_CurrentLesion = data.value()->GetData().GetLesion(); } else { return; } if (false == m_SemanticRelations->InstanceExists(m_CaseID, m_CurrentLesion)) { // no UID of a existing lesion found; cannot create a lesion return; } // if selected data nodes are set, reset to empty list to // the "selected data nodes presence background highlighting" in the model if (!m_StorageModel->GetSelectedDataNodes().isEmpty()) { m_StorageModel->SetDataNodeSelection(QList()); } emit LesionSelectionChanged(m_CurrentLesion); } void QmitkLesionInfoWidget::OnLesionListContextMenuRequested(const QPoint& pos) { 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_CaseID.empty()) { QMessageBox msgBox; msgBox.setWindowTitle("No case ID set."); msgBox.setText("In order to link a lesion to a segmentation, please specify the current case / patient."); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } if (m_DataStorage.IsExpired()) { return; } auto dataStorage = m_DataStorage.Lock(); QmitkSemanticRelationsNodeSelectionDialog* dialog = new QmitkSemanticRelationsNodeSelectionDialog(this, "Select segmentation to link to the selected lesion.", ""); dialog->SetDataStorage(dataStorage); dialog->setWindowTitle("Select segmentation node"); dialog->SetNodePredicate(mitk::NodePredicates::GetSegmentationPredicate()); dialog->SetSelectOnlyVisibleNodes(true); dialog->SetSelectionMode(QAbstractItemView::SingleSelection); 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; msgBox.setWindowTitle("No valid segmentation node selected."); msgBox.setText("In order to link the selected lesion to a segmentation, please specify a valid segmentation node."); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } mitk::BaseData* baseData = selectedDataNode->GetData(); if (nullptr == baseData) { QMessageBox msgBox; msgBox.setWindowTitle("No valid base data."); msgBox.setText("In order to link the selected lesion to a segmentation, please specify a valid segmentation node."); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } try { m_SemanticRelations->LinkSegmentationToLesion(selectedDataNode, selectedLesion); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox; msgBox.setWindowTitle("Could not link the selected lesion."); msgBox.setText("The program wasn't able to correctly link the selected lesion with the selected segmentation.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); } } void QmitkLesionInfoWidget::OnSetLesionName(mitk::SemanticTypes::Lesion selectedLesion) { if (m_CaseID.empty()) { QMessageBox msgBox; msgBox.setWindowTitle("No case ID set."); msgBox.setText("In order to set a lesion name, please specify the current case / patient."); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } // 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; } std::string newLesionName = inputDialog->GetLineEditText().toStdString(); selectedLesion.name = newLesionName; m_SemanticRelations->OverwriteLesion(m_CaseID, selectedLesion); } void QmitkLesionInfoWidget::OnSetLesionClass(mitk::SemanticTypes::Lesion selectedLesion) { if (m_CaseID.empty()) { QMessageBox msgBox; msgBox.setWindowTitle("No case ID set."); msgBox.setText("In order to set a lesion class, please specify the current case / patient."); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } // 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 = m_SemanticRelations->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_SemanticRelations->OverwriteLesion(m_CaseID, selectedLesion); } void QmitkLesionInfoWidget::OnPropagateLesion(mitk::SemanticTypes::Lesion selectedLesion) { if (m_CaseID.empty()) { QMessageBox msgBox; msgBox.setWindowTitle("No case ID set."); msgBox.setText("In order to propagate a lesion to an image, please specify the current case / patient."); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } if (m_DataStorage.IsExpired()) { return; } auto dataStorage = m_DataStorage.Lock(); QmitkSemanticRelationsNodeSelectionDialog* dialog = new QmitkSemanticRelationsNodeSelectionDialog(this, "Select data node to propagate the selected lesion.", ""); dialog->SetDataStorage(dataStorage); dialog->setWindowTitle("Select image node"); dialog->SetNodePredicate(mitk::NodePredicates::GetImagePredicate()); dialog->SetSelectOnlyVisibleNodes(true); dialog->SetSelectionMode(QAbstractItemView::SingleSelection); 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; msgBox.setWindowTitle("No valid image node selected."); msgBox.setText("In order to propagate the selected lesion to an image, please specify a valid image node."); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } mitk::BaseData* baseData = selectedDataNode->GetData(); if (nullptr == baseData) { QMessageBox msgBox; msgBox.setWindowTitle("No valid base data."); msgBox.setText("In order to propagate the selected lesion to an image, please specify a valid image node."); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } try { auto allSegmentationsOfLesion = m_SemanticRelations->GetAllSegmentationsOfLesion(m_CaseID, selectedLesion); + mitk::FindClosestSegmentationMask(); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox; msgBox.setWindowTitle("Could not propagate the selected lesion."); msgBox.setText("The program wasn't able to correctly propagate the selected lesion to the selected image.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); } } void QmitkLesionInfoWidget::OnRemoveLesion(mitk::SemanticTypes::Lesion selectedLesion) { if (m_CaseID.empty()) { QMessageBox msgBox; msgBox.setWindowTitle("No case ID set."); msgBox.setText("In order to remove a lesion, please specify the current case / patient."); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } try { m_SemanticRelations->RemoveLesion(m_CaseID, selectedLesion); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox; msgBox.setWindowTitle("Could not remove the selected lesion."); msgBox.setText("The program wasn't able to correctly remove the selected lesion from the semantic relations model.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); } }