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/Core/src/DataManagement/mitkVectorProperty.cpp b/Modules/Core/src/DataManagement/mitkVectorProperty.cpp index 5d2066957d..3f44f4fc78 100644 --- a/Modules/Core/src/DataManagement/mitkVectorProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkVectorProperty.cpp @@ -1,92 +1,94 @@ /*=================================================================== 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 "mitkVectorProperty.h" namespace mitk { template bool VectorProperty::IsEqual(const BaseProperty &property) const { return this->m_PropertyContent == static_cast(property).m_PropertyContent; } template bool VectorProperty::Assign(const BaseProperty &property) { this->m_PropertyContent = static_cast(property).m_PropertyContent; return true; } template itk::LightObject::Pointer VectorProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); return result; } template std::string VectorProperty::GetValueAsString() const { const size_t displayBlockLength = 3; size_t beginningElementsCount = displayBlockLength; size_t endElementsCount = displayBlockLength; if (m_PropertyContent.size() <= 2 * displayBlockLength) { beginningElementsCount = m_PropertyContent.size(); endElementsCount = 0; } // return either a block of all items // if the total number of maximum 2*displayBlockLength // // or return the first and last "displayBlockLength" // number of items separated by "[...]"; std::stringstream string_collector; for (size_t i = 0; i < beginningElementsCount; i++) string_collector << m_PropertyContent[i] << "\n"; if (endElementsCount) string_collector << "[... " << m_PropertyContent.size() - 2 * displayBlockLength << " more]\n"; for (size_t i = m_PropertyContent.size() - endElementsCount; i < m_PropertyContent.size(); ++i) string_collector << m_PropertyContent[i] << "\n"; std::string return_value = string_collector.str(); // remove last '\n' if (!return_value.empty()) return_value.erase(return_value.size() - 1); return return_value; } template void VectorProperty::SetValue(const VectorType &newValue) { m_PropertyContent = newValue; } template const typename VectorProperty::VectorType &VectorProperty::GetValue() const { return m_PropertyContent; } // Explicit instantiation for defined types. MITK_DEFINE_VECTOR_PROPERTY(double) MITK_DEFINE_VECTOR_PROPERTY(int) + MITK_DEFINE_VECTOR_PROPERTY(unsigned int) + MITK_DEFINE_VECTOR_PROPERTY(std::string) } // namespace mitk diff --git a/Modules/ModuleList.cmake b/Modules/ModuleList.cmake index 2db311b104..a6aeb0cc0d 100644 --- a/Modules/ModuleList.cmake +++ b/Modules/ModuleList.cmake @@ -1,84 +1,87 @@ # 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 DICOMQI DICOMTesting SceneSerializationBase PlanarFigure ImageDenoising ImageExtraction SceneSerialization Gizmo GraphAlgorithms Multilabel Chart ImageStatistics ContourModel SurfaceInterpolation Segmentation PlanarFigureSegmentation QtWidgets QtWidgetsExt ImageStatisticsUI 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 ModelFit ModelFitUI Pharmacokinetics PharmacokineticsUI ) if(MITK_ENABLE_PIC_READER) list(APPEND MITK_MODULES IpPicSupportIO) endif() diff --git a/Modules/QtWidgets/files.cmake b/Modules/QtWidgets/files.cmake index 337a7adb14..3e96e687dd 100644 --- a/Modules/QtWidgets/files.cmake +++ b/Modules/QtWidgets/files.cmake @@ -1,111 +1,113 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES QmitkAbstractDataStorageModel.cpp QmitkApplicationCursor.cpp QmitkDataStorageComboBox.cpp QmitkDataStorageDefaultListModel.cpp QmitkDataStorageListModel.cpp QmitkDataStorageTableModel.cpp QmitkDataStorageSimpleTreeModel.cpp QmitkDataStorageTreeModel.cpp QmitkDataStorageTreeModelInternalItem.cpp + QmitkDnDDataNodeWidget.cpp QmitkFileReaderOptionsDialog.cpp QmitkFileReaderWriterOptionsWidget.cpp QmitkFileWriterOptionsDialog.cpp QmitkIOUtil.cpp QmitkLevelWindowPresetDefinitionDialog.cpp QmitkLevelWindowRangeChangeDialog.cpp QmitkLevelWindowWidgetContextMenu.cpp QmitkLevelWindowWidget.cpp QmitkLineEditLevelWindowWidget.cpp QmitkMemoryUsageIndicatorView.cpp QmitkMimeTypes.cpp QmitkNodeDescriptor.cpp QmitkColoredNodeDescriptor.cpp QmitkNodeDescriptorManager.cpp QmitkRenderWindowMenu.cpp QmitkProgressBar.cpp QmitkPropertiesTableEditor.cpp QmitkPropertiesTableModel.cpp QmitkPropertyDelegate.cpp QmitkRegisterClasses.cpp QmitkRenderingManager.cpp QmitkRenderingManagerFactory.cpp QmitkRenderWindow.cpp QmitkServiceListWidget.cpp QmitkSliderLevelWindowWidget.cpp QmitkStdMultiWidget.cpp QmitkMouseModeSwitcher.cpp QmitkDataStorageFilterProxyModel.cpp QmitkDataStorageComboBoxWithSelectNone.cpp QmitkPropertyItem.cpp QmitkPropertyItemDelegate.cpp QmitkPropertyItemModel.cpp QmitkStyleManager.cpp QmitkAbstractDataStorageInspector.cpp QmitkDataStorageListInspector.cpp QmitkDataStorageTreeInspector.cpp QmitkModelViewSelectionConnector.cpp mitkIDataStorageInspectorProvider.cpp mitkQtWidgetsActivator.cpp mitkDataStorageInspectorGenerator.cpp QmitkOverlayWidget.cpp QmitkNodeDetailsDialog.cpp ) set(MOC_H_FILES include/QmitkAbstractDataStorageModel.h include/QmitkDataStorageComboBox.h include/QmitkDataStorageTableModel.h include/QmitkDataStorageTreeModel.h include/QmitkDataStorageSimpleTreeModel.h include/QmitkDataStorageDefaultListModel.h + include/QmitkDnDDataNodeWidget.h include/QmitkFileReaderOptionsDialog.h include/QmitkFileReaderWriterOptionsWidget.h include/QmitkFileWriterOptionsDialog.h include/QmitkLevelWindowPresetDefinitionDialog.h include/QmitkLevelWindowRangeChangeDialog.h include/QmitkLevelWindowWidgetContextMenu.h include/QmitkLevelWindowWidget.h include/QmitkLineEditLevelWindowWidget.h include/QmitkMemoryUsageIndicatorView.h include/QmitkNodeDescriptor.h include/QmitkColoredNodeDescriptor.h include/QmitkNodeDescriptorManager.h include/QmitkRenderWindowMenu.h include/QmitkProgressBar.h include/QmitkPropertiesTableEditor.h include/QmitkPropertyDelegate.h include/QmitkRenderingManager.h include/QmitkRenderWindow.h include/QmitkServiceListWidget.h include/QmitkSliderLevelWindowWidget.h include/QmitkStdMultiWidget.h include/QmitkMouseModeSwitcher.h include/QmitkDataStorageComboBoxWithSelectNone.h include/QmitkPropertyItemDelegate.h include/QmitkPropertyItemModel.h include/QmitkDataStorageListInspector.h include/QmitkAbstractDataStorageInspector.h include/QmitkDataStorageTreeInspector.h include/QmitkModelViewSelectionConnector.h include/QmitkOverlayWidget.h include/QmitkNodeDetailsDialog.h ) set(UI_FILES src/QmitkFileReaderOptionsDialog.ui src/QmitkFileWriterOptionsDialog.ui src/QmitkLevelWindowPresetDefinition.ui src/QmitkLevelWindowWidget.ui src/QmitkLevelWindowRangeChange.ui src/QmitkMemoryUsageIndicator.ui src/QmitkServiceListWidgetControls.ui src/QmitkDataStorageListInspector.ui src/QmitkDataStorageTreeInspector.ui ) set(QRC_FILES resource/Qmitk.qrc ) diff --git a/Modules/QtWidgets/include/QmitkDnDDataNodeWidget.h b/Modules/QtWidgets/include/QmitkDnDDataNodeWidget.h new file mode 100644 index 0000000000..baad4fe34b --- /dev/null +++ b/Modules/QtWidgets/include/QmitkDnDDataNodeWidget.h @@ -0,0 +1,64 @@ +/*=================================================================== + +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 QMITKDNDDATANODEWIDGET_H +#define QMITKDNDDATANODEWIDGET_H + +#include "MitkQtWidgetsExports.h" + +// mitk core module +#include + +// qt +#include + +class QDragEnterEvent; +class QDropEvent; + +/** +* @brief A drag 'n' drop widget (QFrame) that checks the mime data of the incoming +* event. If the drag event is coming from another widget and the mime data +* is of type 'application/x-qmitk-datanode-ptrs' the node(s) will be dropped +* and a signal is emitted. +* A class including this DnD-widget can than handle the vector of dropped nodes +* appropriately. +*/ +class MITKQTWIDGETS_EXPORT QmitkDnDDataNodeWidget : public QFrame +{ + Q_OBJECT + +public: + + QmitkDnDDataNodeWidget(QWidget* parent = nullptr); + ~QmitkDnDDataNodeWidget() override; + +Q_SIGNALS: + + /** + * @brief The signal will be emitted if the drag events are accepted. + * It contains the vector of nodes that have been dropped. + */ + void NodesDropped(std::vector nodes); + +private: + + void dragEnterEvent(QDragEnterEvent* event) override; + void dragMoveEvent(QDragMoveEvent* event) override; + void dropEvent(QDropEvent* event) override; + +}; + +#endif // QMITKDNDDATANODEWIDGET_H diff --git a/Modules/QtWidgets/src/QmitkDnDDataNodeWidget.cpp b/Modules/QtWidgets/src/QmitkDnDDataNodeWidget.cpp new file mode 100644 index 0000000000..fbdab13999 --- /dev/null +++ b/Modules/QtWidgets/src/QmitkDnDDataNodeWidget.cpp @@ -0,0 +1,70 @@ +/*=================================================================== + +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 + +// mitk qt widgets module +#include "QmitkMimeTypes.h" + +// qt +#include + +QmitkDnDDataNodeWidget::QmitkDnDDataNodeWidget(QWidget* parent /*= nullptr*/) + : QFrame(parent) +{ + this->setAcceptDrops(true); +} + +QmitkDnDDataNodeWidget::~QmitkDnDDataNodeWidget() +{ + // nothing here +} + +void QmitkDnDDataNodeWidget::dragEnterEvent(QDragEnterEvent* event) +{ + if (this != event->source()) + { + event->acceptProposedAction(); + } + else + { + event->ignore(); + } +} + +void QmitkDnDDataNodeWidget::dragMoveEvent(QDragMoveEvent* event) +{ + if (event->mimeData()->hasFormat(QmitkMimeTypes::DataNodePtrs)) + { + event->acceptProposedAction(); + } + else + { + event->ignore(); + } +} + +void QmitkDnDDataNodeWidget::dropEvent(QDropEvent* event) +{ + QList dataNodeList = QmitkMimeTypes::ToDataNodePtrList(event->mimeData()); + if (!dataNodeList.empty()) + { + emit NodesDropped(dataNodeList.toVector().toStdVector()); + } + + event->acceptProposedAction(); +} + 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/Modules/SemanticRelations/CMakeLists.txt b/Modules/SemanticRelations/CMakeLists.txt new file mode 100644 index 0000000000..72db95214f --- /dev/null +++ b/Modules/SemanticRelations/CMakeLists.txt @@ -0,0 +1,7 @@ +MITK_CREATE_MODULE( + DEPENDS MitkSceneSerializationBase MitkDICOMReader MitkMultilabel MitkPersistence +) + +if(BUILD_TESTING) + ADD_SUBDIRECTORY(Test) +endif(BUILD_TESTING) diff --git a/Modules/SemanticRelations/Test/CMakeLists.txt b/Modules/SemanticRelations/Test/CMakeLists.txt new file mode 100644 index 0000000000..153cd81e2e --- /dev/null +++ b/Modules/SemanticRelations/Test/CMakeLists.txt @@ -0,0 +1 @@ +MITK_CREATE_MODULE_TESTS() diff --git a/Modules/SemanticRelations/Test/files.cmake b/Modules/SemanticRelations/Test/files.cmake new file mode 100644 index 0000000000..3e491291c0 --- /dev/null +++ b/Modules/SemanticRelations/Test/files.cmake @@ -0,0 +1,7 @@ +set(MODULE_TESTS + mitkSemanticRelationsTest.cpp +) + +set(CPP_FILES + mitkSemanticRelationsTestHelper.cpp +) diff --git a/Modules/SemanticRelations/Test/mitkSemanticRelationsTest.cpp b/Modules/SemanticRelations/Test/mitkSemanticRelationsTest.cpp new file mode 100644 index 0000000000..c38f4dcde7 --- /dev/null +++ b/Modules/SemanticRelations/Test/mitkSemanticRelationsTest.cpp @@ -0,0 +1,812 @@ +/*=================================================================== + +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. + +===================================================================*/ + +// mitk semantic relations +#include "mitkSemanticRelationException.h" +#include "mitkSemanticRelationsInference.h" +#include "mitkSemanticRelationsIntegration.h" +#include "mitkSemanticRelationsDataStorageAccess.h" +#include "mitkRelationStorage.h" +#include "mitkControlPointManager.h" +#include "mitkDICOMHelper.h" +#include "mitkLesionManager.h" +#include "mitkSemanticRelationsTestHelper.h" +#include "mitkUIDGeneratorBoost.h" + +// mitk core +#include +#include +#include +#include + +// mitk persistence +#include + +class mitkSemanticRelationsTestSuite : public mitk::TestFixture +{ + CPPUNIT_TEST_SUITE(mitkSemanticRelationsTestSuite); + MITK_TEST(IntegrationTest); + MITK_TEST(InferenceTest); + MITK_TEST(DataStorageAccessTest); + MITK_TEST(RemoveAndUnlinkTest); + CPPUNIT_TEST_SUITE_END(); + +private: + + mitk::DataStorage::Pointer m_DataStorage; + +public: + + void setUp() override + { + mitk::PersistenceService::LoadModule(); + m_DataStorage = mitk::StandaloneDataStorage::New(); + } + + void tearDown() override + { + // clear semantic relations storage + mitk::SemanticRelationsTestHelper::ClearRelationStorage(); + } + + ////////////////////////////////////////////////////////////////////////// + // SPECIFIC TEST GROUPS + ////////////////////////////////////////////////////////////////////////// + void IntegrationTest() + { + MITK_INFO << "=== IntegrationTest start ==="; + AddNewData(); + ExaminationPeriod(); + SegmentationAndLesion(); + InvalidData(); + MITK_INFO << "=== IntegrationTest end ==="; + } + + void InferenceTest() + { + MITK_INFO << "=== InferenceTest start ==="; + CombinedQueries(); + InstanceExistences(); + MITK_INFO << "=== InferenceTest end ==="; + } + + void DataStorageAccessTest() + { + MITK_INFO << "=== DataStorageAccessTest start ==="; + DataStorageAccess(); + MITK_INFO << "=== DataStorageAccessTest end ==="; + } + + void RemoveAndUnlinkTest() + { + MITK_INFO << "=== RemoveAndUnlinkTest start ==="; + CPRemoveAndUnlink(); + LesionRemoveAndUnlink(); + RemoveImagesAndSegmentation(); + MITK_INFO << "=== RemoveAndUnlinkTest end ==="; + } + + ////////////////////////////////////////////////////////////////////////// + // SPECIFIC TESTS + ////////////////////////////////////////////////////////////////////////// + // IntegrationTest + void AddNewData() + { + MITK_INFO << "=== AddNewData"; + + // load data + mitk::SemanticRelationsIntegration semanticRelationsIntegration; + + auto image = mitk::SemanticRelationsTestHelper::GetPatientOneCTImage(); + m_DataStorage->Add(image); + CPPUNIT_ASSERT_MESSAGE("Not a valid image data node", image.IsNotNull()); + CPPUNIT_ASSERT_MESSAGE("Image data is empty", image->GetData() != nullptr); + semanticRelationsIntegration.AddImage(image); + + // start test + auto allCaseIDs = mitk::RelationStorage::GetAllCaseIDs(); + CPPUNIT_ASSERT_MESSAGE("CaseID not correctly stored", allCaseIDs.front() == "Patient1"); + auto caseID = allCaseIDs.front(); + + // test control point + auto allControlPoints = mitk::RelationStorage::GetAllControlPointsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("One control point should be stored", allControlPoints.size() == 1); + + auto controlPointOfImage = mitk::SemanticRelationsInference::GetControlPointOfImage(image); + mitk::SemanticTypes::ControlPoint controlPointToCompare; + controlPointToCompare.date = boost::gregorian::date(2019, 01, 01); + CPPUNIT_ASSERT_MESSAGE("Control point not correctly stored", controlPointOfImage.date == controlPointToCompare.date); + + // test information type + auto allInformationTypes = mitk::RelationStorage::GetAllInformationTypesOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("One information type should be stored", allInformationTypes.size() == 1); + + auto informationType = mitk::SemanticRelationsInference::GetInformationTypeOfImage(image); + CPPUNIT_ASSERT_MESSAGE("Information type not correctly stored", informationType == "CT"); + + semanticRelationsIntegration.SetInformationType(image, "MR"); + informationType = mitk::SemanticRelationsInference::GetInformationTypeOfImage(image); + CPPUNIT_ASSERT_MESSAGE("Information type not correctly stored", informationType == "MR"); + } + + void ExaminationPeriod() + { + MITK_INFO << "=== ExaminationPeriod"; + + // load data + mitk::SemanticRelationsIntegration semanticRelationsIntegration; + + auto MRImage = mitk::SemanticRelationsTestHelper::GetPatientOneMRImage(); + m_DataStorage->Add(MRImage); + CPPUNIT_ASSERT_MESSAGE("Not a valid image data node", MRImage.IsNotNull()); + CPPUNIT_ASSERT_MESSAGE("Image data is empty", MRImage->GetData() != nullptr); + semanticRelationsIntegration.AddImage(MRImage); + + // start test + auto allCaseIDs = mitk::RelationStorage::GetAllCaseIDs(); + CPPUNIT_ASSERT_MESSAGE("One CaseID should be stored", allCaseIDs.size() == 1); + auto caseID = allCaseIDs.front(); + + auto allControlPoints = mitk::RelationStorage::GetAllControlPointsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("One control point should be stored", allControlPoints.size() == 1); + + auto controlPointOfImage = mitk::SemanticRelationsInference::GetControlPointOfImage(MRImage); + mitk::SemanticTypes::ControlPoint controlPoint; + controlPoint.date = boost::gregorian::date(2019, 01, 01); + CPPUNIT_ASSERT_MESSAGE("Control point not correctly stored", controlPointOfImage.date == controlPoint.date); + + auto examinationPeriod = mitk::RelationStorage::GetAllExaminationPeriodsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("One examination period should be stored", examinationPeriod.size() == 1); + + auto allImageIDs = mitk::RelationStorage::GetAllImageIDsOfControlPoint(caseID, controlPointOfImage); + CPPUNIT_ASSERT_MESSAGE("Two images should reference the same control point", allImageIDs.size() == 2); + + // add additional control point to the same examination period + // load data + auto CTImage = mitk::SemanticRelationsTestHelper::GetPatientOneOtherCTImage(); + m_DataStorage->Add(CTImage); + CPPUNIT_ASSERT_MESSAGE("Not a valid image data node", CTImage.IsNotNull()); + CPPUNIT_ASSERT_MESSAGE("Image data is empty", CTImage->GetData() != nullptr); + semanticRelationsIntegration.AddImage(CTImage); + + // start test + allControlPoints = mitk::RelationStorage::GetAllControlPointsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("Two control points should be stored", allControlPoints.size() == 2); + + examinationPeriod = mitk::RelationStorage::GetAllExaminationPeriodsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("One examination period should be stored", examinationPeriod.size() == 1); + + // modify control point to create new examination period + // current control point is 2019, 01, 31 + // new control point should exceed threshold (30 days) and create new examination period + // new control point is 2019, 02, 01 + controlPoint.UID = mitk::UIDGeneratorBoost::GenerateUID(); + controlPoint.date = boost::gregorian::date(2019, 02, 01); + semanticRelationsIntegration.UnlinkImageFromControlPoint(CTImage); + semanticRelationsIntegration.SetControlPointOfImage(CTImage, controlPoint); + + allControlPoints = mitk::RelationStorage::GetAllControlPointsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("Two control points should be stored", allControlPoints.size() == 2); + + examinationPeriod = mitk::RelationStorage::GetAllExaminationPeriodsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("Two examination periods should be stored", examinationPeriod.size() == 2); + } + + void SegmentationAndLesion() + { + MITK_INFO << "=== SegmentationAndLesion"; + + // load data + mitk::SemanticRelationsIntegration semanticRelationsIntegration; + + // Patient2 + auto image = mitk::SemanticRelationsTestHelper::GetPatientTwoPETImage(); + m_DataStorage->Add(image); + CPPUNIT_ASSERT_MESSAGE("Not a valid image data node", image.IsNotNull()); + CPPUNIT_ASSERT_MESSAGE("Image data is empty", image->GetData() != nullptr); + semanticRelationsIntegration.AddImage(image); + + auto segmentation = mitk::SemanticRelationsTestHelper::GetPatientTwoSegmentation(); + m_DataStorage->Add(segmentation, image); + CPPUNIT_ASSERT_MESSAGE("Not a valid segmentation data node", segmentation.IsNotNull()); + CPPUNIT_ASSERT_MESSAGE("Segmentation data is empty", segmentation->GetData() != nullptr); + semanticRelationsIntegration.AddSegmentation(segmentation, image); + + // start test + auto allCaseIDs = mitk::RelationStorage::GetAllCaseIDs(); + CPPUNIT_ASSERT_MESSAGE("Two CaseIDs should be stored", allCaseIDs.size() == 2); + + // test lesion (add and link) + auto lesion = mitk::GenerateNewLesion(); + semanticRelationsIntegration.AddLesionAndLinkSegmentation(segmentation, lesion); + + auto representedLesion = mitk::SemanticRelationsInference::GetLesionOfSegmentation(segmentation); + CPPUNIT_ASSERT_MESSAGE("Represented lesion should be the only stored lesion", lesion.UID == representedLesion.UID); + } + + void InvalidData() + { + MITK_INFO << "=== InvalidData"; + + // TEST INVALID CASE ID + std::string caseID = "invalidID"; + auto allLesions = mitk::RelationStorage::GetAllLesionsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("No lesion should be stored, given a non-existing CaseID", allLesions.size() == 0); + + auto allControlPoints = mitk::RelationStorage::GetAllControlPointsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("No control point should be stored, given a non-existing CaseID", allControlPoints.size() == 0); + + auto allExaminationPeriods = mitk::RelationStorage::GetAllExaminationPeriodsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("No examination periods should be stored, given a non-existing CaseID", allExaminationPeriods.size() == 0); + + auto allInformationTypes = mitk::RelationStorage::GetAllInformationTypesOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("No information type should be stored, given a non-existing CaseID", allInformationTypes.size() == 0); + + auto allImageIDsOfCase = mitk::RelationStorage::GetAllImageIDsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("No image ID should be stored, given a non-existing CaseID", allControlPoints.size() == 0); + + // TEST INVALID DATE + mitk::DataNode::Pointer invalidDateImage = mitk::SemanticRelationsTestHelper::GetInvalidDate(); + m_DataStorage->Add(invalidDateImage); + + CPPUNIT_ASSERT_MESSAGE("Not a valid image data node", invalidDateImage.IsNotNull()); + CPPUNIT_ASSERT_MESSAGE("Image data is empty", invalidDateImage->GetData() != nullptr); + + mitk::SemanticRelationsIntegration semanticRelationsIntegration; + CPPUNIT_ASSERT_THROW_MESSAGE("Semantic relation exception not thrown: AddImage", + semanticRelationsIntegration.AddImage(invalidDateImage), + mitk::SemanticRelationException); + + auto controlPointOfImage = mitk::SemanticRelationsInference::GetControlPointOfImage(invalidDateImage); + CPPUNIT_ASSERT_MESSAGE("No control point should be stored for an invalid DICOM date", controlPointOfImage.UID.empty()); + + // set invalid date format for current image + // 0x0008, 0x0022 (AcquisitionDate), should be 20180101 + invalidDateImage->GetData()->SetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str(), + mitk::StringProperty::New("201811")); + CPPUNIT_ASSERT_THROW_MESSAGE("Semantic relation exception not thrown: GenerateControlPoint", + mitk::GenerateControlPoint(invalidDateImage), + mitk::SemanticRelationException); + + // set valid date format for current image and add image again (only control point should be updated) + // 0x0008, 0x0022 (AcquisitionDate), should be 20180101 + invalidDateImage->GetData()->SetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str(), + mitk::StringProperty::New("20180101")); + semanticRelationsIntegration.AddImage(invalidDateImage); + + controlPointOfImage = mitk::SemanticRelationsInference::GetControlPointOfImage(invalidDateImage); + mitk::SemanticTypes::ControlPoint controlPointToCompare; + controlPointToCompare.date = boost::gregorian::date(2018, 01, 01); + CPPUNIT_ASSERT_MESSAGE("Control point not correctly stored", controlPointOfImage.date == controlPointToCompare.date); + + // TEST INVALID MODALITY + mitk::DataNode::Pointer invalidModalityImage = mitk::SemanticRelationsTestHelper::GetInvalidModality(); + m_DataStorage->Add(invalidModalityImage); + + CPPUNIT_ASSERT_MESSAGE("Not a valid image data node", invalidModalityImage.IsNotNull()); + CPPUNIT_ASSERT_MESSAGE("Image data is empty", invalidModalityImage->GetData() != nullptr); + + CPPUNIT_ASSERT_THROW_MESSAGE("Semantic relation exception not thrown: AddImage", + semanticRelationsIntegration.AddImage(invalidModalityImage), + mitk::SemanticRelationException); + + auto informationTypeOfImage = mitk::SemanticRelationsInference::GetInformationTypeOfImage(invalidModalityImage); + CPPUNIT_ASSERT_MESSAGE("No information type should be stored for an invalid DICOM date", informationTypeOfImage.empty()); + + // set valid modality for current image and add image again + // 0x0008, 0x0060 (Modality), should be CT + invalidModalityImage->GetData()->SetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), + mitk::StringProperty::New("CT")); + semanticRelationsIntegration.AddImage(invalidModalityImage); + + auto informationType = mitk::SemanticRelationsInference::GetInformationTypeOfImage(invalidModalityImage); + CPPUNIT_ASSERT_MESSAGE("Information type not correctly stored", informationType == "CT"); + + // TEST INVALID ID + mitk::DataNode::Pointer invalidIDImage = mitk::SemanticRelationsTestHelper::GetInvalidID(); + m_DataStorage->Add(invalidIDImage); + + CPPUNIT_ASSERT_MESSAGE("Not a valid image data node", invalidModalityImage.IsNotNull()); + CPPUNIT_ASSERT_MESSAGE("Image data is empty", invalidModalityImage->GetData() != nullptr); + + CPPUNIT_ASSERT_THROW_MESSAGE("Semantic relation exception not thrown: AddImage", + semanticRelationsIntegration.AddImage(invalidIDImage), + mitk::SemanticRelationException); + + // set valid ID for current image and add image again + // 0x0020, 0x000e (SeriesInstanceUID) + auto generatedUID = mitk::UIDGeneratorBoost::GenerateUID(); + invalidIDImage->GetData()->SetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str(), + mitk::StringProperty::New(generatedUID)); + semanticRelationsIntegration.AddImage(invalidIDImage); + + auto IDFromNode = mitk::GetIDFromDataNode(invalidIDImage); + CPPUNIT_ASSERT_MESSAGE("ID not correctly stored", IDFromNode == generatedUID); + + // TEST INVALID CASE ID + mitk::DataNode::Pointer invalidCaseIDImage = mitk::SemanticRelationsTestHelper::GetInvalidCaseID(); + m_DataStorage->Add(invalidCaseIDImage); + + CPPUNIT_ASSERT_MESSAGE("Not a valid image data node", invalidModalityImage.IsNotNull()); + CPPUNIT_ASSERT_MESSAGE("Image data is empty", invalidModalityImage->GetData() != nullptr); + + CPPUNIT_ASSERT_THROW_MESSAGE("Semantic relation exception not thrown: AddImage", + semanticRelationsIntegration.AddImage(invalidCaseIDImage), + mitk::SemanticRelationException); + + // set valid case ID for current image and add image again + // 0x0010, 0x0010 (PatientName) + invalidCaseIDImage->GetData()->SetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str(), + mitk::StringProperty::New("Patient7")); + semanticRelationsIntegration.AddImage(invalidCaseIDImage); + + auto caseIDFromNode = mitk::GetCaseIDFromDataNode(invalidCaseIDImage); + CPPUNIT_ASSERT_MESSAGE("Case ID not correctly stored", caseIDFromNode == "Patient7"); + } + + // InferenceTest + void CombinedQueries() + { + MITK_INFO << "=== CombinedQueries"; + + // add image with segmentation and lesion + auto image = mitk::SemanticRelationsTestHelper::GetPatientTwoPETImage(); + m_DataStorage->Add(image); + + CPPUNIT_ASSERT_MESSAGE("Not a valid image data node", image.IsNotNull()); + CPPUNIT_ASSERT_MESSAGE("Image data is empty", image->GetData() != nullptr); + + auto segmentation = mitk::SemanticRelationsTestHelper::GetPatientTwoSegmentation(); + m_DataStorage->Add(segmentation, image); + + CPPUNIT_ASSERT_MESSAGE("Not a valid segmentation data node", segmentation.IsNotNull()); + CPPUNIT_ASSERT_MESSAGE("Segmentation data is empty", segmentation->GetData() != nullptr); + + mitk::SemanticRelationsIntegration semanticRelationsIntegration; + semanticRelationsIntegration.AddImage(image); + semanticRelationsIntegration.AddSegmentation(segmentation, image); + + auto caseID = mitk::GetCaseIDFromDataNode(image); + + auto lesion = mitk::GenerateNewLesion(); + semanticRelationsIntegration.AddLesionAndLinkSegmentation(segmentation, lesion); + + auto allLesionsOfImage = mitk::SemanticRelationsInference::GetAllLesionsOfImage(image); + CPPUNIT_ASSERT_MESSAGE("One lesion should be stored", allLesionsOfImage.size() == 1); + + auto controlPoint = mitk::SemanticRelationsInference::GetControlPointOfImage(image); + auto allLesionsOfControlPoint = mitk::SemanticRelationsInference::GetAllLesionsOfControlPoint(caseID, controlPoint); + CPPUNIT_ASSERT_MESSAGE("Lesions should be the same", (allLesionsOfImage.size() == allLesionsOfControlPoint.size()) + && (allLesionsOfImage.front().UID == allLesionsOfControlPoint.front().UID)); + + auto allImageIDsOfLesion = mitk::SemanticRelationsInference::GetAllImageIDsOfLesion(caseID, allLesionsOfControlPoint.front()); + CPPUNIT_ASSERT_MESSAGE("Image IDs should be the same", (allImageIDsOfLesion.size() == 1) + && (allImageIDsOfLesion.front() == mitk::GetIDFromDataNode(image))); + + auto allControlPointsOfLesion = mitk::SemanticRelationsInference::GetAllControlPointsOfLesion(caseID, allLesionsOfImage.front()); + CPPUNIT_ASSERT_MESSAGE("Control points should be the same", (allControlPointsOfLesion.size() == 1) + && (allControlPointsOfLesion.front().date == controlPoint.date)); + + auto informationType = mitk::SemanticRelationsInference::GetInformationTypeOfImage(image); + auto allControlPointsOfInformationType = mitk::SemanticRelationsInference::GetAllControlPointsOfInformationType(caseID, informationType); + CPPUNIT_ASSERT_MESSAGE("Control points should be the same", (allControlPointsOfLesion.size() == 1) + && (allControlPointsOfLesion.front().date == controlPoint.date)); + + auto allInformationTypesOfControlPoint = mitk::SemanticRelationsInference::GetAllInformationTypesOfControlPoint(caseID, controlPoint); + CPPUNIT_ASSERT_MESSAGE("Information types should be the same", (allInformationTypesOfControlPoint.size() == 1) + && (allInformationTypesOfControlPoint.front() == informationType)); + } + + void InstanceExistences() + { + MITK_INFO << "=== InstanceExistences"; + + mitk::SemanticRelationsDataStorageAccess semanticRelationsDataStorageAccess(m_DataStorage); + auto allSegmentationsOfCase = semanticRelationsDataStorageAccess.GetAllSegmentationsOfCase("Patient2"); + CPPUNIT_ASSERT_MESSAGE("One segmentation should be stored", allSegmentationsOfCase.size() == 1); + + auto segmentation = allSegmentationsOfCase.front(); + CPPUNIT_ASSERT_MESSAGE("Not a valid segmentation data node", segmentation.IsNotNull()); + CPPUNIT_ASSERT_MESSAGE("Segmentation data is empty", segmentation->GetData() != nullptr); + + bool valid = mitk::SemanticRelationsInference::IsRepresentingALesion(segmentation); + CPPUNIT_ASSERT_MESSAGE("Segmentation node should represent a lesion", valid); + + auto caseID = "Patient2"; + valid = mitk::SemanticRelationsInference::IsRepresentingALesion(caseID, mitk::GetIDFromDataNode(segmentation)); + CPPUNIT_ASSERT_MESSAGE("Segmentation (via ID) should represent a lesion", valid); + + auto allLesions = mitk::RelationStorage::GetAllLesionsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("One lesion should be stored", allLesions.size() == 1); + + auto lesion = allLesions.front(); + valid = mitk::SemanticRelationsInference::IsLesionPresent(lesion, segmentation); + CPPUNIT_ASSERT_MESSAGE("Lesion should be present on segmentation", valid); + + auto allImagesOfCase = semanticRelationsDataStorageAccess.GetAllImagesOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("One image should be stored", allImagesOfCase.size() == 1); + + auto image = allImagesOfCase.front(); + CPPUNIT_ASSERT_MESSAGE("Not a valid image data node", image.IsNotNull()); + CPPUNIT_ASSERT_MESSAGE("Image data is empty", image->GetData() != nullptr); + + valid = mitk::SemanticRelationsInference::IsLesionPresent(lesion, image); + CPPUNIT_ASSERT_MESSAGE("Lesion should be present on image", valid); + + valid = mitk::SemanticRelationsInference::IsLesionPresentOnImage(caseID, lesion, mitk::GetIDFromDataNode(image)); + CPPUNIT_ASSERT_MESSAGE("Lesion should be present on segmentation", valid); + + valid = mitk::SemanticRelationsInference::IsLesionPresentOnSegmentation(caseID, lesion, mitk::GetIDFromDataNode(segmentation)); + CPPUNIT_ASSERT_MESSAGE("Lesion should be present on image", valid); + + auto controlPoint = mitk::SemanticRelationsInference::GetControlPointOfImage(image); + valid = mitk::SemanticRelationsInference::IsLesionPresentAtControlPoint(caseID, lesion, controlPoint); + CPPUNIT_ASSERT_MESSAGE("Lesion should be present at control point", valid); + + controlPoint.date = boost::gregorian::date(2019, 01, 01); + valid = mitk::SemanticRelationsInference::IsLesionPresentAtControlPoint(caseID, lesion, controlPoint); + CPPUNIT_ASSERT_MESSAGE("No lesion should be present at control point", !valid); + + valid = mitk::SemanticRelationsInference::InstanceExists(image); + CPPUNIT_ASSERT_MESSAGE("Image should exist", valid); + + valid = mitk::SemanticRelationsInference::InstanceExists(segmentation); + CPPUNIT_ASSERT_MESSAGE("Segmentation should exist", valid); + + valid = mitk::SemanticRelationsInference::InstanceExists(caseID, lesion); + CPPUNIT_ASSERT_MESSAGE("Lesion should exist", valid); + + auto emptyLesion = mitk::GenerateNewLesion("ExampleLesionClass"); + valid = mitk::SemanticRelationsInference::InstanceExists(caseID, emptyLesion); + CPPUNIT_ASSERT_MESSAGE("Lesion should not exist", !valid); + + auto newControlPoint = mitk::SemanticTypes::ControlPoint(); + valid = mitk::SemanticRelationsInference::InstanceExists(caseID, newControlPoint); + CPPUNIT_ASSERT_MESSAGE("Control point should not exist for this case", !valid); + + valid = mitk::SemanticRelationsInference::InstanceExists(caseID, controlPoint); + CPPUNIT_ASSERT_MESSAGE("Control point should exist for this case", valid); + + auto allExaminationPeriod = mitk::RelationStorage::GetAllExaminationPeriodsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("One examination period should be stored", allExaminationPeriod.size() == 1); + auto examinationPeriod = allExaminationPeriod.front(); + + valid = mitk::SemanticRelationsInference::InstanceExists(caseID, examinationPeriod); + CPPUNIT_ASSERT_MESSAGE("Examination period should exist for this case", valid); + + auto informationType = mitk::SemanticRelationsInference::GetInformationTypeOfImage(image); + valid = mitk::SemanticRelationsInference::InstanceExists(caseID, informationType); + CPPUNIT_ASSERT_MESSAGE("Control point should exist for this case", valid); + + valid = mitk::SemanticRelationsInference::InstanceExists(caseID, "CT"); + CPPUNIT_ASSERT_MESSAGE("Control point should not exist for this case", !valid); + + valid = mitk::SemanticRelationsInference::SpecificImageExists(caseID, lesion, informationType); + CPPUNIT_ASSERT_MESSAGE("Specific image should exist for this case", valid); + + valid = mitk::SemanticRelationsInference::SpecificImageExists(caseID, lesion, controlPoint); + CPPUNIT_ASSERT_MESSAGE("Specific image should exist for this case", valid); + + valid = mitk::SemanticRelationsInference::SpecificImageExists(caseID, informationType, controlPoint); + CPPUNIT_ASSERT_MESSAGE("Specific image should exist for this case", valid); + } + + // DataStorageAccessTest + void DataStorageAccess() + { + MITK_INFO << "=== DataStorageAccess"; + + // load data + mitk::SemanticRelationsIntegration semanticRelationsIntegration; + + // Patient1 + auto CTImage = mitk::SemanticRelationsTestHelper::GetPatientOneCTImage(); + m_DataStorage->Add(CTImage); + CPPUNIT_ASSERT_MESSAGE("Not a valid image data node", CTImage.IsNotNull()); + CPPUNIT_ASSERT_MESSAGE("Image data is empty", CTImage->GetData() != nullptr); + semanticRelationsIntegration.AddImage(CTImage); + + auto MRImage = mitk::SemanticRelationsTestHelper::GetPatientOneMRImage(); + m_DataStorage->Add(MRImage); + CPPUNIT_ASSERT_MESSAGE("Not a valid image data node", MRImage.IsNotNull()); + CPPUNIT_ASSERT_MESSAGE("Image data is empty", MRImage->GetData() != nullptr); + semanticRelationsIntegration.AddImage(MRImage); + + auto otherCTImage = mitk::SemanticRelationsTestHelper::GetPatientOneOtherCTImage(); + m_DataStorage->Add(otherCTImage); + CPPUNIT_ASSERT_MESSAGE("Not a valid image data node", otherCTImage.IsNotNull()); + CPPUNIT_ASSERT_MESSAGE("Image data is empty", otherCTImage->GetData() != nullptr); + semanticRelationsIntegration.AddImage(otherCTImage); + + // Patient2 + auto PETImage = mitk::SemanticRelationsTestHelper::GetPatientTwoPETImage(); + m_DataStorage->Add(PETImage); + CPPUNIT_ASSERT_MESSAGE("Not a valid image data node", PETImage.IsNotNull()); + CPPUNIT_ASSERT_MESSAGE("Image data is empty", PETImage->GetData() != nullptr); + semanticRelationsIntegration.AddImage(PETImage); + + auto PETSegmentation = mitk::SemanticRelationsTestHelper::GetPatientTwoSegmentation(); + m_DataStorage->Add(PETSegmentation, PETImage); + CPPUNIT_ASSERT_MESSAGE("Not a valid segmentation data node", PETSegmentation.IsNotNull()); + CPPUNIT_ASSERT_MESSAGE("Segmentation data is empty", PETSegmentation->GetData() != nullptr); + semanticRelationsIntegration.AddSegmentation(PETSegmentation, PETImage); + + auto lesion = mitk::GenerateNewLesion(); + semanticRelationsIntegration.AddLesionAndLinkSegmentation(PETSegmentation, lesion); + + // start test + mitk::SemanticRelationsDataStorageAccess semanticRelationsDataStorageAccess(m_DataStorage); + + auto caseID = "Patient1"; + auto allSegmentationsOfCase = semanticRelationsDataStorageAccess.GetAllSegmentationsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("No segmentation should be stored", allSegmentationsOfCase.size() == 0); + + auto allImagesOfCase = semanticRelationsDataStorageAccess.GetAllImagesOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("Three images should be stored", allImagesOfCase.size() == 3); + + auto allLesions = mitk::RelationStorage::GetAllLesionsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("No lesion should be stored", allLesions.size() == 0); + + auto emptyLesion = mitk::GenerateNewLesion("ExampleLesionClass"); + CPPUNIT_ASSERT_THROW_MESSAGE("Semantic relation exception not thrown: GetAllSegmentationsOfLesion", + semanticRelationsDataStorageAccess.GetAllSegmentationsOfLesion(caseID, emptyLesion), + mitk::SemanticRelationException); + + caseID = "Patient2"; + allLesions = mitk::RelationStorage::GetAllLesionsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("One lesion should be stored", allLesions.size() == 1); + + lesion = allLesions.front(); + auto allSegmentationsOfLesion = semanticRelationsDataStorageAccess.GetAllSegmentationsOfLesion(caseID, lesion); + CPPUNIT_ASSERT_MESSAGE("One segmentation should be stored", allSegmentationsOfLesion.size() == 1); + + auto allImagesOfLesion = semanticRelationsDataStorageAccess.GetAllImagesOfLesion(caseID, lesion); + CPPUNIT_ASSERT_MESSAGE("One image should be stored", allImagesOfLesion.size() == 1); + + auto allControlPointsOfLesion = mitk::SemanticRelationsInference::GetAllControlPointsOfLesion(caseID, lesion); + CPPUNIT_ASSERT_MESSAGE("One control point should be stored", allControlPointsOfLesion.size() == 1); + auto controlPoint = allControlPointsOfLesion.front(); + + auto allSpecificImages = semanticRelationsDataStorageAccess.GetAllSpecificImages(caseID, controlPoint, "PT"); + CPPUNIT_ASSERT_MESSAGE("One image should be stored", allSpecificImages.size() == 1); + + CPPUNIT_ASSERT_THROW_MESSAGE("Semantic relation exception not thrown: GetAllSpecificImages", + semanticRelationsDataStorageAccess.GetAllSpecificImages(caseID, controlPoint, "CT"), + mitk::SemanticRelationException); + + auto allSpecificSegmentations = semanticRelationsDataStorageAccess.GetAllSpecificSegmentations(caseID, controlPoint, "PT"); + CPPUNIT_ASSERT_MESSAGE("One segmentation should be stored", allSpecificSegmentations.size() == 1); + + CPPUNIT_ASSERT_THROW_MESSAGE("Semantic relation exception not thrown: GetAllSpecificSegmentations", + semanticRelationsDataStorageAccess.GetAllSpecificSegmentations(caseID, controlPoint, "MR"), + mitk::SemanticRelationException); + } + + // RemoveAndUnlinkTest + void CPRemoveAndUnlink() + { + MITK_INFO << "=== RemoveAndUnlink"; + + // load data + mitk::SemanticRelationsIntegration semanticRelationsIntegration; + + auto CTImage = mitk::SemanticRelationsTestHelper::GetPatientThreeCTImage(); + m_DataStorage->Add(CTImage); + CPPUNIT_ASSERT_MESSAGE("Not a valid image data node", CTImage.IsNotNull()); + CPPUNIT_ASSERT_MESSAGE("Image data is empty", CTImage->GetData() != nullptr); + semanticRelationsIntegration.AddImage(CTImage); + + auto MRImage = mitk::SemanticRelationsTestHelper::GetPatientThreeMRImage(); + m_DataStorage->Add(MRImage); + CPPUNIT_ASSERT_MESSAGE("Not a valid image data node", MRImage.IsNotNull()); + CPPUNIT_ASSERT_MESSAGE("Image data is empty", MRImage->GetData() != nullptr); + semanticRelationsIntegration.AddImage(MRImage); + + // start test + auto allCaseIDs = mitk::RelationStorage::GetAllCaseIDs(); + CPPUNIT_ASSERT_MESSAGE("One CaseID should be stored", allCaseIDs.size() == 1); + + auto caseID = allCaseIDs.front(); + CPPUNIT_ASSERT_MESSAGE("CaseID should be Patient3", caseID == "Patient3"); + + auto allExaminationPeriods = mitk::RelationStorage::GetAllExaminationPeriodsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("One examination period should be sored", allExaminationPeriods.size() == 1); + + auto allControlPoints = mitk::RelationStorage::GetAllControlPointsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("Two control points should be stored", allControlPoints.size() == 2); + + // control points + auto CTControlPoint = mitk::SemanticRelationsInference::GetControlPointOfImage(CTImage); + semanticRelationsIntegration.UnlinkImageFromControlPoint(CTImage); + + allExaminationPeriods = mitk::RelationStorage::GetAllExaminationPeriodsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("One examination period should be stored", allExaminationPeriods.size() == 1); + + allControlPoints = mitk::RelationStorage::GetAllControlPointsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("One control point should be stored since one has been removed", allControlPoints.size() == 1); + + CTControlPoint = mitk::SemanticRelationsInference::GetControlPointOfImage(CTImage); + CPPUNIT_ASSERT_MESSAGE("Control point should be removed", CTControlPoint.UID.empty()); + + CTControlPoint = mitk::GenerateControlPoint(CTImage); + semanticRelationsIntegration.SetControlPointOfImage(CTImage, CTControlPoint); + + allExaminationPeriods = mitk::RelationStorage::GetAllExaminationPeriodsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("One examination period should be sored", allExaminationPeriods.size() == 1); + + allControlPoints = mitk::RelationStorage::GetAllControlPointsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("Two control points should be stored", allControlPoints.size() == 2); + + mitk::SemanticTypes::ControlPoint newCTControlPoint; + newCTControlPoint.UID = mitk::UIDGeneratorBoost::GenerateUID(); + newCTControlPoint.date = boost::gregorian::date(2019, 03, 01); + semanticRelationsIntegration.SetControlPointOfImage(CTImage, newCTControlPoint); + + allExaminationPeriods = mitk::RelationStorage::GetAllExaminationPeriodsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("One examination period should be sored", allExaminationPeriods.size() == 1); + + allControlPoints = mitk::RelationStorage::GetAllControlPointsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("Two control points should be stored", allControlPoints.size() == 2); + + newCTControlPoint.UID = mitk::UIDGeneratorBoost::GenerateUID(); + newCTControlPoint.date = boost::gregorian::date(2019, 01, 01); + semanticRelationsIntegration.SetControlPointOfImage(CTImage, newCTControlPoint); + + allExaminationPeriods = mitk::RelationStorage::GetAllExaminationPeriodsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("Two examination periods should be stored", allExaminationPeriods.size() == 2); + + allControlPoints = mitk::RelationStorage::GetAllControlPointsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("Two control points should be stored", allControlPoints.size() == 2); + } + + void LesionRemoveAndUnlink() + { + MITK_INFO << "=== LesionRemoveAndUnlink"; + + // load data + mitk::SemanticRelationsIntegration semanticRelationsIntegration; + mitk::SemanticRelationsDataStorageAccess semanticRelationsDataStorageAccess(m_DataStorage); + + auto allCaseIDs = mitk::RelationStorage::GetAllCaseIDs(); + CPPUNIT_ASSERT_MESSAGE("One CaseID should be stored", allCaseIDs.size() == 1); + auto caseID = allCaseIDs.front(); + + auto allImages = semanticRelationsDataStorageAccess.GetAllImagesOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("Two images should be stored", allImages.size() == 2); + + auto allCTImageIDs = mitk::RelationStorage::GetAllImageIDsOfInformationType(caseID, "CT"); + CPPUNIT_ASSERT_MESSAGE("One image should be stored", allCTImageIDs.size() == 1); + + auto allMRImageIDs = mitk::RelationStorage::GetAllImageIDsOfInformationType(caseID, "MR"); + CPPUNIT_ASSERT_MESSAGE("One image should be stored", allMRImageIDs.size() == 1); + + mitk::DataNode::Pointer CTImage; + mitk::DataNode::Pointer MRImage; + for (const auto& image : allImages) + { + auto imageID = mitk::GetIDFromDataNode(image); + if (imageID == allCTImageIDs.front()) + { + CTImage = image; + } + else if (imageID == allMRImageIDs.front()) + { + MRImage = image; + } + } + + CPPUNIT_ASSERT_MESSAGE("Not a valid image data node", CTImage.IsNotNull()); + CPPUNIT_ASSERT_MESSAGE("Image data is empty", CTImage->GetData() != nullptr); + + CPPUNIT_ASSERT_MESSAGE("Not a valid image data node", MRImage.IsNotNull()); + CPPUNIT_ASSERT_MESSAGE("Image data is empty", MRImage->GetData() != nullptr); + + auto CTSegmentation = mitk::SemanticRelationsTestHelper::GetPatientThreeCTSegmentation(); + m_DataStorage->Add(CTSegmentation, CTImage); + CPPUNIT_ASSERT_MESSAGE("Not a valid segmentation data node", CTSegmentation.IsNotNull()); + CPPUNIT_ASSERT_MESSAGE("Segmentation data is empty", CTSegmentation->GetData() != nullptr); + semanticRelationsIntegration.AddSegmentation(CTSegmentation, CTImage); + + auto MRSegmentation = mitk::SemanticRelationsTestHelper::GetPatientThreeMRSegmentation(); + m_DataStorage->Add(MRSegmentation, MRImage); + CPPUNIT_ASSERT_MESSAGE("Not a valid segmentation data node", MRSegmentation.IsNotNull()); + CPPUNIT_ASSERT_MESSAGE("Segmentation data is empty", MRSegmentation->GetData() != nullptr); + semanticRelationsIntegration.AddSegmentation(MRSegmentation, MRImage); + + auto newLesion = mitk::GenerateNewLesion(); + semanticRelationsIntegration.AddLesionAndLinkSegmentation(CTSegmentation, newLesion); + newLesion = mitk::GenerateNewLesion(); + semanticRelationsIntegration.AddLesionAndLinkSegmentation(MRSegmentation, newLesion); + + // start test + auto allLesions = mitk::RelationStorage::GetAllLesionsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("Two lesions should be stored", allLesions.size() == 2); + + auto MRLesion = mitk::SemanticRelationsInference::GetLesionOfSegmentation(MRSegmentation); + semanticRelationsIntegration.UnlinkSegmentationFromLesion(MRSegmentation); + + allLesions = mitk::RelationStorage::GetAllLesionsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("Two lesions should be stored", allLesions.size() == 2); + + auto emptyLesion = mitk::SemanticRelationsInference::GetLesionOfSegmentation(MRSegmentation); + CPPUNIT_ASSERT_MESSAGE("Lesion should be removed", emptyLesion.UID.empty()); + + semanticRelationsIntegration.LinkSegmentationToLesion(MRSegmentation, MRLesion); + allLesions = mitk::RelationStorage::GetAllLesionsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("Two lesions should be stored", allLesions.size() == 2); + + auto CTLesion = mitk::SemanticRelationsInference::GetLesionOfSegmentation(CTSegmentation); + CPPUNIT_ASSERT_THROW_MESSAGE("Semantic relation exception not thrown: RemoveLesion", + semanticRelationsIntegration.RemoveLesion(caseID, CTLesion), + mitk::SemanticRelationException); + + semanticRelationsIntegration.UnlinkSegmentationFromLesion(CTSegmentation); + semanticRelationsIntegration.RemoveLesion(caseID, CTLesion); + + allLesions = mitk::RelationStorage::GetAllLesionsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("One lesions should be stored since one has been removed", allLesions.size() == 1); + + CTLesion = mitk::SemanticRelationsInference::GetLesionOfSegmentation(CTSegmentation); + CPPUNIT_ASSERT_MESSAGE("Lesion should be removed", CTLesion.UID.empty()); + } + + void RemoveImagesAndSegmentation() + { + MITK_INFO << "=== RemoveImagesAndSegmentation"; + + // load data + mitk::SemanticRelationsIntegration semanticRelationsIntegration; + mitk::SemanticRelationsDataStorageAccess semanticRelationsDataStorageAccess(m_DataStorage); + + auto allCaseIDs = mitk::RelationStorage::GetAllCaseIDs(); + CPPUNIT_ASSERT_MESSAGE("One CaseID should be stored", allCaseIDs.size() == 1); + auto caseID = allCaseIDs.front(); + + auto allImages = semanticRelationsDataStorageAccess.GetAllImagesOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("Two images should be stored", allImages.size() == 2); + + auto allSegmentations = semanticRelationsDataStorageAccess.GetAllSegmentationsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("Two segmentations should be stored", allSegmentations.size() == 2); + + for (const auto& image : allImages) + { + semanticRelationsIntegration.RemoveImage(image); + } + + for (const auto& segmentation : allSegmentations) + { + semanticRelationsIntegration.RemoveSegmentation(segmentation); + } + + allImages = semanticRelationsDataStorageAccess.GetAllImagesOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("No images should be stored", allImages.size() == 0); + + allSegmentations = semanticRelationsDataStorageAccess.GetAllSegmentationsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("No segmentations should be stored", allSegmentations.size() == 0); + + auto allControlPoints = mitk::RelationStorage::GetAllControlPointsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("No control point should be stored", allControlPoints.size() == 0); + + auto allInformationTypes = mitk::RelationStorage::GetAllInformationTypesOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("No information type should be stored", allInformationTypes.size() == 0); + + auto allLesions = mitk::RelationStorage::GetAllLesionsOfCase(caseID); + CPPUNIT_ASSERT_MESSAGE("One lesions should be stored", allLesions.size() == 1); + } + +}; + +MITK_TEST_SUITE_REGISTRATION(mitkSemanticRelations) diff --git a/Modules/SemanticRelations/Test/mitkSemanticRelationsTestHelper.cpp b/Modules/SemanticRelations/Test/mitkSemanticRelationsTestHelper.cpp new file mode 100644 index 0000000000..8701928af5 --- /dev/null +++ b/Modules/SemanticRelations/Test/mitkSemanticRelationsTestHelper.cpp @@ -0,0 +1,398 @@ +/*=================================================================== + +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 "mitkSemanticRelationsTestHelper.h" + +// mitk semantic relations +#include "mitkRelationStorage.h" +#include "mitkUIDGeneratorBoost.h" + +// mitk core +#include +#include +#include + +// mitk multilabel +#include + +mitk::DataNode::Pointer mitk::SemanticRelationsTestHelper::GetPatientOneCTImage() +{ + // create new empty image + Image::Pointer image = Image::New(); + + // set properties of image (base data) + // 0x0010, 0x0010 (PatientName) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str(), + StringProperty::New("Patient1")); + // 0x0020, 0x000e (SeriesInstanceUID) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str(), + StringProperty::New(UIDGeneratorBoost::GenerateUID())); + + // 0x0008, 0x0022 (AcquisitionDate) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str(), + StringProperty::New("20190101")); + // 0x0008, 0x0060 (Modality) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), + StringProperty::New("CT")); + + // create new data node and add image as base data + DataNode::Pointer dataNode = DataNode::New(); + dataNode->SetData(image); + + return dataNode; +} + +mitk::DataNode::Pointer mitk::SemanticRelationsTestHelper::GetPatientOneMRImage() +{ + // create new empty image + Image::Pointer image = Image::New(); + + // set properties of image (base data) + // 0x0010, 0x0010 (PatientName) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str(), + StringProperty::New("Patient1")); + // 0x0020, 0x000e (SeriesInstanceUID) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str(), + StringProperty::New(UIDGeneratorBoost::GenerateUID())); + + // 0x0008, 0x0022 (AcquisitionDate) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str(), + StringProperty::New("20190101")); + // 0x0008, 0x0060 (Modality) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), + StringProperty::New("MR")); + + // create new data node and add image as base data + DataNode::Pointer dataNode = DataNode::New(); + dataNode->SetData(image); + + return dataNode; +} + +mitk::DataNode::Pointer mitk::SemanticRelationsTestHelper::GetPatientOneOtherCTImage() +{ + // create new empty image + Image::Pointer image = Image::New(); + + // set properties of image (base data) + // 0x0010, 0x0010 (PatientName) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str(), + StringProperty::New("Patient1")); + // 0x0020, 0x000e (SeriesInstanceUID) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str(), + StringProperty::New(UIDGeneratorBoost::GenerateUID())); + + // 0x0008, 0x0022 (AcquisitionDate) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str(), + StringProperty::New("20190131")); + // 0x0008, 0x0060 (Modality) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), + StringProperty::New("CT")); + + // create new data node and add image as base data + DataNode::Pointer dataNode = DataNode::New(); + dataNode->SetData(image); + + return dataNode; +} + +mitk::DataNode::Pointer mitk::SemanticRelationsTestHelper::GetPatientTwoPETImage() +{ + // create new empty image + Image::Pointer image = Image::New(); + + // set properties of image (base data) + // 0x0010, 0x0010 (PatientName) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str(), + StringProperty::New("Patient2")); + // 0x0020, 0x000e (SeriesInstanceUID) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str(), + StringProperty::New(UIDGeneratorBoost::GenerateUID())); + + // 0x0008, 0x0022 (AcquisitionDate) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str(), + StringProperty::New("20180101")); + // 0x0008, 0x0060 (Modality) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), + StringProperty::New("PT")); + + // create new data node and add image as base data + DataNode::Pointer dataNode = DataNode::New(); + dataNode->SetData(image); + + return dataNode; +} + +mitk::DataNode::Pointer mitk::SemanticRelationsTestHelper::GetPatientTwoSegmentation() +{ + // create new empty segmentation + LabelSetImage::Pointer segmentation = LabelSetImage::New(); + + // set properties of image (base data) + // 0x0010, 0x0010 (PatientName) + segmentation->SetProperty(GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str(), + StringProperty::New("Patient2")); + // 0x0020, 0x000e (SeriesInstanceUID) + segmentation->SetProperty(GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str(), + StringProperty::New(UIDGeneratorBoost::GenerateUID())); + + // 0x0008, 0x0022 (AcquisitionDate) + segmentation->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str(), + StringProperty::New("20180101")); + // 0x0008, 0x0060 (Modality) + segmentation->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), + StringProperty::New("PT")); + + // create new data node and add image as base data + DataNode::Pointer dataNode = DataNode::New(); + dataNode->SetData(segmentation); + + return dataNode; +} + +mitk::DataNode::Pointer mitk::SemanticRelationsTestHelper::GetPatientThreeCTImage() +{ + // create new empty image + Image::Pointer image = Image::New(); + + // set properties of image (base data) + // 0x0010, 0x0010 (PatientName) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str(), + StringProperty::New("Patient3")); + // 0x0020, 0x000e (SeriesInstanceUID) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str(), + StringProperty::New(UIDGeneratorBoost::GenerateUID())); + + // 0x0008, 0x0022 (AcquisitionDate) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str(), + StringProperty::New("20190201")); + // 0x0008, 0x0060 (Modality) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), + StringProperty::New("CT")); + + // create new data node and add image as base data + DataNode::Pointer dataNode = DataNode::New(); + dataNode->SetData(image); + + return dataNode; +} + +mitk::DataNode::Pointer mitk::SemanticRelationsTestHelper::GetPatientThreeCTSegmentation() +{ + // create new empty segmentation + LabelSetImage::Pointer segmentation = LabelSetImage::New(); + + // set properties of segmentation (base data) + // 0x0010, 0x0010 (PatientName) + segmentation->SetProperty(GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str(), + StringProperty::New("Patient3")); + // 0x0020, 0x000e (SeriesInstanceUID) + segmentation->SetProperty(GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str(), + StringProperty::New(UIDGeneratorBoost::GenerateUID())); + + // 0x0008, 0x0022 (AcquisitionDate) + segmentation->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str(), + StringProperty::New("20190201")); + // 0x0008, 0x0060 (Modality) + segmentation->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), + StringProperty::New("CT")); + + // create new data node and add segmentation as base data + DataNode::Pointer dataNode = DataNode::New(); + dataNode->SetData(segmentation); + + return dataNode; +} + +mitk::DataNode::Pointer mitk::SemanticRelationsTestHelper::GetPatientThreeMRImage() +{ + // create new empty image + Image::Pointer image = Image::New(); + + // set properties of image (base data) + // 0x0010, 0x0010 (PatientName) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str(), + StringProperty::New("Patient3")); + // 0x0020, 0x000e (SeriesInstanceUID) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str(), + StringProperty::New(UIDGeneratorBoost::GenerateUID())); + + // 0x0008, 0x0022 (AcquisitionDate) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str(), + StringProperty::New("20190215")); + // 0x0008, 0x0060 (Modality) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), + StringProperty::New("MR")); + + // create new data node and add image as base data + DataNode::Pointer dataNode = DataNode::New(); + dataNode->SetData(image); + + return dataNode; +} + +mitk::DataNode::Pointer mitk::SemanticRelationsTestHelper::GetPatientThreeMRSegmentation() +{ + // create new empty segmentation + LabelSetImage::Pointer segmentation = LabelSetImage::New(); + + // set properties of segmentation (base data) + // 0x0010, 0x0010 (PatientName) + segmentation->SetProperty(GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str(), + StringProperty::New("Patient3")); + // 0x0020, 0x000e (SeriesInstanceUID) + segmentation->SetProperty(GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str(), + StringProperty::New(UIDGeneratorBoost::GenerateUID())); + + // 0x0008, 0x0022 (AcquisitionDate) + segmentation->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str(), + StringProperty::New("20190215")); + // 0x0008, 0x0060 (Modality) + segmentation->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), + StringProperty::New("MR")); + + // create new data node and add segmentation as base data + DataNode::Pointer dataNode = DataNode::New(); + dataNode->SetData(segmentation); + + return dataNode; +} + +mitk::DataNode::Pointer mitk::SemanticRelationsTestHelper::GetInvalidDate() +{ + // create new empty image + Image::Pointer image = Image::New(); + + // set properties of image (base data) + // 0x0010, 0x0010 (PatientName) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str(), + StringProperty::New("Patient4")); + // 0x0020, 0x000e (SeriesInstanceUID) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str(), + StringProperty::New(UIDGeneratorBoost::GenerateUID())); + + // no DICOM information for 0x0008, 0x0022 (AcquisitionDate) + + // 0x0008, 0x0060 (Modality) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), + StringProperty::New("CT")); + + // create new data node and add image as base data + DataNode::Pointer dataNode = DataNode::New(); + dataNode->SetData(image); + + return dataNode; +} + +mitk::DataNode::Pointer mitk::SemanticRelationsTestHelper::GetInvalidModality() +{ + // create new empty image + Image::Pointer image = Image::New(); + + // set properties of image (base data) + // 0x0010, 0x0010 (PatientName) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str(), + StringProperty::New("Patient5")); + // 0x0020, 0x000e (SeriesInstanceUID) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str(), + StringProperty::New(UIDGeneratorBoost::GenerateUID())); + + // 0x0008, 0x0022 (AcquisitionDate) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str(), + StringProperty::New("20180101")); + + // no DICOM information for 0x0008, 0x0060 (Modality) + + // create new data node and add image as base data + DataNode::Pointer dataNode = DataNode::New(); + dataNode->SetData(image); + + return dataNode; +} + +mitk::DataNode::Pointer mitk::SemanticRelationsTestHelper::GetInvalidID() +{ + // create new empty image + Image::Pointer image = Image::New(); + + // set properties of image (base data) + // 0x0010, 0x0010 (PatientName) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str(), + StringProperty::New("Patient6")); + + // no DICOM information for 0x0020, 0x000e (SeriesInstanceUID) + + // 0x0008, 0x0022 (AcquisitionDate) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str(), + StringProperty::New("20180101")); + + // 0x0008, 0x0060 (Modality) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), + StringProperty::New("CT")); + + // create new data node and add image as base data + DataNode::Pointer dataNode = DataNode::New(); + dataNode->SetData(image); + + return dataNode; +} + +mitk::DataNode::Pointer mitk::SemanticRelationsTestHelper::GetInvalidCaseID() +{ + // create new empty image + Image::Pointer image = Image::New(); + + // set properties of image (base data) + // // no DICOM information for 0x0010, 0x0010 (PatientName) + + // 0x0020, 0x000e (SeriesInstanceUID) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str(), + StringProperty::New(UIDGeneratorBoost::GenerateUID())); + + // 0x0008, 0x0022 (AcquisitionDate) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str(), + StringProperty::New("20180101")); + + // 0x0008, 0x0060 (Modality) + image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), + StringProperty::New("CT")); + + // create new data node and add image as base data + DataNode::Pointer dataNode = DataNode::New(); + dataNode->SetData(image); + + return dataNode; +} + +void mitk::SemanticRelationsTestHelper::ClearRelationStorage() +{ + // access the storage + PERSISTENCE_GET_SERVICE_MACRO + if (nullptr == persistenceService) + { + MITK_DEBUG << "Persistence service could not be loaded"; + return; + } + + auto allCaseIDs = mitk::RelationStorage::GetAllCaseIDs(); + for (auto& caseID : allCaseIDs) + { + persistenceService->RemovePropertyList(caseID); + } + + std::string listIdentifier("caseIDs"); + persistenceService->RemovePropertyList(listIdentifier); +} diff --git a/Modules/SemanticRelations/Test/mitkSemanticRelationsTestHelper.h b/Modules/SemanticRelations/Test/mitkSemanticRelationsTestHelper.h new file mode 100644 index 0000000000..628b087f64 --- /dev/null +++ b/Modules/SemanticRelations/Test/mitkSemanticRelationsTestHelper.h @@ -0,0 +1,71 @@ +/*=================================================================== + +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. + +===================================================================*/ + +// mitk core +#include + +namespace mitk +{ + namespace SemanticRelationsTestHelper + { + ////////////////////////////////////////////////////////////////////////// + // VALID DATA NODES + ////////////////////////////////////////////////////////////////////////// + DataNode::Pointer GetPatientOneCTImage(); + + DataNode::Pointer GetPatientOneMRImage(); + + DataNode::Pointer GetPatientOneOtherCTImage(); + + DataNode::Pointer GetPatientTwoPETImage(); + + DataNode::Pointer GetPatientTwoSegmentation(); + + DataNode::Pointer GetPatientThreeCTImage(); + + DataNode::Pointer GetPatientThreeCTSegmentation(); + + DataNode::Pointer GetPatientThreeMRImage(); + + DataNode::Pointer GetPatientThreeMRSegmentation(); + + ////////////////////////////////////////////////////////////////////////// + // INVALID DATA NODES + ////////////////////////////////////////////////////////////////////////// + /** + * @brief Date is 0x0008, 0x0022 (AcquisitionDate) + */ + DataNode::Pointer GetInvalidDate(); + /** + * @brief Modality is 0x0008, 0x0060(Modality) + */ + DataNode::Pointer GetInvalidModality(); + /** + * @brief ID is 0x0020, 0x000e (SeriesInstanceUID) + */ + DataNode::Pointer GetInvalidID(); + /** + * @brief CaseID is 0x0010, 0x0010 (PatientName) + */ + DataNode::Pointer GetInvalidCaseID(); + + ////////////////////////////////////////////////////////////////////////// + // AUXILIARY FUNCTIONS + ////////////////////////////////////////////////////////////////////////// + void ClearRelationStorage(); + + } // end SemanticRelationsTestHelper +} // end mitk diff --git a/Modules/SemanticRelations/files.cmake b/Modules/SemanticRelations/files.cmake new file mode 100644 index 0000000000..b5e1bddd7a --- /dev/null +++ b/Modules/SemanticRelations/files.cmake @@ -0,0 +1,14 @@ +file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") + +set(CPP_FILES + mitkControlPointManager.cpp + mitkDICOMHelper.cpp + mitkLesionData.cpp + mitkLesionManager.cpp + mitkNodePredicates.cpp + mitkRelationStorage.cpp + mitkSemanticRelationsDataStorageAccess.cpp + mitkSemanticRelationsInference.cpp + mitkSemanticRelationsIntegration.cpp + mitkUIDGeneratorBoost.cpp +) diff --git a/Modules/SemanticRelations/include/mitkControlPointManager.h b/Modules/SemanticRelations/include/mitkControlPointManager.h new file mode 100644 index 0000000000..c1343ebd8f --- /dev/null +++ b/Modules/SemanticRelations/include/mitkControlPointManager.h @@ -0,0 +1,93 @@ +/*=================================================================== + +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 MITKCONTROLPOINTMANAGER_H +#define MITKCONTROLPOINTMANAGER_H + +#include + +// semantic relations module +#include "mitkSemanticTypes.h" + +// mitk core +#include + +namespace mitk +{ + /** + * @brief Provides helper functions that are needed to work with control points. + * + * These functions help to generate new control points, check for overlapping / containing control points or provide functionality + * to find a fitting control point or even extend an already existing control point. + */ + /** + * @brief Generates a control point from a given data node. + * The date is extracted from the data node by using the 'DICOMHelper::GetDICOMDateFromDataNode'-function. + * + * @param datanode A data node pointer, whose date should be included in the newly generated control point. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint GenerateControlPoint(const mitk::DataNode* datanode); + /** + * @brief Find and return a whole control point including its date given a specific control point UID. + * + * @param controlPointUID The control point UID as string. + * @param allControlPoints All currently known control points of a specific case. + * + * @return The control point with its UID and the date. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint GetControlPointByUID(const SemanticTypes::ID& controlPointUID, const SemanticTypes::ControlPointVector& allControlPoints); + /** + * @brief Returns an already existing control point from the given vector of control points. This existing control point has the + * the same date (year, month, day) as the given single control point. + * If no existing control point can be found an empty control point is returned. + * + * @param controlPoint The control point to check for existence. + * @param allControlPoints The vector of already existing control points. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint FindExistingControlPoint(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ControlPointVector& allControlPoints); + /** + * @brief Returns an already existing close control point from the given vector of control points. This closest control point has a date + * date that is within a certain distance-in-days to the given control point. + * If no closest control point can be found within the distance threshold an empty control point is returned. + * + * @param controlPoint The control point to check for distance. + * @param allControlPoints The vector of already existing control points. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint FindClosestControlPoint(const SemanticTypes::ControlPoint& controlPoint, SemanticTypes::ControlPointVector& allControlPoints); + /** + * @brief Returns the examination period to which the given control point belongs. + * Each examination point holds a vector of control point UIDs so that the UID of the given control point can be compared against the UIDs of the vector. + * An empty examination period is returned if, + * - the given vector of examination periods is empty + * - the examination periods do not contain any control point UIDs + * - the UID of the given control point is not contained in any examination period + * + * @param controlPoint The control point of which the examination period should be found. + * @param allExaminationPeriods All currently known examination periods of a specific case. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ExaminationPeriod FindExaminationPeriod(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriodVector& allExaminationPeriods); + /** + * @brief Sort the given vector of examination periods. + * Each examination period has a vector of control point UIDs (stored in chronological order). + * The examination periods can be sorted by comparing the first control points of the examination periods. + * + * @param allExaminationPeriods All currently known examination periods of a specific case. + * @param allControlPoints All currently known control points of a specific case. + */ + MITKSEMANTICRELATIONS_EXPORT void SortExaminationPeriods(SemanticTypes::ExaminationPeriodVector& allExaminationPeriods, const SemanticTypes::ControlPointVector& allControlPoints); +} // namespace mitk + +#endif // MITKCONTROLPOINTMANAGER_H diff --git a/Modules/SemanticRelations/include/mitkDICOMHelper.h b/Modules/SemanticRelations/include/mitkDICOMHelper.h new file mode 100644 index 0000000000..082ad69e0f --- /dev/null +++ b/Modules/SemanticRelations/include/mitkDICOMHelper.h @@ -0,0 +1,110 @@ +/*=================================================================== + +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 MITKDICOMHELPER_H +#define MITKDICOMHELPER_H + +#include + +// semantic relations module +#include "mitkSemanticTypes.h" + +// mitk core +#include +#include + +// c++ +#include + +/* +* @brief Provides helper functions to convert DICOM Tag information. +* +* In order to identify the patient of an image or segmentation or to set the control point of DICOM data, +* these functions are used to retrieve the DICOM tags from the given data nodes and convert them into semantic types +* that can be used by the SemanticRelations class. +*/ +namespace mitk +{ + /* + * @brief Extracts a specific DICOM tag from the node's base data + * and returns the tag as a string. This tag string is used as an identifier for the patient (case). + * + * @pre The given data node or the node's base data has to be valid (!nullptr). + * @pre The node's base data has to have the specific DICOM Tag property set. + * @throw mitk::Exception if the given data node, the node's base data or the extracted DICOM tag are invalid (==nullptr). + * + * @par dataNode The data node, of which the DICOM tag should be extracted. + * + * @return The extracted DICOM tag as string. + * An empty string, if the DICOM tag can not be extracted (i.e. the data node or + * the underlying base data is invalid or the DICOM tag does not exist for the given data node). + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::CaseID GetCaseIDFromDataNode(const mitk::DataNode* dataNode); + /* + * @brief Extracts a specific DICOM tag (currently "0x0020, 0x000e": SeriesInstanceUID) from the node's base data + * and returns the tag as a string. This tag string is used as an identifier for the image instance. + * + * @pre The given data node or the node's base data has to be valid (!nullptr). + * @pre The node's base data has to have the "0x0020, 0x000e" DICOM Tag property set. + * @throw mitk::Exception if the given data node, the node's base data or the extracted DICOM tag are invalid (==nullptr). + * + * @par dataNode The data node, of which the DICOM tag should be extracted. + * + * @return The extracted DICOM tag as string. + * An empty string, if the DICOM tag can not be extracted (i.e. the data node or + * the underlying base data is invalid or the DICOM tag does not exist for the given data node). + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ID GetIDFromDataNode(const mitk::DataNode* dataNode); + /* + * @brief Extracts a specific DICOM tag (currently "0x0008, 0x0022": AcquisitionDate) from the node's base data + * and returns the tag as a control point. + * + * @pre The given data node or the node's base data has to be valid (!nullptr). + * @pre The node's base data has to have the "0x0008, 0x0022" DICOM Tag property set. + * @throw mitk::Exception if the given data node, the node's base data or the extracted DICOM tag are invalid (==nullptr). + * + * @par dataNode The data node, of which the DICOM tag should be extracted. + * + * @return The extracted DICOM tag as control point. + * An empty control point, if the DICOM tag can not be extracted (i.e. the data node or + * the underlying base data is invalid or the DICOM tag does not exist for the given data node). + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint GetDICOMDateFromDataNode(const mitk::DataNode* dataNode); + /** + * @brief Extracts a specific DICOM tag from the node's base data and returns the tag as a information type (a string). + * + * @pre The given data node or the node's base data has to be valid (!nullptr). + * @pre The node's base data has to have the "0x0008, 0x0060" DICOM Tag property set. + * @throw mitk::Exception if the given data node, the node's base data or the extracted DICOM tag are invalid (==nullptr). + * + * @par dataNode The data node, of which the DICOM tag should be extracted. + * + * @return The extracted DICOM tag as information type (a string). + * An empty information type, if the DICOM tag can not be extracted (i.e. the data node or + * the underlying base data is invalid or the DICOM tag does not exist for the given data node). + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::InformationType GetDICOMModalityFromDataNode(const mitk::DataNode* dataNode); + /* + * @brief Removes leading and trailing whitespace from the given string. + * + * @par identifier The value of a DICOM tag. + * + * @return The trimmed DICOM tag + */ + MITKSEMANTICRELATIONS_EXPORT std::string TrimDICOM(const std::string& identifier); +} // namespace mitk + +#endif // MITKDICOMHELPER_H diff --git a/Modules/SemanticRelations/include/mitkISemanticRelationsObservable.h b/Modules/SemanticRelations/include/mitkISemanticRelationsObservable.h new file mode 100644 index 0000000000..08e50b37ac --- /dev/null +++ b/Modules/SemanticRelations/include/mitkISemanticRelationsObservable.h @@ -0,0 +1,57 @@ +/*=================================================================== + +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 MITKISEMANTICRELATIONSOBSERVABLE_H +#define MITKISEMANTICRELATIONSOBSERVABLE_H + +#include "mitkISemanticRelationsObserver.h" + +#include "mitkSemanticTypes.h" + +namespace mitk +{ + /* + * @brief This interface declares three functions each observable subject has to implement + * in order to be observed in the 'Observer pattern' sense. + * The concrete observable class has to store its observer. + */ + class ISemanticRelationsObservable + { + public: + /* + * @brief Adds the given concrete observer to a container that holds all currently registered observer. + * + * @par observer The concrete observer to register. + */ + virtual void AddObserver(ISemanticRelationsObserver* observer) = 0; + /* + * @brief Removes the given concrete observer from the container that holds all currently registered observer. + * + * @par observer The concrete observer to unregister. + */ + virtual void RemoveObserver(ISemanticRelationsObserver* observer) = 0; + /* + * @brief Updates all concrete observer in the container that holds all currently registered observer. + * The caseID can be used to only update the observer, if the caseID fulfills a certain condition. + * + * @par caseID A caseID that identifies the currently active patient / case. + */ + virtual void NotifyObserver(const mitk::SemanticTypes::CaseID& caseID) const = 0; + + }; // class ISemanticRelationsObservable +} // namespace mitk + +#endif // MITKISEMANTICRELATIONSOBSERVABLE_H diff --git a/Modules/SemanticRelations/include/mitkISemanticRelationsObserver.h b/Modules/SemanticRelations/include/mitkISemanticRelationsObserver.h new file mode 100644 index 0000000000..3fd8651456 --- /dev/null +++ b/Modules/SemanticRelations/include/mitkISemanticRelationsObserver.h @@ -0,0 +1,43 @@ +/*=================================================================== + +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 MITKISEMANTICRELATIONSOBSERVER_H +#define MITKISEMANTICRELATIONSOBSERVER_H + +#include "mitkSemanticTypes.h" + +namespace mitk +{ + /* + * @brief This interface declares a functions each observer has to implement + * in order to be notified in the 'Observer pattern' sense. + */ + class ISemanticRelationsObserver + { + public: + /* + * @brief Updates the concrete observer. + * The caseID can be used to get access to a certain patient (case), + * whose data should be used for updating. + * + * @par caseID The current case ID to identify the currently active patient / case. + */ + virtual void Update(const mitk::SemanticTypes::CaseID& caseID) = 0; + + }; // class ISemanticRelationsObserver +} // namespace mitk + +#endif // MITKISEMANTICRELATIONSOBSERVER_H diff --git a/Modules/SemanticRelations/include/mitkLesionData.h b/Modules/SemanticRelations/include/mitkLesionData.h new file mode 100644 index 0000000000..c67ded1481 --- /dev/null +++ b/Modules/SemanticRelations/include/mitkLesionData.h @@ -0,0 +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 MITKLESIONDATA_H +#define MITKLESIONDATA_H + +#include + +// mitk semantic relations module +#include "mitkSemanticTypes.h" + +// c++ +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4251) +#endif + +namespace mitk +{ + /** + * @brief This class holds the data of each lesion in the lesion tree view. + * + */ + class MITKSEMANTICRELATIONS_EXPORT LesionData + { + public: + /** + * @brief sets the data members to their initial values + */ + LesionData(const SemanticTypes::Lesion& lesion = SemanticTypes::Lesion()); + ~LesionData(); + + SemanticTypes::Lesion GetLesion() const { return m_Lesion; }; + SemanticTypes::ID GetLesionUID() const { return m_Lesion.UID; } + std::string GetLesionName() const { return m_Lesion.name; } + + const std::vector& GetLesionPresence() const { return m_LesionPresence; }; + const std::vector& GetLesionVolume() const { return m_LesionVolume; }; + + void SetLesion(const SemanticTypes::Lesion& lesion); + void SetLesionPresence(const std::vector& lesionPresence); + void SetLesionVolume(const std::vector& lesionVolume); + + private: + + SemanticTypes::Lesion m_Lesion; + std::vector m_LesionPresence; + std::vector m_LesionVolume; + + }; +} // end namespace + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif // MITKLESIONDATA_H diff --git a/Modules/SemanticRelations/include/mitkLesionManager.h b/Modules/SemanticRelations/include/mitkLesionManager.h new file mode 100644 index 0000000000..790382bc0e --- /dev/null +++ b/Modules/SemanticRelations/include/mitkLesionManager.h @@ -0,0 +1,78 @@ +/*=================================================================== + +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 MITKLESIONMANAGER_H +#define MITKLESIONMANAGER_H + +#include + +// semantic relations module +#include "mitkSemanticTypes.h" +#include "mitkLesionData.h" + +// mitk core +#include + +/* +* @brief Provides helper functions that are needed to work with lesions. +* +* These functions help to generate new lesions, check for existing lesions or provide functionality +* to find existing lesion class types. +*/ +namespace mitk +{ + typedef std::vector LesionClassVector; + + /** + * @brief Generate a new lesion and lesion class with UIDs and the given string as lesion class type. + * + * @param lesionClassType The lesion class type as string. Default parameter is "". + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::Lesion GenerateNewLesion(const std::string& lesionClassType = ""); + /** + * @brief Generate a new lesion class with UID and the given string as lesion class type. + * + * @param lesionClassType The lesion class type as string. Default parameter is "". + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionClass GenerateNewLesionClass(const std::string& lesionClassType = ""); + /** + * @brief Find and return a whole lesion including its lesion class given a specific lesion UID. + * + * @param lesionUID The lesion UID as string. + * @param allLesions All currently known lesions of a specific case. + * + * @return The lesion with its UID and the lesion class. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::Lesion GetLesionByUID(const SemanticTypes::ID& lesionUID, const std::vector& allLesions); + /** + * @brief Find and return the whole lesion class including its UID given a specific lesion class type. + * + * @param lesionClassType The lesion class type as string. + * @param allLesionClasses All currently known lesion classes of a specific case. + * + * @return The lesion class with its UID and the class type. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionClass FindExistingLesionClass(const std::string& lesionClassType, const std::vector& allLesionClasses); + /** + * @brief Generate and store additional lesion data such as lesion presence and lesion volume for each control point. + * + * @param lesionData The lesion data that holds the lesion and will hold the additional lesion data. + * @param caseID The current case ID. + */ + MITKSEMANTICRELATIONS_EXPORT void GenerateAdditionalLesionData(LesionData& lesionData, const SemanticTypes::CaseID& caseID); +} // namespace mitk + +#endif // MITKLESIONMANAGER_H diff --git a/Modules/SemanticRelations/include/mitkNodePredicates.h b/Modules/SemanticRelations/include/mitkNodePredicates.h new file mode 100644 index 0000000000..554653b21e --- /dev/null +++ b/Modules/SemanticRelations/include/mitkNodePredicates.h @@ -0,0 +1,47 @@ +/*=================================================================== + +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 MITKNODEPREDICATES_H +#define MITKNODEPREDICATES_H + +#include + +// mitk core +#include + +namespace mitk +{ + namespace NodePredicates + { + /* + * @brief Helper function to get a node predicate that can be used to filter images. + * + * The images are of type 'mitk::Image' but must not be 'helper objects' or 'segmentation nodes'. + * For the definition of 'segmentation nodes' see 'GetSegmentationPredicate'. + */ + MITKSEMANTICRELATIONS_EXPORT mitk::NodePredicateAnd::Pointer GetImagePredicate(); + /* + * @brief Helper function to get a node predicate that can be used to filter segmentations. + * + * The segmentations are of type 'mitk::LabelSetImage' or nodes that have their 'binary' property set to true. + * Segmentations must not be 'helper objects'. + */ + MITKSEMANTICRELATIONS_EXPORT mitk::NodePredicateAnd::Pointer GetSegmentationPredicate(); + + } // namespace NodePredicates +} // namespace mitk + +#endif // MITKNODEPREDICATES_H diff --git a/Modules/SemanticRelations/include/mitkRelationStorage.h b/Modules/SemanticRelations/include/mitkRelationStorage.h new file mode 100644 index 0000000000..08f6aecc12 --- /dev/null +++ b/Modules/SemanticRelations/include/mitkRelationStorage.h @@ -0,0 +1,82 @@ +/*=================================================================== + +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 MITKRELATIONSTORAGE_H +#define MITKRELATIONSTORAGE_H + +#include + +// semantic relations module +#include "mitkSemanticTypes.h" + +namespace mitk +{ + namespace RelationStorage + { + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionVector GetAllLesionsOfCase(const SemanticTypes::CaseID& caseID); + SemanticTypes::Lesion GetLesionOfSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID); + + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPointVector GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID); + SemanticTypes::ControlPoint GetControlPointOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID); + + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ExaminationPeriodVector GetAllExaminationPeriodsOfCase(const SemanticTypes::CaseID& caseID); + + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::InformationTypeVector GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID); + SemanticTypes::InformationType GetInformationTypeOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID); + + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::IDVector GetAllImageIDsOfCase(const SemanticTypes::CaseID& caseID); + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::IDVector GetAllImageIDsOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::IDVector GetAllImageIDsOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType); + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::IDVector GetAllSegmentationIDsOfCase(const SemanticTypes::CaseID& caseID); + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::IDVector GetAllSegmentationIDsOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID); + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::IDVector GetAllSegmentationIDsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); + + SemanticTypes::ID GetImageIDOfSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID); + + MITKSEMANTICRELATIONS_EXPORT std::vector GetAllCaseIDs(); + MITKSEMANTICRELATIONS_EXPORT bool InstanceExists(const SemanticTypes::CaseID& caseID); + + void AddCase(const SemanticTypes::CaseID& caseID); + void AddImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID); + void RemoveImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID); + void AddSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID, const SemanticTypes::ID& parentID); + void RemoveSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID); + + void AddLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); + void OverwriteLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); + void LinkSegmentationToLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID, const SemanticTypes::Lesion& lesion); + void UnlinkSegmentationFromLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID); + void RemoveLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); + void RemoveLesionClass(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& lesionClassID); + + void AddControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); + void LinkImageToControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID, const SemanticTypes::ControlPoint& controlPoint); + void UnlinkImageFromControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID); + void RemoveControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); + + void AddExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod); + void AddControlPointToExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod& examinationPeriod); + void RemoveControlPointFromExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod& examinationPeriod); + void RemoveExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod); + + void AddInformationTypeToImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID, const SemanticTypes::InformationType& informationType); + void RemoveInformationTypeFromImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID); + void RemoveInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType); + + } // namespace RelationStorage +} // namespace mitk + +#endif // MITKRELATIONSTORAGE_H diff --git a/Modules/SemanticRelations/include/mitkSemanticRelationException.h b/Modules/SemanticRelations/include/mitkSemanticRelationException.h new file mode 100644 index 0000000000..8ea55b2d3e --- /dev/null +++ b/Modules/SemanticRelations/include/mitkSemanticRelationException.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 MITKSEMANTICRELATIONEXCEPTION_H +#define MITKSEMANTICRELATIONEXCEPTION_H + +// mitk core +#include + +namespace mitk +{ + class SemanticRelationException : public Exception + { + + public: + + mitkExceptionClassMacro(SemanticRelationException, Exception); + + }; +} // namespace mitk + +#endif // MITKSEMANTICRELATIONEXCEPTION_H diff --git a/Modules/SemanticRelations/include/mitkSemanticRelationsDataStorageAccess.h b/Modules/SemanticRelations/include/mitkSemanticRelationsDataStorageAccess.h new file mode 100644 index 0000000000..07b2854aaf --- /dev/null +++ b/Modules/SemanticRelations/include/mitkSemanticRelationsDataStorageAccess.h @@ -0,0 +1,140 @@ +/*=================================================================== + +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 MITKSEMANTICRELATIONSDATASTORAGEACCESS_H +#define MITKSEMANTICRELATIONSDATASTORAGEACCESS_H + +#include + +// semantic relations module +#include "mitkSemanticTypes.h" + +// mitk core +#include +#include + +namespace mitk +{ + /** + * @brief The API provides functions to query and manipulate image relations and instances, + * that are helpful during follow-up examination, like control-points (time period), + * types of the images or lesions that may be visible on multiple images. + * + * The class is able to generate IDs from given data nodes using DICOM information. + * These IDs are used to identify the corresponding instances of a specific case. + * The case can also be directly identified by the given case ID. + * + * In order for most functions to work the case ID has to be used as a parameter. + * If not, these functions do nothing. + */ + class MITKSEMANTICRELATIONS_EXPORT SemanticRelationsDataStorageAccess + { + public: + + using DataNodeVector = std::vector; + + SemanticRelationsDataStorageAccess(DataStorage* dataStorage); + ~SemanticRelationsDataStorageAccess(); + + /************************************************************************/ + /* functions to get instances / attributes */ + /************************************************************************/ + /** + * @brief Return a vector of all segmentations that are currently available for the given case. + * The segmentations may be connected / not connected to a lesion of the case. + * If no segmentations are stored for the current case, an empty vector is returned. + * + * @pre The data storage member has to be valid (!nullptr). + * @throw SemanticRelationException, if the data storage member is invalid (==nullptr). + * + * @param caseID The current case identifier is defined by the given string. + * @return A vector of data nodes representing segmentations. + */ + DataNodeVector GetAllSegmentationsOfCase(const SemanticTypes::CaseID& caseID) const; + /** + * @brief Return a vector of all segmentations that define the given lesion. These segmentations don't have to be linked to the same image. + * If the lesion is not referred to by any segmentation, an empty vector is returned. + * + * @pre The data storage member has to be valid (!nullptr). + * @throw SemanticRelationException, if the data storage member is invalid (==nullptr). + * @pre The UID of the lesion has to exist for a lesion instance. + * @throw SemanticRelationException, if UID of the lesion does not exist for a lesion instance (this can be checked via 'InstanceExists'). + * + * @param caseID The current case identifier is defined by the given string. + * @param lesion A Lesion with a UID that identifies the corresponding lesion instance. + * @return A vector of data nodes representing segmentations that define the given lesion. + */ + DataNodeVector GetAllSegmentationsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const; + /** + * @brief Return a vector of all images that are currently available for the given case. + * + * @pre The data storage member has to be valid (!nullptr). + * @throw SemanticRelationException, if the data storage member is invalid (==nullptr). + * + * @param caseID The current case identifier is defined by the given string. + * @return A vector of data nodes representing images. + */ + DataNodeVector GetAllImagesOfCase(const SemanticTypes::CaseID& caseID) const; + /** + * @brief Return a vector of all images that are connected to those segmentations that are linked to the given lesion. + * If the lesion is not referred to by any segmentation, an empty vector is returned. + * + * @pre The UID of the lesion has to exist for a lesion instance. + * @throw SemanticRelationException, if UID of the lesion does not exist for a lesion instance (this can be checked via 'InstanceExists'). + * + * @param caseID The current case identifier is defined by the given string. + * @param lesion A Lesion with a UID that identifies the corresponding lesion instance. + * @return A vector of data nodes representing images on which the lesions are visible. + */ + DataNodeVector GetAllImagesOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const; + /** + * @brief Return a vector of all image nodes that are defined with the given information type and with the given control point. + * + * @pre The UID of the control point has to exist for a control point instance. + * The information type has to exist for the given case (and is therefore used by at least one data node). + * @throw SemanticRelationException, if the UID of the control point does not exist for a control point instance (this can be checked via 'InstanceExists') or + * if the information type is not used by any data node (this can be checked via 'InstanceExists'). + * + * @param caseID The current case identifier is defined by the given string. + * @param controlPoint A control point with a UID that identifies the corresponding control point instance. + * @param informationType An information type that identifies the corresponding information type instance. + * @return A vector of image nodes that are defined with the given information type with the given control point. + */ + DataNodeVector GetAllSpecificImages(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const; + /** + * @brief Return a vector of all segmentation nodes that are defined with the given information type and with the given control point. + * The function uses the 'GetAllSpecificImages'-function to retrieve the specific images and then searches for the derived nodes (segmentation child nodes). + * + * @pre The UID of the control point has to exist for a control point instance. + * The information type has to exist for the given case (and is therefore used by at least one data node). + * @throw SemanticRelationException, if the UID of the control point does not exist for a control point instance (this can be checked via 'InstanceExists') or + * if the information type is not used by any data node (this can be checked via 'InstanceExists'). + * + * @param caseID The current case identifier is defined by the given string. + * @param controlPoint A control point with a UID that identifies the corresponding control point instance. + * @param informationType An information type that identifies the corresponding information type instance. + * @return A vector of segmentation nodes that are defined with the given information type with the given control point. + */ + DataNodeVector GetAllSpecificSegmentations(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const; + + private: + + WeakPointer m_DataStorage; + + }; +} // namespace mitk + +#endif // MITKSEMANTICRELATIONSDATASTORAGEACCESS_H diff --git a/Modules/SemanticRelations/include/mitkSemanticRelationsInference.h b/Modules/SemanticRelations/include/mitkSemanticRelationsInference.h new file mode 100644 index 0000000000..fa9faeda4d --- /dev/null +++ b/Modules/SemanticRelations/include/mitkSemanticRelationsInference.h @@ -0,0 +1,292 @@ +/*=================================================================== + +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 MITKSEMANTICRELATIONSINFERENCE_H +#define MITKSEMANTICRELATIONSINFERENCE_H + +#include + +// semantic relations module +#include "mitkSemanticTypes.h" + +// mitk core +#include + +namespace mitk +{ + /** + * @brief The API provides functions to query image relations and instances + * that are helpful during follow-up examination, like control-points (time period), + * types of the images or lesions that may be visible on multiple images. + * + * The class is able to generate IDs from given data nodes using DICOM information. + * These IDs are used to identify the corresponding instances of a specific case. + * The case can also be directly identified by the given case ID. + * + * In order for most functions to work the case ID has to be used as a parameter. + * If not, these functions do nothing. + */ + namespace SemanticRelationsInference + { + + /************************************************************************/ + /* functions to get instances / attributes */ + /************************************************************************/ + /** + * @brief Return a vector of lesion classes that are currently available for the given case. + * + * @param caseID The current case identifier is defined by the given string. + * @return A vector of lesion classes. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionClassVector GetAllLesionClassesOfCase(const SemanticTypes::CaseID& caseID); + /** + * @brief Return the lesion that is defined by the given segmentation. + * + * @pre The given segmentation data node has to be valid (!nullptr). + * @throw SemanticRelationException, if the given segmentation is invalid (==nullptr). + * @pre The segmentation data node has to represent a lesion. If not, the retrieved lesion will be empty, which leads to an exception. + * @throw SemanticRelationException, if the segmentation does not represent an existing lesion (this can be checked via 'IsRepresentingALesion'). + * + * @param segmentationNode The segmentation identifier is extracted from the given data node. + * @return The represented lesion. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::Lesion GetLesionOfSegmentation(const DataNode* segmentationNode); + /** + * @brief Returns a vector of all lesions that are currently available for the current case and are connected to the given image (via a segmentation). + * If no lesions are stored for the current case, an empty vector is returned. If no segmentations are + * connected with the image node, no lesions for the specific image will be found and an empty vector is returned. + * + * @pre The given image data node has to be valid (!nullptr). + * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). + * + * @param imageNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. + * @return A vector of lesions. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionVector GetAllLesionsOfImage(const DataNode* imageNode); + /** + * @brief Returns a vector of all lesions that are valid for the given case, given a specific control point. + * + * @param caseID The current case identifier is defined by the given string. + * @param controlPoint A specific control point which has to be available at a returned (found) lesion: + * Only those lesions are returned for which the image of the associated segmentation is linked to the given control point. + * If the control point instance does not exist, an empty vector is returned. + * @return A vector of control points. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionVector GetAllLesionsOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); + /** + * @brief Check if the given segmentation refers to an existing lesion instance. + * This function can be used before calling 'GetRepresentedLesion' in order to avoid a possible exception. + * + * @param segmentationNode The segmentation identifier is extracted from the given data node. + * @return True, if the segmentation refers to an existing lesion; false otherwise. + */ + MITKSEMANTICRELATIONS_EXPORT bool IsRepresentingALesion(const DataNode* segmentationNode); + /** + * @brief Check if the segmentation identified by the given segmentation ID refers to an existing lesion instance. + * This function can be used before calling 'GetRepresentedLesion' in order to avoid a possible exception. + * + * @param caseID The current case identifier is defined by the given string. + * @param segmentationID The segmentation node identifier is defined by the given string. + * @return True, if the segmentation refers to an existing lesion; false otherwise. + */ + MITKSEMANTICRELATIONS_EXPORT bool IsRepresentingALesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID); + /** + * @brief Check if the given lesion is present on the given data node. + * The function receives the case- and the node-ID from the DICOM tags of the node itself. + * It uses node predicates to decide if the node is an image or a segmentation node. + * + * @param lesion A lesion with a UID that identifies the corresponding lesion instance. + * @param dataNode A data node to check. + * @return True, if the lesion is present on the data node; false otherwise. + */ + MITKSEMANTICRELATIONS_EXPORT bool IsLesionPresent(const SemanticTypes::Lesion& lesion, const DataNode* dataNode); + /** + * @brief Check if the given lesion is related to the image identified by the given image ID. + * Each lesion is represented by a segmentation which is connected to its parent image. + * + * @param caseID The current case identifier is defined by the given string. + * @param lesion A lesion with a UID that identifies the corresponding lesion instance. + * @param imageID The image node identifier is defined by the given string. + * @return True, if the lesion is related to image identified by the given image ID; false otherwise. + */ + MITKSEMANTICRELATIONS_EXPORT bool IsLesionPresentOnImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ID& imageID); + /** + * @brief Check if the given lesion is present on the segmentation identified by the given segmentation ID. + * + * @param caseID The current case identifier is defined by the given string. + * @param lesion A lesion with a UID that identifies the corresponding lesion instance. + * @param segmentationID The segmentation node identifier is defined by the given string. + * @return True, if the lesion is present on the segmentation identified by the given segmentation ID; false otherwise. + */ + MITKSEMANTICRELATIONS_EXPORT bool IsLesionPresentOnSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ID& segmentationID); + /** + * @brief Check if the given lesion is present at the given control point. + * + * @param caseID The current case identifier is defined by the given string. + * @param lesion A lesion with a UID that identifies the corresponding lesion instance. + * @param controlPoint A control point with a UID that identifies the corresponding control point instance. + * @return True, if the lesion is present at the given control point; false otherwise. + */ + MITKSEMANTICRELATIONS_EXPORT bool IsLesionPresentAtControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ControlPoint& controlPoint); + /** + * @brief Check if the given data node exists in the relation storage. + * The function receives the case- and the node-ID from the DICOM tags of the node itself. + * It uses node predicates to decide if the node is an image or a segmentation node and searches + * through the corresponding relations. + * + * @param dataNode A data node to check. + * @return True, if the data node exists; false otherwise. + */ + MITKSEMANTICRELATIONS_EXPORT bool InstanceExists(const DataNode* dataNode); + /** + * @brief Check if the given lesion instance exists. + * This function can be used before calling 'AddLesionInstance' in order to avoid a possible exception. + * + * @param caseID The current case identifier is defined by the given string. + * @param lesion A lesion with a UID that identifies the corresponding lesion instance. + * @return True, if the lesion instance exists; false otherwise. + */ + MITKSEMANTICRELATIONS_EXPORT bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); + /** + * @brief Return a vector of all image IDs that identify images that are related to the given lesion. + * Each lesion is represented by a segmentation which is connected to its parent image. + * If the lesion is not represented by any segmentation, an empty vector is returned. + * + * @pre The UID of the lesion has to exist for a lesion instance. + * @throw SemanticRelationException, if UID of the lesion does not exist for a lesion instance (this can be checked via 'InstanceExists'). + * + * @param caseID The current case identifier is defined by the given string. + * @param lesion A lesion with a UID that identifies the corresponding lesion instance. + * @return A vector of IDs identifying images that identify images that are related to the given lesion. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::IDVector GetAllImageIDsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); + /** + * @brief Return the control point of a data node. + * If the data node is not linked to a control point or the data node refers to a non-existing control point, + * a control point with an empty UID is returned. + * + * @pre The given image data node has to be valid (!nullptr). + * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). + * + * @param dataNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. + * @return The control point of the given data node. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint GetControlPointOfImage(const DataNode* dataNode); + /** + * @brief Return a vector of all control points that are valid for the given case, given a specific lesion + * + * @param caseID The current case identifier is defined by the given string. + * @param lesion A specific lesion which has to be available at a returned (found) control point: + * Only those control points are returned for which an associated data has a segmentation that references the given lesion. + * If the lesion does not exists, an empty vector is returned. + * @return A vector of control points. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPointVector GetAllControlPointsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); + /** + * @brief Return a vector of all control points that are valid for the given case, given a specific information type. + * + * @param caseID The current case identifier is defined by the given string. + * @param informationType A specific information type which has to be available at a returned (found) control point: + * Only those control points are returned for which an associated data has the given information type. + * If the information type instance does not exists, an empty vector is returned. + * @return A vector of control points. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPointVector GetAllControlPointsOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType); + /** + * @brief Check if the given control point instance exists. + * This function can be used before adding, linking and unlinking control points to avoid a possible exception. + * + * @param caseID The current case identifier is defined by the given string. + * @param controlPoint A control point with a UID that identifies the corresponding control point instance. + * @return True, if the control point instance exists; false otherwise. + */ + MITKSEMANTICRELATIONS_EXPORT bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); + /** + * @brief Check if the given examination period instance exists. + * This function can be used before calling 'AddExaminationPeriod' in order to avoid a possible exception. + * + * @param caseID The current case identifier is defined by the given string. + * @param examinationPeriod An examination period with a UID that identifies the corresponding examination period instance. + * @return True, if the examination period instance exists; false otherwise. + */ + MITKSEMANTICRELATIONS_EXPORT bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod); + /** + * @brief Return the information type of the given image. + * If the image does not contain any information type, an empty information type is returned. + * + * @pre The given image data node has to be valid (!nullptr). + * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). + * + * @param imageNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. + * @return The information type of the given data node. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::InformationType GetInformationTypeOfImage(const DataNode* imageNode); + /** + * @brief Return a vector of all information types that are valid for the given case, given a specific control point. + * + * @param caseID The current case identifier is defined by the given string. + * @param controlPoint A specific control point which has to be available at a returned (found) information type: + * Only those information types are returned for which an associated data is linked to the given control point. + * If the control point instance does not exist, an empty vector is returned. + * @return A vector of information types. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::InformationTypeVector GetAllInformationTypesOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); + /** + * @brief Check if the given information type exists. + * + * @param caseID The current case identifier is defined by the given string. + * @param informationType An information type. + * @return True, if the information type exists; false otherwise. + */ + MITKSEMANTICRELATIONS_EXPORT bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType); + /** + * @brief Determine if the given information type contains images, which are connected to segmentations that represent the given lesion. + * If the lesion or the information type are not correctly stored, the function returns false. + * + * @param caseID The current case identifier is defined by the given string. + * @param lesion A Lesion with a UID that identifies the corresponding lesion instance. + * @param informationType An information type that identifies the corresponding information type instance. + * + * @return True, if the given information type contains data that is related to the given lesion; false otherwise. + */ + MITKSEMANTICRELATIONS_EXPORT bool SpecificImageExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::InformationType& informationType); + /** + * @brief Determine if the given control point contains images, which are connected to segmentations that represent the given lesion. + * If the lesion or the control point are not correctly stored, the function returns false. + * + * @param caseID The current case identifier is defined by the given string. + * @param lesion A Lesion with a UID that identifies the corresponding lesion instance. + * @param controlPoint A control point with a UID that identifies the corresponding control point instance. + * + * @return True, if the given control point contains data that is related to the given lesion; false otherwise. + */ + MITKSEMANTICRELATIONS_EXPORT bool SpecificImageExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ControlPoint& controlPoint); + /** + * @brief Determine if the given control point contains images, which refer to the given information type. + * If the information type or the control point are not correctly stored, the function returns false. + * + * @param caseID The current case identifier is defined by the given string. + * @param informationType An information type that identifies the corresponding information type instance. + * @param controlPoint A control point with a UID that identifies the corresponding control point instance. + * + * @return True, if the given control point contains data that is related to the given information type; false otherwise. + */ + MITKSEMANTICRELATIONS_EXPORT bool SpecificImageExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType, const SemanticTypes::ControlPoint& controlPoint); + + } // namespace SemanticRelationsInference +} // namespace mitk + +#endif // MITKSEMANTICRELATIONSINFERENCE_H diff --git a/Modules/SemanticRelations/include/mitkSemanticRelationsIntegration.h b/Modules/SemanticRelations/include/mitkSemanticRelationsIntegration.h new file mode 100644 index 0000000000..2babb30592 --- /dev/null +++ b/Modules/SemanticRelations/include/mitkSemanticRelationsIntegration.h @@ -0,0 +1,314 @@ +/*=================================================================== + +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 MITKSEMANTICRELATIONSINTEGRATION_H +#define MITKSEMANTICRELATIONSINTEGRATION_H + +#include + +// semantic relations module +#include "mitkISemanticRelationsObservable.h" +#include "mitkISemanticRelationsObserver.h" +#include "mitkSemanticTypes.h" + +// mitk core +#include + +namespace mitk +{ + /** + * @brief The API provides functions to manipulate image relations and instances + * that are helpful during follow-up examination, like control-points (time period), + * types of the images or lesions that may be visible on multiple images. + * + * The class is able to generate IDs from given data nodes using DICOM information. + * These IDs are used to identify the corresponding instances of a specific case. + * The case can also be directly identified by the given case ID. + * + * In order for most functions to work the case ID has to be used as a parameter. + * If not, these functions do nothing. + * + * The class implements the ISemanticRelationsObservable interface to allow observers to + * be informed about changes in the semantic relation storage. + */ + class MITKSEMANTICRELATIONS_EXPORT SemanticRelationsIntegration : public ISemanticRelationsObservable + { + public: + + /************************************************************************/ + /* functions to implement the observer pattern */ + /************************************************************************/ + /** + * @brief Adds the given concrete observer to the vector that holds all currently registered observer. + * If the observer is already registered, it will not be added to the observer vector. + * + * @param observer The concrete observer to register. + */ + virtual void AddObserver(ISemanticRelationsObserver* observer) override; + /** + * @brief Removes the given concrete observer from the vector that holds all currently registered observer. + * + * @param observer The concrete observer to unregister. + */ + virtual void RemoveObserver(ISemanticRelationsObserver* observer) override; + + /************************************************************************/ + /* functions to add / remove instances / attributes */ + /************************************************************************/ + /** + * @brief Add the given image to the set of already existing images. + * The date is extracted from the DICOM data of the image node and is compared to already existing control points in the semantic relations model. + * The function tries to find a fitting control point or to extend an already existing control point, if the extracted control point is close to + * any other, already existing control point. + * Finally, the image is linked to the correct control point. + * + * @pre The given image data node has to be valid (!nullptr). + * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). + * + * @param imageNode The current case identifier and node identifier is extracted from the given image data node, which contains DICOM information about the case and the node. + */ + void AddImage(const DataNode* imageNode); + /** + * @brief Remove the given image from the set of already existing images. + * + * @pre The given image data node has to be valid (!nullptr). + * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). + * + * @param imageNode The current case identifier and node identifier is extracted from the given image data node, which contains DICOM information about the case and the node. + */ + void RemoveImage(const DataNode* imageNode); + /** + * @brief Add a newly created lesion to the set of already existing lesions - with no connection to a specific image / segmentation of the case data. + * + * @pre The UID of the lesion must not already exist for a lesion instance. + * @throw SemanticRelationException, it the UID of the lesion already exists for a lesion instance (this can be checked via 'InstanceExists'). + * + * @param caseID The current case identifier is defined by the given string. + * @param lesion The lesion instance to add. + */ + void AddLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); + /** + * @brief Overwrite an already existing lesion instance (this may be useful to overwrite the lesion with a different lesion class). + * + * @pre The UID of the lesion has to exist for a lesion instance. + * @throw SemanticRelationException, if the UID of the lesion does not exist for a lesion instance (this can be checked via 'InstanceExists'). + * + * @param caseID The current case identifier is defined by the given string. + * @param lesion The lesion instance that overwrites an existing lesion. + */ + void OverwriteLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); + /** + * @brief Add a newly created lesion to the set of already existing lesions. The lesion is added and a reference to + * the lesion is added to the segmentation. If the segmentation is already linked to a lesion, the + * old linkage is overwritten (this can be checked via 'IsRepresentingALesion'). + * + * @pre The given segmentation data node has to be valid (!nullptr). + * @throw SemanticRelationException, if the given segmentation data node is invalid (==nullptr). + * @pre The UID of the lesion must not already exist for a lesion instance. + * @throw SemanticRelationException, if the UID of the lesion already exists for a lesion instance (this can be checked via 'InstanceExists'). + * + * @param segmentationNode The segmentation identifier is extracted from the given segmentation data node. The segmentation node has DICOM information from its parent node. + * @param lesion The lesion instance to add and link. + */ + void AddLesionAndLinkSegmentation(const DataNode* segmentationNode, const SemanticTypes::Lesion& lesion); + /** + * @brief Remove the given lesion from the set of already existing lesions. + * + * @pre The UID of the lesion has to exist for a lesion instance. + * @throw SemanticRelationException, if the UID of the lesion does not exist for a lesion instance (this can be checked via 'InstanceExists'). + * @pre The function needs to assure that no segmentation is still representing (linked to) this lesion. + * @throw SemanticRelationException, if the lesion instance to remove is still linked to by any segmentation (this can be checked via 'GetAllSegmentationsOfLesion'). + * + * @param caseID The current case identifier is defined by the given string. + * @param lesion The lesion instance to remove. + */ + void RemoveLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); + /** + * @brief Add a segmentation instance to the set of already existing segmentations - with no connection to a specific lesion. + * + * @param segmentationNode The segmentation identifier is extracted from the given segmentation data node. The segmentation node has DICOM information from its parent node. + * @param parentNode The node identifier of the parent node is extracted from the given parent data node. + */ + void AddSegmentation(const DataNode* segmentationNode, const DataNode* parentNode); + /** + * @brief Link the given segmentation instance to an an already existing lesion instance. If the segmentation is already linked to a lesion instance, the + * old linkage is overwritten (this can be checked via 'IsRepresentingALesion'). + * + * @pre The given segmentation data node has to be valid (!nullptr). + * @throw SemanticRelationException, if the given segmentation data node is invalid (==nullptr). + * @pre The UID of the lesion has to exist for a lesion instance. + * @throw SemanticRelationException, if the UID of the lesion does not exist for a lesion instance (this can be checked via 'InstanceExists'). + * + * @param segmentationNode The segmentation identifier is extracted from the given segmentation data node. The segmentation node has DICOM information from its parent node. + * @param lesion The lesion instance to link. + */ + void LinkSegmentationToLesion(const DataNode* segmentationNode, const SemanticTypes::Lesion& lesion); + /** + * @brief Unlink the given segmentation instance from the linked lesion instance. + * The lesion may stay unlinked to any segmentation. + * + * @pre The given segmentation data node has to be valid (!nullptr). + * @throw SemanticRelationException, if the given segmentation data node is invalid (==nullptr). + * + * @param segmentationNode The segmentation identifier is extracted from the given segmentation data node. The segmentation node has DICOM information from its parent node. + */ + void UnlinkSegmentationFromLesion(const DataNode* segmentationNode); + /** + * @brief Remove the given segmentation from the set of already existing segmentations. + * + * @pre The given segmentation data node has to be valid (!nullptr). + * @throw SemanticRelationException, if the given segmentation data node is invalid (==nullptr). + * + * @param segmentationNode The segmentation identifier is extracted from the given segmentation data node. The segmentation node has DICOM information from its parent node. + */ + void RemoveSegmentation(const DataNode* segmentationNode); + /** + * @brief Set the control point for the given image. + * + * @pre The given image data node has to be valid (!nullptr). + * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). + * + * @param imageNode The current case identifier and node identifier is extracted from the given image data node, which contains DICOM information about the case and the node. + * @param controlPoint The control point instance which is used for the given image. + */ + void SetControlPointOfImage(const DataNode* imageNode, const SemanticTypes::ControlPoint& controlPoint); + /** + * @brief Add a newly created control point to the set of already existing control points. A reference to the control point is added to the given image. + * This function combines adding a control point and linking it, since a control point with no associated data is not allowed. + * + * @pre The given image data node has to be valid (!nullptr). + * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). + * @pre The UID of the control point must not already exist for a control point instance. + * @throw SemanticRelationException, if the UID of the control point already exists for a control point instance (this can be checked via 'InstanceExists'). + * @pre The given control point must not already be contained in an existing control point interval. + * @throw SemanticRelationException, if the given control point is already contained in an existing control point interval (this can be checked via 'CheckContainingControlPoint'). + * @pre The given control point must contain the date of the given image data node (if parameter 'checkConsistence = true'). + * @throw SemanticRelationException, if the given control point does not contain the date of the given image data node and 'checkConsistence = true' (this can be checked via 'ControlPointManager::InsideControlPoint'). + * + * @param imageNode The current case identifier and node identifier is extracted from the given image data node, which contains DICOM information about the case and the node. + * @param controlPoint The control point instance to add. For a newly added control point always has "startDate = endDate". + * @param checkConsistence If true, the function checks, whether the date of the image data node actually lies inside the control point to link. + */ + void AddControlPointAndLinkImage(const DataNode* imageNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence = true); + /** + * @brief Link the given image to an already existing control point. + * + * @pre The given image data node has to be valid (!nullptr). + * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). + * @pre The UID of the control point has to exist for a control point instance. + * @throw SemanticRelationException, if the UID of the control point does not exists for a control point instance (this can be checked via 'InstanceExists'). + * @pre The given control point must contain the date of the given image data node (if parameter 'checkConsistence = true'). + * @throw SemanticRelationException, if the given control point does not contain the date of the given image data node and 'checkConsistence = true' (this can be checked via 'ControlPointManager::InsideControlPoint'). + * + * @param imageNode The current case identifier and node identifier is extracted from the given image data node, which contains DICOM information about the case and the node. + * @param controlPoint The control point instance to link. + * @param checkConsistence If true, the function checks, whether the date of the image data node actually lies inside the control point to link. + */ + void LinkImageToControlPoint(const DataNode* imageNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence = true); + /** + * @brief Unlink the given image from the linked control point. + * If an image is unlinked from a control point, the function needs to check whether the control point is still linked to any other image: + * - if not, the control point instance will be removed (has to be removed since a control point with no associated image is not allowed). + * - if so, the function has to make sure that the control point instance is shortened to its minimum time period (e.g. moving the end point to an earlier date). + * + * @param imageNode The current case identifier and node identifier is extracted from the given image data node, which contains DICOM information about the case and the node. + */ + void UnlinkImageFromControlPoint(const DataNode* imageNode); + /** + * @brief Add an examination period instance to the set of already existing examination periods - with no connection to a specific control point. + * + * @pre The UID of the examination period must not already exist for an examination period instance. + * @throw SemanticRelationException, if the UID of the examination period already exists for a examination period instance (this can be checked via 'InstanceExists'). + * + * @param caseID The current case identifier is defined by the given string. + * @param examinationPeriod The examination period to add. + */ + void AddExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod); + /** + * @brief Add a control point to the vector of control point UIDs of an existing examination period. + * + * @pre The UID of the control point has to exist for a control point instance. + * @throw SemanticRelationException, if the UID of the control point does not exists for a control point instance (this can be checked via 'InstanceExists'). + * @pre The UID of the examination period must not already exist for an examination period instance. + * @throw SemanticRelationException, if the UID of the examination period already exists for a examination period instance (this can be checked via 'InstanceExists'). + * + * @param caseID The current case identifier is defined by the given string. + * @param controlPoint The control point instance to add to the examination period. + * @param examinationPeriod The examination period to which the control point should be added. + */ + void AddControlPointToExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod& examinationPeriod); + /** + * @brief Set (and possibly overwrite) the information type of the given image. + * An already associated information type might be removed if is not referenced by any other image: + * + * @pre The given image data node has to be valid (!nullptr). + * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). + * @post If the information type instance did not exist before, it is now added. + * + * @param imageNode The current case identifier is extracted from the given image data node, which contains DICOM information about the case. + * @param informationType An information type that identifies the corresponding information type instance. + */ + void SetInformationType(const DataNode* imageNode, const SemanticTypes::InformationType& informationType); + /** + * @brief Set the information type of the given image. + * + * @pre The given image data node has to be valid (!nullptr). + * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). + * @post If the information type instance did not exist before, it is now added. + * + * @param imageNode The current case identifier is extracted from the given image data node, which contains DICOM information about the case. + * @param informationType An information type that identifies the corresponding information type instance. + */ + void AddInformationTypeToImage(const DataNode* imageNode, const SemanticTypes::InformationType& informationType); + /** + * @brief Remove the information type of the given image. + * If the information type is removed, the function needs to check whether the information type is referenced by any other image: + * - if not, the information type instance can be removed (has to be removed since an information type with no associated image is not allowed). + * - if so, the information type is just removed from the given image. + * + * @pre The given image data node has to be valid (!nullptr). + * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). + * + * @param imageNode The current case identifier is extracted from the given image data node, which contains DICOM information about the case. + */ + void RemoveInformationTypeFromImage(const DataNode* imageNode); + + private: + /** + * @brief A vector that stores the currently registered observer of this observable subject. + */ + static std::vector m_ObserverVector; + /** + * @brief The class notifies (updates) the observer with a given case ID. + * The view's caseID was set before in the GUI. The parts of the view that observe changes in the semantic relations are only updated, + * if the given case ID is equal to the observer's current caseID and thus the observer currently shows the semantic information of the given case. + * + * @param caseID The caseID that identifies the currently active patient / case. + */ + virtual void NotifyObserver(const mitk::SemanticTypes::CaseID& caseID) const override; + /** + * @brief Remove all control points from the storage that are not referenced by any image anymore. + * This might happen if an image has been removed (and unlinked from the corresponding control point) + * or if the user sets a new control point for an image manually in the GUI. + * + * @param caseID The current case identifier is defined by the given string. + */ + void ClearControlPoints(const SemanticTypes::CaseID& caseID); + + }; +} // namespace mitk + +#endif // MITKSEMANTICRELATIONSINTEGRATION_H diff --git a/Modules/SemanticRelations/include/mitkSemanticTypes.h b/Modules/SemanticRelations/include/mitkSemanticTypes.h new file mode 100644 index 0000000000..2731adb52e --- /dev/null +++ b/Modules/SemanticRelations/include/mitkSemanticTypes.h @@ -0,0 +1,129 @@ +/*=================================================================== + +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 MITKSEMANTICTYPES_H +#define MITKSEMANTICTYPES_H + +#define BOOST_DATE_TIME_NO_LIB +#if defined(BOOST_ALL_DYN_LINK) +#undef BOOST_ALL_DYN_LINK +#endif + +// boost +#include + +// c++ +#include +#include +#include + +namespace mitk +{ + namespace SemanticTypes + { + using ID = std::string; + using CaseID = std::string; + using InformationType = std::string; + + /* + * @brief The concept of a control point. + */ + struct ControlPoint + { + ID UID; + boost::gregorian::date date; + + ControlPoint() + { + date = boost::gregorian::date(boost::gregorian::min_date_time); + } + + // less comparison to sort containers of control points + bool operator<(const ControlPoint& other) const + { + return date < other.date; + } + + std::string ToString() const + { + std::stringstream controlPointAsString; + if (date.is_not_a_date()) + { + return ""; + } + + controlPointAsString << std::to_string(date.year()) << "-" + << std::setfill('0') << std::setw(2) << std::to_string(date.month()) << "-" + << std::setfill('0') << std::setw(2) << std::to_string(date.day()); + + return controlPointAsString.str(); + } + + void SetDateFromString(const std::string& dateAsString) + { + date = boost::gregorian::from_undelimited_string(dateAsString); + } + + int DistanceInDays(const ControlPoint& other) const + { + boost::gregorian::date_duration duration = date - other.date; + return std::abs(duration.days()); + } + }; + + /** + * @brief The concept of an examination period. + * An examination period holds a vector of control point UIDs. + * The semantic relation storage stores the UIDs such that + * the represented control points are in chronological order. + */ + struct ExaminationPeriod + { + ID UID; + std::string name = ""; + std::vector controlPointUIDs; + }; + + /* + * @brief The concept of a lesion class. + */ + struct LesionClass + { + ID UID; + std::string classType = ""; + }; + + /* + * @brief The concept of a lesion. + */ + struct Lesion + { + ID UID; + std::string name = ""; + LesionClass lesionClass; + }; + + using IDVector = std::vector; + using LesionVector = std::vector; + using LesionClassVector = std::vector; + using ControlPointVector = std::vector; + using ExaminationPeriodVector = std::vector; + using InformationTypeVector = std::vector; + + } // namespace SemanticTypes +} // namespace mitk + +#endif // MITKSEMANTICTYPES_H diff --git a/Modules/SemanticRelations/include/mitkUIDGeneratorBoost.h b/Modules/SemanticRelations/include/mitkUIDGeneratorBoost.h new file mode 100644 index 0000000000..90347efcf3 --- /dev/null +++ b/Modules/SemanticRelations/include/mitkUIDGeneratorBoost.h @@ -0,0 +1,32 @@ +/*=================================================================== + +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 MITKUIDGENERATORBOOST_H +#define MITKUIDGENERATORBOOST_H + +#include + +#include + +namespace mitk +{ + namespace UIDGeneratorBoost + { + MITKSEMANTICRELATIONS_EXPORT std::string GenerateUID(); + } // namespace UIDGeneratorBoost +} // namespace mitk + +#endif // MITKUIDGENERATORBOOST_H diff --git a/Modules/SemanticRelations/src/mitkControlPointManager.cpp b/Modules/SemanticRelations/src/mitkControlPointManager.cpp new file mode 100644 index 0000000000..16dc2a314b --- /dev/null +++ b/Modules/SemanticRelations/src/mitkControlPointManager.cpp @@ -0,0 +1,170 @@ +/*=================================================================== + +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 module +#include "mitkControlPointManager.h" +#include "mitkDICOMHelper.h" +#include "mitkUIDGeneratorBoost.h" + +// mitk core +#include + +mitk::SemanticTypes::ControlPoint mitk::GenerateControlPoint(const DataNode* datanode) +{ + SemanticTypes::ControlPoint controlPoint = GetDICOMDateFromDataNode(datanode); + controlPoint.UID = UIDGeneratorBoost::GenerateUID(); + + return controlPoint; +} + +mitk::SemanticTypes::ControlPoint mitk::GetControlPointByUID(const SemanticTypes::ID& controlPointUID, const std::vector& allControlPoints) +{ + auto lambda = [&controlPointUID](const SemanticTypes::ControlPoint& currentControlPoint) + { + return currentControlPoint.UID == controlPointUID; + }; + + const auto existingControlPoint = std::find_if(allControlPoints.begin(), allControlPoints.end(), lambda); + + mitk::SemanticTypes::ControlPoint controlPoint; + if (existingControlPoint != allControlPoints.end()) + { + controlPoint = *existingControlPoint; + } + + return controlPoint; +} + +mitk::SemanticTypes::ControlPoint mitk::FindExistingControlPoint(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ControlPointVector& allControlPoints) +{ + for (const auto& currentControlPoint : allControlPoints) + { + if (controlPoint.date == currentControlPoint.date) + { + return currentControlPoint; + } + } + + return SemanticTypes::ControlPoint(); +} + +mitk::SemanticTypes::ControlPoint mitk::FindClosestControlPoint(const SemanticTypes::ControlPoint& controlPoint, SemanticTypes::ControlPointVector& allControlPoints) +{ + if (allControlPoints.empty()) + { + return SemanticTypes::ControlPoint(); + } + + // sort the vector of control points for easier lookup + std::sort(allControlPoints.begin(), allControlPoints.end()); + // new control point does not match an existing control point + // check if the control point is close to an already existing control point + std::vector::const_iterator it; + for (it = allControlPoints.begin(); it != allControlPoints.end(); ++it) + { + if (controlPoint.date < it->date) + { + break; + } + } + + SemanticTypes::ControlPoint nextControlPoint; + SemanticTypes::ControlPoint previousControlPoint; + if (it == allControlPoints.begin()) + { + // new date is smaller ("older") than the smallest already existing control point + nextControlPoint = *it; + } + else if (it != allControlPoints.end()) + { + // new date is greater ("newer") than an already existing control point, + // but smaller ("older") than another already existing control point + nextControlPoint = *it; + previousControlPoint = *(--it); + } + else + { + // new date is greater ("newer") than the greatest already existing control point + previousControlPoint = *(--it); + } + + // test distance to next and previous time period + double distanceToNextExaminationPeriod = nextControlPoint.DistanceInDays(controlPoint); + double distanceToPreviousExaminationPeriod = previousControlPoint.DistanceInDays(controlPoint); + + SemanticTypes::ControlPoint closestControlPoint; + int closestDistanceInDays = 0; + if (distanceToNextExaminationPeriod < distanceToPreviousExaminationPeriod) + { + // control point is closer to the next control point + closestControlPoint = nextControlPoint; + closestDistanceInDays = distanceToNextExaminationPeriod; + } + else + { + // control point is closer to the previous control point + closestControlPoint = previousControlPoint; + closestDistanceInDays = distanceToPreviousExaminationPeriod; + } + + int THRESHOLD_DISTANCE_IN_DAYS = 30; + if (closestDistanceInDays <= THRESHOLD_DISTANCE_IN_DAYS) + { + return closestControlPoint; + } + + return SemanticTypes::ControlPoint(); +} + +mitk::SemanticTypes::ExaminationPeriod mitk::FindExaminationPeriod(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriodVector& allExaminationPeriods) +{ + for (const auto& examinationPeriod : allExaminationPeriods) + { + for (const auto& UID : examinationPeriod.controlPointUIDs) + { + if (controlPoint.UID == UID) + { + return examinationPeriod; + } + } + } + + return SemanticTypes::ExaminationPeriod(); +} + +void mitk::SortExaminationPeriods(SemanticTypes::ExaminationPeriodVector& allExaminationPeriods, const SemanticTypes::ControlPointVector& allControlPoints) +{ + auto lambda = [allControlPoints](const SemanticTypes::ExaminationPeriod& leftExaminationPeriod, const SemanticTypes::ExaminationPeriod& rightExaminationPeriod) + { + if (leftExaminationPeriod.controlPointUIDs.empty()) + { + return true; + } + + if (rightExaminationPeriod.controlPointUIDs.empty()) + { + return false; + } + const auto leftUID = leftExaminationPeriod.controlPointUIDs.front(); + const auto rightUID = rightExaminationPeriod.controlPointUIDs.front(); + const auto& leftControlPoint = GetControlPointByUID(leftUID, allControlPoints); + const auto& rightControlPoint = GetControlPointByUID(rightUID, allControlPoints); + + return leftControlPoint.date < rightControlPoint.date; + }; + + std::sort(allExaminationPeriods.begin(), allExaminationPeriods.end(), lambda); +} diff --git a/Modules/SemanticRelations/src/mitkDICOMHelper.cpp b/Modules/SemanticRelations/src/mitkDICOMHelper.cpp new file mode 100644 index 0000000000..aeb41fdc8b --- /dev/null +++ b/Modules/SemanticRelations/src/mitkDICOMHelper.cpp @@ -0,0 +1,170 @@ +/*=================================================================== + +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 module +#include "mitkDICOMHelper.h" +#include "mitkSemanticRelationException.h" +#include "mitkUIDGeneratorBoost.h" + +// mitk core +#include + +// c++ +#include + +mitk::SemanticTypes::ControlPoint GetControlPointFromString(const std::string& dateAsString) +{ + // date expected to be YYYYMMDD (8 characters) + if (dateAsString.size() != 8) + { + // string does not represent a DICOM date + mitkThrowException(mitk::SemanticRelationException) << "Not a valid DICOM date format."; + } + + mitk::SemanticTypes::ControlPoint controlPoint; + controlPoint.SetDateFromString(dateAsString); + + return controlPoint; +} + +mitk::SemanticTypes::CaseID mitk::GetCaseIDFromDataNode(const mitk::DataNode* dataNode) +{ + if (nullptr == dataNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid data node."; + } + + mitk::BaseData* baseData = dataNode->GetData(); + if (nullptr == baseData) + { + mitkThrowException(SemanticRelationException) << "No valid base data."; + } + + // extract suitable DICOM tag to use as the case id + // two alternatives can be used: + // - DICOM tag "0x0010, 0x0010" is PatientName + // - DICOM tag "0x0010, 0x0020" is PatientID + // in the current implementation the PatientID (0x0010, 0x0010) is used + mitk::BaseProperty* dicomTag = baseData->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str()); + if (nullptr == dicomTag) + { + mitkThrowException(SemanticRelationException) << "Not a valid DICOM property."; + } + + std::string dicomTagAsString = dicomTag->GetValueAsString(); + return dicomTagAsString; +} + +mitk::SemanticTypes::ID mitk::GetIDFromDataNode(const mitk::DataNode* dataNode) +{ + if (nullptr == dataNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid data node."; + } + + mitk::BaseData* baseData = dataNode->GetData(); + if (nullptr == baseData) + { + mitkThrowException(SemanticRelationException) << "No valid base data."; + } + + // extract suitable DICOM tag to use as the data node id + // DICOM tag "0x0020, 0x000e" is SeriesInstanceUID + mitk::BaseProperty* dicomTag = baseData->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str()); + if (nullptr == dicomTag) + { + mitkThrowException(SemanticRelationException) << "Not a valid DICOM property."; + } + std::string dicomTagAsString = dicomTag->GetValueAsString(); + return dicomTagAsString; +} + +mitk::SemanticTypes::ControlPoint mitk::GetDICOMDateFromDataNode(const mitk::DataNode* dataNode) +{ + if (nullptr == dataNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid data node."; + } + + mitk::BaseData* baseData = dataNode->GetData(); + if (nullptr == baseData) + { + mitkThrowException(SemanticRelationException) << "No valid base data."; + } + + // extract suitable DICOM tag to use as the data node id + // DICOM tag "0x0008, 0x0022" is AcquisitionDate + mitk::BaseProperty* acquisitionDateProperty = baseData->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str()); + if (nullptr == acquisitionDateProperty) + { + mitkThrowException(SemanticRelationException) << "Not a valid DICOM property."; + } + std::string acquisitionDateAsString = acquisitionDateProperty->GetValueAsString(); + + SemanticTypes::ControlPoint controlPoint; + try + { + controlPoint = GetControlPointFromString(acquisitionDateAsString); + } + catch (SemanticRelationException &e) + { + mitkReThrow(e) << "Cannot retrieve a valid DICOM date."; + } + + return controlPoint; +} + +mitk::SemanticTypes::InformationType mitk::GetDICOMModalityFromDataNode(const mitk::DataNode* dataNode) +{ + if (nullptr == dataNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid data node."; + } + + mitk::BaseData* baseData = dataNode->GetData(); + if (nullptr == baseData) + { + mitkThrowException(SemanticRelationException) << "No valid base data."; + } + + // extract suitable DICOM tag to use as the information type + // DICOM tag "0x0008, 0x0060" is Modality + mitk::BaseProperty* dicomTag = baseData->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str()); + if (nullptr == dicomTag) + { + mitkThrowException(SemanticRelationException) << "Not a valid DICOM property."; + } + std::string dicomTagAsString = dicomTag->GetValueAsString(); + return dicomTagAsString; +} + +std::string mitk::TrimDICOM(const std::string& identifier) +{ + if (identifier.empty()) + { + return identifier; + } + + // leading whitespace + std::size_t first = identifier.find_first_not_of(' '); + if (std::string::npos == first) + { + return ""; + } + // trailing whitespace + std::size_t last = identifier.find_last_not_of(' '); + return identifier.substr(first, last - first + 1); +} diff --git a/Modules/SemanticRelations/src/mitkLesionData.cpp b/Modules/SemanticRelations/src/mitkLesionData.cpp new file mode 100644 index 0000000000..172b364f65 --- /dev/null +++ b/Modules/SemanticRelations/src/mitkLesionData.cpp @@ -0,0 +1,43 @@ +/*=================================================================== + +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 module +#include "mitkLesionData.h" + +mitk::LesionData::LesionData(const SemanticTypes::Lesion& lesion/* = SemanticTypes::Lesion()*/) +{ + m_Lesion = lesion; +} + +mitk::LesionData::~LesionData() +{ + // nothing here +} + +void mitk::LesionData::SetLesion(const SemanticTypes::Lesion& lesion) +{ + m_Lesion = lesion; +} + +void mitk::LesionData::SetLesionPresence(const std::vector& lesionPresence) +{ + m_LesionPresence = lesionPresence; +} + +void mitk::LesionData::SetLesionVolume(const std::vector& lesionVolume) +{ + m_LesionVolume = lesionVolume; +} diff --git a/Modules/SemanticRelations/src/mitkLesionManager.cpp b/Modules/SemanticRelations/src/mitkLesionManager.cpp new file mode 100644 index 0000000000..12af5b9253 --- /dev/null +++ b/Modules/SemanticRelations/src/mitkLesionManager.cpp @@ -0,0 +1,120 @@ +/*=================================================================== + +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 module +#include "mitkLesionManager.h" +#include "mitkSemanticRelationException.h" +#include "mitkSemanticRelationsInference.h" +#include "mitkRelationStorage.h" +#include "mitkUIDGeneratorBoost.h" + +double GetLesionVolume(const mitk::SemanticTypes::CaseID& caseID, const mitk::SemanticTypes::Lesion& lesion, const mitk::SemanticTypes::ControlPoint& controlPoint); + +mitk::SemanticTypes::Lesion mitk::GenerateNewLesion(const std::string& lesionClassType/* = ""*/) +{ + SemanticTypes::Lesion lesion; + lesion.UID = mitk::UIDGeneratorBoost::GenerateUID(); + lesion.name = "New lesion"; + lesion.lesionClass = GenerateNewLesionClass(lesionClassType); + + return lesion; +} + +mitk::SemanticTypes::LesionClass mitk::GenerateNewLesionClass(const std::string& lesionClassType/* = ""*/) +{ + SemanticTypes::LesionClass lesionClass; + lesionClass.UID = mitk::UIDGeneratorBoost::GenerateUID(); + lesionClass.classType = lesionClassType; + + return lesionClass; +} + +mitk::SemanticTypes::Lesion mitk::GetLesionByUID(const SemanticTypes::ID& lesionUID, const std::vector& allLesions) +{ + auto lambda = [&lesionUID](const SemanticTypes::Lesion& currentLesion) + { + return currentLesion.UID == lesionUID; + }; + + const auto existingLesion = std::find_if(allLesions.begin(), allLesions.end(), lambda); + + SemanticTypes::Lesion lesion; + if (existingLesion != allLesions.end()) + { + lesion = *existingLesion; + } + + return lesion; +} + +mitk::SemanticTypes::LesionClass mitk::FindExistingLesionClass(const std::string& lesionClassType, const std::vector& allLesionClasses) +{ + auto lambda = [&lesionClassType](const SemanticTypes::LesionClass& currentLesionClass) + { + return currentLesionClass.classType == lesionClassType; + }; + + const auto existingLesionClass = std::find_if(allLesionClasses.begin(), allLesionClasses.end(), lambda); + + SemanticTypes::LesionClass lesionClass; + if (existingLesionClass != allLesionClasses.end()) + { + lesionClass = *existingLesionClass; + } + + return lesionClass; +} + +void mitk::GenerateAdditionalLesionData(LesionData& lesionData, const SemanticTypes::CaseID& caseID) +{ + std::vector lesionPresence; + std::vector lesionVolume; + SemanticTypes::Lesion lesion = lesionData.GetLesion(); + bool presence = false; + double volume = 0.0; + try + { + SemanticTypes::ControlPointVector controlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); + for (const auto& controlPoint : controlPoints) + { + presence = SemanticRelationsInference::IsLesionPresentAtControlPoint(caseID, lesion, controlPoint); + lesionPresence.push_back(presence); + + volume = GetLesionVolume(caseID, lesion, controlPoint); + lesionVolume.push_back(volume); + } + } + catch (const SemanticRelationException&) + { + return; + } + + lesionData.SetLesionPresence(lesionPresence); + lesionData.SetLesionVolume(lesionVolume); +} + +double GetLesionVolume(const mitk::SemanticTypes::CaseID& caseID, const mitk::SemanticTypes::Lesion& lesion, const mitk::SemanticTypes::ControlPoint& controlPoint) +{ + bool presence = mitk::SemanticRelationsInference::IsLesionPresentAtControlPoint(caseID, lesion, controlPoint); + if (presence) + { + return 1.0; + } + else + { + return 0.0; + } +} diff --git a/Modules/SemanticRelations/src/mitkNodePredicates.cpp b/Modules/SemanticRelations/src/mitkNodePredicates.cpp new file mode 100644 index 0000000000..d0c4b52a1a --- /dev/null +++ b/Modules/SemanticRelations/src/mitkNodePredicates.cpp @@ -0,0 +1,55 @@ +/*=================================================================== + +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 "mitkNodePredicates.h" + +// mitk core +#include +#include +#include +#include + +// multi label module +#include + +mitk::NodePredicateAnd::Pointer mitk::NodePredicates::GetImagePredicate() +{ + TNodePredicateDataType::Pointer isImage = TNodePredicateDataType::New(); + + NodePredicateOr::Pointer validImages = NodePredicateOr::New(); + validImages->AddPredicate(isImage); + + NodePredicateAnd::Pointer imagePredicate = NodePredicateAnd::New(); + imagePredicate->AddPredicate(validImages); + imagePredicate->AddPredicate(NodePredicateNot::New(GetSegmentationPredicate())); + imagePredicate->AddPredicate(NodePredicateNot::New(NodePredicateProperty::New("helper object"))); + + return imagePredicate; +} + +mitk::NodePredicateAnd::Pointer mitk::NodePredicates::GetSegmentationPredicate() +{ + NodePredicateAnd::Pointer segmentationPredicate = NodePredicateAnd::New(); + + NodePredicateProperty::Pointer isBinary = NodePredicateProperty::New("binary", BoolProperty::New(true)); + TNodePredicateDataType::Pointer isLabelSetImage = TNodePredicateDataType::New(); + NodePredicateOr::Pointer allSegmentations = NodePredicateOr::New(isBinary, isLabelSetImage); + + segmentationPredicate->AddPredicate(allSegmentations); + segmentationPredicate->AddPredicate(NodePredicateNot::New(NodePredicateProperty::New("helper object"))); + + return segmentationPredicate; +} diff --git a/Modules/SemanticRelations/src/mitkRelationStorage.cpp b/Modules/SemanticRelations/src/mitkRelationStorage.cpp new file mode 100644 index 0000000000..0a1f1c9e40 --- /dev/null +++ b/Modules/SemanticRelations/src/mitkRelationStorage.cpp @@ -0,0 +1,1539 @@ +/*=================================================================== + +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 "mitkRelationStorage.h" + +// semantic relations module +#include "mitkDICOMHelper.h" + +// multi label module +#include + +// mitk core +#include +#include + +// c++ +#include +#include + +namespace +{ + std::vector GetCaseIDs() + { + PERSISTENCE_GET_SERVICE_MACRO + if (nullptr == persistenceService) + { + MITK_DEBUG << "Persistence service could not be loaded"; + return std::vector(); + } + // the property list is valid for a certain scenario and contains all the case IDs of the radiological user's MITK session + std::string listIdentifier = "caseIDs"; + mitk::PropertyList::Pointer propertyList = persistenceService->GetPropertyList(listIdentifier); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << listIdentifier << " for the current MITK workbench / session."; + return std::vector(); + } + // retrieve a vector property that contains all case IDs + mitk::VectorProperty* caseIDsVectorProperty = dynamic_cast*>(propertyList->GetProperty(listIdentifier)); + if (nullptr == caseIDsVectorProperty) + { + MITK_DEBUG << "Could not find the property " << listIdentifier << " for the " << listIdentifier << " property list."; + return std::vector(); + } + + return caseIDsVectorProperty->GetValue(); + } + + bool CaseIDExists(const mitk::SemanticTypes::CaseID& caseID) + { + auto allCaseIDs = GetCaseIDs(); + auto existingCase = std::find(allCaseIDs.begin(), allCaseIDs.end(), caseID); + if (existingCase == allCaseIDs.end()) + { + return false; + } + + return true; + } + + mitk::PropertyList::Pointer GetStorageData(const mitk::SemanticTypes::CaseID& caseID) + { + // access the storage + PERSISTENCE_GET_SERVICE_MACRO + if (nullptr == persistenceService) + { + MITK_DEBUG << "Persistence service could not be loaded"; + return nullptr; + } + + // The persistence service may create a new property list with the given ID, if no property list is found. + // Since we don't want to return a new property list but rather inform the user that the given case + // is not a valid, stored case, we will return nullptr in that case. + if (CaseIDExists(caseID)) + { + // the property list is valid for a whole case and contains all the properties for the current case + return persistenceService->GetPropertyList(const_cast(caseID)); + } + + return nullptr; + } + + mitk::SemanticTypes::Lesion GenerateLesion(const mitk::SemanticTypes::CaseID& caseID, const mitk::SemanticTypes::ID& lesionID) + { + mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return mitk::SemanticTypes::Lesion(); + } + + mitk::VectorProperty* lesionDataProperty = dynamic_cast*>(propertyList->GetProperty(lesionID)); + if (nullptr == lesionDataProperty) + { + MITK_DEBUG << "Lesion " << lesionID << " not found. Lesion can not be retrieved."; + return mitk::SemanticTypes::Lesion(); + } + + std::vector lesionData = lesionDataProperty->GetValue(); + // a lesion date has to have exactly two values (the name of the lesion and the UID of the lesion class) + if (lesionData.size() != 2) + { + MITK_DEBUG << "Incorrect lesion data storage. Not two (2) strings of the lesion name and the lesion UID are stored."; + return mitk::SemanticTypes::Lesion(); + } + + // the lesion class ID is stored as the second property + std::string lesionClassID = lesionData[1]; + mitk::StringProperty* lesionClassProperty = dynamic_cast(propertyList->GetProperty(lesionClassID)); + if (nullptr != lesionClassProperty) + { + mitk::SemanticTypes::LesionClass generatedLesionClass; + generatedLesionClass.UID = lesionClassID; + generatedLesionClass.classType = lesionClassProperty->GetValue(); + + mitk::SemanticTypes::Lesion generatedLesion; + generatedLesion.UID = lesionID; + generatedLesion.name = lesionData[0]; + generatedLesion.lesionClass = generatedLesionClass; + + return generatedLesion; + } + + MITK_DEBUG << "Incorrect lesion class storage. Lesion " << lesionID << " can not be retrieved."; + return mitk::SemanticTypes::Lesion(); + } + + mitk::SemanticTypes::ControlPoint GenerateControlpoint(const mitk::SemanticTypes::CaseID& caseID, const mitk::SemanticTypes::ID& controlPointUID) + { + mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return mitk::SemanticTypes::ControlPoint(); + } + + // retrieve a vector property that contains the integer values of the date of a control point (0. year 1. month 2. day) + mitk::VectorProperty* controlPointVectorProperty = dynamic_cast*>(propertyList->GetProperty(controlPointUID)); + if (nullptr == controlPointVectorProperty) + { + MITK_DEBUG << "Could not find the control point " << controlPointUID << " in the storage."; + return mitk::SemanticTypes::ControlPoint(); + } + + std::vector controlPointVectorPropertyValue = controlPointVectorProperty->GetValue(); + // a control point has to have exactly three integer values (year, month and day) + if (controlPointVectorPropertyValue.size() != 3) + { + MITK_DEBUG << "Incorrect control point storage. Not three (3) values of the date are stored."; + return mitk::SemanticTypes::ControlPoint(); + } + + // set the values of the control point + mitk::SemanticTypes::ControlPoint generatedControlPoint; + generatedControlPoint.UID = controlPointUID; + generatedControlPoint.date = boost::gregorian::date(controlPointVectorPropertyValue[0], + controlPointVectorPropertyValue[1], + controlPointVectorPropertyValue[2]); + + return generatedControlPoint; + } +} + +mitk::SemanticTypes::LesionVector mitk::RelationStorage::GetAllLesionsOfCase(const SemanticTypes::CaseID& caseID) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return SemanticTypes::LesionVector(); + } + // retrieve a vector property that contains the valid lesion-IDs for the current case + VectorProperty* lesionsVectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); + if (nullptr == lesionsVectorProperty) + { + MITK_DEBUG << "Could not find any lesion in the storage."; + return SemanticTypes::LesionVector(); + } + + std::vector lesionsVectorPropertyValue = lesionsVectorProperty->GetValue(); + SemanticTypes::LesionVector allLesionsOfCase; + for (const auto& lesionID : lesionsVectorPropertyValue) + { + SemanticTypes::Lesion generatedLesion = GenerateLesion(caseID, lesionID); + if (!generatedLesion.UID.empty()) + { + allLesionsOfCase.push_back(generatedLesion); + } + } + + return allLesionsOfCase; +} + +mitk::SemanticTypes::Lesion mitk::RelationStorage::GetLesionOfSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return SemanticTypes::Lesion(); + } + + // retrieve a vector property that contains the referenced ID of a segmentation (0. image ID 1. lesion ID) + VectorProperty* segmentationVectorProperty = dynamic_cast*>(propertyList->GetProperty(segmentationID)); + if (nullptr == segmentationVectorProperty) + { + MITK_DEBUG << "Could not find the segmentation " << segmentationID << " in the storage."; + return SemanticTypes::Lesion(); + } + + std::vector segmentationVectorPropertyValue = segmentationVectorProperty->GetValue(); + // the lesion ID of a segmentation is the second value in the vector + if (segmentationVectorPropertyValue.size() != 2) + { + MITK_DEBUG << "Incorrect segmentation storage. Not two (2) IDs stored."; + return SemanticTypes::Lesion(); + } + + std::string lesionID = segmentationVectorPropertyValue[1]; + if (lesionID.empty()) + { + // segmentation does not refer to any lesion; return empty lesion + return SemanticTypes::Lesion(); + } + + return GenerateLesion(caseID, lesionID); +} + +mitk::SemanticTypes::ControlPointVector mitk::RelationStorage::GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return SemanticTypes::ControlPointVector(); + } + + // retrieve a vector property that contains the valid control point-IDs for the current case + VectorProperty* controlPointsVectorProperty = dynamic_cast*>(propertyList->GetProperty("controlpoints")); + if (nullptr == controlPointsVectorProperty) + { + MITK_DEBUG << "Could not find any control points in the storage."; + return SemanticTypes::ControlPointVector(); + } + + std::vector controlPointsVectorPropertyValue = controlPointsVectorProperty->GetValue(); + SemanticTypes::ControlPointVector allControlPointsOfCase; + for (const auto& controlPointUID : controlPointsVectorPropertyValue) + { + SemanticTypes::ControlPoint generatedControlPoint = GenerateControlpoint(caseID, controlPointUID); + if (!generatedControlPoint.UID.empty()) + { + allControlPointsOfCase.push_back(generatedControlPoint); + } + } + + return allControlPointsOfCase; +} + +mitk::SemanticTypes::ControlPoint mitk::RelationStorage::GetControlPointOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return SemanticTypes::ControlPoint(); + } + // retrieve a vector property that contains the information type and the referenced ID of a control point (0. information type 1. control point ID) + VectorProperty* imageVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); + if (nullptr == imageVectorProperty) + { + MITK_DEBUG << "Could not find the image " << imageID << " in the storage."; + return SemanticTypes::ControlPoint(); + } + + std::vector imageVectorPropertyValue = imageVectorProperty->GetValue(); + SemanticTypes::ControlPoint controlPoint; + // an image has to have exactly two values (the information type and the ID of the control point) + if (imageVectorPropertyValue.size() != 2) + { + MITK_DEBUG << "Incorrect data storage. Not two (2) values stored."; + return SemanticTypes::ControlPoint(); + } + + // the second value of the image vector is the ID of the referenced control point + std::string controlPointID = imageVectorPropertyValue[1]; + // retrieve a vector property that contains the integer values of the date of a control point (0. year 1. month 2. day) + VectorProperty* controlPointVectorProperty = dynamic_cast*>(propertyList->GetProperty(controlPointID)); + if (nullptr == controlPointVectorProperty) + { + MITK_DEBUG << "Could not find the control point " << controlPointID << " in the storage."; + return SemanticTypes::ControlPoint(); + } + + std::vector controlPointVectorPropertyValue = controlPointVectorProperty->GetValue(); + // a control point has to have exactly three integer values (year, month and day) + if (controlPointVectorPropertyValue.size() != 3) + { + MITK_DEBUG << "Incorrect control point storage. Not three (3) values of the date are stored."; + return SemanticTypes::ControlPoint(); + } + + // set the values of the control point + controlPoint.UID = controlPointID; + controlPoint.date = boost::gregorian::date(controlPointVectorPropertyValue[0], + controlPointVectorPropertyValue[1], + controlPointVectorPropertyValue[2]); + + return controlPoint; +} + +mitk::SemanticTypes::ExaminationPeriodVector mitk::RelationStorage::GetAllExaminationPeriodsOfCase(const SemanticTypes::CaseID& caseID) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return SemanticTypes::ExaminationPeriodVector(); + } + + // retrieve a vector property that contains the valid examination period UIDs for the current case + VectorProperty::Pointer examinationPeriodsVectorProperty = dynamic_cast*>(propertyList->GetProperty("examinationperiods")); + if (nullptr == examinationPeriodsVectorProperty) + { + MITK_DEBUG << "Could not find any examination periods in the storage."; + return SemanticTypes::ExaminationPeriodVector(); + } + + std::vector examinationPeriodsVectorPropertyValue = examinationPeriodsVectorProperty->GetValue(); + SemanticTypes::ExaminationPeriodVector allExaminationPeriods; + for (const auto& examinationPeriodID : examinationPeriodsVectorPropertyValue) + { + // retrieve a vector property that contains the represented control point-IDs + VectorProperty::Pointer examinationPeriodVectorProperty = dynamic_cast*>(propertyList->GetProperty(examinationPeriodID)); + if (nullptr == examinationPeriodVectorProperty) + { + MITK_DEBUG << "Could not find the examination period " << examinationPeriodID << " in the storage."; + continue; + } + + std::vector examinationPeriodVectorPropertyValue = examinationPeriodVectorProperty->GetValue(); + // an examination period has an arbitrary number of vector values (name and control point UIDs) (at least one for the name) + if (examinationPeriodVectorPropertyValue.empty()) + { + MITK_DEBUG << "Incorrect examination period storage. At least one (1) value for the examination period name has to be stored."; + continue; + } + else + { + // set the values of the name and the control points + SemanticTypes::ExaminationPeriod generatedExaminationPeriod; + generatedExaminationPeriod.UID = examinationPeriodID; + generatedExaminationPeriod.name = examinationPeriodVectorPropertyValue[0]; + for (size_t i = 1; i < examinationPeriodVectorPropertyValue.size(); ++i) + { + generatedExaminationPeriod.controlPointUIDs.push_back(examinationPeriodVectorPropertyValue[i]); + } + + allExaminationPeriods.push_back(generatedExaminationPeriod); + } + } + return allExaminationPeriods; +} + +mitk::SemanticTypes::InformationTypeVector mitk::RelationStorage::GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return SemanticTypes::InformationTypeVector(); + } + // retrieve a vector property that contains the valid information types of the current case + VectorProperty* informationTypesVectorProperty = dynamic_cast*>(propertyList->GetProperty("informationtypes")); + if (nullptr == informationTypesVectorProperty) + { + MITK_DEBUG << "Could not find any information types in the storage."; + return SemanticTypes::InformationTypeVector(); + } + + return informationTypesVectorProperty->GetValue(); +} + +mitk::SemanticTypes::InformationType mitk::RelationStorage::GetInformationTypeOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return SemanticTypes::InformationType(); + } + // retrieve a vector property that contains the information type and the referenced ID of an image (0. information type 1. control point ID) + VectorProperty* imageVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); + if (nullptr == imageVectorProperty) + { + MITK_DEBUG << "Could not find the image " << imageID << " in the storage."; + return SemanticTypes::InformationType(); + } + + std::vector imageVectorPropertyValue = imageVectorProperty->GetValue(); + // an image has to have exactly two values (the information type and the ID of the control point) + if (imageVectorPropertyValue.size() != 2) + { + MITK_DEBUG << "Incorrect data storage. Not two (2) values stored."; + return SemanticTypes::InformationType(); + } + + // the first value of the image vector is the information type + return imageVectorPropertyValue[0]; +} + +mitk::SemanticTypes::IDVector mitk::RelationStorage::GetAllImageIDsOfCase(const SemanticTypes::CaseID& caseID) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return SemanticTypes::IDVector(); + } + // retrieve a vector property that contains the valid image-IDs of the current case + VectorProperty* imagesVectorProperty = dynamic_cast*>(propertyList->GetProperty("images")); + if (nullptr == imagesVectorProperty) + { + MITK_DEBUG << "Could not find any image in the storage."; + return SemanticTypes::IDVector(); + } + + return imagesVectorProperty->GetValue(); +} + +mitk::SemanticTypes::IDVector mitk::RelationStorage::GetAllImageIDsOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return SemanticTypes::IDVector(); + } + // retrieve a vector property that contains the valid image-IDs of the current case + VectorProperty* imagesVectorProperty = dynamic_cast*>(propertyList->GetProperty("images")); + if (nullptr == imagesVectorProperty) + { + MITK_DEBUG << "Could not find any image in the storage."; + return SemanticTypes::IDVector(); + } + + mitk::SemanticTypes::IDVector allImageIDsOfControlPoint; + std::vector imagesVectorPropertyValue = imagesVectorProperty->GetValue(); + for (const auto& imageID : imagesVectorPropertyValue) + { + // retrieve a vector property that contains the referenced ID of an image (0. information type 1. control point ID) + VectorProperty* imageVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); + if (nullptr == imageVectorProperty) + { + continue; + } + + std::vector imageVectorPropertyValue = imageVectorProperty->GetValue(); + // an image has to have exactly two values (the information type and the ID of the control point) + if (imageVectorPropertyValue.size() != 2) + { + continue; + } + + // the second value of the image vector is the ID of the referenced control point + if (imageVectorPropertyValue[1] == controlPoint.UID) + { + allImageIDsOfControlPoint.push_back(imageID); + } + } + + return allImageIDsOfControlPoint; +} + +mitk::SemanticTypes::IDVector mitk::RelationStorage::GetAllImageIDsOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return SemanticTypes::IDVector(); + } + // retrieve a vector property that contains the valid image-IDs of the current case + VectorProperty* imagesVectorProperty = dynamic_cast*>(propertyList->GetProperty("images")); + if (nullptr == imagesVectorProperty) + { + MITK_DEBUG << "Could not find any image in the storage."; + return SemanticTypes::IDVector(); + } + + mitk::SemanticTypes::IDVector allImageIDsOfInformationType; + std::vector imagesVectorPropertyValue = imagesVectorProperty->GetValue(); + for (const auto& imageID : imagesVectorPropertyValue) + { + // retrieve a vector property that contains the referenced ID of an image (0. information type 1. control point ID) + VectorProperty* imageVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); + if (nullptr == imageVectorProperty) + { + continue; + } + + std::vector imageVectorPropertyValue = imageVectorProperty->GetValue(); + // an image has to have exactly two values (the information type and the ID of the control point) + if (imageVectorPropertyValue.size() != 2) + { + continue; + } + + // the first value of the image vector is the ID of the referenced information type + if (imageVectorPropertyValue[0] == informationType) + { + allImageIDsOfInformationType.push_back(imageID); + } + } + + return allImageIDsOfInformationType; +} + +mitk::SemanticTypes::IDVector mitk::RelationStorage::GetAllSegmentationIDsOfCase(const SemanticTypes::CaseID& caseID) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return SemanticTypes::IDVector(); + } + // retrieve a vector property that contains the valid segmentation-IDs for the current case + VectorProperty* segmentationsVectorProperty = dynamic_cast*>(propertyList->GetProperty("segmentations")); + if (nullptr == segmentationsVectorProperty) + { + MITK_DEBUG << "Could not find any segmentation in the storage."; + return SemanticTypes::IDVector(); + } + + return segmentationsVectorProperty->GetValue(); +} + +mitk::SemanticTypes::IDVector mitk::RelationStorage::GetAllSegmentationIDsOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return SemanticTypes::IDVector(); + } + // retrieve a vector property that contains the valid segmentation-IDs for the current case + VectorProperty* segmentationsVectorProperty = dynamic_cast*>(propertyList->GetProperty("segmentations")); + if (nullptr == segmentationsVectorProperty) + { + MITK_DEBUG << "Could not find any segmentation in the storage."; + return SemanticTypes::IDVector(); + } + + mitk::SemanticTypes::IDVector allSegmentationIDsOfImage; + std::vector segmentationsVectorPropertyValue = segmentationsVectorProperty->GetValue(); + for (const auto& segmentationID : segmentationsVectorPropertyValue) + { + // retrieve a vector property that contains the referenced ID of a segmentation (0. image ID 1. lesion ID) + VectorProperty* segmentationVectorProperty = dynamic_cast*>(propertyList->GetProperty(segmentationID)); + if (nullptr == segmentationVectorProperty) + { + continue; + } + + std::vector segmentationVectorPropertyValue = segmentationVectorProperty->GetValue(); + // a segmentation has to have exactly two values (the ID of the referenced image and the ID of the referenced lesion) + if (segmentationVectorPropertyValue.size() != 2) + { + continue; + } + + // the first value of the segmentation vector is the ID of the referenced image + if (segmentationVectorPropertyValue[0] == imageID) + { + allSegmentationIDsOfImage.push_back(segmentationID); + } + } + + return allSegmentationIDsOfImage; +} + +mitk::SemanticTypes::IDVector mitk::RelationStorage::GetAllSegmentationIDsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return SemanticTypes::IDVector(); + } + // retrieve a vector property that contains the valid segmentation-IDs for the current case + VectorProperty* segmentationsVectorProperty = dynamic_cast*>(propertyList->GetProperty("segmentations")); + if (nullptr == segmentationsVectorProperty) + { + MITK_DEBUG << "Could not find any segmentation in the storage."; + return SemanticTypes::IDVector(); + } + + mitk::SemanticTypes::IDVector allSegmentationIDsOfLesion; + std::vector segmentationsVectorPropertyValue = segmentationsVectorProperty->GetValue(); + for (const auto& segmentationID : segmentationsVectorPropertyValue) + { + // retrieve a vector property that contains the referenced ID of a segmentation (0. image ID 1. lesion ID) + VectorProperty* segmentationVectorProperty = dynamic_cast*>(propertyList->GetProperty(segmentationID)); + if (nullptr == segmentationVectorProperty) + { + continue; + } + + std::vector segmentationVectorPropertyValue = segmentationVectorProperty->GetValue(); + // a segmentation has to have exactly two values (the ID of the referenced image and the ID of the referenced lesion) + if (segmentationVectorPropertyValue.size() != 2) + { + continue; + } + + // the second value of the segmentation vector is the ID of the referenced lesion + if (segmentationVectorPropertyValue[1] == lesion.UID) + { + allSegmentationIDsOfLesion.push_back(segmentationID); + } + } + + return allSegmentationIDsOfLesion; +} + +mitk::SemanticTypes::ID mitk::RelationStorage::GetImageIDOfSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return SemanticTypes::ID(); + } + + // retrieve a vector property that contains the referenced ID of a segmentation (0. image ID 1. lesion ID) + VectorProperty* segmentationVectorProperty = dynamic_cast*>(propertyList->GetProperty(segmentationID)); + if (nullptr == segmentationVectorProperty) + { + MITK_DEBUG << "Could not find the segmentation " << segmentationID << " in the storage."; + return SemanticTypes::ID(); + } + + std::vector segmentationVectorPropertyValue = segmentationVectorProperty->GetValue(); + // the lesion ID of a segmentation is the second value in the vector + if (segmentationVectorPropertyValue.size() != 2) + { + MITK_DEBUG << "Incorrect segmentation storage. Not two (2) IDs stored."; + return SemanticTypes::ID(); + } + + return segmentationVectorPropertyValue[0]; +} + +std::vector mitk::RelationStorage::GetAllCaseIDs() +{ + return GetCaseIDs(); +} + +bool mitk::RelationStorage::InstanceExists(const SemanticTypes::CaseID& caseID) +{ + return CaseIDExists(caseID); +} + +void mitk::RelationStorage::AddCase(const SemanticTypes::CaseID& caseID) +{ + PERSISTENCE_GET_SERVICE_MACRO + if (nullptr == persistenceService) + { + MITK_DEBUG << "Persistence service could not be loaded"; + return; + } + // the property list is valid for a certain scenario and contains all the case IDs of the radiological user's MITK session + std::string listIdentifier = "caseIDs"; + PropertyList::Pointer propertyList = persistenceService->GetPropertyList(listIdentifier); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << listIdentifier << " for the current MITK workbench / session."; + return; + } + // retrieve a vector property that contains all case IDs + VectorProperty::Pointer caseIDsVectorProperty = dynamic_cast*>(propertyList->GetProperty(listIdentifier)); + std::vector caseIDsVectorPropertyValue; + if (nullptr == caseIDsVectorProperty) + { + caseIDsVectorProperty = VectorProperty::New(); + } + else + { + caseIDsVectorPropertyValue = caseIDsVectorProperty->GetValue(); + } + + auto existingCase = std::find(caseIDsVectorPropertyValue.begin(), caseIDsVectorPropertyValue.end(), caseID); + if (existingCase != caseIDsVectorPropertyValue.end()) + { + return; + } + + // add case to the "caseIDs" property list + caseIDsVectorPropertyValue.push_back(caseID); + caseIDsVectorProperty->SetValue(caseIDsVectorPropertyValue); + propertyList->SetProperty(listIdentifier, caseIDsVectorProperty); +} + +void mitk::RelationStorage::AddImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return; + } + + // retrieve a vector property that contains the valid image-IDs for the current case + VectorProperty::Pointer imagesVectorProperty = dynamic_cast*>(propertyList->GetProperty("images")); + std::vector imagesVectorPropertyValue; + if (nullptr == imagesVectorProperty) + { + imagesVectorProperty = VectorProperty::New(); + } + else + { + imagesVectorPropertyValue = imagesVectorProperty->GetValue(); + } + + auto existingImage = std::find(imagesVectorPropertyValue.begin(), imagesVectorPropertyValue.end(), imageID); + if (existingImage != imagesVectorPropertyValue.end()) + { + return; + } + + // add image to the "images" property list + imagesVectorPropertyValue.push_back(imageID); + imagesVectorProperty->SetValue(imagesVectorPropertyValue); + propertyList->SetProperty("images", imagesVectorProperty); + + // add the image itself + VectorProperty::Pointer imageVectorProperty = VectorProperty::New(); + // an image has to have exactly two values (the information type and the ID of the control point) + std::vector imageVectorPropertyValue(2); + imageVectorProperty->SetValue(imageVectorPropertyValue); + propertyList->SetProperty(imageID, imageVectorProperty); +} + +void mitk::RelationStorage::RemoveImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return; + } + + // retrieve a vector property that contains the valid image-IDs for the current case + VectorProperty::Pointer imagesVectorProperty = dynamic_cast*>(propertyList->GetProperty("images")); + if (nullptr == imagesVectorProperty) + { + MITK_DEBUG << "Could not find any images in the storage."; + return; + } + + // remove the image reference from the list of all images of the current case + std::vector imagesVectorPropertyValue = imagesVectorProperty->GetValue(); + imagesVectorPropertyValue.erase(std::remove(imagesVectorPropertyValue.begin(), imagesVectorPropertyValue.end(), imageID), imagesVectorPropertyValue.end()); + if (imagesVectorPropertyValue.empty()) + { + // no more images stored -> remove the images property list + propertyList->DeleteProperty("images"); + } + else + { + // or store the modified vector value + imagesVectorProperty->SetValue(imagesVectorPropertyValue); + } + + // remove the image instance itself + propertyList->DeleteProperty(imageID); +} + +void mitk::RelationStorage::AddSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID, const SemanticTypes::ID& parentID) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return; + } + + // retrieve a vector property that contains the valid segmentation-IDs for the current case + VectorProperty::Pointer segmentationsVectorProperty = dynamic_cast*>(propertyList->GetProperty("segmentations")); + std::vector segmentationsVectorPropertyValue; + if (nullptr == segmentationsVectorProperty) + { + segmentationsVectorProperty = VectorProperty::New(); + } + else + { + segmentationsVectorPropertyValue = segmentationsVectorProperty->GetValue(); + } + + auto existingSegmentation = std::find(segmentationsVectorPropertyValue.begin(), segmentationsVectorPropertyValue.end(), segmentationID); + if (existingSegmentation != segmentationsVectorPropertyValue.end()) + { + return; + } + + // add segmentation to the "segmentations" property list + segmentationsVectorPropertyValue.push_back(segmentationID); + segmentationsVectorProperty->SetValue(segmentationsVectorPropertyValue); + propertyList->SetProperty("segmentations", segmentationsVectorProperty); + + // add the segmentation itself + VectorProperty::Pointer segmentationVectorProperty = VectorProperty::New(); + // a segmentation has to have exactly two values (the ID of the referenced image and the ID of the referenced lesion) + std::vector segmentationVectorPropertyValue(2); + segmentationVectorPropertyValue[0] = parentID; + segmentationVectorProperty->SetValue(segmentationVectorPropertyValue); + propertyList->SetProperty(segmentationID, segmentationVectorProperty); +} + +void mitk::RelationStorage::RemoveSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return; + } + + // retrieve a vector property that contains the valid segmentation-IDs for the current case + VectorProperty::Pointer segmentationsVectorProperty = dynamic_cast*>(propertyList->GetProperty("segmentations")); + if (nullptr == segmentationsVectorProperty) + { + MITK_DEBUG << "Could not find any segmentation in the storage."; + return; + } + + // remove the lesion reference from the list of all lesions of the current case + std::vector segmentationsVectorPropertyValue = segmentationsVectorProperty->GetValue(); + segmentationsVectorPropertyValue.erase(std::remove(segmentationsVectorPropertyValue.begin(), segmentationsVectorPropertyValue.end(), segmentationID), segmentationsVectorPropertyValue.end()); + if (segmentationsVectorPropertyValue.empty()) + { + // no more segmentations stored -> remove the segmentations property list + propertyList->DeleteProperty("segmentations"); + } + else + { + // or store the modified vector value + segmentationsVectorProperty->SetValue(segmentationsVectorPropertyValue); + } + + // remove the lesion instance itself + propertyList->DeleteProperty(segmentationID); +} + +void mitk::RelationStorage::AddLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return; + } + // retrieve a vector property that contains the valid lesion-IDs for the current case + VectorProperty::Pointer lesionsVectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); + std::vector lesionsVectorPropertyValue; + if (nullptr == lesionsVectorProperty) + { + lesionsVectorProperty = VectorProperty::New(); + } + else + { + lesionsVectorPropertyValue = lesionsVectorProperty->GetValue(); + } + + const auto& existingIndex = std::find(lesionsVectorPropertyValue.begin(), lesionsVectorPropertyValue.end(), lesion.UID); + if (existingIndex != lesionsVectorPropertyValue.end()) + { + return; + } + + // add the new lesion id from the given lesion to the vector of all current lesion IDs + lesionsVectorPropertyValue.push_back(lesion.UID); + // overwrite the current vector property with the new, extended string vector + lesionsVectorProperty->SetValue(lesionsVectorPropertyValue); + propertyList->SetProperty("lesions", lesionsVectorProperty); + + // add the lesion with the lesion UID as the key and the lesion information as value + std::vector lesionData; + lesionData.push_back(lesion.name); + lesionData.push_back(lesion.lesionClass.UID); + VectorProperty::Pointer newLesionVectorProperty = VectorProperty::New(); + newLesionVectorProperty->SetValue(lesionData); + propertyList->SetProperty(lesion.UID, newLesionVectorProperty); + + // add the lesion class with the lesion class UID as key and the class type as value + std::string lesionClassType = lesion.lesionClass.classType; + propertyList->SetStringProperty(lesion.lesionClass.UID.c_str(), lesionClassType.c_str()); +} + +void mitk::RelationStorage::OverwriteLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return; + } + // retrieve a vector property that contains the valid lesion-IDs for the current case + VectorProperty* lesionVectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); + if (nullptr == lesionVectorProperty) + { + MITK_DEBUG << "Could not find any lesion in the storage."; + return; + } + + std::vector lesionVectorPropertyValue = lesionVectorProperty->GetValue(); + const auto existingLesion = std::find(lesionVectorPropertyValue.begin(), lesionVectorPropertyValue.end(), lesion.UID); + if (existingLesion != lesionVectorPropertyValue.end()) + { + // overwrite the referenced lesion class UID with the new, given lesion class data + std::vector lesionData; + lesionData.push_back(lesion.name); + lesionData.push_back(lesion.lesionClass.UID); + VectorProperty::Pointer newLesionVectorProperty = VectorProperty::New(); + newLesionVectorProperty->SetValue(lesionData); + propertyList->SetProperty(lesion.UID, newLesionVectorProperty); + + // overwrite the lesion class with the lesion class UID as key and the new, given class type as value + std::string lesionClassType = lesion.lesionClass.classType; + propertyList->SetStringProperty(lesion.lesionClass.UID.c_str(), lesionClassType.c_str()); + } + else + { + MITK_DEBUG << "Could not find lesion " << lesion.UID << " in the storage. Cannot overwrite the lesion."; + } +} + +void mitk::RelationStorage::LinkSegmentationToLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID, const SemanticTypes::Lesion& lesion) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return; + } + // retrieve a vector property that contains the valid lesion-IDs for the current case + VectorProperty* lesionVectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); + if (nullptr == lesionVectorProperty) + { + MITK_DEBUG << "Could not find any lesion in the storage."; + return; + } + + std::vector lesionVectorPropertyValue = lesionVectorProperty->GetValue(); + const auto existingLesion = std::find(lesionVectorPropertyValue.begin(), lesionVectorPropertyValue.end(), lesion.UID); + if (existingLesion != lesionVectorPropertyValue.end()) + { + // set / overwrite the lesion reference of the given segmentation + // retrieve a vector property that contains the referenced ID of a segmentation (0. image ID 1. lesion ID) + VectorProperty* segmentationVectorProperty = dynamic_cast*>(propertyList->GetProperty(segmentationID)); + if (nullptr == segmentationVectorProperty) + { + MITK_DEBUG << "Could not find the segmentation " << segmentationID << " in the storage. Cannot link segmentation to lesion."; + return; + } + + std::vector segmentationVectorPropertyValue = segmentationVectorProperty->GetValue(); + if (segmentationVectorPropertyValue.size() != 2) + { + MITK_DEBUG << "Incorrect segmentation storage. Not two (2) IDs stored."; + return; + } + + // the lesion ID of a segmentation is the second value in the vector + segmentationVectorPropertyValue[1] = lesion.UID; + segmentationVectorProperty->SetValue(segmentationVectorPropertyValue); + return; + } + + MITK_DEBUG << "Could not find lesion " << lesion.UID << " in the storage. Cannot link segmentation to lesion."; +} + +void mitk::RelationStorage::UnlinkSegmentationFromLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return; + } + // retrieve a vector property that contains the referenced ID of a segmentation (0. image ID 1. lesion ID) + VectorProperty* segmentationVectorProperty = dynamic_cast*>(propertyList->GetProperty(segmentationID)); + if (nullptr == segmentationVectorProperty) + { + MITK_DEBUG << "Could not find the segmentation " << segmentationID << " in the storage. Cannot unlink lesion from segmentation."; + return; + } + + std::vector segmentationVectorPropertyValue = segmentationVectorProperty->GetValue(); + // a segmentation has to have exactly two values (the ID of the linked image and the ID of the lesion) + if (segmentationVectorPropertyValue.size() != 2) + { + MITK_DEBUG << "Incorrect data storage. Not two (2) values stored."; + return; + } + + // the second value of the segmentation vector is the ID of the referenced lesion + // set the lesion reference to an empty string for removal + segmentationVectorPropertyValue[1] = ""; + segmentationVectorProperty->SetValue(segmentationVectorPropertyValue); +} + +void mitk::RelationStorage::RemoveLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return; + } + // retrieve a vector property that contains the valid lesions of the current case + VectorProperty* lesionVectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); + if (nullptr == lesionVectorProperty) + { + MITK_DEBUG << "Could not find any lesion in the storage."; + return; + } + + // remove the lesion reference from the list of all lesions of the current case + std::vector lesionsVectorPropertyValue = lesionVectorProperty->GetValue(); + lesionsVectorPropertyValue.erase(std::remove(lesionsVectorPropertyValue.begin(), lesionsVectorPropertyValue.end(), lesion.UID), lesionsVectorPropertyValue.end()); + if (lesionsVectorPropertyValue.empty()) + { + // no more lesions stored -> remove the lesions property list + propertyList->DeleteProperty("lesions"); + } + else + { + // or store the modified vector value + lesionVectorProperty->SetValue(lesionsVectorPropertyValue); + } + + // remove the lesion instance itself + // the lesion data is stored under the lesion ID + VectorProperty* lesionDataProperty = dynamic_cast*>(propertyList->GetProperty(lesion.UID)); + if (nullptr == lesionDataProperty) + { + MITK_DEBUG << "Lesion " << lesion.UID << " not found (already removed?). Cannot remove the lesion."; + return; + } + + std::vector lesionData = lesionDataProperty->GetValue(); + // a lesion date has to have exactly two values (the name of the lesion and the UID of the lesion class) + if (lesionData.size() != 2) + { + MITK_DEBUG << "Incorrect lesion data storage. Not two (2) strings of the lesion UID and the lesion name are stored."; + } + else + { + std::string lesionClassID = lesionData[1]; + RemoveLesionClass(caseID, lesionClassID); + } + propertyList->DeleteProperty(lesion.UID); +} + +void mitk::RelationStorage::RemoveLesionClass(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& lesionClassID) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return; + } + + // retrieve a vector property that contains the lesion class + StringProperty* lesionClassProperty = dynamic_cast(propertyList->GetProperty(lesionClassID)); + if (nullptr == lesionClassProperty) + { + MITK_DEBUG << "Lesion class " << lesionClassID << " not found (already removed?). Cannot remove the lesion class."; + return; + } + + // retrieve a vector property that contains the valid lesions of the current case + VectorProperty* lesionVectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); + if (nullptr == lesionVectorProperty) + { + return; + } + + // check if the lesion class ID is referenced by any other lesion + std::vector lesionsVectorPropertyValue = lesionVectorProperty->GetValue(); + const auto existingLesionClass = std::find_if(lesionsVectorPropertyValue.begin(), lesionsVectorPropertyValue.end(), + [&propertyList, &lesionClassID](const std::string& lesionID) + { + VectorProperty* lesionDataProperty = dynamic_cast*>(propertyList->GetProperty(lesionID)); + if (nullptr == lesionDataProperty) + { + return false; + } + + std::vector lesionData = lesionDataProperty->GetValue(); + // a lesion date has to have exactly two values (the name of the lesion and the UID of the lesion class) + if (lesionData.size() != 2) + { + return false; + } + + return lesionData[1] == lesionClassID; + }); + + if (existingLesionClass == lesionsVectorPropertyValue.end()) + { + // lesion class ID not referenced; remove lesion class + propertyList->DeleteProperty(lesionClassID); + } +} + +void mitk::RelationStorage::AddControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return; + } + // retrieve a vector property that contains the valid controlPoint UIDs for the current case + VectorProperty::Pointer controlPointsVectorProperty = dynamic_cast*>(propertyList->GetProperty("controlpoints")); + std::vector controlPointsVectorPropertyValue; + if (nullptr == controlPointsVectorProperty) + { + controlPointsVectorProperty = VectorProperty::New(); + } + else + { + controlPointsVectorPropertyValue = controlPointsVectorProperty->GetValue(); + } + + const auto existingControlPoint = std::find(controlPointsVectorPropertyValue.begin(), controlPointsVectorPropertyValue.end(), controlPoint.UID); + if (existingControlPoint != controlPointsVectorPropertyValue.end()) + { + return; + } + + // add the new control point UID from the given control point to the vector of all current control point UIDs + controlPointsVectorPropertyValue.push_back(controlPoint.UID); + // overwrite the current vector property with the new, extended string vector + controlPointsVectorProperty->SetValue(controlPointsVectorPropertyValue); + propertyList->SetProperty("controlpoints", controlPointsVectorProperty); + + // store the control point values (the three integer values of a date) + std::vector controlPointDate; + controlPointDate.push_back(controlPoint.date.year()); + controlPointDate.push_back(controlPoint.date.month()); + controlPointDate.push_back(controlPoint.date.day()); + + VectorProperty::Pointer newControlPointVectorProperty = VectorProperty::New(); + newControlPointVectorProperty->SetValue(controlPointDate); + propertyList->SetProperty(controlPoint.UID, newControlPointVectorProperty); +} + +void mitk::RelationStorage::LinkImageToControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID, const SemanticTypes::ControlPoint& controlPoint) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return; + } + // retrieve a vector property that contains the valid controlPoint UIDs for the current case + VectorProperty* controlPointsVectorProperty = dynamic_cast*>(propertyList->GetProperty("controlpoints")); + if (nullptr == controlPointsVectorProperty) + { + MITK_DEBUG << "Could not find any control point in the storage."; + return; + } + + std::vector controlPointsVectorPropertyValue = controlPointsVectorProperty->GetValue(); + const auto existingControlPoint = std::find(controlPointsVectorPropertyValue.begin(), controlPointsVectorPropertyValue.end(), controlPoint.UID); + if (existingControlPoint != controlPointsVectorPropertyValue.end()) + { + // set / overwrite the control point reference of the given data + // retrieve a vector property that contains the referenced ID of a image (0. information type 1. control point ID) + VectorProperty* imageVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); + if (nullptr == imageVectorProperty) + { + MITK_DEBUG << "Could not find the image " << imageID << " in the storage. Cannot link data to control point."; + return; + } + + std::vector imageVectorPropertyValue = imageVectorProperty->GetValue(); + // an image has to have exactly two values (the information type and the ID of the control point) + if (imageVectorPropertyValue.size() != 2) + { + MITK_DEBUG << "Incorrect data storage. Not two (2) values stored."; + return; + } + + // the second value of the image vector is the ID of the referenced control point + imageVectorPropertyValue[1] = controlPoint.UID; + imageVectorProperty->SetValue(imageVectorPropertyValue); + return; + } + + MITK_DEBUG << "Could not find control point " << controlPoint.UID << " in the storage. Cannot link data to control point."; +} + +void mitk::RelationStorage::UnlinkImageFromControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return; + } + // retrieve a vector property that contains the referenced ID of a date (0. information type 1. control point ID) + VectorProperty* imageVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); + if (nullptr == imageVectorProperty) + { + MITK_DEBUG << "Could not find the date " << imageID << " in the storage. Cannot unlink control point from date."; + return; + } + + std::vector imageVectorPropertyValue = imageVectorProperty->GetValue(); + // an image has to have exactly two values (the information type and the ID of the control point) + if (imageVectorPropertyValue.size() != 2) + { + MITK_DEBUG << "Incorrect data storage. Not two (2) values stored."; + return; + } + + // the second value of the image vector is the ID of the referenced control point + // set the control point reference to an empty string for removal + imageVectorPropertyValue[1] = ""; + imageVectorProperty->SetValue(imageVectorPropertyValue); +} + +void mitk::RelationStorage::RemoveControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return; + } + // retrieve a vector property that contains the valid controlPoint UIDs for the current case + VectorProperty* controlPointsVectorProperty = dynamic_cast*>(propertyList->GetProperty("controlpoints")); + if (nullptr == controlPointsVectorProperty) + { + MITK_DEBUG << "Could not find any control point in the storage."; + return; + } + + // remove the control point reference from the list of all control points of the current case + std::vector controlPointsVectorPropertyValue = controlPointsVectorProperty->GetValue(); + controlPointsVectorPropertyValue.erase(std::remove(controlPointsVectorPropertyValue.begin(), controlPointsVectorPropertyValue.end(), controlPoint.UID), controlPointsVectorPropertyValue.end()); + if (controlPointsVectorPropertyValue.empty()) + { + // no more control points stored -> remove the control point property list + propertyList->DeleteProperty("controlpoints"); + } + else + { + // or store the modified vector value + controlPointsVectorProperty->SetValue(controlPointsVectorPropertyValue); + } + + // remove the control point instance itself + propertyList->DeleteProperty(controlPoint.UID); +} + +void mitk::RelationStorage::AddExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return; + } + // retrieve a vector property that contains the valid examination period UIDs for the current case + VectorProperty::Pointer examinationPeriodsVectorProperty = dynamic_cast*>(propertyList->GetProperty("examinationperiods")); + std::vector examinationPeriodsVectorPropertyValue; + if (nullptr == examinationPeriodsVectorProperty) + { + examinationPeriodsVectorProperty = VectorProperty::New(); + } + else + { + examinationPeriodsVectorPropertyValue = examinationPeriodsVectorProperty->GetValue(); + } + + const auto& existingIndex = std::find(examinationPeriodsVectorPropertyValue.begin(), examinationPeriodsVectorPropertyValue.end(), examinationPeriod.UID); + if (existingIndex != examinationPeriodsVectorPropertyValue.end()) + { + return; + } + + // add the new examination period id from the given examination period to the vector of all current examination period UIDs + examinationPeriodsVectorPropertyValue.push_back(examinationPeriod.UID); + // overwrite the current vector property with the new, extended string vector + examinationPeriodsVectorProperty->SetValue(examinationPeriodsVectorPropertyValue); + propertyList->SetProperty("examinationperiods", examinationPeriodsVectorProperty); + + // add the examination period with the UID as the key and the name as as the vector value + std::vector examinationPeriodData; + examinationPeriodData.push_back(examinationPeriod.name); + VectorProperty::Pointer newExaminationPeriodVectorProperty = VectorProperty::New(); + newExaminationPeriodVectorProperty->SetValue(examinationPeriodData); + propertyList->SetProperty(examinationPeriod.UID, newExaminationPeriodVectorProperty); +} + +void mitk::RelationStorage::AddControlPointToExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod& examinationPeriod) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return; + } + + // retrieve a vector property that contains the represented control point UIDs of the given examination period + VectorProperty* controlPointUIDsVectorProperty = dynamic_cast*>(propertyList->GetProperty(examinationPeriod.UID)); + if (nullptr == controlPointUIDsVectorProperty) + { + MITK_DEBUG << "Could not find the examination period " << examinationPeriod.UID << " in the storage. Cannot add the control point to the examination period."; + return; + } + + std::vector controlPointUIDsVectorPropertyValue = controlPointUIDsVectorProperty->GetValue(); + // store the control point UID + controlPointUIDsVectorPropertyValue.push_back(controlPoint.UID); + // sort the vector according to the date of the control points referenced by the UIDs + auto lambda = [&caseID](const SemanticTypes::ID& leftControlPointUID, const SemanticTypes::ID& rightControlPointUID) + { + const auto& leftControlPoint = GenerateControlpoint(caseID, leftControlPointUID); + const auto& rightControlPoint = GenerateControlpoint(caseID, rightControlPointUID); + + return leftControlPoint.date <= rightControlPoint.date; + }; + + std::sort(controlPointUIDsVectorPropertyValue.begin(), controlPointUIDsVectorPropertyValue.end(), lambda); + // store the modified and sorted control point UID vector of this examination period + controlPointUIDsVectorProperty->SetValue(controlPointUIDsVectorPropertyValue); +} + +void mitk::RelationStorage::RemoveControlPointFromExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod& examinationPeriod) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return; + } + + // retrieve a vector property that contains the represented control point UIDs of the given examination period + VectorProperty* controlPointUIDsVectorProperty = dynamic_cast*>(propertyList->GetProperty(examinationPeriod.UID)); + if (nullptr == controlPointUIDsVectorProperty) + { + MITK_DEBUG << "Could not find examination period " << examinationPeriod.UID << " in the storage. Cannot add the control point to the examination period."; + return; + } + + std::vector controlPointUIDsVectorPropertyValue = controlPointUIDsVectorProperty->GetValue(); + // an examination period has an arbitrary number of vector values (name and control point UIDs) (at least one for the name) + if (controlPointUIDsVectorPropertyValue.size() < 2) + { + MITK_DEBUG << "Incorrect examination period storage. At least one (1) control point ID has to be stored."; + return; + } + else + { + controlPointUIDsVectorPropertyValue.erase(std::remove(controlPointUIDsVectorPropertyValue.begin(), controlPointUIDsVectorPropertyValue.end(), controlPoint.UID), controlPointUIDsVectorPropertyValue.end()); + if (controlPointUIDsVectorPropertyValue.size() < 2) + { + RemoveExaminationPeriod(caseID, examinationPeriod); + } + else + { + // store the modified vector value + controlPointUIDsVectorProperty->SetValue(controlPointUIDsVectorPropertyValue); + } + } +} + +void mitk::RelationStorage::RemoveExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return; + } + // retrieve a vector property that contains the valid examination period UIDs for the current case + VectorProperty::Pointer examinationPeriodsVectorProperty = dynamic_cast*>(propertyList->GetProperty("examinationperiods")); + if (nullptr == examinationPeriodsVectorProperty) + { + MITK_DEBUG << "Could not find any examination periods in the storage."; + return; + } + + std::vector examinationPeriodsVectorPropertyValue = examinationPeriodsVectorProperty->GetValue(); + examinationPeriodsVectorPropertyValue.erase(std::remove(examinationPeriodsVectorPropertyValue.begin(), examinationPeriodsVectorPropertyValue.end(), examinationPeriod.UID), examinationPeriodsVectorPropertyValue.end()); + if (examinationPeriodsVectorPropertyValue.empty()) + { + // no more examination periods stored -> remove the examination period property list + propertyList->DeleteProperty("examinationperiods"); + } + else + { + // or store the modified vector value + examinationPeriodsVectorProperty->SetValue(examinationPeriodsVectorPropertyValue); + } + + // remove the examination period instance itself + propertyList->DeleteProperty(examinationPeriod.UID); +} + +void mitk::RelationStorage::AddInformationTypeToImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID, const SemanticTypes::InformationType& informationType) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return; + } + // retrieve a vector property that contains the valid information types of the current case + VectorProperty::Pointer informationTypesVectorProperty = dynamic_cast*>(propertyList->GetProperty("informationtypes")); + std::vector informationTypesVectorPropertyValue; + if (nullptr == informationTypesVectorProperty) + { + informationTypesVectorProperty = VectorProperty::New(); + } + else + { + informationTypesVectorPropertyValue = informationTypesVectorProperty->GetValue(); + } + + const auto existingInformationType = std::find(informationTypesVectorPropertyValue.begin(), informationTypesVectorPropertyValue.end(), informationType); + if (existingInformationType == informationTypesVectorPropertyValue.end()) + { + // at first: add the information type to the storage + informationTypesVectorPropertyValue.push_back(informationType); + informationTypesVectorProperty->SetValue(informationTypesVectorPropertyValue); + propertyList->SetProperty("informationtypes", informationTypesVectorProperty); + } + + // set / overwrite the information type of the given data + // retrieve a vector property that contains the referenced ID of an image (0. information type 1. control point ID) + VectorProperty* imageVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); + if (nullptr == imageVectorProperty) + { + MITK_DEBUG << "Could not find the image " << imageID << " in the storage. Cannot add information type to image."; + return; + } + + std::vector imageVectorPropertyValue = imageVectorProperty->GetValue(); + // an image has to have exactly two values (the information type and the ID of the control point) + if (imageVectorPropertyValue.size() != 2) + { + MITK_DEBUG << "Incorrect data storage. Not two (2) values stored."; + return; + } + + // the first value of the image vector is the information type + imageVectorPropertyValue[0] = informationType; + imageVectorProperty->SetValue(imageVectorPropertyValue); +} + +void mitk::RelationStorage::RemoveInformationTypeFromImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return; + } + // retrieve a vector property that contains the referenced ID of an image (0. information type 1. control point ID) + VectorProperty* imageVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); + if (nullptr == imageVectorProperty) + { + MITK_DEBUG << "Could not find the image " << imageID << " in the storage. Cannot remove information type from image."; + return; + } + + std::vector imageVectorPropertyValue = imageVectorProperty->GetValue(); + // an image has to have exactly two values (the information type and the ID of the control point) + if (imageVectorPropertyValue.size() != 2) + { + MITK_DEBUG << "Incorrect data storage. Not two (2) values stored."; + return; + } + + // the first value of the image vector is the information type + // set the information type to an empty string for removal + imageVectorPropertyValue[0] = ""; + imageVectorProperty->SetValue(imageVectorPropertyValue); +} + +void mitk::RelationStorage::RemoveInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return; + } + // retrieve a vector property that contains the valid information types of the current case + VectorProperty* informationTypesVectorProperty = dynamic_cast*>(propertyList->GetProperty("informationtypes")); + if (nullptr == informationTypesVectorProperty) + { + MITK_DEBUG << "Could not find any information type in the storage."; + return; + } + + std::vector informationTypesVectorPropertyValue = informationTypesVectorProperty->GetValue(); + informationTypesVectorPropertyValue.erase(std::remove(informationTypesVectorPropertyValue.begin(), informationTypesVectorPropertyValue.end(), informationType), informationTypesVectorPropertyValue.end()); + if (informationTypesVectorPropertyValue.empty()) + { + // no more information types stored -> remove the information types property list + propertyList->DeleteProperty("informationtypes"); + } + else + { + // or store the modified vector value + informationTypesVectorProperty->SetValue(informationTypesVectorPropertyValue); + } +} diff --git a/Modules/SemanticRelations/src/mitkSemanticRelationsDataStorageAccess.cpp b/Modules/SemanticRelations/src/mitkSemanticRelationsDataStorageAccess.cpp new file mode 100644 index 0000000000..b02e89d69e --- /dev/null +++ b/Modules/SemanticRelations/src/mitkSemanticRelationsDataStorageAccess.cpp @@ -0,0 +1,218 @@ +/*=================================================================== + +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 "mitkSemanticRelationsDataStorageAccess.h" + +// semantic relations module +#include "mitkControlPointManager.h" +#include "mitkDICOMHelper.h" +#include "mitkNodePredicates.h" +#include "mitkRelationStorage.h" +#include "mitkSemanticRelationException.h" +#include "mitkSemanticRelationsInference.h" + +// c++ +#include +#include + +mitk::SemanticRelationsDataStorageAccess::SemanticRelationsDataStorageAccess(DataStorage* dataStorage) + : m_DataStorage(dataStorage) +{ + // nothing here +} + +mitk::SemanticRelationsDataStorageAccess::~SemanticRelationsDataStorageAccess() +{ + // nothing here +} + +/************************************************************************/ +/* functions to get instances / attributes */ +/************************************************************************/ + +mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllSegmentationsOfCase(const SemanticTypes::CaseID& caseID) const +{ + if (m_DataStorage.IsExpired()) + { + mitkThrowException(SemanticRelationException) << "Not a valid data storage."; + } + + std::vector allSegmentationIDsOfCase = RelationStorage::GetAllSegmentationIDsOfCase(caseID); + std::vector allSegmentationsOfCase; + // get all segmentation nodes of the current data storage + // only those nodes are respected, that are currently held in the data storage + DataStorage::SetOfObjects::ConstPointer segmentationNodes = m_DataStorage.Lock()->GetSubset(NodePredicates::GetSegmentationPredicate()); + for (auto it = segmentationNodes->Begin(); it != segmentationNodes->End(); ++it) + { + DataNode* segmentationNode = it->Value(); + try + { + // find the corresponding segmentation node for the given segmentation ID + std::string nodeCaseID = GetCaseIDFromDataNode(segmentationNode); + std::string nodeSegmentationID = GetIDFromDataNode(segmentationNode); + if (nodeCaseID == caseID && (std::find(allSegmentationIDsOfCase.begin(), allSegmentationIDsOfCase.end(), nodeSegmentationID) != allSegmentationIDsOfCase.end())) + { + // found current image node in the storage, add it to the return vector + allSegmentationsOfCase.push_back(segmentationNode); + } + } + catch (const std::exception&) + { + // found a segmentation node that is not stored in the semantic relations + // this segmentation node does not have any DICOM information --> exception thrown + // continue with the next segmentation to compare IDs + continue; + } + } + + return allSegmentationsOfCase; +} + +mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllSegmentationsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const +{ + if (SemanticRelationsInference::InstanceExists(caseID, lesion)) + { + // lesion exists, retrieve all case segmentations from the storage + DataNodeVector allSegmentationsOfLesion = GetAllSegmentationsOfCase(caseID); + + // filter all segmentations: check for semantic relation with the given lesion using a lambda function + auto lambda = [&lesion, this](DataNode::Pointer segmentation) + { + try + { + SemanticTypes::Lesion representedLesion = SemanticRelationsInference::GetLesionOfSegmentation(segmentation); + return lesion.UID != representedLesion.UID; + } + catch (const SemanticRelationException&) + { + return true; + } + }; + allSegmentationsOfLesion.erase(std::remove_if(allSegmentationsOfLesion.begin(), allSegmentationsOfLesion.end(), lambda), allSegmentationsOfLesion.end()); + + return allSegmentationsOfLesion; + } + else + { + mitkThrowException(SemanticRelationException) << "Could not find an existing lesion instance for the given caseID " << caseID << " and lesion " << lesion.UID << "."; + } +} + +mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllImagesOfCase(const SemanticTypes::CaseID& caseID) const +{ + if (m_DataStorage.IsExpired()) + { + mitkThrowException(SemanticRelationException) << "Not a valid data storage."; + } + + std::vector allImageIDsOfCase = RelationStorage::GetAllImageIDsOfCase(caseID); + std::vector allImagesOfCase; + // get all image nodes of the current data storage + // only those nodes are respected, that are currently held in the data storage + DataStorage::SetOfObjects::ConstPointer imageNodes = m_DataStorage.Lock()->GetSubset(NodePredicates::GetImagePredicate()); + for (auto it = imageNodes->Begin(); it != imageNodes->End(); ++it) + { + DataNode* imageNode = it->Value(); + // find the corresponding image node for the given segmentation ID + std::string nodeCaseID = GetCaseIDFromDataNode(imageNode); + std::string nodeImageID = GetIDFromDataNode(imageNode); + if (nodeCaseID == caseID && (std::find(allImageIDsOfCase.begin(), allImageIDsOfCase.end(), nodeImageID) != allImageIDsOfCase.end())) + { + // found current image node in the storage, add it to the return vector + allImagesOfCase.push_back(imageNode); + } + } + + return allImagesOfCase; +} + +mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllImagesOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const +{ + if (m_DataStorage.IsExpired()) + { + mitkThrowException(SemanticRelationException) << "Not a valid data storage."; + } + + DataNodeVector allImagesOfLesion; + // 1. get all segmentations that define the lesion + // 2. retrieve the parent node (source) of the found segmentation node + DataNodeVector allSegmentationsOfLesion = GetAllSegmentationsOfLesion(caseID, lesion); + for (const auto& segmentationNode : allSegmentationsOfLesion) + { + // get parent node of the current segmentation node with the node predicate + DataStorage::SetOfObjects::ConstPointer parentNodes = m_DataStorage.Lock()->GetSources(segmentationNode, NodePredicates::GetImagePredicate(), false); + for (auto it = parentNodes->Begin(); it != parentNodes->End(); ++it) + { + DataNode::Pointer dataNode = it->Value(); + allImagesOfLesion.push_back(it->Value()); + } + } + + std::sort(allImagesOfLesion.begin(), allImagesOfLesion.end()); + allImagesOfLesion.erase(std::unique(allImagesOfLesion.begin(), allImagesOfLesion.end()), allImagesOfLesion.end()); + return allImagesOfLesion; +} + +mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllSpecificImages(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const +{ + if (SemanticRelationsInference::InstanceExists(caseID, controlPoint)) + { + if (SemanticRelationsInference::InstanceExists(caseID, informationType)) + { + // control point exists, information type exists, retrieve all images from the storage + DataNodeVector allImagesOfCase = GetAllImagesOfCase(caseID); + // filter all images to remove the ones with a different control point and information type using a lambda function + auto lambda = [&controlPoint, &informationType, this](DataNode::Pointer imageNode) + { + return (informationType != SemanticRelationsInference::GetInformationTypeOfImage(imageNode)) + || (controlPoint.date != SemanticRelationsInference::GetControlPointOfImage(imageNode).date); + }; + + allImagesOfCase.erase(std::remove_if(allImagesOfCase.begin(), allImagesOfCase.end(), lambda), allImagesOfCase.end()); + + return allImagesOfCase; + } + else + { + mitkThrowException(SemanticRelationException) << "Could not find an existing information type for the given caseID " << caseID << " and information type " << informationType; + } + } + else + { + mitkThrowException(SemanticRelationException) << "Could not find an existing control point for the given caseID " << caseID << " and control point " << controlPoint.UID; + } +} + +mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllSpecificSegmentations(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const +{ + if (m_DataStorage.IsExpired()) + { + mitkThrow() << "Not a valid data storage."; + } + + DataNodeVector allSpecificImages = GetAllSpecificImages(caseID, controlPoint, informationType); + DataNodeVector allSpecificSegmentations; + for (const auto& imageNode : allSpecificImages) + { + DataStorage::SetOfObjects::ConstPointer segmentationNodes = m_DataStorage.Lock()->GetDerivations(imageNode, NodePredicates::GetSegmentationPredicate(), false); + for (auto it = segmentationNodes->Begin(); it != segmentationNodes->End(); ++it) + { + allSpecificSegmentations.push_back(it->Value()); + } + } + + return allSpecificSegmentations; +} diff --git a/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp b/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp new file mode 100644 index 0000000000..f8919d2c8d --- /dev/null +++ b/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp @@ -0,0 +1,479 @@ +/*=================================================================== + +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 "mitkSemanticRelationsInference.h" + +// semantic relations module +#include "mitkControlPointManager.h" +#include "mitkDICOMHelper.h" +#include "mitkNodePredicates.h" +#include "mitkRelationStorage.h" +#include "mitkSemanticRelationException.h" + +/************************************************************************/ +/* functions to get instances / attributes */ +/************************************************************************/ + +mitk::SemanticTypes::LesionClassVector mitk::SemanticRelationsInference::GetAllLesionClassesOfCase(const SemanticTypes::CaseID& caseID) +{ + SemanticTypes::LesionVector allLesionsOfCase = RelationStorage::GetAllLesionsOfCase(caseID); + SemanticTypes::LesionClassVector allLesionClassesOfCase; + + for (const auto& lesion : allLesionsOfCase) + { + allLesionClassesOfCase.push_back(lesion.lesionClass); + } + + // remove duplicate entries + auto lessThan = [](const SemanticTypes::LesionClass& lesionClassLeft, const SemanticTypes::LesionClass& lesionClassRight) + { + return lesionClassLeft.UID < lesionClassRight.UID; + }; + + auto equal = [](const SemanticTypes::LesionClass& lesionClassLeft, const SemanticTypes::LesionClass& lesionClassRight) + { + return lesionClassLeft.UID == lesionClassRight.UID; + }; + + std::sort(allLesionClassesOfCase.begin(), allLesionClassesOfCase.end(), lessThan); + allLesionClassesOfCase.erase(std::unique(allLesionClassesOfCase.begin(), allLesionClassesOfCase.end(), equal), allLesionClassesOfCase.end()); + + return allLesionClassesOfCase; +} + +mitk::SemanticTypes::Lesion mitk::SemanticRelationsInference::GetLesionOfSegmentation(const DataNode* segmentationNode) +{ + if (nullptr == segmentationNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; + } + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); + SemanticTypes::ID segmentationID = GetIDFromDataNode(segmentationNode); + return RelationStorage::GetLesionOfSegmentation(caseID, segmentationID); +} + +mitk::SemanticTypes::LesionVector mitk::SemanticRelationsInference::GetAllLesionsOfImage(const DataNode* imageNode) +{ + if (nullptr == imageNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid image data node."; + } + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); + SemanticTypes::ID imageID = GetIDFromDataNode(imageNode); + + SemanticTypes::LesionVector allLesionsOfImage; + // 1. get all segmentations that are connected to the given image + // 2. get the lesion of each segmentation + // 3. guarantee uniqueness of lesions + SemanticTypes::IDVector allSegmentationIDsOfImage = RelationStorage::GetAllSegmentationIDsOfImage(caseID, imageID); + for (const auto& segmentationID : allSegmentationIDsOfImage) + { + // get represented lesion of the current segmentation + SemanticTypes::Lesion representedLesion = RelationStorage::GetLesionOfSegmentation(caseID, segmentationID); + if (!representedLesion.UID.empty()) + { + allLesionsOfImage.push_back(representedLesion); + } + } + + // remove duplicate entries + auto lessThan = [](const SemanticTypes::Lesion& lesionLeft, const SemanticTypes::Lesion& lesionRight) + { + return lesionLeft.UID < lesionRight.UID; + }; + + auto equal = [](const SemanticTypes::Lesion& lesionLeft, const SemanticTypes::Lesion& lesionRight) + { + return lesionLeft.UID == lesionRight.UID; + }; + + std::sort(allLesionsOfImage.begin(), allLesionsOfImage.end(), lessThan); + allLesionsOfImage.erase(std::unique(allLesionsOfImage.begin(), allLesionsOfImage.end(), equal), allLesionsOfImage.end()); + + return allLesionsOfImage; +} + +mitk::SemanticTypes::LesionVector mitk::SemanticRelationsInference::GetAllLesionsOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) +{ + SemanticTypes::LesionVector allLesions = RelationStorage::GetAllLesionsOfCase(caseID); + + // filter the lesions: use only those, where the associated data is connected to image data that refers to the given control point using a lambda function + auto lambda = [&caseID, &controlPoint](const SemanticTypes::Lesion& lesion) + { + return !SpecificImageExists(caseID, lesion, controlPoint); + }; + + allLesions.erase(std::remove_if(allLesions.begin(), allLesions.end(), lambda), allLesions.end()); + + return allLesions; +} + +bool mitk::SemanticRelationsInference::IsRepresentingALesion(const DataNode* segmentationNode) +{ + try + { + SemanticTypes::Lesion representedLesion = GetLesionOfSegmentation(segmentationNode); + if (representedLesion.UID.empty()) + { + return false; + } + } + catch (const Exception&) + { + return false; + } + + return true; +} + +bool mitk::SemanticRelationsInference::IsRepresentingALesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID) +{ + SemanticTypes::Lesion representedLesion = RelationStorage::GetLesionOfSegmentation(caseID, segmentationID); + if (representedLesion.UID.empty()) + { + return false; + } + + return true; +} + +bool mitk::SemanticRelationsInference::IsLesionPresent(const SemanticTypes::Lesion& lesion, const DataNode* dataNode) +{ + try + { + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); + SemanticTypes::ID dataNodeID = GetIDFromDataNode(dataNode); + + if (NodePredicates::GetImagePredicate()->CheckNode(dataNode)) + { + return IsLesionPresentOnImage(caseID, lesion, dataNodeID); + } + + if (NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) + { + return IsLesionPresentOnSegmentation(caseID, lesion, dataNodeID); + } + + return false; + } + catch (const SemanticRelationException&) + { + return false; + } +} + +bool mitk::SemanticRelationsInference::IsLesionPresentOnImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ID& imageID) +{ + SemanticTypes::IDVector allImageIDsOfLesion = GetAllImageIDsOfLesion(caseID, lesion); + for (const auto& imageIDOfLesion : allImageIDsOfLesion) + { + if (imageIDOfLesion == imageID) + { + return true; + } + } + + return false; +} + +bool mitk::SemanticRelationsInference::IsLesionPresentOnSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ID& segmentationID) +{ + const auto representedLesion = RelationStorage::GetLesionOfSegmentation(caseID, segmentationID); + return lesion.UID == representedLesion.UID; +} + +bool mitk::SemanticRelationsInference::IsLesionPresentAtControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ControlPoint& controlPoint) +{ + SemanticTypes::IDVector allImageIDsOfLesion = GetAllImageIDsOfLesion(caseID, lesion); + for (const auto& imageID : allImageIDsOfLesion) + { + auto imageControlPoint = mitk::RelationStorage::GetControlPointOfImage(caseID, imageID); + if (imageControlPoint.date == controlPoint.date) + { + return true; + } + } + + return false; +} + +bool mitk::SemanticRelationsInference::InstanceExists(const DataNode* dataNode) +{ + try + { + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); + SemanticTypes::ID dataNodeID = GetIDFromDataNode(dataNode); + + if (NodePredicates::GetImagePredicate()->CheckNode(dataNode)) + { + std::vector allImageIDsOfCase = RelationStorage::GetAllImageIDsOfCase(caseID); + return std::find(allImageIDsOfCase.begin(), allImageIDsOfCase.end(), dataNodeID) != allImageIDsOfCase.end(); + } + else if (NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) + { + std::vector allSegmentationIDsOfCase = RelationStorage::GetAllSegmentationIDsOfCase(caseID); + return std::find(allSegmentationIDsOfCase.begin(), allSegmentationIDsOfCase.end(), dataNodeID) != allSegmentationIDsOfCase.end(); + } + else + { + return false; + } + } + catch (const SemanticRelationException&) + { + return false; + } +} + +bool mitk::SemanticRelationsInference::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) +{ + SemanticTypes::LesionVector allLesions = RelationStorage::GetAllLesionsOfCase(caseID); + + // filter all lesions: check for equality with the given lesion using a lambda function + auto lambda = [&lesion](const SemanticTypes::Lesion& currentLesion) { return currentLesion.UID == lesion.UID; }; + const auto existingLesion = std::find_if(allLesions.begin(), allLesions.end(), lambda); + + if (existingLesion != allLesions.end()) + { + return true; + } + else + { + return false; + } +} + +mitk::SemanticTypes::IDVector mitk::SemanticRelationsInference::GetAllImageIDsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) +{ + if (!InstanceExists(caseID, lesion)) + { + mitkThrowException(SemanticRelationException) << "Could not find an existing lesion instance for the given caseID " << caseID << " and lesion " << lesion.UID << "."; + } + + SemanticTypes::IDVector allImageIDsOfLesion; + // 1. get all segmentations that define the lesion + // 2. get the parentID (imageID) of each segmentation + // 3. guarantee uniqueness of image IDs + SemanticTypes::IDVector allSegmentationIDsOfLesion = RelationStorage::GetAllSegmentationIDsOfLesion(caseID, lesion); + for (const auto& segmentationID : allSegmentationIDsOfLesion) + { + // get parent ID of the current segmentation ID + SemanticTypes::ID imageID = RelationStorage::GetImageIDOfSegmentation(caseID, segmentationID); + if(!imageID.empty()) + { + allImageIDsOfLesion.push_back(imageID); + } + } + + std::sort(allImageIDsOfLesion.begin(), allImageIDsOfLesion.end()); + allImageIDsOfLesion.erase(std::unique(allImageIDsOfLesion.begin(), allImageIDsOfLesion.end()), allImageIDsOfLesion.end()); + + return allImageIDsOfLesion; +} + +mitk::SemanticTypes::ControlPoint mitk::SemanticRelationsInference::GetControlPointOfImage(const DataNode* imageNode) +{ + if (nullptr == imageNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid data node."; + } + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); + SemanticTypes::ID dataNodeID = GetIDFromDataNode(imageNode); + return RelationStorage::GetControlPointOfImage(caseID, dataNodeID); +} + +mitk::SemanticTypes::ControlPointVector mitk::SemanticRelationsInference::GetAllControlPointsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) +{ + SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); + + // filter the control points: use only those, where the associated image data has a segmentation that refers to the given lesion using a lambda function + auto lambda = [&caseID, &lesion](const SemanticTypes::ControlPoint& controlPoint) + { + return !SpecificImageExists(caseID, lesion, controlPoint); + }; + + allControlPoints.erase(std::remove_if(allControlPoints.begin(), allControlPoints.end(), lambda), allControlPoints.end()); + + return allControlPoints; +} + +mitk::SemanticTypes::ControlPointVector mitk::SemanticRelationsInference::GetAllControlPointsOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) +{ + SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); + + // filter the control points: use only those, where the associated image data refers to the given information type using a lambda function + auto lambda = [&caseID, &informationType](const SemanticTypes::ControlPoint& controlPoint) + { + return !SpecificImageExists(caseID, informationType, controlPoint); + }; + + allControlPoints.erase(std::remove_if(allControlPoints.begin(), allControlPoints.end(), lambda), allControlPoints.end()); + + return allControlPoints; +} + +bool mitk::SemanticRelationsInference::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) +{ + SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); + + // filter all control points: check for equality with the given control point using a lambda function + auto lambda = [&controlPoint](const SemanticTypes::ControlPoint& currentControlPoint) { return currentControlPoint.UID == controlPoint.UID; }; + const auto existingControlPoint = std::find_if(allControlPoints.begin(), allControlPoints.end(), lambda); + + if (existingControlPoint != allControlPoints.end()) + { + return true; + } + else + { + return false; + } +} + +bool mitk::SemanticRelationsInference::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) +{ + SemanticTypes::ExaminationPeriodVector allExaminationPeriods = RelationStorage::GetAllExaminationPeriodsOfCase(caseID); + + // filter all examination periods: check for equality with the given examination period using a lambda function + auto lambda = [&examinationPeriod](const SemanticTypes::ExaminationPeriod& currentExaminationPeriod) { return currentExaminationPeriod.UID == examinationPeriod.UID; }; + const auto existingExaminationPeriod = std::find_if(allExaminationPeriods.begin(), allExaminationPeriods.end(), lambda); + + if (existingExaminationPeriod != allExaminationPeriods.end()) + { + return true; + } + else + { + return false; + } +} + +mitk::SemanticTypes::InformationType mitk::SemanticRelationsInference::GetInformationTypeOfImage(const DataNode* imageNode) +{ + if (nullptr == imageNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid image data node."; + } + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); + SemanticTypes::ID imageID = GetIDFromDataNode(imageNode); + return RelationStorage::GetInformationTypeOfImage(caseID, imageID); +} + +mitk::SemanticTypes::InformationTypeVector mitk::SemanticRelationsInference::GetAllInformationTypesOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) +{ + SemanticTypes::InformationTypeVector allInformationTypes = RelationStorage::GetAllInformationTypesOfCase(caseID); + + // filter the information types: use only those, where the associated data refers to the given control point using a lambda function + auto lambda = [&caseID, &controlPoint](const SemanticTypes::InformationType& informationType) + { + return !SpecificImageExists(caseID, informationType, controlPoint); + }; + + allInformationTypes.erase(std::remove_if(allInformationTypes.begin(), allInformationTypes.end(), lambda), allInformationTypes.end()); + + return allInformationTypes; +} + +bool mitk::SemanticRelationsInference::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) +{ + SemanticTypes::InformationTypeVector allInformationTypes = RelationStorage::GetAllInformationTypesOfCase(caseID); + + // filter all information types: check for equality with the given information type using a lambda function + auto lambda = [&informationType](const SemanticTypes::InformationType& currentInformationType) { return currentInformationType == informationType; }; + const auto existingInformationType = std::find_if(allInformationTypes.begin(), allInformationTypes.end(), lambda); + + if (existingInformationType != allInformationTypes.end()) + { + return true; + } + else + { + return false; + } +} + +bool mitk::SemanticRelationsInference::SpecificImageExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::InformationType& informationType) +{ + SemanticTypes::IDVector allImageIDsOfLesion; + try + { + allImageIDsOfLesion = GetAllImageIDsOfLesion(caseID, lesion); + } + catch (const SemanticRelationException&) + { + return false; + } + + SemanticTypes::IDVector allImageIDsOfInformationType = RelationStorage::GetAllImageIDsOfInformationType(caseID, informationType); + + std::sort(allImageIDsOfLesion.begin(), allImageIDsOfLesion.end()); + std::sort(allImageIDsOfInformationType.begin(), allImageIDsOfInformationType.end()); + SemanticTypes::IDVector allImageIDsIntersection; + // set_intersection removes duplicated nodes, since 'GetAllImageIDsOfInformationType' only contains at most one of each node + std::set_intersection(allImageIDsOfLesion.begin(), allImageIDsOfLesion.end(), + allImageIDsOfInformationType.begin(), allImageIDsOfInformationType.end(), + std::back_inserter(allImageIDsIntersection)); + + // if the vector of intersecting image IDs is empty, the information type does not contain the lesion + return !allImageIDsIntersection.empty(); +} + +bool mitk::SemanticRelationsInference::SpecificImageExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ControlPoint& controlPoint) +{ + SemanticTypes::IDVector allImageIDsOfLesion; + try + { + allImageIDsOfLesion = GetAllImageIDsOfLesion(caseID, lesion); + } + catch (const SemanticRelationException&) + { + return false; + } + + SemanticTypes::IDVector allImageIDsOfControlPoint = RelationStorage::GetAllImageIDsOfControlPoint(caseID, controlPoint); + + std::sort(allImageIDsOfLesion.begin(), allImageIDsOfLesion.end()); + std::sort(allImageIDsOfControlPoint.begin(), allImageIDsOfControlPoint.end()); + SemanticTypes::IDVector allImageIDsIntersection; + // set_intersection removes duplicated nodes, since 'GetAllImageIDsOfControlPoint' only contains at most one of each node + std::set_intersection(allImageIDsOfLesion.begin(), allImageIDsOfLesion.end(), + allImageIDsOfControlPoint.begin(), allImageIDsOfControlPoint.end(), + std::back_inserter(allImageIDsIntersection)); + + // if the vector of intersecting image IDs is empty, the control point does not contain the lesion + return !allImageIDsIntersection.empty(); +} + +bool mitk::SemanticRelationsInference::SpecificImageExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType, const SemanticTypes::ControlPoint& controlPoint) +{ + SemanticTypes::IDVector allImageIDsOfInformationType = RelationStorage::GetAllImageIDsOfInformationType(caseID, informationType); + SemanticTypes::IDVector allImageIDsOfControlPoint = RelationStorage::GetAllImageIDsOfControlPoint(caseID, controlPoint); + + std::sort(allImageIDsOfInformationType.begin(), allImageIDsOfInformationType.end()); + std::sort(allImageIDsOfControlPoint.begin(), allImageIDsOfControlPoint.end()); + SemanticTypes::IDVector allImageIDsIntersection; + // set_intersection removes duplicated nodes + std::set_intersection(allImageIDsOfInformationType.begin(), allImageIDsOfInformationType.end(), + allImageIDsOfControlPoint.begin(), allImageIDsOfControlPoint.end(), + std::back_inserter(allImageIDsIntersection)); + + // if the vector of intersecting image IDs is empty no image exists for the given information type and control point + return !allImageIDsIntersection.empty(); +} diff --git a/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp b/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp new file mode 100644 index 0000000000..4aad423f13 --- /dev/null +++ b/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp @@ -0,0 +1,442 @@ +/*=================================================================== + +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 "mitkSemanticRelationsIntegration.h" + +// semantic relations module +#include "mitkControlPointManager.h" +#include "mitkDICOMHelper.h" +#include "mitkNodePredicates.h" +#include "mitkRelationStorage.h" +#include "mitkSemanticRelationException.h" +#include "mitkSemanticRelationsInference.h" +#include "mitkUIDGeneratorBoost.h" + +// multi label module +#include + +// c++ +#include +#include + +std::vector mitk::SemanticRelationsIntegration::m_ObserverVector; + +void mitk::SemanticRelationsIntegration::AddObserver(ISemanticRelationsObserver* observer) +{ + std::vector::iterator existingObserver = std::find(m_ObserverVector.begin(), m_ObserverVector.end(), observer); + if (existingObserver != m_ObserverVector.end()) + { + // no need to add the already existing observer + return; + } + + m_ObserverVector.push_back(observer); +} + +void mitk::SemanticRelationsIntegration::RemoveObserver(ISemanticRelationsObserver* observer) +{ + m_ObserverVector.erase(std::remove(m_ObserverVector.begin(), m_ObserverVector.end(), observer), m_ObserverVector.end()); +} + +/************************************************************************/ +/* functions to add / remove instances / attributes */ +/************************************************************************/ + +void mitk::SemanticRelationsIntegration::AddImage(const DataNode* imageNode) +{ + if (nullptr == imageNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid image data node."; + } + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); + SemanticTypes::ID nodeID = GetIDFromDataNode(imageNode); + + RelationStorage::AddCase(caseID); + RelationStorage::AddImage(caseID, nodeID); + + SemanticTypes::InformationType informationType = GetDICOMModalityFromDataNode(imageNode); + AddInformationTypeToImage(imageNode, informationType); + + // set the correct control point for this image + SemanticTypes::ControlPoint controlPoint = GenerateControlPoint(imageNode); + SetControlPointOfImage(imageNode, controlPoint); +} + +void mitk::SemanticRelationsIntegration::RemoveImage(const DataNode* imageNode) +{ + if (nullptr == imageNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid image data node."; + } + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); + SemanticTypes::ID nodeID = GetIDFromDataNode(imageNode); + + RemoveInformationTypeFromImage(imageNode); + UnlinkImageFromControlPoint(imageNode); + RelationStorage::RemoveImage(caseID, nodeID); + NotifyObserver(caseID); +} + +void mitk::SemanticRelationsIntegration::AddLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) +{ + if (SemanticRelationsInference::InstanceExists(caseID, lesion)) + { + mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to add already exists for the given case."; + } + else + { + RelationStorage::AddLesion(caseID, lesion); + NotifyObserver(caseID); + } +} + +void mitk::SemanticRelationsIntegration::OverwriteLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) +{ + if (SemanticRelationsInference::InstanceExists(caseID, lesion)) + { + RelationStorage::OverwriteLesion(caseID, lesion); + NotifyObserver(caseID); + } + else + { + mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to overwrite does not exist for the given case."; + } +} + +void mitk::SemanticRelationsIntegration::AddLesionAndLinkSegmentation(const DataNode* segmentationNode, const SemanticTypes::Lesion& lesion) +{ + if (nullptr == segmentationNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; + } + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); + AddLesion(caseID, lesion); + LinkSegmentationToLesion(segmentationNode, lesion); + NotifyObserver(caseID); +} + +void mitk::SemanticRelationsIntegration::RemoveLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) +{ + if (SemanticRelationsInference::InstanceExists(caseID, lesion)) + { + SemanticTypes::IDVector allSegmentationIDsOfLesion = RelationStorage::GetAllSegmentationIDsOfLesion(caseID, lesion); + if (allSegmentationIDsOfLesion.empty()) + { + // no more segmentations are linked to the specific lesion + // the lesion can be removed from the storage + RelationStorage::RemoveLesion(caseID, lesion); + NotifyObserver(caseID); + } + else + { + mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to remove is still referred to by a segmentation node. Lesion will not be removed."; + } + } + else + { + mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to remove does not exist for the given case."; + } +} + +void mitk::SemanticRelationsIntegration::AddSegmentation(const DataNode* segmentationNode, const DataNode* parentNode) +{ + if (nullptr == segmentationNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; + } + + if (nullptr == parentNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid parent data node."; + } + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); + SemanticTypes::ID segmentationNodeID = GetIDFromDataNode(segmentationNode); + SemanticTypes::ID parentNodeID = GetIDFromDataNode(parentNode); + + RelationStorage::AddSegmentation(caseID, segmentationNodeID, parentNodeID); + NotifyObserver(caseID); +} + +void mitk::SemanticRelationsIntegration::LinkSegmentationToLesion(const DataNode* segmentationNode, const SemanticTypes::Lesion& lesion) +{ + if (nullptr == segmentationNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; + } + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); + if (SemanticRelationsInference::InstanceExists(caseID, lesion)) + { + SemanticTypes::ID segmentationID = GetIDFromDataNode(segmentationNode); + RelationStorage::LinkSegmentationToLesion(caseID, segmentationID, lesion); + NotifyObserver(caseID); + } + else + { + mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to link does not exist for the given case."; + } +} + +void mitk::SemanticRelationsIntegration::UnlinkSegmentationFromLesion(const DataNode* segmentationNode) +{ + if (nullptr == segmentationNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; + } + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); + SemanticTypes::ID segmentationID = GetIDFromDataNode(segmentationNode); + RelationStorage::UnlinkSegmentationFromLesion(caseID, segmentationID); + NotifyObserver(caseID); +} + +void mitk::SemanticRelationsIntegration::RemoveSegmentation(const DataNode* segmentationNode) +{ + if (nullptr == segmentationNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; + } + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); + SemanticTypes::ID segmentationNodeID = GetIDFromDataNode(segmentationNode); + RelationStorage::RemoveSegmentation(caseID, segmentationNodeID); + NotifyObserver(caseID); +} + +void mitk::SemanticRelationsIntegration::SetControlPointOfImage(const DataNode* imageNode, const SemanticTypes::ControlPoint& controlPoint) +{ + if (nullptr == imageNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid image data node."; + } + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); + SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); + // need to check if an already existing control point fits/contains the user control point + SemanticTypes::ControlPoint existingControlPoint = FindExistingControlPoint(controlPoint, allControlPoints); + if (!existingControlPoint.UID.empty()) + { + try + { + // found an already existing control point + LinkImageToControlPoint(imageNode, existingControlPoint, false); + } + catch (const SemanticRelationException&) + { + mitkThrowException(SemanticRelationException) << "The image data node can not be linked. Inconsistency in the semantic relations storage assumed."; + } + } + else + { + try + { + AddControlPointAndLinkImage(imageNode, controlPoint, false); + // added a new control point + // find closest control point to add the new control point to the correct examination period + SemanticTypes::ControlPoint closestControlPoint = FindClosestControlPoint(controlPoint, allControlPoints); + SemanticTypes::ExaminationPeriodVector allExaminationPeriods = RelationStorage::GetAllExaminationPeriodsOfCase(caseID); + SemanticTypes::ExaminationPeriod examinationPeriod = FindExaminationPeriod(closestControlPoint, allExaminationPeriods); + if (examinationPeriod.UID.empty()) + { + // no closest control point (exceed threshold) or no examination period found + // create a new examination period for this control point and add it to the storage + examinationPeriod.UID = UIDGeneratorBoost::GenerateUID(); + examinationPeriod.name = "New examination period " + std::to_string(allExaminationPeriods.size()); + AddExaminationPeriod(caseID, examinationPeriod); + } + + // add the control point to the (newly created or found / close) examination period + AddControlPointToExaminationPeriod(caseID, controlPoint, examinationPeriod); + } + catch (const SemanticRelationException&) + { + mitkThrowException(SemanticRelationException) << "The image data node can not be linked. Inconsistency in the semantic relations storage assumed."; + } + } + + ClearControlPoints(caseID); + NotifyObserver(caseID); +} + +void mitk::SemanticRelationsIntegration::AddControlPointAndLinkImage(const DataNode* imageNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence) +{ + if (nullptr == imageNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid image data node."; + } + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); + if (SemanticRelationsInference::InstanceExists(caseID, controlPoint)) + { + mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to add already exists for the given case. \n Use 'LinkImageToControlPoint' instead."; + } + + RelationStorage::AddControlPoint(caseID, controlPoint); + LinkImageToControlPoint(imageNode, controlPoint, checkConsistence); +} + +void mitk::SemanticRelationsIntegration::LinkImageToControlPoint(const DataNode* imageNode, const SemanticTypes::ControlPoint& controlPoint, bool /*checkConsistence*/) +{ + if (nullptr == imageNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid image data node."; + } + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); + if (SemanticRelationsInference::InstanceExists(caseID, controlPoint)) + { + SemanticTypes::ID dataID = GetIDFromDataNode(imageNode); + RelationStorage::LinkImageToControlPoint(caseID, dataID, controlPoint); + } + else + { + mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to link does not exist for the given case."; + } +} + +void mitk::SemanticRelationsIntegration::UnlinkImageFromControlPoint(const DataNode* imageNode) +{ + if (nullptr == imageNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid image data node."; + } + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); + SemanticTypes::ID dataID = GetIDFromDataNode(imageNode); + SemanticTypes::ControlPoint controlPoint = RelationStorage::GetControlPointOfImage(caseID, dataID); + RelationStorage::UnlinkImageFromControlPoint(caseID, dataID); + ClearControlPoints(caseID); +} + +void mitk::SemanticRelationsIntegration::AddExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) +{ + if (SemanticRelationsInference::InstanceExists(caseID, examinationPeriod)) + { + mitkThrowException(SemanticRelationException) << "The examination period " << examinationPeriod.UID << " to add already exists for the given case."; + } + else + { + RelationStorage::AddExaminationPeriod(caseID, examinationPeriod); + } +} + +void mitk::SemanticRelationsIntegration::AddControlPointToExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod& examinationPeriod) +{ + if (!SemanticRelationsInference::InstanceExists(caseID, controlPoint)) + { + mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to add does not exist for the given case."; + } + + if (!SemanticRelationsInference::InstanceExists(caseID, examinationPeriod)) + { + mitkThrowException(SemanticRelationException) << "The examination period " << examinationPeriod.UID << " does not exist for the given case. \n Use 'AddExaminationPeriod' before."; + } + + RelationStorage::AddControlPointToExaminationPeriod(caseID, controlPoint, examinationPeriod); +} + +void mitk::SemanticRelationsIntegration::SetInformationType(const DataNode* imageNode, const SemanticTypes::InformationType& informationType) +{ + RemoveInformationTypeFromImage(imageNode); + AddInformationTypeToImage(imageNode, informationType); + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); + NotifyObserver(caseID); +} + +void mitk::SemanticRelationsIntegration::AddInformationTypeToImage(const DataNode* imageNode, const SemanticTypes::InformationType& informationType) +{ + if (nullptr == imageNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid image data node."; + } + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); + SemanticTypes::ID imageID = GetIDFromDataNode(imageNode); + RelationStorage::AddInformationTypeToImage(caseID, imageID, informationType); +} + +void mitk::SemanticRelationsIntegration::RemoveInformationTypeFromImage(const DataNode* imageNode) +{ + if (nullptr == imageNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid image data node."; + } + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); + SemanticTypes::ID imageID = GetIDFromDataNode(imageNode); + SemanticTypes::InformationType originalInformationType = RelationStorage::GetInformationTypeOfImage(caseID, imageID); + RelationStorage::RemoveInformationTypeFromImage(caseID, imageID); + + // check for further references to the removed information type + std::vector allImageIDsVectorValue = RelationStorage::GetAllImageIDsOfCase(caseID); + for (const auto& otherImageID : allImageIDsVectorValue) + { + SemanticTypes::InformationType otherInformationType = RelationStorage::GetInformationTypeOfImage(caseID, otherImageID); + if (otherInformationType == originalInformationType) + { + // found the information type in another image -> cannot remove the information type from the case + return; + } + } + + // given information type was not referred by any other image of the case -> the information type can be removed from the case + RelationStorage::RemoveInformationType(caseID, originalInformationType); +} + +/************************************************************************/ +/* private functions */ +/************************************************************************/ +void mitk::SemanticRelationsIntegration::NotifyObserver(const SemanticTypes::CaseID& caseID) const +{ + for (auto& observer : m_ObserverVector) + { + observer->Update(caseID); + } +} + +void mitk::SemanticRelationsIntegration::ClearControlPoints(const SemanticTypes::CaseID& caseID) +{ + SemanticTypes::ControlPointVector allControlPointsOfCase = RelationStorage::GetAllControlPointsOfCase(caseID); + + std::vector allImageIDsVectorValue = RelationStorage::GetAllImageIDsOfCase(caseID); + SemanticTypes::ControlPointVector referencedControlPoints; + for (const auto& imageID : allImageIDsVectorValue) + { + auto controlPointOfImage = RelationStorage::GetControlPointOfImage(caseID, imageID); + referencedControlPoints.push_back(controlPointOfImage); + } + + std::sort(allControlPointsOfCase.begin(), allControlPointsOfCase.end()); + std::sort(referencedControlPoints.begin(), referencedControlPoints.end()); + + SemanticTypes::ControlPointVector nonReferencedControlPoints; + std::set_difference(allControlPointsOfCase.begin(), allControlPointsOfCase.end(), + referencedControlPoints.begin(), referencedControlPoints.end(), + std::inserter(nonReferencedControlPoints, nonReferencedControlPoints.begin())); + + auto allExaminationPeriods = RelationStorage::GetAllExaminationPeriodsOfCase(caseID); + for (const auto& controlPoint : nonReferencedControlPoints) + { + const auto& examinationPeriod = FindExaminationPeriod(controlPoint, allExaminationPeriods); + RelationStorage::RemoveControlPointFromExaminationPeriod(caseID, controlPoint, examinationPeriod); + RelationStorage::RemoveControlPoint(caseID, controlPoint); + } +} diff --git a/Modules/SemanticRelations/src/mitkUIDGeneratorBoost.cpp b/Modules/SemanticRelations/src/mitkUIDGeneratorBoost.cpp new file mode 100644 index 0000000000..a7913deaa5 --- /dev/null +++ b/Modules/SemanticRelations/src/mitkUIDGeneratorBoost.cpp @@ -0,0 +1,28 @@ +/*=================================================================== + +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 relation module +#include "mitkUIDGeneratorBoost.h" + +#include +#include +#include + +std::string mitk::UIDGeneratorBoost::GenerateUID() +{ + boost::uuids::uuid uuid = boost::uuids::random_generator()(); + return boost::uuids::to_string(uuid); +} diff --git a/Modules/SemanticRelationsUI/CMakeLists.txt b/Modules/SemanticRelationsUI/CMakeLists.txt new file mode 100644 index 0000000000..1a94ea6b53 --- /dev/null +++ b/Modules/SemanticRelationsUI/CMakeLists.txt @@ -0,0 +1,4 @@ +MITK_CREATE_MODULE( + DEPENDS MitkQtWidgets MitkSemanticRelations + PACKAGE_DEPENDS Qt5|Core +) diff --git a/Modules/SemanticRelationsUI/files.cmake b/Modules/SemanticRelationsUI/files.cmake new file mode 100644 index 0000000000..8208ef2d16 --- /dev/null +++ b/Modules/SemanticRelationsUI/files.cmake @@ -0,0 +1,32 @@ +file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") + +set(CPP_FILES + mitkModuleActivator.cpp + QmitkAbstractSemanticRelationsStorageInspector.cpp + QmitkAbstractSemanticRelationsStorageModel.cpp + QmitkControlPointDialog.cpp + QmitkLesionTextDialog.cpp + QmitkLesionTreeItem.cpp + QmitkLesionTreeModel.cpp + QmitkPatientTableHeaderView.cpp + QmitkPatientTableInspector.cpp + QmitkPatientTableModel.cpp + QmitkSemanticRelationsUIHelper.cpp + QmitkTableItemThumbnailDelegate.cpp +) + +set(MOC_H_FILES + include/QmitkAbstractSemanticRelationsStorageInspector.h + include/QmitkAbstractSemanticRelationsStorageModel.h + include/QmitkControlPointDialog.h + include/QmitkLesionTextDialog.h + include/QmitkLesionTreeModel.h + include/QmitkPatientTableHeaderView.h + include/QmitkPatientTableInspector.h + include/QmitkPatientTableModel.h + include/QmitkTableItemThumbnailDelegate.h +) + +set(UI_FILES + src/QmitkPatientTableInspector.ui +) diff --git a/Modules/SemanticRelationsUI/include/QmitkAbstractSemanticRelationsStorageInspector.h b/Modules/SemanticRelationsUI/include/QmitkAbstractSemanticRelationsStorageInspector.h new file mode 100644 index 0000000000..1bd7d46a0c --- /dev/null +++ b/Modules/SemanticRelationsUI/include/QmitkAbstractSemanticRelationsStorageInspector.h @@ -0,0 +1,61 @@ +/*=================================================================== + +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 QMITKABSTRACTSEMANTICRELATIONSSTORAGEINSPECTOR_H +#define QMITKABSTRACTSEMANTICRELATIONSSTORAGEINSPECTOR_H + +// semantic relations UI module +#include "MitkSemanticRelationsUIExports.h" + +// semantic relations module +#include "mitkSemanticTypes.h" + +// qt widgets module +#include "QmitkAbstractDataStorageInspector.h" + +/* +* @brief The QmitkAbstractSemanticRelationsStorageInspector is a QmitkAbstractDataStorageInspector that can be used to +* show the currently available data of an (abstract) semantic relations storage model. +*/ +class MITKSEMANTICRELATIONSUI_EXPORT QmitkAbstractSemanticRelationsStorageInspector : public QmitkAbstractDataStorageInspector +{ + Q_OBJECT + +public: + + virtual ~QmitkAbstractSemanticRelationsStorageInspector(); + + /** + * @brief Extends the abstract base class to allow setting the current case ID which is needed to access the + * semantic relations storage. The function sets the case ID in the storage model. + * + * @param caseID A case ID as string + */ + virtual void SetCaseID(const mitk::SemanticTypes::CaseID& caseID) = 0; + /** + * @brief Extends the abstract base class to allow setting the current lesion. + * The function sets the lesion in the storage model. + * + *@param lesion The selected lesion + */ + virtual void SetLesion(const mitk::SemanticTypes::Lesion& lesion) = 0; + +protected: + + QmitkAbstractSemanticRelationsStorageInspector(QWidget* parent = nullptr); +}; + +#endif // QMITKABSTRACTSEMANTICRELATIONSSTORAGEINSPECTOR_H diff --git a/Modules/SemanticRelationsUI/include/QmitkAbstractSemanticRelationsStorageModel.h b/Modules/SemanticRelationsUI/include/QmitkAbstractSemanticRelationsStorageModel.h new file mode 100644 index 0000000000..b2e4ac65ad --- /dev/null +++ b/Modules/SemanticRelationsUI/include/QmitkAbstractSemanticRelationsStorageModel.h @@ -0,0 +1,119 @@ +/*=================================================================== + +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 QMITKABSTRACTSEMANTICRELATIONSSTORAGEMODEL_H +#define QMITKABSTRACTSEMANTICRELATIONSSTORAGEMODEL_H + +// mitk semantic relations UI +#include "MitkSemanticRelationsUIExports.h" + +// semantic relations module +#include +#include +#include +#include + +// qt widgets module +#include "QmitkAbstractDataStorageModel.h" + +/* +* @brief The QmitkAbstractSemanticRelationsStorageModel is a subclass of 'QmitkAbstractDataStorageModel' and provides additional +* functionality to set and store a semantic relations instance, the current case ID and the current lesion. +*/ +class MITKSEMANTICRELATIONSUI_EXPORT QmitkAbstractSemanticRelationsStorageModel : public QmitkAbstractDataStorageModel, public mitk::ISemanticRelationsObserver +{ + Q_OBJECT + +public: + + QmitkAbstractSemanticRelationsStorageModel(QObject* parent = nullptr); + virtual ~QmitkAbstractSemanticRelationsStorageModel(); + + /* + * @brief Update this model with the data from the semantic relations. + * + * Overridden from 'ISemanticRelationsObserver'. + * In order for the Update-function to be called, this model has to be added as a observer of SemanticRelation + * (e.g. m_SemanticRelations->AddObserver(m_SemanticRelationsStorageModel);) + * + * @par caseID The current case ID to identify the currently active patient / case. + */ + virtual void Update(const mitk::SemanticTypes::CaseID& caseID) override; + + /** + * @brief Set the current case ID which is needed to access the semantic relations storage. + * + * @param caseID A case ID as string + */ + void SetCaseID(const mitk::SemanticTypes::CaseID& caseID); + + const mitk::SemanticTypes::CaseID& GetCaseID() const { return m_CaseID; } + /** + * @brief Set the current lesion which can be used to show on which images the lesion is visible. + * + * @param lesion The selected lesion + */ + void SetLesion(const mitk::SemanticTypes::Lesion& lesion); + + const mitk::SemanticTypes::Lesion& GetLesion() const { return m_Lesion; } + /** + * @brief Set the current data node selection which can be used to show which lesions + * are visible on the node selection. + * + * @param dataNodeSelection The selected data nodes + */ + void SetDataNodeSelection(const QList& dataNodeSelection); + + const QList& GetSelectedDataNodes() const { return m_SelectedDataNodes; }; + /* + * @brief Update the semantic relations storage model with the current data from the semantic relations model, + * if the case ID is equal to the currently selected case ID of the table model. + */ + void UpdateModelData(const mitk::SemanticTypes::CaseID& caseID); + /* + * @brief Update the semantic relations storage model with the current data from the semantic relations model + * and the current case ID. + */ + void UpdateModelData(); + +Q_SIGNALS: + void ModelUpdated(); + +protected: + + /** + * @brief Create a new 'SemanticRelationsDataStorageAccess' instance with the new data storage and + * update the model data. + * This functions is called inside the 'SetDataStorage'-function from the parent class. + */ + virtual void DataStorageChanged() override; + + /** + * @brief This function is called if the model data is updated. It can be used by subclasses to define + * the way the data of a specific model is generated. It typically consists of access to the + * semantic relations storage to retrieve certain information. + */ + virtual void SetData() = 0; + + std::unique_ptr m_SemanticRelationsDataStorageAccess; + std::unique_ptr m_SemanticRelationsIntegration; + + mitk::SemanticTypes::CaseID m_CaseID; + QList m_SelectedDataNodes; + mitk::SemanticTypes::Lesion m_Lesion; +}; + +#endif // QMITKABSTRACTSEMANTICRELATIONSSTORAGEMODEL_H diff --git a/Modules/SemanticRelationsUI/include/QmitkControlPointDialog.h b/Modules/SemanticRelationsUI/include/QmitkControlPointDialog.h new file mode 100644 index 0000000000..f8c23395da --- /dev/null +++ b/Modules/SemanticRelationsUI/include/QmitkControlPointDialog.h @@ -0,0 +1,46 @@ +/*=================================================================== + +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 QMITKCONTROLPOINTDIALOG_H +#define QMITKCONTROLPOINTDIALOG_H + +#include + +// semantic relations module +#include "mitkSemanticTypes.h" + +#include +#include + +class MITKSEMANTICRELATIONSUI_EXPORT QmitkControlPointDialog : public QDialog +{ + Q_OBJECT + +public: + + QmitkControlPointDialog(QWidget *parent = nullptr); + + virtual ~QmitkControlPointDialog(); + + void SetCurrentDate(mitk::SemanticTypes::ControlPoint currentControlPoint); + QDate GetCurrentDate() const; + +private: + + QDateEdit* m_DateEdit; +}; + +#endif // QMITKCONTROLPOINTDIALOG_H diff --git a/Modules/SemanticRelationsUI/include/QmitkLesionTextDialog.h b/Modules/SemanticRelationsUI/include/QmitkLesionTextDialog.h new file mode 100644 index 0000000000..b50806adf9 --- /dev/null +++ b/Modules/SemanticRelationsUI/include/QmitkLesionTextDialog.h @@ -0,0 +1,49 @@ +/*=================================================================== + +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 QMITKLESIONTEXTDIALOG_H +#define QMITKLESIONTEXTDIALOG_H + +#include + +// semantic relations module +#include "mitkSemanticTypes.h" + +#include +#include + +class MITKSEMANTICRELATIONSUI_EXPORT QmitkLesionTextDialog : public QDialog +{ + Q_OBJECT + +public: + + QmitkLesionTextDialog(QWidget *parent = nullptr); + + virtual ~QmitkLesionTextDialog(); + + void SetLineEditText(const std::string& lineEditText); + QString GetLineEditText() const; + + QLineEdit* GetLineEdit() const; + +private: + + QLineEdit* m_LineEdit; + +}; + +#endif // QMITKLESIONTEXTDIALOG_H diff --git a/Modules/SemanticRelationsUI/include/QmitkLesionTreeItem.h b/Modules/SemanticRelationsUI/include/QmitkLesionTreeItem.h new file mode 100644 index 0000000000..2cd05eec35 --- /dev/null +++ b/Modules/SemanticRelationsUI/include/QmitkLesionTreeItem.h @@ -0,0 +1,123 @@ +/*=================================================================== + +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 QMITKLESIONTREEITEM_H +#define QMITKLESIONTREEITEM_H + +// mitk semantic relations UI +#include "mitkLesionData.h" + +// mitk semantic relations +#include + +// qt +#include + +// c++ +#include +#include + +/* +* @brief +*/ +class QmitkLesionTreeItem : public std::enable_shared_from_this +{ +public: + + using ChildPointer = std::shared_ptr; + using ChildConstPointer = std::shared_ptr; + using ParentPointer = std::weak_ptr; + + QmitkLesionTreeItem(mitk::LesionData lesionData = mitk::LesionData()); + ~QmitkLesionTreeItem(); + + /** + * @brief Return the child of this item at a specific position. + * + * @param row Determines the position of a child item to return. + * + * @return The child of this item at a specific position. + */ + ChildPointer GetChildInRow(int row) const { return m_Children.at(row); }; + /** + * @brief Return the parent item. + * + * @return The parent item as std::weak_ptr. + */ + ParentPointer GetParent() const { return m_ParentItem; }; + /** + * @brief Set the parent item of this item. + * + * @param parent The new parent item of this item. + */ + void SetParent(ParentPointer parent); + /** + * @brief Return the item data, which contains ... + * + * see mitk::LesionItemData + */ + mitk::LesionData& GetData() { return m_ItemData; }; + /** + * @brief Get the row of this item relative to its parent item using 'GetRowOfChild'. + * + * @return The row of this item relative to its parent item. + */ + int GetRow() const; + /** + * @brief Get the row of the given child item relative to this item. + * + * @param child The child item whose row is to be determined. + * + * @return The row of the child item. + */ + int GetRowOfChild(ChildConstPointer child) const; + /** + * @brief Return the number of child items. + * + * @return Number of child items. + */ + size_t ChildCount() const { return m_Children.size(); }; + /** + * @brief Add a new child to the list of children of this item if it is not already a child item. + * + * @param child The child item to add to this item. + */ + void AddChild(ChildPointer child); + /** + * @brief Remove a child from the list of children of this item. + * + * @param child The child item to remove from this item. + */ + void RemoveChild(ChildPointer child); + /** + * @brief Set the item data of this item. + * + * @param value LesionData that provides information about this item. + */ + void SetData(const mitk::LesionData& lesionData); + +private: + + ParentPointer m_ParentItem; + std::vector m_Children; + mitk::LesionData m_ItemData; + +}; + +Q_DECLARE_METATYPE(QmitkLesionTreeItem) +Q_DECLARE_METATYPE(QmitkLesionTreeItem*) + +#endif // QMITKLESIONTREEITEM_H diff --git a/Modules/SemanticRelationsUI/include/QmitkLesionTreeModel.h b/Modules/SemanticRelationsUI/include/QmitkLesionTreeModel.h new file mode 100644 index 0000000000..899d912e61 --- /dev/null +++ b/Modules/SemanticRelationsUI/include/QmitkLesionTreeModel.h @@ -0,0 +1,98 @@ +/*=================================================================== + +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 QMITKLESIONTREEMODEL_H +#define QMITKLESIONTREEMODEL_H + +// mitk semantic relations UI +#include "MitkSemanticRelationsUIExports.h" +#include "QmitkAbstractSemanticRelationsStorageModel.h" +#include "QmitkLesionTreeItem.h" + +// c++ +#include + +/* +* @brief +*/ +class MITKSEMANTICRELATIONSUI_EXPORT QmitkLesionTreeModel : public QmitkAbstractSemanticRelationsStorageModel +{ + Q_OBJECT + +public: + + /** + * @brief Initialize the root item of the model. The root item does not have a parent item. + */ + QmitkLesionTreeModel(QObject* parent = nullptr); + ~QmitkLesionTreeModel(); + + ////////////////////////////////////////////////////////////////////////// + // overridden virtual functions from QAbstractItemModel + ////////////////////////////////////////////////////////////////////////// + virtual QModelIndex index(int row, int column, const QModelIndex& itemIndex = QModelIndex()) const override; + virtual QModelIndex parent(const QModelIndex& itemIndex) const override; + + virtual int rowCount(const QModelIndex& itemIndex = QModelIndex()) const override; + virtual int columnCount(const QModelIndex& itemIndex = QModelIndex()) const override; + + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + ////////////////////////////////////////////////////////////////////////// + // end override + ////////////////////////////////////////////////////////////////////////// + +protected: + + // the following functions have to be overridden but are not implemented in this model + virtual void NodePredicateChanged() override { } + virtual void NodeAdded(const mitk::DataNode*) override { } + virtual void NodeChanged(const mitk::DataNode*) override { } + virtual void NodeRemoved(const mitk::DataNode*) override { } + /** + * @brief Overridden from 'QmitkAbstractSemanticRelationsStorageModel': This function retrieves all control points + * of the current case and stores them to define the header of the tree. + * Furthermore all lesions are retrieved and the lesion data is stored and show in the tree view. + */ + virtual void SetData() override; + +private: + + void SetLesionData(); + void AddLesion(const mitk::SemanticTypes::Lesion& lesion); + void SetSelectedDataNodesPresence(); + /** + * @brief The function uses the ID of the lesion to see if a data node presence was already set. + * If not, the given bool value is used and stored inside a member variable. If the lesion presence + * was already set, it will be overwritten. + * The function is used by the 'SetSelectedDataNodesPresence' function. + * + * @param lesion The lesion whose data node presence should be set + * @param dataNodePresence The bool value that defines the data node presence of the given lesion + */ + void SetDataNodePresenceOfLesion(const mitk::SemanticTypes::Lesion* lesion, bool dataNodePresence); + + QmitkLesionTreeItem* GetItemByIndex(const QModelIndex& index) const; + + std::map m_DataNodePresence; + + std::shared_ptr m_RootItem; + mitk::SemanticTypes::ControlPointVector m_ControlPoints; + mitk::SemanticTypes::LesionVector m_CurrentLesions; +}; + +#endif // QMITKLESIONTREEMODEL_H diff --git a/Modules/SemanticRelationsUI/include/QmitkPatientInfoWidget.h b/Modules/SemanticRelationsUI/include/QmitkPatientInfoWidget.h new file mode 100644 index 0000000000..039e4b8326 --- /dev/null +++ b/Modules/SemanticRelationsUI/include/QmitkPatientInfoWidget.h @@ -0,0 +1,54 @@ +/*=================================================================== + +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 QMITKPATIENTINFOWIDGET_H +#define QMITKPATIENTINFOWIDGET_H + +// semantic relations UI module +#include "MitkSemanticRelationsUIExports.h" +#include + +// qt +#include + +// mitk core +#include + +/* +* @brief The QmitkPatientInfoWidget is a widget that shows some DICOM information about a specific patient (data node). +* +* CURRENTLY NOT USED +*/ +class MITKSEMANTICRELATIONSUI_EXPORT QmitkPatientInfoWidget : public QWidget +{ + Q_OBJECT + +public: + + QmitkPatientInfoWidget(QWidget* parent = nullptr); + ~QmitkPatientInfoWidget(); + + void SetPatientInfo(const mitk::DataNode* dataNode); + +private: + + void QmitkPatientInfoWidget::Init(); + + Ui::QmitkPatientInfoWidgetControls m_Controls; + +}; + +#endif // QMITKPATIENTINFOWIDGET_H diff --git a/Modules/SemanticRelationsUI/include/QmitkPatientTableHeaderView.h b/Modules/SemanticRelationsUI/include/QmitkPatientTableHeaderView.h new file mode 100644 index 0000000000..263baafa41 --- /dev/null +++ b/Modules/SemanticRelationsUI/include/QmitkPatientTableHeaderView.h @@ -0,0 +1,83 @@ +/*=================================================================== + +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 QMITKPATIENTTABLEHEADERVIEW_H +#define QMITKPATIENTTABLEHEADERVIEW_H + +// semantic relations ui module +#include "MitkSemanticRelationsUIExports.h" + +// qt +#include +#include +#include + +/* +* @brief +*/ +class MITKSEMANTICRELATIONSUI_EXPORT QmitkPatientTableHeaderView : public QHeaderView +{ + Q_OBJECT + +public: + + QmitkPatientTableHeaderView(QWidget* parent = nullptr); + ~QmitkPatientTableHeaderView(); + + enum HeaderDataModelRoles + { + HorizontalHeaderDataRole = Qt::UserRole + }; + + /** + * @brief Set the model of the table view of this header view. + * This model returns a standard item model for the 'HorizontalHeaderDataRole'-role. + * The header model has been previously filled with header data. + * This function is overwritten from QHeaderView. + */ + virtual void setModel(QAbstractItemModel* model) override; + +protected: + + /** + * @brief Paint each header using 'PaintHeader'. + * This function is overwritten from QHeaderView. + */ + virtual void paintSection(QPainter* painter, const QRect& rect, int logicalIndex) const override; + /** + * @brief Get the section size by retrieving the text content. + * The section size is dependent on the child and parent headers. + * This function is overwritten from QHeaderView. + */ + QSize sectionSizeFromContents(int logicalIndex) const override; + +private: + + int PaintHeader(QPainter* painter, const QModelIndex& currentIndex, int logicalIndex, const QRect& sectionRect, int top) const; + QSize HeaderSize(const QModelIndex& index) const; + int CurrentHeaderLeft(const QModelIndex& currentIndex, const QModelIndex& headerIndex, int sectionIndex, int left) const; + int CurrentHeaderWidth(const QModelIndex& currentIndex, const QModelIndex& headerIndex, int sectionIndex) const; + + QModelIndexList ParentIndexList(QModelIndex index) const; + QModelIndex HeaderIndex(int sectionIndex) const; + QModelIndex FindHeader(const QModelIndex& currentIndex, int sectionIndex, int& currentHeaderIndex) const; + QModelIndexList ListHeader(const QModelIndex& currentIndex) const; + + QStandardItemModel* m_HeaderModel; + +}; + +#endif // QMITKPATIENTTABLEHEADERVIEW_H diff --git a/Modules/SemanticRelationsUI/include/QmitkPatientTableInspector.h b/Modules/SemanticRelationsUI/include/QmitkPatientTableInspector.h new file mode 100644 index 0000000000..57d5780ce7 --- /dev/null +++ b/Modules/SemanticRelationsUI/include/QmitkPatientTableInspector.h @@ -0,0 +1,89 @@ +/*=================================================================== + +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 QMITKPATIENTTABLEINSPECTOR_H +#define QMITKPATIENTTABLEINSPECTOR_H + +// semantic relations UI module +#include "MitkSemanticRelationsUIExports.h" +#include "QmitkAbstractSemanticRelationsStorageInspector.h" +#include "QmitkPatientTableModel.h" +#include "QmitkTableItemThumbnailDelegate.h" + +#include "ui_QmitkPatientTableInspector.h" + +// qt widgets module +#include + +// qt +#include + +/* +* @brief The QmitkPatientTableInspector is a QmitkAbstractSemanticRelationsStorageInspector that shows the currently +* available data of the semantic relations storage model in a control-point - information type matrix. +* +* The QmitkPatientTableInspector uses the QmitkSemanticRelationsStorageModel, a QmitkAbstractDataStorageModel that +* presents the semantic relations data as a table, showing a QPixmap as thumbnail for the data nodes. +*/ +class MITKSEMANTICRELATIONSUI_EXPORT QmitkPatientTableInspector : public QmitkAbstractSemanticRelationsStorageInspector +{ + Q_OBJECT + +public: + + QmitkPatientTableInspector(QWidget* parent = nullptr); + + virtual QAbstractItemView* GetView() override; + virtual const QAbstractItemView* GetView() const override; + + virtual void SetSelectionMode(SelectionMode mode) override; + virtual SelectionMode GetSelectionMode() const override; + + virtual void SetCaseID(const mitk::SemanticTypes::CaseID& caseID) override; + virtual void SetLesion(const mitk::SemanticTypes::Lesion& lesion) override; + + QItemSelectionModel* GetSelectionModel(); + +Q_SIGNALS: + + void DataNodeDoubleClicked(const mitk::DataNode*); + void OnContextMenuRequested(const QPoint&); + void OnNodeRemoved(const mitk::DataNode*); + +private Q_SLOTS: + + void OnModelUpdated(); + void OnNodeButtonClicked(const QString&); + void OnDataNodeSelectionChanged(const QList&); + void OnItemDoubleClicked(const QModelIndex&); + +protected: + + virtual void Initialize() override; + +private: + + void SetUpConnections(); + + virtual void keyPressEvent(QKeyEvent* e) override; + + Ui::QmitkPatientTableInspector m_Controls; + QmitkPatientTableModel* m_StorageModel; + QmitkTableItemThumbnailDelegate* m_ItemDelegate; + +}; + +#endif // QMITKPATIENTTABLEINSPECTOR_H diff --git a/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h b/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h new file mode 100644 index 0000000000..16a38ef02b --- /dev/null +++ b/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h @@ -0,0 +1,134 @@ +/*=================================================================== + +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 QMITKPATIENTTABLEMODEL_H +#define QMITKPATIENTTABLEMODEL_H + +// semantic relations UI module +#include "QmitkAbstractSemanticRelationsStorageModel.h" + +// semantic relations module +#include +#include + +// mitk core +#include + +// qt +#include +#include + +/** +* @brief The QmitkPatientTableModel is a subclass of the QmitkAbstractSemanticRelationsStorageModel and holds the semantic relations data of the currently selected case. +* +* The QmitkPatientTableModel uses the 'data' function to return either the data node of a table cell or the thumbnail of the underlying image. +* The horizontal header of the table shows the control points of the current case and the vertical header of the table shows the information types of the current case. +* Using the 'GetFilteredData'-function of the SemanticRelations-class the model is able to retrieve the correct data node for each table entry. +* +* Additionally the model creates and holds the QPixmaps of the known data nodes in order to return a thumbnail, if needed. +*/ +class QmitkPatientTableModel : public QmitkAbstractSemanticRelationsStorageModel +{ + Q_OBJECT + +public: + + QmitkPatientTableModel(QObject* parent = nullptr); + ~QmitkPatientTableModel(); + + ////////////////////////////////////////////////////////////////////////// + // overridden functions from QAbstractItemModel + ////////////////////////////////////////////////////////////////////////// + virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; + virtual QModelIndex parent(const QModelIndex& child) const override; + + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; + virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override; + + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + virtual Qt::ItemFlags flags(const QModelIndex& index) const override; + ////////////////////////////////////////////////////////////////////////// + /// end override + ///////////////////////////////////////////////////////////////////////// + + void SetNodeType(const std::string& nodeType); + +protected: + + // the following functions have to be overridden... + virtual void NodePredicateChanged() override; + // but are not implemented in this model + virtual void NodeAdded(const mitk::DataNode*) override { } + virtual void NodeChanged(const mitk::DataNode*) override { } + virtual void NodeRemoved(const mitk::DataNode*) override { } + /** + * @brief Overridden from 'QmitkAbstractSemanticRelationsStorageModel': This function retrieves all control points + * and information types of the current case and stores them to define the header of the table. + * Furthermore all images are retrieved and the pixmap of the images are generated and stored. + */ + virtual void SetData() override; + +private: + + void SetHeaderModel(); + void SetPixmaps(); + void SetLesionPresences(); + + /** + * @brief The function uses the ID of the node to see if a pixmap was already set. If not, the given pixmap + * is used and stored inside a member variable. If the pixmap was already set, it will be overwritten. + * Using 'nullptr' as a pixmap will erase the entry for the given data node. + * + * @param dataNode The data node whose pixmap should be set + * @param pixmapFromImage The pixmap that shows an image of the content of the data node + */ + void SetPixmapOfNode(const mitk::DataNode* dataNode, QPixmap* pixmapFromImage); + /** + * @brief The function uses the ID of the node to see if a lesion presence was already set. If not, the given + * bool value is used and stored inside a member variable. If the lesion presence was already set, it + * will be overwritten. + * The function is used by the 'SetLesionPresences' function. + * + * @param dataNode The data node whose lesion presence should be set + * @param lesionPresence The bool value that defines the lesion presence of the given data node + */ + void SetLesionPresenceOfNode(const mitk::DataNode* dataNode, bool lesionPresence); + /** + * @brief Returns the data node that is associated with the given table entry (index). + * + * The function uses the SemanticRelations-class and the current control point data and information type data to + * filter the nodes of the current case. + * The index is used to access the correct row in the table (information type) and the correct column in the table (control point). + * + * @par index The QModelIndex of the table entry + */ + mitk::DataNode* GetCurrentDataNode(const QModelIndex &index) const; + + std::map m_PixmapMap; + std::map m_LesionPresence; + + mitk::SemanticTypes::InformationTypeVector m_InformationTypes; + mitk::SemanticTypes::ControlPointVector m_ControlPoints; + mitk::SemanticTypes::ExaminationPeriodVector m_ExaminationPeriods; + mitk::SemanticRelationsDataStorageAccess::DataNodeVector m_CurrentDataNodes; + std::string m_SelectedNodeType; + + QStandardItemModel* m_HeaderModel; +}; + +#endif // QMITKPATIENTTABLEMODEL_H diff --git a/Modules/SemanticRelationsUI/include/QmitkSemanticRelationsUIHelper.h b/Modules/SemanticRelationsUI/include/QmitkSemanticRelationsUIHelper.h new file mode 100644 index 0000000000..4c804178f0 --- /dev/null +++ b/Modules/SemanticRelationsUI/include/QmitkSemanticRelationsUIHelper.h @@ -0,0 +1,45 @@ +/*=================================================================== + +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 QMITKSEMANTICRELATIONSUIHELPER_H +#define QMITKSEMANTICRELATIONSUIHELPER_H + +// semantic relations ui module +#include "MitkSemanticRelationsUIExports.h" + +// mitk core +#include + +// qt +#include + +/** +* @brief Provides a helper function to generate a pixmap from a given image node. +*/ +namespace QmitkSemanticRelationsUIHelper +{ + /* + * @brief Generates a QPixmap of a DICOM image. + * + * The center sagittal image slice is extracted and used as the thumbnail image. + * + * @par dataNode The data node that holds the image data. + */ + MITKSEMANTICRELATIONSUI_EXPORT QPixmap GetPixmapFromImageNode(const mitk::DataNode* dataNode); + +} // namespace QmitkSemanticRelationsUIHelper + +#endif // QMITKSEMANTICRELATIONSUIHELPER_H diff --git a/Modules/SemanticRelationsUI/include/QmitkTableItemThumbnailDelegate.h b/Modules/SemanticRelationsUI/include/QmitkTableItemThumbnailDelegate.h new file mode 100644 index 0000000000..c65150847f --- /dev/null +++ b/Modules/SemanticRelationsUI/include/QmitkTableItemThumbnailDelegate.h @@ -0,0 +1,36 @@ +/*=================================================================== + +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 QMITKTABLEITEMTHUMBNAILDELEGATE_H +#define QMITKTABLEITEMTHUMBNAILDELEGATE_H + +#include + +/* +* @brief The QmitkTableItemThumbnailDelegate is a subclass of the QmitkAbstractSemanticRelationsStorageModel +*/ +class QmitkTableItemThumbnailDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + + QmitkTableItemThumbnailDelegate(QObject* parent = nullptr); + + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; +}; + +#endif // QMITKTABLEITEMTHUMBNAILDELEGATE_H diff --git a/Modules/SemanticRelationsUI/include/mitkModuleActivator.h b/Modules/SemanticRelationsUI/include/mitkModuleActivator.h new file mode 100644 index 0000000000..2fe949967c --- /dev/null +++ b/Modules/SemanticRelationsUI/include/mitkModuleActivator.h @@ -0,0 +1,46 @@ +/*=================================================================== + +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 MITKMODULEACTIVATOR_H +#define MITKMODULEACTIVATOR_H + +// Micro Services +#include +#include +#include +#include + +#include + +// qt widgets module +#include "mitkIDataStorageInspectorProvider.h" + +namespace mitk +{ + /* + * This is the module activator for the "SemanticRelationsUI" module. + */ + class SemanticRelationsUIActivator : public us::ModuleActivator + { + public: + void Load(us::ModuleContext* context) override; + void Unload(us::ModuleContext* context) override; + + private: + std::unique_ptr m_PatientTableInspector; + }; +} +#endif // MITKMODULEACTIVATOR_H diff --git a/Modules/SemanticRelationsUI/src/QmitkAbstractSemanticRelationsStorageInspector.cpp b/Modules/SemanticRelationsUI/src/QmitkAbstractSemanticRelationsStorageInspector.cpp new file mode 100644 index 0000000000..2f6013c589 --- /dev/null +++ b/Modules/SemanticRelationsUI/src/QmitkAbstractSemanticRelationsStorageInspector.cpp @@ -0,0 +1,29 @@ +/*=================================================================== + +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 UI module +#include "QmitkAbstractSemanticRelationsStorageInspector.h" + +QmitkAbstractSemanticRelationsStorageInspector::QmitkAbstractSemanticRelationsStorageInspector(QWidget* parent /*= nullptr*/) + : QmitkAbstractDataStorageInspector(parent) +{ + // nothing here +} + +QmitkAbstractSemanticRelationsStorageInspector::~QmitkAbstractSemanticRelationsStorageInspector() +{ + // nothing here +} diff --git a/Modules/SemanticRelationsUI/src/QmitkAbstractSemanticRelationsStorageModel.cpp b/Modules/SemanticRelationsUI/src/QmitkAbstractSemanticRelationsStorageModel.cpp new file mode 100644 index 0000000000..8b973ef1db --- /dev/null +++ b/Modules/SemanticRelationsUI/src/QmitkAbstractSemanticRelationsStorageModel.cpp @@ -0,0 +1,90 @@ +/*=================================================================== + +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 UI module +#include "QmitkAbstractSemanticRelationsStorageModel.h" + +#include "QmitkCustomVariants.h" + +QmitkAbstractSemanticRelationsStorageModel::QmitkAbstractSemanticRelationsStorageModel(QObject* parent /*= nullptr*/) + : QmitkAbstractDataStorageModel(parent) + , m_SemanticRelationsDataStorageAccess(nullptr) +{ + m_SemanticRelationsIntegration = std::make_unique(); + m_SemanticRelationsIntegration->AddObserver(this); +} + +QmitkAbstractSemanticRelationsStorageModel::~QmitkAbstractSemanticRelationsStorageModel() +{ + if (nullptr != m_SemanticRelationsIntegration) + { + m_SemanticRelationsIntegration->RemoveObserver(this); + } +} + +void QmitkAbstractSemanticRelationsStorageModel::Update(const mitk::SemanticTypes::CaseID& caseID) +{ + UpdateModelData(caseID); +} + +void QmitkAbstractSemanticRelationsStorageModel::SetCaseID(const mitk::SemanticTypes::CaseID& caseID) +{ + m_CaseID = caseID; + UpdateModelData(); +} + +void QmitkAbstractSemanticRelationsStorageModel::SetLesion(const mitk::SemanticTypes::Lesion& lesion) +{ + m_Lesion = lesion; + UpdateModelData(); +} + +void QmitkAbstractSemanticRelationsStorageModel::SetDataNodeSelection(const QList& dataNodeSelection) +{ + m_SelectedDataNodes = dataNodeSelection; + UpdateModelData(); +} + +void QmitkAbstractSemanticRelationsStorageModel::UpdateModelData(const mitk::SemanticTypes::CaseID& caseID) +{ + // if the case ID of updated instance is equal to the currently active caseID + if (caseID == m_CaseID) + { + UpdateModelData(); + } +} + +void QmitkAbstractSemanticRelationsStorageModel::UpdateModelData() +{ + if (nullptr == m_SemanticRelationsDataStorageAccess) + { + return; + } + + // update the model, so that the table will be filled with the new patient information + beginResetModel(); + + SetData(); + + endResetModel(); + emit ModelUpdated(); +} + +void QmitkAbstractSemanticRelationsStorageModel::DataStorageChanged() +{ + m_SemanticRelationsDataStorageAccess = std::make_unique(m_DataStorage.Lock()); + UpdateModelData(); +} diff --git a/Modules/SemanticRelationsUI/src/QmitkControlPointDialog.cpp b/Modules/SemanticRelationsUI/src/QmitkControlPointDialog.cpp new file mode 100644 index 0000000000..348aade318 --- /dev/null +++ b/Modules/SemanticRelationsUI/src/QmitkControlPointDialog.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 "QmitkControlPointDialog.h" + +#include +#include +#include +#include +#include + +QmitkControlPointDialog::QmitkControlPointDialog(QWidget* parent) + : QDialog(parent) +{ + QBoxLayout* verticalLayout = new QVBoxLayout(this); + verticalLayout->setMargin(5); + verticalLayout->setSpacing(5); + + QLabel* dateLabel = new QLabel(tr("Set date"), this); + verticalLayout->addWidget(dateLabel); + + m_DateEdit = new QDateEdit(this); + m_DateEdit->setDisplayFormat("yyyy-MM-dd"); + m_DateEdit->setFocus(); + verticalLayout->addWidget(m_DateEdit); + + QPushButton* acceptButton = new QPushButton(tr("Ok"), this); + QPushButton* cancelButton = new QPushButton(tr("Cancel"), this); + acceptButton->setDefault(true); + + connect(acceptButton, &QPushButton::clicked, this, &QmitkControlPointDialog::accept); + connect(cancelButton, &QPushButton::clicked, this, &QmitkControlPointDialog::reject); + + QBoxLayout* horizontalLayout = new QHBoxLayout(); + horizontalLayout->setSpacing(5); + horizontalLayout->addStretch(); + horizontalLayout->addWidget(acceptButton); + horizontalLayout->addWidget(cancelButton); + verticalLayout->addLayout(horizontalLayout); + + setMinimumSize(250, 100); +} + +QmitkControlPointDialog::~QmitkControlPointDialog() +{ +} + +void QmitkControlPointDialog::SetCurrentDate(mitk::SemanticTypes::ControlPoint currentControlPoint) +{ + m_DateEdit->setDate(QDate(currentControlPoint.date.year(), currentControlPoint.date.month(), currentControlPoint.date.day())); +} + +QDate QmitkControlPointDialog::GetCurrentDate() const +{ + return m_DateEdit->date(); +} diff --git a/Modules/SemanticRelationsUI/src/QmitkLesionTextDialog.cpp b/Modules/SemanticRelationsUI/src/QmitkLesionTextDialog.cpp new file mode 100644 index 0000000000..3ca3416a27 --- /dev/null +++ b/Modules/SemanticRelationsUI/src/QmitkLesionTextDialog.cpp @@ -0,0 +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. + +===================================================================*/ + +#include "QmitkLesionTextDialog.h" + +#include +#include +#include + +QmitkLesionTextDialog::QmitkLesionTextDialog(QWidget* parent) + : QDialog(parent) +{ + QBoxLayout* verticalLayout = new QVBoxLayout(this); + verticalLayout->setMargin(5); + verticalLayout->setSpacing(5); + + QLabel* dialogLabel = new QLabel(tr("Set lesion information"), this); + verticalLayout->addWidget(dialogLabel); + + m_LineEdit = new QLineEdit(this); + m_LineEdit->setFocus(); + verticalLayout->addWidget(m_LineEdit); + + QPushButton* acceptButton = new QPushButton(tr("Ok"), this); + QPushButton* cancelButton = new QPushButton(tr("Cancel"), this); + acceptButton->setDefault(true); + + connect(acceptButton, &QPushButton::clicked, this, &QmitkLesionTextDialog::accept); + connect(cancelButton, &QPushButton::clicked, this, &QmitkLesionTextDialog::reject); + + QBoxLayout* horizontalLayout = new QHBoxLayout(); + horizontalLayout->setSpacing(5); + horizontalLayout->addStretch(); + horizontalLayout->addWidget(acceptButton); + horizontalLayout->addWidget(cancelButton); + verticalLayout->addLayout(horizontalLayout); + + setMinimumSize(250, 100); +} + +QmitkLesionTextDialog::~QmitkLesionTextDialog() +{ + // nothing here +} + +void QmitkLesionTextDialog::SetLineEditText(const std::string& lineEditText) +{ + m_LineEdit->setText(QString::fromStdString(lineEditText)); +} + +QString QmitkLesionTextDialog::GetLineEditText() const +{ + return m_LineEdit->text(); +} + +QLineEdit* QmitkLesionTextDialog::GetLineEdit() const +{ + return m_LineEdit; +} diff --git a/Modules/SemanticRelationsUI/src/QmitkLesionTreeItem.cpp b/Modules/SemanticRelationsUI/src/QmitkLesionTreeItem.cpp new file mode 100644 index 0000000000..a698270871 --- /dev/null +++ b/Modules/SemanticRelationsUI/src/QmitkLesionTreeItem.cpp @@ -0,0 +1,86 @@ +/*=================================================================== + +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 UI module +#include "QmitkLesionTreeItem.h" + +// qt +#include + +QmitkLesionTreeItem::QmitkLesionTreeItem(mitk::LesionData lesionData/* = mitk::LesionTreeItemData()*/) +{ + m_ItemData = lesionData; +} + +QmitkLesionTreeItem::~QmitkLesionTreeItem() +{ + // nothing here +} + +void QmitkLesionTreeItem::SetParent(ParentPointer parent) +{ + m_ParentItem = parent; +} + +int QmitkLesionTreeItem::GetRow() const +{ + if (m_ParentItem.expired()) + { + return 0; + } + + auto parentItem = m_ParentItem.lock(); + return parentItem->GetRowOfChild(this->shared_from_this()); +} + +int QmitkLesionTreeItem::GetRowOfChild(ChildConstPointer child) const +{ + auto it = std::find(m_Children.begin(), m_Children.end(), child); + if (it == m_Children.end()) + { + return -1; + } + else + { + return std::distance(m_Children.begin(), it); + } +} + +void QmitkLesionTreeItem::AddChild(ChildPointer child) +{ + auto it = std::find(m_Children.begin(), m_Children.end(), child); + if (it == m_Children.end()) + { + // child does not already exist; add to vector of children + m_Children.push_back(child); + // add parent item + child->SetParent(this->shared_from_this()); + } +} + +void QmitkLesionTreeItem::RemoveChild(ChildPointer child) +{ + auto it = std::find(m_Children.begin(), m_Children.end(), child); + if (it != m_Children.end()) + { + m_Children.erase(it); + } +} + +void QmitkLesionTreeItem::SetData(const mitk::LesionData& lesionData) +{ + m_ItemData = lesionData; +} diff --git a/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp b/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp new file mode 100644 index 0000000000..07c1071fc3 --- /dev/null +++ b/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp @@ -0,0 +1,303 @@ +/*=================================================================== + +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 UI module +#include "QmitkLesionTreeModel.h" + +// semantic relations module +#include +#include +#include +#include +#include + +// qt +#include + +QmitkLesionTreeModel::QmitkLesionTreeModel(QObject* parent/* = nullptr*/) + : QmitkAbstractSemanticRelationsStorageModel(parent) + , m_RootItem(std::make_shared(mitk::LesionData())) +{ + // nothing here +} + +QmitkLesionTreeModel::~QmitkLesionTreeModel() +{ + // nothing here +} + +////////////////////////////////////////////////////////////////////////// +// overridden virtual functions from QAbstractItemModel +////////////////////////////////////////////////////////////////////////// +QModelIndex QmitkLesionTreeModel::index(int row, int column, const QModelIndex& itemIndex) const +{ + if (!hasIndex(row, column, itemIndex)) + { + return QModelIndex(); + } + + auto childItem = GetItemByIndex(itemIndex)->GetChildInRow(row); + if (nullptr == childItem) + { + return QModelIndex(); + } + + return createIndex(row, column, childItem.get()); +} + +QModelIndex QmitkLesionTreeModel::parent(const QModelIndex& itemIndex) const +{ + if (!itemIndex.isValid()) + { + return QModelIndex(); + } + + auto parentItem = GetItemByIndex(itemIndex)->GetParent(); + if (parentItem.expired()) + { + return QModelIndex(); + } + + auto sharedParent = parentItem.lock(); + if (sharedParent == m_RootItem) + { + return QModelIndex(); + } + + return createIndex(sharedParent->GetRow(), 0, sharedParent.get()); +} + +int QmitkLesionTreeModel::rowCount(const QModelIndex& itemIndex/* = QModelIndex()*/) const +{ + return GetItemByIndex(itemIndex)->ChildCount(); +} + +int QmitkLesionTreeModel::columnCount(const QModelIndex&/* itemIndex = QModelIndex() */) const +{ + if (0 == m_RootItem->ChildCount()) + { + // no lesion items stored, no need to display columns + return 0; + } + + return m_ControlPoints.size() + 1; +} + +QVariant QmitkLesionTreeModel::data(const QModelIndex& index, int role) const +{ + if (!index.isValid()) + { + return QVariant(); + } + + if (index.column() < 0 || index.column() > static_cast(m_ControlPoints.size())) + { + return QVariant(); + } + + QmitkLesionTreeItem* currentItem = GetItemByIndex(index); + if (Qt::DisplayRole == role) + { + if (currentItem->GetParent().expired()) + { + return QVariant(); + } + + auto parentItem = currentItem->GetParent().lock(); + // parent exists and is the root item -> 1. item of a lesion entry + if (m_RootItem == parentItem) + { + // display role fills the first columns with the lesion UID / name + if (0 == index.column()) + { + std::string itemString = currentItem->GetData().GetLesionName(); + if (itemString.empty()) + { + itemString = currentItem->GetData().GetLesionUID(); + } + return QString::fromStdString(itemString); + } + else + { + // display role fills other columns with the lesion presence info + const auto lesionPresence = currentItem->GetData().GetLesionPresence(); + if (index.column() - 1 > static_cast(lesionPresence.size())) + { + return ""; + } + + return QVariant(lesionPresence.at(index.column() - 1)); + } + } + // parent is not the root item -> 2. item of a lesion entry + else + { + // display role fills the first columns with the property name "Volume" + if (0 == index.column()) + { + return "Volume"; + } + else + { + // display role fills other columns with the lesion volume info + const auto lesionVolume= currentItem->GetData().GetLesionVolume(); + if (index.column() - 1 > static_cast(lesionVolume.size())) + { + return ""; + } + return QVariant(lesionVolume.at(index.column() - 1)); + } + } + } + + if (Qt::BackgroundColorRole == role) + { + auto it = m_DataNodePresence.find(currentItem->GetData().GetLesion().UID); + if (it != m_DataNodePresence.end()) + { + return it->second ? QVariant(QColor(Qt::darkGreen)) : QVariant(QColor(Qt::transparent)); + } + + return QVariant(QColor(Qt::transparent)); + } + + if (Qt::UserRole == role) + { + return QVariant::fromValue(currentItem); + } + + return QVariant(); +} + +QVariant QmitkLesionTreeModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (0 == m_RootItem->ChildCount()) + { + // no lesion items stored, no need to display the header + return QVariant(); + } + + if (Qt::Horizontal == orientation && Qt::DisplayRole == role) + { + if (0 == section) + { + return QVariant("Lesion"); + } + + if (static_cast(m_ControlPoints.size()) >= section) + { + mitk::SemanticTypes::ControlPoint currentControlPoint = m_ControlPoints.at(section-1); + return QVariant(QString::fromStdString(currentControlPoint.ToString())); + } + } + + return QVariant(); +} + +void QmitkLesionTreeModel::SetData() +{ + m_RootItem = std::make_shared(mitk::LesionData()); + + // get all control points of current case + m_ControlPoints = mitk::RelationStorage::GetAllControlPointsOfCase(m_CaseID); + // sort the vector of control points for the timeline + std::sort(m_ControlPoints.begin(), m_ControlPoints.end()); + + SetLesionData(); + SetSelectedDataNodesPresence(); +} + +void QmitkLesionTreeModel::SetLesionData() +{ + m_CurrentLesions = mitk::RelationStorage::GetAllLesionsOfCase(m_CaseID); + for (auto& lesion : m_CurrentLesions) + { + AddLesion(lesion); + } +} + +void QmitkLesionTreeModel::AddLesion(const mitk::SemanticTypes::Lesion& lesion) +{ + // create new lesion tree item data and modify it according to the control point data + mitk::LesionData lesionData(lesion); + mitk::GenerateAdditionalLesionData(lesionData, m_CaseID); + + // add the 1. level lesion item to the root item + std::shared_ptr newLesionTreeItem = std::make_shared(lesionData); + m_RootItem->AddChild(newLesionTreeItem); + + // add the 2. level lesion item to the 1. level lesion item + std::shared_ptr newChildItem = std::make_shared(lesionData); + newLesionTreeItem->AddChild(newChildItem); +} + +void QmitkLesionTreeModel::SetSelectedDataNodesPresence() +{ + m_DataNodePresence.clear(); + for (const auto& dataNode : m_SelectedDataNodes) + { + if (!mitk::SemanticRelationsInference::InstanceExists(dataNode)) + { + continue; + } + + for (const auto& lesion : m_CurrentLesions) + { + if (!mitk::SemanticRelationsInference::InstanceExists(m_CaseID, lesion)) + { + continue; + } + try + { + // set the lesion presence for the current node + bool dataNodePresence = mitk::SemanticRelationsInference::IsLesionPresent(lesion, dataNode); + SetDataNodePresenceOfLesion(&lesion, dataNodePresence); + } + catch (const mitk::SemanticRelationException&) + { + continue; + } + } + } +} + +void QmitkLesionTreeModel::SetDataNodePresenceOfLesion(const mitk::SemanticTypes::Lesion* lesion, bool dataNodePresence) +{ + std::map::iterator iter = m_DataNodePresence.find(lesion->UID); + if (iter != m_DataNodePresence.end()) + { + // key already existing, overwrite already stored bool value + iter->second = dataNodePresence; + } + else + { + m_DataNodePresence.insert(std::make_pair(lesion->UID, dataNodePresence)); + } +} + +QmitkLesionTreeItem* QmitkLesionTreeModel::GetItemByIndex(const QModelIndex& index) const +{ + if (index.isValid()) + { + auto item = static_cast(index.internalPointer()); + if (nullptr != item) + { + return item; + } + } + + return m_RootItem.get(); +} diff --git a/Modules/SemanticRelationsUI/src/QmitkPatientInfoWidget.cpp b/Modules/SemanticRelationsUI/src/QmitkPatientInfoWidget.cpp new file mode 100644 index 0000000000..0f1c9e6099 --- /dev/null +++ b/Modules/SemanticRelationsUI/src/QmitkPatientInfoWidget.cpp @@ -0,0 +1,105 @@ +/*=================================================================== + +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 "QmitkPatientInfoWidget.h" + +// semantic relations module +#include + +// mitk core +#include + +QmitkPatientInfoWidget::QmitkPatientInfoWidget(QWidget* parent) + : QWidget(parent) +{ + Init(); +} + +QmitkPatientInfoWidget::~QmitkPatientInfoWidget() +{ + // nothing here +} + +void QmitkPatientInfoWidget::Init() +{ + // Create GUI widgets from the Qt Designer's .ui file + m_Controls.setupUi(this); +} + +void QmitkPatientInfoWidget::SetPatientInfo(const mitk::DataNode* dataNode) +{ + if (nullptr == dataNode) + { + MITK_INFO << "Not a valid data node."; + return; + } + + mitk::BaseData* baseData = dataNode->GetData(); + if (nullptr == baseData) + { + MITK_INFO << "No valid base data."; + return; + } + + mitk::BaseProperty* acquisitionDate = baseData->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str()); + std::string acquisitionDateAsString = ""; + if (nullptr != acquisitionDate) + { + acquisitionDateAsString = acquisitionDate->GetValueAsString(); + mitk::DICOMHelper::ReformatDICOMTag(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str(), acquisitionDateAsString); + } + + mitk::BaseProperty* acquisitionTime = baseData->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0032).c_str()); + std::string acquisitionTimeAsString = ""; + if (nullptr != acquisitionTime) + { + acquisitionTimeAsString = acquisitionTime->GetValueAsString(); + mitk::DICOMHelper::ReformatDICOMTag(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0032).c_str(), acquisitionTimeAsString); + } + + std::string patiensBirthDateAsString = ""; + mitk::BaseProperty* patientBirthDate = baseData->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0010, 0x0030).c_str()); + if (nullptr != patientBirthDate) + { + patiensBirthDateAsString = patientBirthDate->GetValueAsString(); + mitk::DICOMHelper::ReformatDICOMTag(mitk::GeneratePropertyNameForDICOMTag(0x0010, 0x0030).c_str(), patiensBirthDateAsString); + } + + mitk::BaseProperty* modality = baseData->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str()); + std::string modalityAsString = ""; + if (nullptr != modality) + { + modalityAsString = modality->GetValueAsString(); + mitk::DICOMHelper::ReformatDICOMTag(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), modalityAsString); + } + + mitk::BaseProperty* gender = baseData->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0010, 0x0040).c_str()); + std::string genderAsString = ""; + if (nullptr != gender) + { + genderAsString = gender->GetValueAsString(); + mitk::DICOMHelper::ReformatDICOMTag(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), genderAsString); + } + + m_Controls.dataIDLineEdit->setText(QString::fromStdString(mitk::DICOMHelper::GetIDFromDataNode(dataNode))); + m_Controls.nameLineEdit->setText(QString::fromStdString(mitk::DICOMHelper::GetCaseIDFromDataNode(dataNode))); + m_Controls.dateLineEdit->setText(QString::fromStdString(acquisitionDateAsString)); + m_Controls.timeLineEdit->setText(QString::fromStdString(acquisitionTimeAsString)); + m_Controls.scannerLineEdit->setText(""); + m_Controls.modalityLineEdit->setText(QString::fromStdString(modalityAsString)); + m_Controls.birthdateLineEdit->setText(QString::fromStdString(patiensBirthDateAsString)); + m_Controls.genderLineEdit->setText(QString::fromStdString(genderAsString)); +} diff --git a/Modules/SemanticRelationsUI/src/QmitkPatientInfoWidgetControls.ui b/Modules/SemanticRelationsUI/src/QmitkPatientInfoWidgetControls.ui new file mode 100644 index 0000000000..3ffbe69f32 --- /dev/null +++ b/Modules/SemanticRelationsUI/src/QmitkPatientInfoWidgetControls.ui @@ -0,0 +1,166 @@ + + + QmitkPatientInfoWidgetControls + + + true + + + + 0 + 0 + 350 + 300 + + + + + + + + 75 + false + true + + + + Patient data information + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + ID*: + + + + + + + true + + + + + + + Name: + + + + + + + true + + + + + + + Acq. Date: + + + + + + + true + + + YYYY-MM-DD + + + + + + + Time: + + + + + + + true + + + hh:mm:ss + + + + + + + Scanner: + + + + + + + true + + + + + + + Modality: + + + + + + + true + + + + + + + Birthdate: + + + + + + + true + + + YYYY-MM-DD + + + + + + + Gender: + + + + + + + true + + + + + + + * mandatory field + + + + + + + + diff --git a/Modules/SemanticRelationsUI/src/QmitkPatientTableHeaderView.cpp b/Modules/SemanticRelationsUI/src/QmitkPatientTableHeaderView.cpp new file mode 100644 index 0000000000..e35970cd00 --- /dev/null +++ b/Modules/SemanticRelationsUI/src/QmitkPatientTableHeaderView.cpp @@ -0,0 +1,244 @@ +/*=================================================================== + +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 UI module +#include "QmitkPatientTableHeaderView.h" + +// qt +#include +#include + +QmitkPatientTableHeaderView::QmitkPatientTableHeaderView(QWidget* parent/* = nullptr*/) + : QHeaderView(Qt::Horizontal, parent) + , m_HeaderModel(nullptr) +{ + // nothing here +} + +QmitkPatientTableHeaderView::~QmitkPatientTableHeaderView() +{ + // nothing here +} + +void QmitkPatientTableHeaderView::setModel(QAbstractItemModel* model) +{ + // retrieve the header model from the given table model + QVariant variant = model->data(QModelIndex(), HorizontalHeaderDataRole); + if (variant.isValid() && variant.canConvert()) + { + m_HeaderModel = variant.value(); + } + + QHeaderView::setModel(model); +} + +void QmitkPatientTableHeaderView::paintSection(QPainter* painter, const QRect& rect, int logicalIndex) const +{ + if (rect.isValid()) + { + int top = rect.y(); + + QModelIndex leafIndex = HeaderIndex(logicalIndex); + QModelIndexList indexes = ParentIndexList(leafIndex); + for (const auto& index : indexes) + { + top = PaintHeader(painter, index, logicalIndex, rect, top); + } + + return; + } + + QHeaderView::paintSection(painter, rect, logicalIndex); +} + +QSize QmitkPatientTableHeaderView::sectionSizeFromContents(int logicalIndex) const +{ + if (nullptr != m_HeaderModel) + { + QModelIndex headerIndex = HeaderIndex(logicalIndex); + if (headerIndex.isValid()) + { + QSize headerSize = HeaderSize(headerIndex); + headerIndex = headerIndex.parent(); + while (headerIndex.isValid()) + { + QSize currentHeaderSize = HeaderSize(headerIndex); + headerSize.rheight() += currentHeaderSize.height(); + if (currentHeaderSize.width() > headerSize.width()) + { + headerSize.rwidth() = currentHeaderSize.width(); + } + + headerIndex = headerIndex.parent(); + } + return headerSize; + } + } + + return QHeaderView::sectionSizeFromContents(logicalIndex); +} + +int QmitkPatientTableHeaderView::PaintHeader(QPainter* painter, const QModelIndex& currentIndex, int logicalIndex, const QRect& sectionRect, int top) const +{ + QModelIndex headerIndex = HeaderIndex(logicalIndex); + int height = HeaderSize(currentIndex).height(); + if (currentIndex == headerIndex) + { + height = sectionRect.height() - top; + } + int left = CurrentHeaderLeft(currentIndex, headerIndex, logicalIndex, sectionRect.left()); + int width = CurrentHeaderWidth(currentIndex, headerIndex, logicalIndex); + + QStyleOptionHeader headerStyleOptions; + initStyleOption(&headerStyleOptions); + headerStyleOptions.text = currentIndex.data(Qt::DisplayRole).toString(); + headerStyleOptions.textAlignment = Qt::AlignCenter; + painter->save(); + + QRect rect(left, top, width, height); + headerStyleOptions.rect = rect; + style()->drawControl(QStyle::CE_Header, &headerStyleOptions, painter, this); + painter->restore(); + + return top + height; +} + +QSize QmitkPatientTableHeaderView::HeaderSize(const QModelIndex& index) const +{ + QFont font = this->font(); + font.setBold(true); + QFontMetrics fontMetrics(font); + QSize fontSize = fontMetrics.size(0, index.data(Qt::DisplayRole).toString()); + QSize emptyFontSize = fontMetrics.size(0, ""); + + return fontSize + emptyFontSize; +} + +int QmitkPatientTableHeaderView::CurrentHeaderLeft(const QModelIndex& currentIndex, const QModelIndex& headerIndex, int sectionIndex, int left) const +{ + QModelIndexList headerList = ListHeader(currentIndex); + if (!headerList.empty()) + { + int index = headerList.indexOf(headerIndex); + int firstHeaderSectionIndex = sectionIndex - index; + --index; + for (; index >= 0; --index) + { + left -= sectionSize(firstHeaderSectionIndex + index); + } + } + + return left; +} + +int QmitkPatientTableHeaderView::CurrentHeaderWidth(const QModelIndex& currentIndex, const QModelIndex& headerIndex, int sectionIndex) const +{ + QModelIndexList headerList = ListHeader(currentIndex); + if (headerList.empty()) + { + return sectionSize(sectionIndex); + } + + int width = 0; + int index = headerList.indexOf(headerIndex); + int firstHeaderSectionIndex = sectionIndex - index; + for (int i = 0; i < headerList.size(); ++i) + { + width += sectionSize(firstHeaderSectionIndex + i); + } + + return width; +} + +QModelIndexList QmitkPatientTableHeaderView::ParentIndexList(QModelIndex index) const +{ + QModelIndexList indexList; + while (index.isValid()) + { + indexList.push_front(index); + index = index.parent(); + } + + return indexList; +} + +QModelIndex QmitkPatientTableHeaderView::HeaderIndex(int sectionIndex) const +{ + if (nullptr != m_HeaderModel) + { + int currentHeaderIndex = -1; + for (int i = 0; i < m_HeaderModel->columnCount(); ++i) + { + QModelIndex modelIndex = FindHeader(m_HeaderModel->index(0, i), sectionIndex, currentHeaderIndex); + if (modelIndex.isValid()) + { + return modelIndex; + } + } + } + + return QModelIndex(); +} + +QModelIndex QmitkPatientTableHeaderView::FindHeader(const QModelIndex& currentIndex, int sectionIndex, int& currentHeaderIndex) const +{ + if (currentIndex.isValid()) + { + int childCount = currentIndex.model()->columnCount(currentIndex); + if (childCount > 0) + { + for (int i = 0; i < childCount; ++i) + { + QModelIndex modelIndex = FindHeader(currentIndex.child(0, i), sectionIndex, currentHeaderIndex); + if (modelIndex.isValid()) + { + return modelIndex; + } + } + } + else + { + ++currentHeaderIndex; + if (currentHeaderIndex == sectionIndex) + { + return currentIndex; + } + } + } + + return QModelIndex(); +} + +QModelIndexList QmitkPatientTableHeaderView::ListHeader(const QModelIndex& currentIndex) const +{ + QModelIndexList headerList; + if (currentIndex.isValid()) + { + int childCount = currentIndex.model()->columnCount(currentIndex); + if (childCount > 0) + { + for (int i = 0; i < childCount; ++i) + { + headerList += ListHeader(currentIndex.child(0, i)); + } + } + else + { + headerList.push_back(currentIndex); + } + } + return headerList; +} diff --git a/Modules/SemanticRelationsUI/src/QmitkPatientTableInspector.cpp b/Modules/SemanticRelationsUI/src/QmitkPatientTableInspector.cpp new file mode 100644 index 0000000000..9f8f4d3ca8 --- /dev/null +++ b/Modules/SemanticRelationsUI/src/QmitkPatientTableInspector.cpp @@ -0,0 +1,183 @@ +/*=================================================================== + +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 UI module +#include "QmitkPatientTableInspector.h" +#include "QmitkPatientTableHeaderView.h" + +// mitk qt widgets module +#include "QmitkCustomVariants.h" +#include "QmitkEnums.h" + +// qt +#include +#include + +QmitkPatientTableInspector::QmitkPatientTableInspector(QWidget* parent/* =nullptr*/) + : QmitkAbstractSemanticRelationsStorageInspector(parent) +{ + m_Controls.setupUi(this); + + QmitkPatientTableHeaderView* patientTableHeaderView = new QmitkPatientTableHeaderView(m_Controls.tableView); + m_Controls.tableView->setHorizontalHeader(patientTableHeaderView); + m_Controls.tableView->horizontalHeader()->setHighlightSections(false); + m_Controls.tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed); + m_Controls.tableView->verticalHeader()->setHighlightSections(false); + m_Controls.tableView->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); + m_Controls.tableView->setSelectionMode(QAbstractItemView::SingleSelection); + m_Controls.tableView->setSelectionBehavior(QAbstractItemView::SelectItems); + m_Controls.tableView->setContextMenuPolicy(Qt::CustomContextMenu); + + m_StorageModel = new QmitkPatientTableModel(m_Controls.tableView); + m_Controls.tableView->setModel(m_StorageModel); + m_ItemDelegate = new QmitkTableItemThumbnailDelegate(m_Controls.tableView); + //m_Controls.tableView->setItemDelegate(m_ItemDelegate); + + SetUpConnections(); +} + +QAbstractItemView* QmitkPatientTableInspector::GetView() +{ + return m_Controls.tableView; +} + +const QAbstractItemView* QmitkPatientTableInspector::GetView() const +{ + return m_Controls.tableView; +} + +void QmitkPatientTableInspector::SetSelectionMode(SelectionMode mode) +{ + m_Controls.tableView->setSelectionMode(mode); +} + +QmitkPatientTableInspector::SelectionMode QmitkPatientTableInspector::GetSelectionMode() const +{ + return m_Controls.tableView->selectionMode(); +} + +void QmitkPatientTableInspector::SetCaseID(const mitk::SemanticTypes::CaseID& caseID) +{ + m_StorageModel->SetCaseID(caseID); +} + +void QmitkPatientTableInspector::SetLesion(const mitk::SemanticTypes::Lesion& lesion) +{ + m_StorageModel->SetLesion(lesion); +} + +QItemSelectionModel* QmitkPatientTableInspector::GetSelectionModel() +{ + return m_Controls.tableView->selectionModel(); +} + +void QmitkPatientTableInspector::Initialize() +{ + if (m_DataStorage.IsExpired()) + { + return; + } + + auto dataStorage = m_DataStorage.Lock(); + + m_StorageModel->SetDataStorage(dataStorage); + m_StorageModel->SetNodePredicate(m_NodePredicate); + + m_Connector->SetView(m_Controls.tableView); +} + +void QmitkPatientTableInspector::OnModelUpdated() +{ + m_Controls.tableView->resizeRowsToContents(); + m_Controls.tableView->resizeColumnsToContents(); +} + +void QmitkPatientTableInspector::OnNodeButtonClicked(const QString& nodeType) +{ + m_StorageModel->SetNodeType(nodeType.toStdString()); +} + +void QmitkPatientTableInspector::OnDataNodeSelectionChanged(const QList& dataNodeSelection) +{ + if (m_StorageModel->GetLesion().UID.empty()) + { + return; + } + + // if lesion is set, reset to empty lesion to hide the "lesion presence background highlighting" in the model + m_StorageModel->SetLesion(mitk::SemanticTypes::Lesion()); + // need to explicitly set the data node selection + SetCurrentSelection(dataNodeSelection); +} + +void QmitkPatientTableInspector::OnItemDoubleClicked(const QModelIndex& itemIndex) +{ + if (itemIndex.isValid()) + { + QVariant qvariantDataNode = m_StorageModel->data(itemIndex, QmitkDataNodeRawPointerRole); + if (qvariantDataNode.canConvert()) + { + mitk::DataNode* dataNode = qvariantDataNode.value(); + emit DataNodeDoubleClicked(dataNode); + } + } +} + +void QmitkPatientTableInspector::SetUpConnections() +{ + connect(m_StorageModel, &QmitkPatientTableModel::ModelUpdated, this, &QmitkPatientTableInspector::OnModelUpdated); + connect(m_Controls.tableView, &QTableView::customContextMenuRequested, this, &QmitkPatientTableInspector::OnContextMenuRequested); + + QSignalMapper* nodeButtonSignalMapper = new QSignalMapper(this); + nodeButtonSignalMapper->setMapping(m_Controls.imageNodeButton, QString("Image")); + nodeButtonSignalMapper->setMapping(m_Controls.segmentationNodeButton, QString("Segmentation")); + connect(nodeButtonSignalMapper, static_cast(&QSignalMapper::mapped), this, &QmitkPatientTableInspector::OnNodeButtonClicked); + connect(m_Controls.imageNodeButton, &QRadioButton::clicked, nodeButtonSignalMapper, static_cast(&QSignalMapper::map)); + connect(m_Controls.segmentationNodeButton, &QRadioButton::clicked, nodeButtonSignalMapper, static_cast(&QSignalMapper::map)); + m_Controls.imageNodeButton->setChecked(true); + + connect(this, &QmitkPatientTableInspector::CurrentSelectionChanged, this, &QmitkPatientTableInspector::OnDataNodeSelectionChanged); + connect(m_Controls.tableView, &QTableView::doubleClicked, this, &QmitkPatientTableInspector::OnItemDoubleClicked); +} + +void QmitkPatientTableInspector::keyPressEvent(QKeyEvent* e) +{ + mitk::DataNode* dataNode = nullptr; + QModelIndex selectedIndex = m_Controls.tableView->currentIndex(); + if (selectedIndex.isValid()) + { + QVariant qvariantDataNode = m_StorageModel->data(selectedIndex, QmitkDataNodeRawPointerRole); + if (qvariantDataNode.canConvert()) + { + dataNode = qvariantDataNode.value(); + } + } + + if (nullptr == dataNode) + { + return; + } + + int key = e->key(); + switch (key) + { + case Qt::Key_Delete: + emit OnNodeRemoved(dataNode); + break; + default: + break; + } +} diff --git a/Modules/SemanticRelationsUI/src/QmitkPatientTableInspector.ui b/Modules/SemanticRelationsUI/src/QmitkPatientTableInspector.ui new file mode 100644 index 0000000000..c953d1b58a --- /dev/null +++ b/Modules/SemanticRelationsUI/src/QmitkPatientTableInspector.ui @@ -0,0 +1,63 @@ + + + QmitkPatientTableInspector + + + + 0 + 0 + 350 + 300 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Semantic Relations content: + + + + + + + Show segmentation nodes + + + nodeButtonGroup + + + + + + + Show image nodes + + + nodeButtonGroup + + + + + + + + + + + + + + diff --git a/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp b/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp new file mode 100644 index 0000000000..17069bfe7f --- /dev/null +++ b/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp @@ -0,0 +1,349 @@ +/*=================================================================== + +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 UI module +#include "QmitkPatientTableModel.h" +#include "QmitkPatientTableHeaderView.h" +#include "QmitkSemanticRelationsUIHelper.h" + +// semantic relations module +#include +#include +#include +#include +#include + +#include +#include + +// qt +#include + +// c++ +#include +#include + +QmitkPatientTableModel::QmitkPatientTableModel(QObject* parent /*= nullptr*/) + : QmitkAbstractSemanticRelationsStorageModel(parent) + , m_SelectedNodeType("Image") +{ + m_HeaderModel = new QStandardItemModel(this); +} + +QmitkPatientTableModel::~QmitkPatientTableModel() +{ + // nothing here +} + +QModelIndex QmitkPatientTableModel::index(int row, int column, const QModelIndex& parent/* = QModelIndex()*/) const +{ + if (hasIndex(row, column, parent)) + { + return createIndex(row, column); + } + + return QModelIndex(); +} + +QModelIndex QmitkPatientTableModel::parent(const QModelIndex& /*child*/) const +{ + return QModelIndex(); +} + +int QmitkPatientTableModel::rowCount(const QModelIndex& parent/* = QModelIndex()*/) const +{ + if (parent.isValid()) + { + return 0; + } + + return m_InformationTypes.size(); +} + +int QmitkPatientTableModel::columnCount(const QModelIndex& parent/* = QModelIndex()*/) const +{ + if (parent.isValid()) + { + return 0; + } + + return m_ControlPoints.size(); +} + +QVariant QmitkPatientTableModel::data(const QModelIndex& index, int role/* = Qt::DisplayRole*/) const +{ + // special role for returning the horizontal header + if (QmitkPatientTableHeaderView::HorizontalHeaderDataRole == role) + { + return QVariant::fromValue(m_HeaderModel); + } + + if (!index.isValid()) + { + return QVariant(); + } + + if (index.row() < 0 || index.row() >= static_cast(m_InformationTypes.size()) + || index.column() < 0 || index.column() >= static_cast(m_ControlPoints.size())) + { + return QVariant(); + } + + mitk::DataNode* dataNode = GetCurrentDataNode(index); + if (nullptr == dataNode) + { + return QVariant(); + } + + if (Qt::DecorationRole == role) + { + auto it = m_PixmapMap.find(dataNode); + if (it != m_PixmapMap.end()) + { + return QVariant(it->second); + } + } + + if (QmitkDataNodeRole == role) + { + return QVariant::fromValue(mitk::DataNode::Pointer(dataNode)); + } + + if (QmitkDataNodeRawPointerRole == role) + { + return QVariant::fromValue(dataNode); + } + + if (Qt::BackgroundColorRole == role) + { + auto it = m_LesionPresence.find(dataNode); + if (it != m_LesionPresence.end()) + { + return it->second ? QVariant(QColor(Qt::darkGreen)) : QVariant(QColor(Qt::transparent)); + } + + return QVariant(QColor(Qt::transparent)); + } + + return QVariant(); +} + +QVariant QmitkPatientTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (Qt::Vertical == orientation && Qt::DisplayRole == role) + { + if (static_cast(m_InformationTypes.size()) > section) + { + mitk::SemanticTypes::InformationType currentInformationType = m_InformationTypes.at(section); + return QVariant(QString::fromStdString(currentInformationType)); + } + } + + return QVariant(); +} + +Qt::ItemFlags QmitkPatientTableModel::flags(const QModelIndex& index) const +{ + Qt::ItemFlags flags; + mitk::DataNode* dataNode = GetCurrentDataNode(index); + if (nullptr != dataNode) + { + flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; + } + + return flags; +} + +void QmitkPatientTableModel::SetNodeType(const std::string& nodeType) +{ + m_SelectedNodeType = nodeType; + UpdateModelData(); +} + +void QmitkPatientTableModel::NodePredicateChanged() +{ + UpdateModelData(); +} + +void QmitkPatientTableModel::SetData() +{ + // get all control points of current case + m_ControlPoints = mitk::RelationStorage::GetAllControlPointsOfCase(m_CaseID); + // sort the vector of control points for the timeline + std::sort(m_ControlPoints.begin(), m_ControlPoints.end()); + + // get all examination periods of current case + m_ExaminationPeriods = mitk::RelationStorage::GetAllExaminationPeriodsOfCase(m_CaseID); + // sort the vector of examination periods for the timeline + mitk::SortExaminationPeriods(m_ExaminationPeriods, m_ControlPoints); + + // get all information types points of current case + m_InformationTypes = mitk::RelationStorage::GetAllInformationTypesOfCase(m_CaseID); + + if ("Image" == m_SelectedNodeType) + { + m_CurrentDataNodes = m_SemanticRelationsDataStorageAccess->GetAllImagesOfCase(m_CaseID); + } + else if ("Segmentation" == m_SelectedNodeType) + { + m_CurrentDataNodes = m_SemanticRelationsDataStorageAccess->GetAllSegmentationsOfCase(m_CaseID); + } + + SetHeaderModel(); + SetPixmaps(); + SetLesionPresences(); +} + +void QmitkPatientTableModel::SetHeaderModel() +{ + m_HeaderModel->clear(); + QStandardItem* rootItem = new QStandardItem("Timeline"); + QList standardItems; + + for (const auto& examinationPeriod : m_ExaminationPeriods) + { + QStandardItem* examinationPeriodItem = new QStandardItem(QString::fromStdString(examinationPeriod.name)); + standardItems.push_back(examinationPeriodItem); + rootItem->appendColumn(standardItems); + standardItems.clear(); + + const auto& currentControlPoints = examinationPeriod.controlPointUIDs; + for (const auto& controlPointUID : currentControlPoints) + { + const auto& controlPoint = mitk::GetControlPointByUID(controlPointUID, m_ControlPoints); + QStandardItem* controlPointItem = new QStandardItem(QString::fromStdString(controlPoint.ToString())); + standardItems.push_back(controlPointItem); + examinationPeriodItem->appendColumn(standardItems); + standardItems.clear(); + } + } + + m_HeaderModel->setItem(0, 0, rootItem); +} + +void QmitkPatientTableModel::SetPixmaps() +{ + m_PixmapMap.clear(); + for (const auto& dataNode : m_CurrentDataNodes) + { + // set the pixmap for the current node + QPixmap pixmapFromImage = QmitkSemanticRelationsUIHelper::GetPixmapFromImageNode(dataNode); + SetPixmapOfNode(dataNode, &pixmapFromImage); + } +} + +void QmitkPatientTableModel::SetPixmapOfNode(const mitk::DataNode* dataNode, QPixmap* pixmapFromImage) +{ + if (nullptr == dataNode) + { + return; + } + + std::map::iterator iter = m_PixmapMap.find(dataNode); + if (iter != m_PixmapMap.end()) + { + // key already existing + if (nullptr != pixmapFromImage) + { + // overwrite already stored pixmap + iter->second = pixmapFromImage->scaled(120, 120, Qt::IgnoreAspectRatio); + } + else + { + // remove key if no pixmap is given + m_PixmapMap.erase(iter); + } + } + else + { + m_PixmapMap.insert(std::make_pair(dataNode, pixmapFromImage->scaled(120, 120, Qt::IgnoreAspectRatio))); + } +} + +void QmitkPatientTableModel::SetLesionPresences() +{ + m_LesionPresence.clear(); + if (!mitk::SemanticRelationsInference::InstanceExists(m_CaseID, m_Lesion)) + { + return; + } + + for (const auto& dataNode : m_CurrentDataNodes) + { + if (!mitk::SemanticRelationsInference::InstanceExists(dataNode)) + { + continue; + } + + try + { + // set the lesion presence for the current node + bool lesionPresence = mitk::SemanticRelationsInference::IsLesionPresent(m_Lesion, dataNode); + SetLesionPresenceOfNode(dataNode, lesionPresence); + } + catch (const mitk::SemanticRelationException&) + { + continue; + } + } +} + +void QmitkPatientTableModel::SetLesionPresenceOfNode(const mitk::DataNode* dataNode, bool lesionPresence) +{ + std::map::iterator iter = m_LesionPresence.find(dataNode); + if (iter != m_LesionPresence.end()) + { + // key already existing, overwrite already stored bool value + iter->second = lesionPresence; + } + else + { + m_LesionPresence.insert(std::make_pair(dataNode, lesionPresence)); + } +} + +mitk::DataNode* QmitkPatientTableModel::GetCurrentDataNode(const QModelIndex& index) const +{ + if (!index.isValid()) + { + return nullptr; + } + + mitk::SemanticTypes::ControlPoint currentControlPoint = m_ControlPoints.at(index.column()); + mitk::SemanticTypes::InformationType currentInformationType = m_InformationTypes.at(index.row()); + try + { + std::vector filteredDataNodes; + if ("Image" == m_SelectedNodeType) + { + filteredDataNodes = m_SemanticRelationsDataStorageAccess->GetAllSpecificImages(m_CaseID, currentControlPoint, currentInformationType); + } + else if ("Segmentation" == m_SelectedNodeType) + { + filteredDataNodes = m_SemanticRelationsDataStorageAccess->GetAllSpecificSegmentations(m_CaseID, currentControlPoint, currentInformationType); + } + + if (filteredDataNodes.empty()) + { + return nullptr; + } + return filteredDataNodes.front(); + } + catch (const mitk::SemanticRelationException&) + { + return nullptr; + } +} diff --git a/Modules/SemanticRelationsUI/src/QmitkSemanticRelationsUIHelper.cpp b/Modules/SemanticRelationsUI/src/QmitkSemanticRelationsUIHelper.cpp new file mode 100644 index 0000000000..31ba46b7d2 --- /dev/null +++ b/Modules/SemanticRelationsUI/src/QmitkSemanticRelationsUIHelper.cpp @@ -0,0 +1,95 @@ +/*=================================================================== + +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 ui module +#include + +// mitk core +#include +#include +#include +#include + +// vtk +#include + +QPixmap QmitkSemanticRelationsUIHelper::GetPixmapFromImageNode(const mitk::DataNode* dataNode) +{ + if (nullptr == dataNode) + { + return QPixmap(); + } + + const mitk::Image* image = static_cast(dataNode->GetData()); + if (nullptr == image || !image->IsInitialized()) + { + return QPixmap(); + } + + if (image->GetPixelType().GetNumberOfComponents() != 1) // for now only single component are allowed + { + return QPixmap(); + } + + mitk::PlaneGeometry::Pointer sagittalPlaneGeometry = mitk::PlaneGeometry::New(); + int sliceNumber = image->GetDimension(1) / 2; + sagittalPlaneGeometry->InitializeStandardPlane(image->GetGeometry(), mitk::PlaneGeometry::Sagittal, sliceNumber); + + mitk::ExtractSliceFilter::Pointer extractSliceFilter = mitk::ExtractSliceFilter::New(); + extractSliceFilter->SetInput(image); + extractSliceFilter->SetInterpolationMode(mitk::ExtractSliceFilter::RESLICE_CUBIC); + extractSliceFilter->SetResliceTransformByGeometry(image->GetGeometry()); + extractSliceFilter->SetWorldGeometry(sagittalPlaneGeometry); + extractSliceFilter->SetOutputDimensionality(2); + extractSliceFilter->SetVtkOutputRequest(true); + extractSliceFilter->Update(); + + /* + mitk::Vector3D spacing; + mitk::FillVector3D(spacing,1.0,1.0,1.0); + slice->GetGeometry()->SetSpacing(spacing); + // save image slice + mitk::IOUtil::SaveImage( slice, d->m_Path + fileName + ".png" ); + */ + + vtkImageData* imageData = extractSliceFilter->GetVtkOutput(); + + mitk::LevelWindow levelWindow; + dataNode->GetLevelWindow(levelWindow); + vtkSmartPointer lookupTable = vtkSmartPointer::New(); + lookupTable->SetRange(levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound()); + lookupTable->SetSaturationRange(0.0, 0.0); + lookupTable->SetValueRange(0.0, 1.0); + lookupTable->SetHueRange(0.0, 0.0); + lookupTable->SetRampToLinear(); + + vtkSmartPointer levelWindowFilter = vtkSmartPointer::New(); + levelWindowFilter->SetLookupTable(lookupTable); + levelWindowFilter->SetInputData(imageData); + levelWindowFilter->SetMinOpacity(0.0); + levelWindowFilter->SetMaxOpacity(1.0); + int dims[3]; + imageData->GetDimensions(dims); + double clippingBounds[] = { 0.0, static_cast(dims[0]), 0.0, static_cast(dims[1]) }; + levelWindowFilter->SetClippingBounds(clippingBounds); + levelWindowFilter->Update(); + imageData = levelWindowFilter->GetOutput(); + + QImage thumbnailImage(reinterpret_cast(imageData->GetScalarPointer()), dims[0], dims[1], QImage::Format_ARGB32); + + thumbnailImage = thumbnailImage.rgbSwapped().mirrored(false, true); + return QPixmap::fromImage(thumbnailImage); +} diff --git a/Modules/SemanticRelationsUI/src/QmitkTableItemThumbnailDelegate.cpp b/Modules/SemanticRelationsUI/src/QmitkTableItemThumbnailDelegate.cpp new file mode 100644 index 0000000000..02091ed14d --- /dev/null +++ b/Modules/SemanticRelationsUI/src/QmitkTableItemThumbnailDelegate.cpp @@ -0,0 +1,46 @@ +/*=================================================================== + +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 UI module +#include "QmitkTableItemThumbnailDelegate.h" + +// qt +#include +#include +#include + +QmitkTableItemThumbnailDelegate::QmitkTableItemThumbnailDelegate(QObject* parent /*= nullptr*/) + : QStyledItemDelegate(parent) +{ + // nothing here +} + +void QmitkTableItemThumbnailDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + QPixmap pixmap = index.data(Qt::DecorationRole).value(); + QPoint point = option.rect.center() - pixmap.rect().center(); + painter->save(); + + if (option.state & QStyle::State_Selected) + { + painter->setBrush(option.palette.highlightedText()); + const QWidget* widget = option.widget; + QStyle* style = widget ? widget->style() : QApplication::style(); + style->drawControl(QStyle::CE_ItemViewItem, &option, painter, widget); + } + painter->drawPixmap(point, pixmap); + painter->restore(); +} diff --git a/Modules/SemanticRelationsUI/src/mitkModuleActivator.cpp b/Modules/SemanticRelationsUI/src/mitkModuleActivator.cpp new file mode 100644 index 0000000000..38148b4e78 --- /dev/null +++ b/Modules/SemanticRelationsUI/src/mitkModuleActivator.cpp @@ -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. + +===================================================================*/ + +#include "mitkModuleActivator.h" + +// semantic relations ui module +#include "QmitkPatientTableInspector.h" + +// qt widgets module +#include "QmitkDataStorageInspectorProviderBase.h" + +void mitk::SemanticRelationsUIActivator::Load(us::ModuleContext* /*context*/) +{ + m_PatientTableInspector.reset(new QmitkDataStorageInspectorProviderBase("org.mitk.QmitkPatientTableInspector", + "Patient table inspector", "Displays the content of the semantic relations storage in a control-point - modality table.")); + } + +void mitk::SemanticRelationsUIActivator::Unload(us::ModuleContext* /*context*/) +{ +} + +US_EXPORT_MODULE_ACTIVATOR(mitk::SemanticRelationsUIActivator) diff --git a/Plugins/PluginList.cmake b/Plugins/PluginList.cmake index 548f9f33b4..f3316ce4d6 100644 --- a/Plugins/PluginList.cmake +++ b/Plugins/PluginList.cmake @@ -1,114 +1,115 @@ # Plug-ins must be ordered according to their dependencies set(MITK_PLUGINS org.blueberry.core.runtime:ON org.blueberry.core.expressions:OFF org.blueberry.core.commands:OFF org.blueberry.core.jobs:OFF org.blueberry.ui.qt:OFF org.blueberry.ui.qt.help:ON org.blueberry.ui.qt.log:ON org.blueberry.ui.qt.objectinspector:OFF #org.blueberry.test:ON #org.blueberry.uitest:ON #Testing/org.blueberry.core.runtime.tests:ON #Testing/org.blueberry.osgi.tests:ON org.mitk.core.services:ON org.mitk.gui.common:ON org.mitk.planarfigure:ON org.mitk.core.ext:OFF org.mitk.core.jobs:OFF org.mitk.gui.qt.application:ON org.mitk.gui.qt.coreapplication:OFF org.mitk.gui.qt.ext:OFF org.mitk.gui.qt.extapplication:OFF org.mitk.gui.qt.common:ON org.mitk.gui.qt.stdmultiwidgeteditor:ON org.mitk.gui.qt.common.legacy:OFF org.mitk.gui.qt.cmdlinemodules:OFF org.mitk.gui.qt.chartExample:OFF org.mitk.gui.qt.diffusionimagingapp:OFF org.mitk.gui.qt.datamanager:ON org.mitk.gui.qt.datamanagerlight:OFF org.mitk.gui.qt.datastorageviewertest:OFF org.mitk.gui.qt.properties:ON org.mitk.gui.qt.basicimageprocessing:OFF org.mitk.gui.qt.dicom:OFF org.mitk.gui.qt.dicominspector:OFF org.mitk.gui.qt.diffusionimaging:OFF org.mitk.gui.qt.diffusionimaging.connectomics:OFF org.mitk.gui.qt.diffusionimaging.denoising:OFF org.mitk.gui.qt.diffusionimaging.fiberfox:OFF org.mitk.gui.qt.diffusionimaging.fiberprocessing:OFF org.mitk.gui.qt.diffusionimaging.ivim:OFF org.mitk.gui.qt.diffusionimaging.odfpeaks:OFF org.mitk.gui.qt.diffusionimaging.partialvolume:OFF org.mitk.gui.qt.diffusionimaging.preprocessing:OFF org.mitk.gui.qt.diffusionimaging.reconstruction:OFF org.mitk.gui.qt.diffusionimaging.registration:OFF org.mitk.gui.qt.diffusionimaging.tractography:OFF org.mitk.gui.qt.diffusionimaging.python:OFF org.mitk.gui.qt.dosevisualization:OFF org.mitk.gui.qt.geometrytools:OFF org.mitk.gui.qt.igtexamples:OFF org.mitk.gui.qt.igttracking:OFF org.mitk.gui.qt.lasercontrol:OFF org.mitk.gui.qt.openigtlink:OFF org.mitk.gui.qt.imagecropper:OFF org.mitk.gui.qt.imagenavigator:ON org.mitk.gui.qt.viewnavigator:OFF org.mitk.gui.qt.materialeditor:OFF org.mitk.gui.qt.measurementtoolbox:OFF org.mitk.gui.qt.moviemaker:OFF org.mitk.gui.qt.pointsetinteraction:OFF org.mitk.gui.qt.pointsetinteractionmultispectrum:OFF org.mitk.gui.qt.python:OFF org.mitk.gui.qt.remeshing:OFF org.mitk.gui.qt.segmentation:OFF org.mitk.gui.qt.aicpregistration:OFF org.mitk.gui.qt.renderwindowmanager:OFF + org.mitk.gui.qt.semanticrelations:OFF org.mitk.gui.qt.toftutorial:OFF org.mitk.gui.qt.tofutil:OFF org.mitk.gui.qt.tubegraph:OFF org.mitk.gui.qt.ugvisualization:OFF org.mitk.gui.qt.photoacoustics.pausviewer:OFF org.mitk.gui.qt.photoacoustics.pausmotioncompensation:OFF org.mitk.gui.qt.photoacoustics.imageprocessing:OFF org.mitk.gui.qt.photoacoustics.simulation:OFF org.mitk.gui.qt.photoacoustics.spectralunmixing:OFF org.mitk.gui.qt.ultrasound:OFF org.mitk.gui.qt.volumevisualization:OFF org.mitk.gui.qt.eventrecorder:OFF org.mitk.gui.qt.xnat:OFF org.mitk.gui.qt.igt.app.echotrack:OFF org.mitk.gui.qt.spectrocamrecorder:OFF org.mitk.gui.qt.classificationsegmentation:OFF org.mitk.gui.qt.overlaymanager:OFF org.mitk.gui.qt.igt.app.hummelprotocolmeasurements:OFF org.mitk.gui.qt.multilabelsegmentation:OFF org.mitk.matchpoint.core.helper:OFF org.mitk.gui.qt.matchpoint.algorithm.browser:OFF org.mitk.gui.qt.matchpoint.algorithm.control:OFF org.mitk.gui.qt.matchpoint.algorithm.batch:OFF org.mitk.gui.qt.matchpoint.mapper:OFF org.mitk.gui.qt.matchpoint.framereg:OFF org.mitk.gui.qt.matchpoint.visualizer:OFF org.mitk.gui.qt.matchpoint.evaluator:OFF org.mitk.gui.qt.matchpoint.manipulator:OFF org.mitk.gui.qt.preprocessing.resampling:OFF org.mitk.gui.qt.radiomics:OFF org.mitk.gui.qt.cest:OFF org.mitk.gui.qt.fit.demo:OFF org.mitk.gui.qt.fit.inspector:OFF org.mitk.gui.qt.fit.genericfitting:OFF org.mitk.gui.qt.pharmacokinetics.mri:OFF org.mitk.gui.qt.pharmacokinetics.pet:OFF org.mitk.gui.qt.pharmacokinetics.simulation:OFF org.mitk.gui.qt.pharmacokinetics.curvedescriptor:OFF org.mitk.gui.qt.pharmacokinetics.concentration.mri:OFF ) diff --git a/Plugins/org.mitk.gui.qt.application/files.cmake b/Plugins/org.mitk.gui.qt.application/files.cmake index 6af0070f89..9bf54191ea 100644 --- a/Plugins/org.mitk.gui.qt.application/files.cmake +++ b/Plugins/org.mitk.gui.qt.application/files.cmake @@ -1,85 +1,87 @@ set(SRC_CPP_FILES QmitkAbstractDataNodeAction.cpp QmitkCloseProjectAction.cpp QmitkDataNodeColorAction.cpp QmitkDataNodeColorMapAction.cpp QmitkDataNodeComponentAction.cpp QmitkDataNodeContextMenu.cpp QmitkDataNodeGlobalReinitAction.cpp QmitkDataNodeHideAllAction.cpp QmitkDataNodeOpacityAction.cpp + QmitkDataNodeOpenInAction.cpp QmitkDataNodeReinitAction.cpp QmitkDataNodeRemoveAction.cpp QmitkDataNodeShowDetailsAction.cpp QmitkDataNodeShowSelectedNodesAction.cpp QmitkDataNodeSurfaceRepresentationAction.cpp QmitkDataNodeTextureInterpolationAction.cpp QmitkDataNodeToggleVisibilityAction.cpp QmitkDefaultDropTargetListener.cpp QmitkFileExitAction.cpp QmitkFileOpenAction.cpp QmitkFileSaveAction.cpp QmitkUndoAction.cpp QmitkRedoAction.cpp QmitkPreferencesDialog.cpp QmitkStatusBar.cpp ) set(INTERNAL_CPP_FILES org_mitk_gui_qt_application_Activator.cpp QmitkEditorsPreferencePage.cpp QmitkGeneralPreferencePage.cpp QmitkShowPreferencePageHandler.cpp ) set(MOC_H_FILES src/QmitkAbstractDataNodeAction.h src/QmitkCloseProjectAction.h src/QmitkDataNodeColorAction.h src/QmitkDataNodeColorMapAction.h src/QmitkDataNodeComponentAction.h src/QmitkDataNodeGlobalReinitAction.h src/QmitkDataNodeContextMenu.h src/QmitkDataNodeHideAllAction.h src/QmitkDataNodeOpacityAction.h + src/QmitkDataNodeOpenInAction.h src/QmitkDataNodeReinitAction.h src/QmitkDataNodeRemoveAction.h src/QmitkDataNodeShowDetailsAction.h src/QmitkDataNodeShowSelectedNodesAction.h src/QmitkDataNodeSurfaceRepresentationAction.h src/QmitkDataNodeTextureInterpolationAction.h src/QmitkDataNodeToggleVisibilityAction.h src/QmitkFileExitAction.h src/QmitkFileOpenAction.h src/QmitkFileSaveAction.h src/QmitkUndoAction.h src/QmitkRedoAction.h src/QmitkPreferencesDialog.h src/internal/org_mitk_gui_qt_application_Activator.h src/internal/QmitkEditorsPreferencePage.h src/internal/QmitkGeneralPreferencePage.h src/internal/QmitkShowPreferencePageHandler.h ) set(UI_FILES src/QmitkPreferencesDialog.ui ) set(CACHED_RESOURCE_FILES plugin.xml ) set(QRC_FILES resources/resources.qrc ) set(CPP_FILES ) foreach(file ${SRC_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/${file}) endforeach(file ${SRC_CPP_FILES}) foreach(file ${INTERNAL_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/internal/${file}) endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeColorMapAction.cpp b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeColorMapAction.cpp index bd4fafa3c0..18fcb12e2c 100644 --- a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeColorMapAction.cpp +++ b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeColorMapAction.cpp @@ -1,161 +1,158 @@ /*=================================================================== 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 // mitk core #include #include #include #include #include // mitk gui common plugin #include -// berry -#include - // qt #include QmitkDataNodeColorMapAction::QmitkDataNodeColorMapAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchpartSite) : QAction(parent) , QmitkAbstractDataNodeAction(workbenchpartSite) { setText(tr("Colormap")); InitializeAction(); } QmitkDataNodeColorMapAction::QmitkDataNodeColorMapAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchpartSite) : QAction(parent) , QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite::Pointer(workbenchpartSite)) { setText(tr("Colormap")); InitializeAction(); } QmitkDataNodeColorMapAction::~QmitkDataNodeColorMapAction() { // nothing here } void QmitkDataNodeColorMapAction::InitializeAction() { setCheckable(true); setMenu(new QMenu); connect(menu(), &QMenu::aboutToShow, this, &QmitkDataNodeColorMapAction::OnMenuAboutShow); } void QmitkDataNodeColorMapAction::OnMenuAboutShow() { auto dataNode = GetSelectedNode(); if (dataNode.IsNull()) { return; } mitk::LookupTableProperty::Pointer lookupTableProperty = dynamic_cast(dataNode->GetProperty("LookupTable")); if (lookupTableProperty.IsNull()) { mitk::LookupTable::Pointer mitkLut = mitk::LookupTable::New(); lookupTableProperty = mitk::LookupTableProperty::New(); lookupTableProperty->SetLookupTable(mitkLut); dataNode->SetProperty("LookupTable", lookupTableProperty); } mitk::LookupTable::Pointer lookupTable = lookupTableProperty->GetValue(); if (lookupTable.IsNull()) { return; } menu()->clear(); QAction* tmp; int i = 0; std::string lutType = lookupTable->typenameList[i]; while (lutType != "END_OF_ARRAY") { tmp = menu()->addAction(QString::fromStdString(lutType)); tmp->setCheckable(true); if (lutType == lookupTable->GetActiveTypeAsString()) { tmp->setChecked(true); } connect(tmp, &QAction::triggered, this, &QmitkDataNodeColorMapAction::OnActionTriggered); lutType = lookupTable->typenameList[++i]; } } void QmitkDataNodeColorMapAction::OnActionTriggered(bool /*checked*/) { auto selectedNodes = GetSelectedNodes(); for (auto& dataNode : selectedNodes) { if (dataNode.IsNull()) { continue; } mitk::LookupTableProperty::Pointer lookupTableProperty = dynamic_cast(dataNode->GetProperty("LookupTable")); if (lookupTableProperty.IsNull()) { continue; } mitk::LookupTable::Pointer lookupTable = lookupTableProperty->GetValue(); if (lookupTable.IsNull()) { continue; } QAction* senderAction = qobject_cast(QObject::sender()); if (nullptr == senderAction) { continue; } std::string activatedItem = senderAction->text().toStdString(); lookupTable->SetType(activatedItem); lookupTableProperty->SetValue(lookupTable); if (mitk::LookupTable::LookupTableType::MULTILABEL == lookupTable->GetActiveType()) { // special case: multilabel => set the level window to include the whole pixel range UseWholePixelRange(dataNode); } mitk::RenderingModeProperty::Pointer renderingMode = dynamic_cast(dataNode->GetProperty("Image Rendering.Mode")); renderingMode->SetValue(mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkDataNodeColorMapAction::UseWholePixelRange(mitk::DataNode* node) { auto image = dynamic_cast(node->GetData()); if (nullptr != image) { mitk::LevelWindow levelWindow; levelWindow.SetToImageRange(image); node->SetLevelWindow(levelWindow); } } diff --git a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeComponentAction.cpp b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeComponentAction.cpp index 0250be552f..8e6a9a4468 100644 --- a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeComponentAction.cpp +++ b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeComponentAction.cpp @@ -1,111 +1,108 @@ /*=================================================================== 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 // mitk core #include #include #include // mitk gui common plugin #include -// berry -#include - // qt #include #include QmitkDataNodeComponentAction::QmitkDataNodeComponentAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchpartSite) : QWidgetAction(parent) , QmitkAbstractDataNodeAction(workbenchpartSite) { InitializeAction(); } QmitkDataNodeComponentAction::QmitkDataNodeComponentAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchpartSite) : QWidgetAction(parent) , QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite::Pointer(workbenchpartSite)) { InitializeAction(); } QmitkDataNodeComponentAction::~QmitkDataNodeComponentAction() { // nothing here } void QmitkDataNodeComponentAction::InitializeAction() { setCheckable(true); m_ComponentSlider = new QmitkNumberPropertySlider; m_ComponentSlider->setOrientation(Qt::Horizontal); QLabel* componentLabel = new QLabel(tr("Component: ")); QHBoxLayout* componentWidgetLayout = new QHBoxLayout; componentWidgetLayout->setContentsMargins(4, 4, 4, 4); componentWidgetLayout->addWidget(componentLabel); componentWidgetLayout->addWidget(m_ComponentSlider); QLabel* componentValueLabel = new QLabel(); componentWidgetLayout->addWidget(componentValueLabel); connect(m_ComponentSlider, &QmitkNumberPropertySlider::valueChanged, componentValueLabel, static_cast(&QLabel::setNum)); QWidget* componentWidget = new QWidget; componentWidget->setLayout(componentWidgetLayout); setDefaultWidget(componentWidget); connect(this, &QmitkDataNodeComponentAction::changed, this, &QmitkDataNodeComponentAction::OnActionChanged); } void QmitkDataNodeComponentAction::InitializeWithDataNode(const mitk::DataNode* dataNode) { if (nullptr == dataNode) { m_ComponentSlider->SetProperty(static_cast(nullptr)); return; } mitk::Image* img = dynamic_cast(dataNode->GetData()); if (nullptr == img) { m_ComponentSlider->SetProperty(static_cast(nullptr)); return; } int numComponents = 0; numComponents = img->GetPixelType().GetNumberOfComponents(); mitk::IntProperty* componentProperty = dynamic_cast(dataNode->GetProperty("Image.Displayed Component")); if (numComponents <= 1 || nullptr == componentProperty) { m_ComponentSlider->SetProperty(static_cast(nullptr)); return; } m_ComponentSlider->SetProperty(componentProperty); m_ComponentSlider->setMinValue(0); m_ComponentSlider->setMaxValue(numComponents - 1); } void QmitkDataNodeComponentAction::OnActionChanged() { auto dataNode = GetSelectedNode(); InitializeWithDataNode(dataNode); } diff --git a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeOpenInAction.cpp b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeOpenInAction.cpp new file mode 100644 index 0000000000..6f131f7f07 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeOpenInAction.cpp @@ -0,0 +1,129 @@ +/*=================================================================== + +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 "QmitkDataNodeOpenInAction.h" + +// mitk core +#include +#include + +// mitk gui common plugin +#include + +// qt +#include + +// qt +#include + +QmitkDataNodeOpenInAction::QmitkDataNodeOpenInAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) + : QAction(parent) + , QmitkAbstractDataNodeAction(workbenchPartSite) +{ + setText(tr("Open in")); + InitializeAction(); +} + +QmitkDataNodeOpenInAction::QmitkDataNodeOpenInAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) + : QAction(parent) + , QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) +{ + setText(tr("Open in")); + InitializeAction(); +} + +QmitkDataNodeOpenInAction::~QmitkDataNodeOpenInAction() +{ + // nothing here +} + +void QmitkDataNodeOpenInAction::SetControlledRenderer(RendererVector controlledRenderer) +{ + if (m_ControlledRenderer != controlledRenderer) + { + // set the new set of controlled renderer + m_ControlledRenderer = controlledRenderer; + } +} + +void QmitkDataNodeOpenInAction::InitializeAction() +{ + setCheckable(true); + + setMenu(new QMenu); + connect(menu(), &QMenu::aboutToShow, this, &QmitkDataNodeOpenInAction::OnMenuAboutToShow); + + SetControlledRenderer(); +} + +void QmitkDataNodeOpenInAction::OnMenuAboutToShow() +{ + menu()->clear(); + QAction* action; + + for (const auto& renderer : m_ControlledRenderer) + { + action = menu()->addAction(QString::fromStdString(renderer->GetName())); + connect(action, &QAction::triggered, this, &QmitkDataNodeOpenInAction::OnActionTriggered); + } +} + +void QmitkDataNodeOpenInAction::OnActionTriggered(bool /*checked*/) +{ + auto dataNode = GetSelectedNode(); + if (dataNode.IsNull()) + { + return; + } + + QAction* senderAction = qobject_cast(QObject::sender()); + if (nullptr == senderAction) + { + return; + } + + std::string selectedRenderer = senderAction->text().toStdString(); + mitk::BaseRenderer* renderer = mitk::BaseRenderer::GetByName(selectedRenderer); + if (nullptr == renderer) + { + return; + } + + auto image = dynamic_cast(dataNode->GetData()); + if (nullptr == image) + { + return; + } + + mitk::RenderingManager::GetInstance()->InitializeView(renderer->GetRenderWindow(), image->GetTimeGeometry()); +} + +void QmitkDataNodeOpenInAction::SetControlledRenderer() +{ + const mitk::RenderingManager::RenderWindowVector allRegisteredRenderWindows = + mitk::RenderingManager::GetInstance()->GetAllRegisteredRenderWindows(); + mitk::BaseRenderer *baseRenderer = nullptr; + m_ControlledRenderer.clear(); + for (const auto &renderWindow : allRegisteredRenderWindows) + { + baseRenderer = mitk::BaseRenderer::GetInstance(renderWindow); + if (nullptr != baseRenderer) + { + m_ControlledRenderer.push_back(baseRenderer); + } + } +} diff --git a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeOpenInAction.h b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeOpenInAction.h new file mode 100644 index 0000000000..5d18b76ddd --- /dev/null +++ b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeOpenInAction.h @@ -0,0 +1,60 @@ +/*=================================================================== + +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 QMITKDATANODEOPENINACTION_H +#define QMITKDATANODEOPENINACTION_H + +#include + +#include "QmitkAbstractDataNodeAction.h" + +// mitk core +#include + +// qt +#include + +class MITK_QT_APP QmitkDataNodeOpenInAction : public QAction, public QmitkAbstractDataNodeAction +{ + Q_OBJECT + +public: + + typedef std::vector RendererVector; + + QmitkDataNodeOpenInAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite); + QmitkDataNodeOpenInAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite); + + virtual ~QmitkDataNodeOpenInAction() override; + + void SetControlledRenderer(RendererVector controlledRenderer); + +private Q_SLOTS: + + void OnMenuAboutToShow(); + void OnActionTriggered(bool); + +protected: + + virtual void InitializeAction() override; + + void SetControlledRenderer(); + + RendererVector m_ControlledRenderer; + +}; + +#endif // QMITKDATANODEOPENINACTION_H diff --git a/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionDialog.cpp b/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionDialog.cpp index fc219375c5..aff51535dc 100644 --- a/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionDialog.cpp +++ b/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionDialog.cpp @@ -1,187 +1,186 @@ /*=================================================================== 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 "QmitkNodeSelectionDialog.h" #include #include #include QmitkNodeSelectionDialog::QmitkNodeSelectionDialog(QWidget* parent, QString title, QString hint) : QDialog(parent), m_NodePredicate(nullptr), m_SelectOnlyVisibleNodes(false), m_SelectedNodes(NodeList()), m_SelectionMode(QAbstractItemView::SingleSelection) { m_Controls.setupUi(this); auto providers = mitk::DataStorageInspectorGenerator::GetProviders(); auto visibleProviders = mitk::GetVisibleDataStorageInspectors(); auto favoriteID = mitk::GetFavoriteDataStorageInspector(); if (visibleProviders.empty()) { MITK_DEBUG << "No presets for visible node selection inspectors available. Use fallback (show all available inspectors)"; unsigned int order = 0; for (auto proIter : providers) { visibleProviders.insert(std::make_pair(order, proIter.first)); ++order; } } int favIndex = 0; bool favoriteFound = false; for (auto proIter : visibleProviders) { auto finding = providers.find(proIter.second); if (finding != providers.end()) { auto inspector = finding->second->CreateInspector(); QString name = QString::fromStdString(finding->second->GetInspectorDisplayName()); QString desc = QString::fromStdString(finding->second->GetInspectorDescription()); AddPanel(inspector, name, desc); favoriteFound = favoriteFound || proIter.second == favoriteID; if (!favoriteFound) { ++favIndex; } } else { MITK_DEBUG << "No provider registered for inspector that is defined as visible in the preferences. Illegal inspector ID: " << proIter.second; } } m_Controls.tabWidget->setCurrentIndex(favIndex); this->setWindowTitle(title); this->setToolTip(hint); m_Controls.hint->setText(hint); m_Controls.hint->setVisible(!hint.isEmpty()); connect(m_Controls.buttonBox, SIGNAL(accepted()), this, SLOT(OnOK())); connect(m_Controls.buttonBox, SIGNAL(rejected()), this, SLOT(OnCancel())); } void QmitkNodeSelectionDialog::SetDataStorage(mitk::DataStorage* dataStorage) { if (m_DataStorage != dataStorage) { m_DataStorage = dataStorage; if (!m_DataStorage.IsExpired()) { for (auto panel : m_Panels) { panel->SetDataStorage(dataStorage); } } } -}; +} void QmitkNodeSelectionDialog::SetNodePredicate(mitk::NodePredicateBase* nodePredicate) { if (m_NodePredicate != nodePredicate) { m_NodePredicate = nodePredicate; for (auto panel : m_Panels) { panel->SetNodePredicate(m_NodePredicate); } } -}; +} mitk::NodePredicateBase* QmitkNodeSelectionDialog::GetNodePredicate() const { return m_NodePredicate; } QmitkNodeSelectionDialog::NodeList QmitkNodeSelectionDialog::GetSelectedNodes() const { return m_SelectedNodes; -}; +} void QmitkNodeSelectionDialog::SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes) { if (m_SelectOnlyVisibleNodes != selectOnlyVisibleNodes) { m_SelectOnlyVisibleNodes = selectOnlyVisibleNodes; for (auto panel : m_Panels) { panel->SetSelectOnlyVisibleNodes(m_SelectOnlyVisibleNodes); } } -}; +} void QmitkNodeSelectionDialog::SetCurrentSelection(NodeList selectedNodes) { m_SelectedNodes = selectedNodes; for (auto panel : m_Panels) { panel->SetCurrentSelection(selectedNodes); } -}; +} void QmitkNodeSelectionDialog::OnSelectionChanged(NodeList selectedNodes) { SetCurrentSelection(selectedNodes); emit CurrentSelectionChanged(selectedNodes); -}; +} void QmitkNodeSelectionDialog::AddPanel(QmitkAbstractDataStorageInspector* view, QString name, QString desc) { view->setParent(this); view->SetSelectionMode(m_SelectionMode); auto tabPanel = new QWidget(); tabPanel->setObjectName(QString("tab_")+name); tabPanel->setToolTip(desc); m_Controls.tabWidget->insertTab(m_Controls.tabWidget->count(), tabPanel, name); auto verticalLayout = new QVBoxLayout(tabPanel); verticalLayout->setSpacing(0); verticalLayout->setContentsMargins(0, 0, 0, 0); verticalLayout->addWidget(view); m_Panels.push_back(view); connect(view, &QmitkAbstractDataStorageInspector::CurrentSelectionChanged, this, &QmitkNodeSelectionDialog::OnSelectionChanged); -}; +} void QmitkNodeSelectionDialog::OnOK() { this->accept(); -}; +} void QmitkNodeSelectionDialog::OnCancel() { this->reject(); -}; +} void QmitkNodeSelectionDialog::SetSelectionMode(SelectionMode mode) { m_SelectionMode = mode; for (auto panel : m_Panels) { panel->SetSelectionMode(mode); } -}; +} QmitkNodeSelectionDialog::SelectionMode QmitkNodeSelectionDialog::GetSelectionMode() const { return m_SelectionMode; } diff --git a/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionDialog.h b/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionDialog.h index 02f828bcc5..480655c53c 100644 --- a/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionDialog.h +++ b/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionDialog.h @@ -1,127 +1,125 @@ /*=================================================================== 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 QMITK_NODE_SELECTION_DIALOG_H #define QMITK_NODE_SELECTION_DIALOG_H - #include #include #include #include #include "org_mitk_gui_qt_common_Export.h" #include "ui_QmitkNodeSelectionDialog.h" #include /** * \class QmitkNodeSelectionDialog * \brief Widget that allows to show and edit the content of an mitk::IsoDoseLevel instance. */ class MITK_QT_COMMON QmitkNodeSelectionDialog : public QDialog { Q_OBJECT public: explicit QmitkNodeSelectionDialog(QWidget* parent = nullptr, QString caption = "", QString hint = ""); /* * @brief Sets the data storage that will be used /monitored by widget. * * @param dataStorage A pointer to the data storage to set. */ void SetDataStorage(mitk::DataStorage* dataStorage); /* * @brief Sets the node predicate and updates the widget, according to the node predicate. * * @param nodePredicate A pointer to node predicate. */ virtual void SetNodePredicate(mitk::NodePredicateBase* nodePredicate); mitk::NodePredicateBase* GetNodePredicate() const; using NodeList = QList; NodeList GetSelectedNodes() const; bool GetSelectOnlyVisibleNodes() const; using SelectionMode = QAbstractItemView::SelectionMode; void SetSelectionMode(SelectionMode mode); SelectionMode GetSelectionMode() const; Q_SIGNALS: /* * @brief A signal that will be emitted if the selected node has changed. * * @param nodes A list of data nodes that are newly selected. */ void CurrentSelectionChanged(NodeList nodes); public Q_SLOTS: /* * @brief Change the selection modus of the item view's selection model. * * If true, an incoming selection will be filtered (reduced) to only those nodes that are visible by the current view. * An outgoing selection can then at most contain the filtered nodes. * If false, the incoming non-visible selection will be stored and later added to the outgoing selection, * to include the original selection that could not be modified. * The part of the original selection, that is non-visible are the nodes that are not * * @param selectOnlyVisibleNodes The bool value to define the selection modus. */ void SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes); /* * @brief Transform a list of data nodes into a model selection and set this as a new selection of the * selection model of the private member item view. * * The function filters the given list of nodes according to the 'm_SelectOnlyVisibleNodes' member variable. If * necessary, the non-visible nodes are stored. This is done if 'm_SelectOnlyVisibleNodes' is false: In this case * the selection may be filtered and only a subset of the selected nodes may be visible and therefore (de-)selectable * in the data storage viewer. By storing the non-visible nodes it is possible to send the new, modified selection * but also include the selected nodes from the original selection that could not be modified (see 'SetSelectOnlyVisibleNodes'). * * @param nodes A list of data nodes that should be newly selected. */ void SetCurrentSelection(NodeList selectedNodes); protected Q_SLOTS: void OnSelectionChanged(NodeList selectedNodes); void OnOK(); void OnCancel(); protected: void AddPanel(QmitkAbstractDataStorageInspector* view, QString name, QString desc); mitk::WeakPointer m_DataStorage; mitk::NodePredicateBase::Pointer m_NodePredicate; bool m_SelectOnlyVisibleNodes; NodeList m_SelectedNodes; SelectionMode m_SelectionMode; using PanelVectorType = std::vector; PanelVectorType m_Panels; Ui_QmitkNodeSelectionDialog m_Controls; }; #endif // QmitkNodeSelectionDialog_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/CMakeLists.txt b/Plugins/org.mitk.gui.qt.semanticrelations/CMakeLists.txt new file mode 100644 index 0000000000..2ffd63c2e3 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/CMakeLists.txt @@ -0,0 +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 +) diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/files.cmake b/Plugins/org.mitk.gui.qt.semanticrelations/files.cmake new file mode 100644 index 0000000000..8e961c0ceb --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/files.cmake @@ -0,0 +1,43 @@ +set(INTERNAL_CPP_FILES + mitkPluginActivator.cpp + QmitkAbstractSemanticRelationsAction.cpp + QmitkDataNodeAddToSemanticRelationsAction.cpp + QmitkDataNodeRemoveFromSemanticRelationsAction.cpp + QmitkDataNodeUnlinkFromLesionAction.cpp + QmitkDataNodeSetControlPointAction.cpp + QmitkDataNodeSetInformationTypeAction.cpp + QmitkLesionInfoWidget.cpp + QmitkSemanticRelationsContextMenu.cpp + QmitkSemanticRelationsNodeSelectionDialog.cpp + QmitkSemanticRelationsView.cpp +) + +set(UI_FILES + src/internal/QmitkLesionInfoWidgetControls.ui + src/internal/QmitkSemanticRelationsControls.ui +) + +set(MOC_H_FILES + src/internal/mitkPluginActivator.h + src/internal/QmitkDataNodeAddToSemanticRelationsAction.h + src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.h + src/internal/QmitkDataNodeUnlinkFromLesionAction.h + src/internal/QmitkDataNodeSetControlPointAction.h + src/internal/QmitkDataNodeSetInformationTypeAction.h + src/internal/QmitkLesionInfoWidget.h + src/internal/QmitkSemanticRelationsContextMenu.h + src/internal/QmitkSemanticRelationsNodeSelectionDialog.h + src/internal/QmitkSemanticRelationsView.h +) + +set(CACHED_RESOURCE_FILES + resources/SemanticRelations_48.png + plugin.xml +) + +set(QRC_FILES +) + +foreach(file ${INTERNAL_CPP_FILES}) + set(CPP_FILES ${CPP_FILES} src/internal/${file}) +endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/manifest_headers.cmake b/Plugins/org.mitk.gui.qt.semanticrelations/manifest_headers.cmake new file mode 100644 index 0000000000..9da36a3fbc --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/manifest_headers.cmake @@ -0,0 +1,5 @@ +set(Plugin-Name "MITK Semantic relations") +set(Plugin-Version "0.1") +set(Plugin-Vendor "DKFZ") +set(Plugin-ContactAddress "http://www.mitk.org") +set(Require-Plugin org.mitk.gui.qt.common org.mitk.gui.qt.application) diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/plugin.xml b/Plugins/org.mitk.gui.qt.semanticrelations/plugin.xml new file mode 100644 index 0000000000..be0ea613f6 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/plugin.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/resources/SemanticRelations_48.png b/Plugins/org.mitk.gui.qt.semanticrelations/resources/SemanticRelations_48.png new file mode 100644 index 0000000000..fc0096e0ac Binary files /dev/null and b/Plugins/org.mitk.gui.qt.semanticrelations/resources/SemanticRelations_48.png differ diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkAbstractSemanticRelationsAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkAbstractSemanticRelationsAction.cpp new file mode 100644 index 0000000000..dafd7c2a72 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkAbstractSemanticRelationsAction.cpp @@ -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. + +===================================================================*/ + +// semantic relations plugin +#include "QmitkAbstractSemanticRelationsAction.h" + +QmitkAbstractSemanticRelationsAction::QmitkAbstractSemanticRelationsAction(berry::IWorkbenchPartSite::Pointer workbenchPartSite) + : QmitkAbstractDataNodeAction(workbenchPartSite) +{ + m_SemanticRelationsIntegration = std::make_unique(); +} + +QmitkAbstractSemanticRelationsAction::QmitkAbstractSemanticRelationsAction(berry::IWorkbenchPartSite* workbenchPartSite) + : QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) +{ + // nothing here +} + +QmitkAbstractSemanticRelationsAction::~QmitkAbstractSemanticRelationsAction() +{ + // nothing here +} diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkAbstractSemanticRelationsAction.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkAbstractSemanticRelationsAction.h new file mode 100644 index 0000000000..eb2c57cdc4 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkAbstractSemanticRelationsAction.h @@ -0,0 +1,41 @@ +/*=================================================================== + +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 QMITKABSTRACTSEMANTICRELATIONSACTION_H +#define QMITKABSTRACTSEMANTICRELATIONSACTION_H + +// mitk gui qt application plugin +#include + +// semantic relations module +#include + +class QmitkAbstractSemanticRelationsAction : public QmitkAbstractDataNodeAction +{ + +public: + + QmitkAbstractSemanticRelationsAction(berry::IWorkbenchPartSite::Pointer workbenchPartSite); + QmitkAbstractSemanticRelationsAction(berry::IWorkbenchPartSite* workbenchPartSite); + + virtual ~QmitkAbstractSemanticRelationsAction() override; + +protected: + + std::unique_ptr m_SemanticRelationsIntegration; +}; + +#endif // QMITKABSTRACTSEMANTICRELATIONSACTION_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeAddToSemanticRelationsAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeAddToSemanticRelationsAction.cpp new file mode 100644 index 0000000000..c92128429c --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeAddToSemanticRelationsAction.cpp @@ -0,0 +1,171 @@ +/*=================================================================== + +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 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"); + 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" + + // 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" + } + + 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/QmitkDataNodeAddToSemanticRelationsAction.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeAddToSemanticRelationsAction.h new file mode 100644 index 0000000000..2a61c5d1fe --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeAddToSemanticRelationsAction.h @@ -0,0 +1,57 @@ +/*=================================================================== + +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 QMITKDATANODEADDTOSEMANTICRELATIONSACTION_H +#define QMITKDATANODEADDTOSEMANTICRELATIONSACTION_H + +#include + +// mitk gui qt application plugin +#include "QmitkAbstractSemanticRelationsAction.h" + +// qt +#include + +namespace AddToSemanticRelationsAction +{ + MITK_GUI_SEMANTICRELATIONS_EXPORT void Run(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataStorage* dataStorage, const mitk::DataNode* image); + + void AddImage(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataNode* image); + void AddSegmentation(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataStorage* dataStorage, const mitk::DataNode* segmentation); +} + +class MITK_GUI_SEMANTICRELATIONS_EXPORT QmitkDataNodeAddToSemanticRelationsAction : public QAction, public QmitkAbstractSemanticRelationsAction +{ + Q_OBJECT + +public: + + QmitkDataNodeAddToSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite); + QmitkDataNodeAddToSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite); + + virtual ~QmitkDataNodeAddToSemanticRelationsAction() override; + +private Q_SLOTS: + + void OnActionTriggered(bool); + +protected: + + virtual void InitializeAction() override; + +}; + +#endif // QMITKDATANODEADDTOSEMANTICRELATIONSACTION_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.cpp new file mode 100644 index 0000000000..a1b0a04f33 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.cpp @@ -0,0 +1,131 @@ +/*=================================================================== + +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 "QmitkDataNodeRemoveFromSemanticRelationsAction.h" + +// semantic relations module +#include +#include + +// mitk gui common plugin +#include + +// qt +#include + +// namespace that contains the concrete action +namespace RemoveFromSemanticRelationsAction +{ + void Run(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataNode* dataNode) + { + if (nullptr == dataNode) + { + return; + } + + if (mitk::NodePredicates::GetImagePredicate()->CheckNode(dataNode)) + { + RemoveImage(semanticRelationsIntegration, dataNode); + } + else if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) + { + RemoveSegmentation(semanticRelationsIntegration, dataNode); + } + } + + void RemoveImage(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataNode* image) + { + if (nullptr == image) + { + return; + } + + try + { + // remove the image from the semantic relations storage + semanticRelationsIntegration->RemoveImage(image); + } + catch (const mitk::SemanticRelationException& e) + { + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox; + msgBox.setWindowTitle("Could not remove the selected image."); + msgBox.setText("The program wasn't able to correctly remove the selected image.\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str())); + msgBox.setIcon(QMessageBox::Warning); + msgBox.exec(); + return; + } + } + + void RemoveSegmentation(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataNode* segmentation) + { + if (nullptr == segmentation) + { + return; + } + + try + { + // remove the segmentation from the semantic relations storage + semanticRelationsIntegration->RemoveSegmentation(segmentation); + } + catch (const mitk::SemanticRelationException& e) + { + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox; + msgBox.setWindowTitle("Could not remove the selected segmentation."); + msgBox.setText("The program wasn't able to correctly remove the selected segmentation.\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str())); + msgBox.setIcon(QMessageBox::Warning); + msgBox.exec(); + return; + } + } +} + +QmitkDataNodeRemoveFromSemanticRelationsAction::QmitkDataNodeRemoveFromSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) + : QAction(parent) + , QmitkAbstractSemanticRelationsAction(workbenchPartSite) +{ + setText(tr("Remove from semantic relations")); + InitializeAction(); +} + +QmitkDataNodeRemoveFromSemanticRelationsAction::QmitkDataNodeRemoveFromSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) + : QAction(parent) + , QmitkAbstractSemanticRelationsAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) +{ + setText(tr("Remove from semantic relations")); + InitializeAction(); +} + +QmitkDataNodeRemoveFromSemanticRelationsAction::~QmitkDataNodeRemoveFromSemanticRelationsAction() +{ + // nothing here +} + +void QmitkDataNodeRemoveFromSemanticRelationsAction::InitializeAction() +{ + connect(this, &QAction::triggered, this, &QmitkDataNodeRemoveFromSemanticRelationsAction::OnActionTriggered); +} + +void QmitkDataNodeRemoveFromSemanticRelationsAction::OnActionTriggered(bool /*checked*/) +{ + auto dataNode = GetSelectedNode(); + RemoveFromSemanticRelationsAction::Run(m_SemanticRelationsIntegration.get(), dataNode); +} diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.h new file mode 100644 index 0000000000..0a3071f614 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.h @@ -0,0 +1,57 @@ +/*=================================================================== + +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 QMITKDATANODEREMOVEFROMSEMANTICRELATIONSACTION_H +#define QMITKDATANODEREMOVEFROMSEMANTICRELATIONSACTION_H + +#include + +// mitk gui qt application plugin +#include "QmitkAbstractSemanticRelationsAction.h" + +// qt +#include + +namespace RemoveFromSemanticRelationsAction +{ + MITK_GUI_SEMANTICRELATIONS_EXPORT void Run(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataNode* dataNode); + + void RemoveImage(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataNode* image); + void RemoveSegmentation(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataNode* segmentation); +} + +class MITK_GUI_SEMANTICRELATIONS_EXPORT QmitkDataNodeRemoveFromSemanticRelationsAction : public QAction, public QmitkAbstractSemanticRelationsAction +{ + Q_OBJECT + +public: + + QmitkDataNodeRemoveFromSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite); + QmitkDataNodeRemoveFromSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite); + + virtual ~QmitkDataNodeRemoveFromSemanticRelationsAction() override; + +private Q_SLOTS: + + void OnActionTriggered(bool); + +protected: + + virtual void InitializeAction() override; + +}; + +#endif // QMITKDATANODEREMOVEFROMSEMANTICRELATIONSACTION_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetControlPointAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetControlPointAction.cpp new file mode 100644 index 0000000000..825a5abf7f --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetControlPointAction.cpp @@ -0,0 +1,102 @@ +/*=================================================================== + +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 "QmitkDataNodeSetControlPointAction.h" + +// semantic relations module +#include +#include +#include +#include + +// semantic relations UI module +#include "QmitkControlPointDialog.h" + +// mitk gui common plugin +#include + +// qt +#include + +QmitkDataNodeSetControlPointAction::QmitkDataNodeSetControlPointAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) + : QAction(parent) + , QmitkAbstractSemanticRelationsAction(workbenchPartSite) +{ + setText(tr("Set control point")); + m_Parent = parent; + InitializeAction(); +} + +QmitkDataNodeSetControlPointAction::QmitkDataNodeSetControlPointAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) + : QAction(parent) + , QmitkAbstractSemanticRelationsAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) +{ + setText(tr("Set control point")); + m_Parent = parent; + InitializeAction(); +} + +QmitkDataNodeSetControlPointAction::~QmitkDataNodeSetControlPointAction() +{ + // nothing here +} + +void QmitkDataNodeSetControlPointAction::InitializeAction() +{ + connect(this, &QAction::triggered, this, &QmitkDataNodeSetControlPointAction::OnActionTriggered); +} + +void QmitkDataNodeSetControlPointAction::OnActionTriggered(bool /*checked*/) +{ + if (nullptr == m_SemanticRelationsIntegration) + { + return; + } + + auto dataNode = GetSelectedNode(); + if (dataNode.IsNull()) + { + return; + } + + QmitkControlPointDialog* inputDialog = new QmitkControlPointDialog(m_Parent); + inputDialog->setWindowTitle("Set control point"); + inputDialog->SetCurrentDate(mitk::SemanticRelationsInference::GetControlPointOfImage(dataNode)); + + int dialogReturnValue = inputDialog->exec(); + if (QDialog::Rejected == dialogReturnValue) + { + return; + } + + const QDate& userSelectedDate = inputDialog->GetCurrentDate(); + mitk::SemanticTypes::ControlPoint controlPoint; + controlPoint.UID = mitk::UIDGeneratorBoost::GenerateUID(); + controlPoint.date = boost::gregorian::date(userSelectedDate.year(), + userSelectedDate.month(), + userSelectedDate.day()); + + try + { + m_SemanticRelationsIntegration->UnlinkImageFromControlPoint(dataNode); + m_SemanticRelationsIntegration->SetControlPointOfImage(dataNode, controlPoint); + } + catch (const mitk::SemanticRelationException&) + { + return; + } +} diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetControlPointAction.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetControlPointAction.h new file mode 100644 index 0000000000..625e707e92 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetControlPointAction.h @@ -0,0 +1,49 @@ +/*=================================================================== + +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 QMITKDATANODESETCONTROLPOINTACTION_H +#define QMITKDATANODESETCONTROLPOINTACTION_H + +// mitk gui qt semanticrelations plugin +#include "QmitkAbstractSemanticRelationsAction.h" + +// qt +#include + +class QmitkDataNodeSetControlPointAction : public QAction, public QmitkAbstractSemanticRelationsAction +{ + Q_OBJECT + +public: + + QmitkDataNodeSetControlPointAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite); + QmitkDataNodeSetControlPointAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite); + + virtual ~QmitkDataNodeSetControlPointAction() override; + +private Q_SLOTS: + + void OnActionTriggered(bool); + +protected: + + virtual void InitializeAction() override; + + QWidget* m_Parent; + +}; + +#endif // QMITKDATANODESETCONTROLPOINTACTION_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetInformationTypeAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetInformationTypeAction.cpp new file mode 100644 index 0000000000..64ea29b0e6 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetInformationTypeAction.cpp @@ -0,0 +1,91 @@ +/*=================================================================== + +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 "QmitkDataNodeSetInformationTypeAction.h" + +// semantic relations module +#include +#include + +// mitk gui common plugin +#include + +// qt +#include + +QmitkDataNodeSetInformationTypeAction::QmitkDataNodeSetInformationTypeAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) + : QAction(parent) + , QmitkAbstractSemanticRelationsAction(workbenchPartSite) +{ + setText(tr("Set information type")); + m_Parent = parent; + InitializeAction(); +} + +QmitkDataNodeSetInformationTypeAction::QmitkDataNodeSetInformationTypeAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) + : QAction(parent) + , QmitkAbstractSemanticRelationsAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) +{ + setText(tr("Set information type")); + m_Parent = parent; + InitializeAction(); +} + +QmitkDataNodeSetInformationTypeAction::~QmitkDataNodeSetInformationTypeAction() +{ + // nothing here +} + +void QmitkDataNodeSetInformationTypeAction::InitializeAction() +{ + connect(this, &QAction::triggered, this, &QmitkDataNodeSetInformationTypeAction::OnActionTriggered); +} + +void QmitkDataNodeSetInformationTypeAction::OnActionTriggered(bool /*checked*/) +{ + if (nullptr == m_SemanticRelationsIntegration) + { + return; + } + + auto dataNode = GetSelectedNode(); + if (dataNode.IsNull()) + { + return; + } + + QInputDialog* inputDialog = new QInputDialog(m_Parent); + inputDialog->setWindowTitle(tr("Set information type of selected node")); + inputDialog->setLabelText(tr("Information type:")); + inputDialog->setTextValue(QString::fromStdString(mitk::SemanticRelationsInference::GetInformationTypeOfImage(dataNode))); + inputDialog->setMinimumSize(250, 100); + + int dialogReturnValue = inputDialog->exec(); + if (QDialog::Rejected == dialogReturnValue) + { + return; + } + + try + { + m_SemanticRelationsIntegration->SetInformationType(dataNode, inputDialog->textValue().toStdString()); + } + catch (const mitk::SemanticRelationException&) + { + return; + } +} diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetInformationTypeAction.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetInformationTypeAction.h new file mode 100644 index 0000000000..618b5d7f55 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetInformationTypeAction.h @@ -0,0 +1,49 @@ +/*=================================================================== + +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 QMITKDATANODESETINFORMATIONTYPEACTION_H +#define QMITKDATANODESETINFORMATIONTYPEACTION_H + +// mitk gui qt application plugin +#include "QmitkAbstractSemanticRelationsAction.h" + +// qt +#include + +class QmitkDataNodeSetInformationTypeAction : public QAction, public QmitkAbstractSemanticRelationsAction +{ + Q_OBJECT + +public: + + QmitkDataNodeSetInformationTypeAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite); + QmitkDataNodeSetInformationTypeAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite); + + virtual ~QmitkDataNodeSetInformationTypeAction() override; + +private Q_SLOTS: + + void OnActionTriggered(bool); + +protected: + + virtual void InitializeAction() override; + + QWidget* m_Parent; + +}; + +#endif // QMITKDATANODESETINFORMATIONTYPEACTION_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeUnlinkFromLesionAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeUnlinkFromLesionAction.cpp new file mode 100644 index 0000000000..c01e1301b0 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeUnlinkFromLesionAction.cpp @@ -0,0 +1,102 @@ +/*=================================================================== + +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 "QmitkDataNodeUnlinkFromLesionAction.h" + +// semantic relations module +#include +#include +#include + +// mitk gui common plugin +#include + +// qt +#include + +// namespace that contains the concrete action +namespace UnlinkFromLesionAction +{ + void Run(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataNode* dataNode) + { + if (nullptr == dataNode) + { + return; + } + + if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) + { + try + { + semanticRelationsIntegration->UnlinkSegmentationFromLesion(dataNode); + } + catch (const mitk::SemanticRelationException& e) + { + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox(QMessageBox::Warning, + "Could not unlink the selected segmentation.", + "The program wasn't able to correctly unlink the selected segmentation.\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str())); + msgBox.exec(); + } + } + else + { + QMessageBox msgBox(QMessageBox::Warning, + "Could not unlink the selected data node.", + "Please chose a valid segmentation to unlink from its represented lesion!"); + msgBox.exec(); + } + } +} + +QmitkDataNodeUnlinkFromLesionAction::QmitkDataNodeUnlinkFromLesionAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) + : QAction(parent) + , QmitkAbstractSemanticRelationsAction(workbenchPartSite) +{ + setText(tr("Unlink from lesion")); + InitializeAction(); +} + +QmitkDataNodeUnlinkFromLesionAction::QmitkDataNodeUnlinkFromLesionAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) + : QAction(parent) + , QmitkAbstractSemanticRelationsAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) +{ + setText(tr("Unlink from lesion")); + InitializeAction(); +} + +QmitkDataNodeUnlinkFromLesionAction::~QmitkDataNodeUnlinkFromLesionAction() +{ + // nothing here +} + +void QmitkDataNodeUnlinkFromLesionAction::InitializeAction() +{ + connect(this, &QAction::triggered, this, &QmitkDataNodeUnlinkFromLesionAction::OnActionTriggered); +} + +void QmitkDataNodeUnlinkFromLesionAction::OnActionTriggered(bool /*checked*/) +{ + if (nullptr == m_SemanticRelationsIntegration) + { + return; + } + + auto dataNode = GetSelectedNode(); + UnlinkFromLesionAction::Run(m_SemanticRelationsIntegration.get(), dataNode); +} diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeUnlinkFromLesionAction.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeUnlinkFromLesionAction.h new file mode 100644 index 0000000000..452d8b39fa --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeUnlinkFromLesionAction.h @@ -0,0 +1,54 @@ +/*=================================================================== + +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 QMITKDATANODEUNLINKFROMLESIONACTION_H +#define QMITKDATANODEUNLINKFROMLESIONACTION_H + +#include + +// mitk gui qt semanticrelations plugin +#include "QmitkAbstractSemanticRelationsAction.h" + +// qt +#include + +namespace UnlinkFromLesionAction +{ + MITK_GUI_SEMANTICRELATIONS_EXPORT void Run(const mitk::DataNode* dataNode, mitk::SemanticRelationsIntegration* semanticRelationsIntegration); +} + +class MITK_GUI_SEMANTICRELATIONS_EXPORT QmitkDataNodeUnlinkFromLesionAction : public QAction, public QmitkAbstractSemanticRelationsAction +{ + Q_OBJECT + +public: + + QmitkDataNodeUnlinkFromLesionAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite); + QmitkDataNodeUnlinkFromLesionAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite); + + virtual ~QmitkDataNodeUnlinkFromLesionAction() override; + +private Q_SLOTS: + + void OnActionTriggered(bool); + +protected: + + virtual void InitializeAction() override; + +}; + +#endif // QMITKDATANODEUNLINKFROMLESIONACTION_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp new file mode 100644 index 0000000000..201d1232d7 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp @@ -0,0 +1,433 @@ +/*=================================================================== + +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 +#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 + { + /* + auto allSegmentationsOfLesion = m_SemanticRelationsDataStorageAccess->GetAllSegmentationsOfLesion(m_CaseID, selectedLesion); + mitk::FindClosestSegmentationMask(); + */ + } + 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(); + } +} diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.h new file mode 100644 index 0000000000..9939588821 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.h @@ -0,0 +1,102 @@ +/*=================================================================== + +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 QMITKLESIONINFOWIDGET_H +#define QMITKLESIONINFOWIDGET_H + +// semantic relations plugin +#include + +// semantic relations UI module +#include + +// semantic relations module +#include +#include + +// mitk +#include + +// qt +#include + +/* +* @brief The QmitkLesionInfoWidget is a widget that shows and modifies the currently available lesion data of the semantic relations model. +* +* The widget provides a dialogs to add nodes from the data storage to the semantic relations model. +* It provides functionality to create new lesions and link them with segmentation nodes. +* +* The QmitkLesionInfoWidget provides three QListWidgets, that show the lesion data and the referenced segmentation data, as +* well as the connected image data, depending on the selected lesion. +*/ +class QmitkLesionInfoWidget : public QWidget +{ + Q_OBJECT + +public: + + static const QBrush DEFAULT_BACKGROUND_COLOR; + static const QBrush SELECTED_BACKGROUND_COLOR; + static const QBrush CONNECTED_BACKGROUND_COLOR; + + QmitkLesionInfoWidget(mitk::DataStorage* dataStorage, QWidget* parent = nullptr); + ~QmitkLesionInfoWidget(); + + void SetCaseID(const mitk::SemanticTypes::CaseID& caseID); + + void SetDataNodeSelection(const QList& dataNodeSelection); + +Q_SIGNALS: + + void LesionSelectionChanged(const mitk::SemanticTypes::Lesion&); + +private Q_SLOTS: + + void OnModelUpdated(); + /* + * @brief Generates a new, empty lesion to add to the semantic relations model for the current case ID. + */ + void OnAddLesionButtonClicked(); + + // slots for the mouse click events of tree view's selection model + void OnSelectionChanged(const QModelIndex& current, const QModelIndex& previous); + + void OnLesionListContextMenuRequested(const QPoint&); + + // slots for the context menu actions of the lesion list widget + void OnLinkToSegmentation(mitk::SemanticTypes::Lesion); + void OnSetLesionName(mitk::SemanticTypes::Lesion); + void OnSetLesionClass(mitk::SemanticTypes::Lesion); + void OnPropagateLesion(mitk::SemanticTypes::Lesion); + void OnRemoveLesion(mitk::SemanticTypes::Lesion); + +private: + + void Initialize(); + void SetUpConnections(); + + Ui::QmitkLesionInfoWidgetControls m_Controls; + QmitkLesionTreeModel* m_StorageModel; + + mitk::SemanticTypes::CaseID m_CaseID; + + mitk::WeakPointer m_DataStorage; + std::unique_ptr m_SemanticRelationsDataStorageAccess; + std::unique_ptr m_SemanticRelationsIntegration; + +}; + +#endif // QMITKLESIONINFOWIDGET_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidgetControls.ui b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidgetControls.ui new file mode 100644 index 0000000000..35ace22701 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidgetControls.ui @@ -0,0 +1,50 @@ + + + QmitkLesionInfoWidgetControls + + + true + + + + 0 + 0 + 350 + 300 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Available lesions: + + + + + + + + + + Add new lesion + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsContextMenu.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsContextMenu.cpp new file mode 100644 index 0000000000..6e64071913 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsContextMenu.cpp @@ -0,0 +1,77 @@ +/*=================================================================== + +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 "QmitkSemanticRelationsContextMenu.h" + +QmitkSemanticRelationsContextMenu::QmitkSemanticRelationsContextMenu(berry::IWorkbenchPartSite::Pointer workbenchPartSite, QWidget* parent) + : QMenu(parent) + , m_Parent(parent) + , m_WorkbenchPartSite(workbenchPartSite) +{ + InitDefaultActions(); +} + +QmitkSemanticRelationsContextMenu::~QmitkSemanticRelationsContextMenu() +{ + // nothing here +} + +void QmitkSemanticRelationsContextMenu::SetDataStorage(mitk::DataStorage* dataStorage) +{ + if (m_DataStorage != dataStorage) + { + // set the new data storage - also for all actions + m_DataStorage = dataStorage; + m_ControlPointAction->SetDataStorage(m_DataStorage.Lock()); + m_InformationTypeAction->SetDataStorage(m_DataStorage.Lock()); + m_RemoveFromSemanticRelationsAction->SetDataStorage(m_DataStorage.Lock()); + m_UnlinkFromLesionAction->SetDataStorage(m_DataStorage.Lock()); + } +} + +void QmitkSemanticRelationsContextMenu::SetControlledRenderer(RenderWindowLayerUtilities::RendererVector controlledRenderer) +{ + if (m_ControlledRenderer != controlledRenderer) + { + // set the new set of controlled renderer + m_ControlledRenderer = controlledRenderer; + m_OpenInAction->SetControlledRenderer(m_ControlledRenderer); + } +} + +void QmitkSemanticRelationsContextMenu::OnContextMenuRequested(const QPoint& /*pos*/) +{ + popup(QCursor::pos()); +} + +void QmitkSemanticRelationsContextMenu::InitDefaultActions() +{ + m_ControlPointAction = new QmitkDataNodeSetControlPointAction(m_Parent, m_WorkbenchPartSite.Lock()); + addAction(m_ControlPointAction); + + m_InformationTypeAction = new QmitkDataNodeSetInformationTypeAction(m_Parent, m_WorkbenchPartSite.Lock()); + addAction(m_InformationTypeAction); + + m_UnlinkFromLesionAction = new QmitkDataNodeUnlinkFromLesionAction(m_Parent, m_WorkbenchPartSite.Lock()); + addAction(m_UnlinkFromLesionAction); + + m_RemoveFromSemanticRelationsAction = new QmitkDataNodeRemoveFromSemanticRelationsAction(m_Parent, m_WorkbenchPartSite.Lock()); + addAction(m_RemoveFromSemanticRelationsAction); + + m_OpenInAction = new QmitkDataNodeOpenInAction(m_Parent, m_WorkbenchPartSite.Lock()); + addAction(m_OpenInAction); +} diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsContextMenu.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsContextMenu.h new file mode 100644 index 0000000000..73daef38ff --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsContextMenu.h @@ -0,0 +1,75 @@ +/*=================================================================== + +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 QMITKSEMANTICRELATIONSCONTEXTMENU_H +#define QMITKSEMANTICRELATIONSCONTEXTMENU_H + +// semantic relations plugin +#include "QmitkDataNodeSetControlPointAction.h" +#include "QmitkDataNodeSetInformationTypeAction.h" +#include "QmitkDataNodeUnlinkFromLesionAction.h" +#include "QmitkDataNodeRemoveFromSemanticRelationsAction.h" + +// mitk core +#include +#include + +// mitk render window manager module +#include + +// mitk gui qt application plugin +#include + +// blueberry ui qt plugin +#include + +//qt +#include + +class QmitkSemanticRelationsContextMenu : public QMenu +{ + Q_OBJECT + +public: + + QmitkSemanticRelationsContextMenu(berry::IWorkbenchPartSite::Pointer workbenchPartSite, QWidget* parent = nullptr); + virtual ~QmitkSemanticRelationsContextMenu() override; + + void SetDataStorage(mitk::DataStorage* dataStorage); + void SetControlledRenderer(RenderWindowLayerUtilities::RendererVector controlledRenderer); + +public Q_SLOTS: + + void OnContextMenuRequested(const QPoint&); + +private: + + void InitDefaultActions(); + + QWidget* m_Parent; + berry::IWorkbenchPartSite::WeakPtr m_WorkbenchPartSite; + mitk::WeakPointer m_DataStorage; + RenderWindowLayerUtilities::RendererVector m_ControlledRenderer; + + QmitkDataNodeSetControlPointAction* m_ControlPointAction; + QmitkDataNodeSetInformationTypeAction* m_InformationTypeAction; + QmitkDataNodeUnlinkFromLesionAction* m_UnlinkFromLesionAction; + QmitkDataNodeRemoveFromSemanticRelationsAction* m_RemoveFromSemanticRelationsAction; + QmitkDataNodeOpenInAction* m_OpenInAction; + +}; + +#endif // QMITKSEMANTICRELATIONSCONTEXTMENU_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsControls.ui b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsControls.ui new file mode 100644 index 0000000000..bb178d3af5 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsControls.ui @@ -0,0 +1,37 @@ + + + QmitkSemanticRelationsControls + + + + 0 + 0 + 350 + 300 + + + + Semantic relations plugin + + + + + + + 0 + 0 + + + + Currently selected patient: + + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsNodeSelectionDialog.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsNodeSelectionDialog.cpp new file mode 100644 index 0000000000..346e265571 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsNodeSelectionDialog.cpp @@ -0,0 +1,53 @@ +/*=================================================================== + +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 "QmitkSemanticRelationsNodeSelectionDialog.h" + +// semantic relations ui module +#include + +// semantic relations ui module +#include + +QmitkSemanticRelationsNodeSelectionDialog::QmitkSemanticRelationsNodeSelectionDialog(QWidget* parent, QString title, QString hint) + : QmitkNodeSelectionDialog(parent, title, hint) +{ + // nothing here +} + +void QmitkSemanticRelationsNodeSelectionDialog::SetCaseID(const mitk::SemanticTypes::CaseID& caseID) +{ + for (auto panel : m_Panels) + { + QmitkAbstractSemanticRelationsStorageInspector* semanticRelationsStorageInspector = dynamic_cast(panel); + if (nullptr != semanticRelationsStorageInspector) + { + semanticRelationsStorageInspector->SetCaseID(caseID); + } + } +} + +void QmitkSemanticRelationsNodeSelectionDialog::SetLesion(const mitk::SemanticTypes::Lesion& lesion) +{ + for (auto panel : m_Panels) + { + QmitkAbstractSemanticRelationsStorageInspector* semanticRelationsStorageInspector = dynamic_cast(panel); + if (nullptr != semanticRelationsStorageInspector) + { + semanticRelationsStorageInspector->SetLesion(lesion); + } + } +} diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsNodeSelectionDialog.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsNodeSelectionDialog.h new file mode 100644 index 0000000000..3efc40031d --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsNodeSelectionDialog.h @@ -0,0 +1,54 @@ +/*=================================================================== + +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 QMITKSEMANTICRELATIONSNODESELECTIONDIALOG_H +#define QMITKSEMANTICRELATIONSNODESELECTIONDIALOG_H + +// semantic relations module +#include + +// org.mitk.gui.qt.common plugin +#include + +/* +* @brief The QmitkSemanticRelationsNodeSelectionDialog extends the QmitkNodeSelectionDialog so that a case ID and a lesion +* can be set for each known panel that is of type 'QmitkSemanticRelationsStorageInspector'. +*/ +class QmitkSemanticRelationsNodeSelectionDialog : public QmitkNodeSelectionDialog +{ + Q_OBJECT + +public: + + explicit QmitkSemanticRelationsNodeSelectionDialog(QWidget* parent = nullptr, QString caption = "", QString hint = ""); + /** + * @brief Extends the base class to allow setting the current case ID which is needed to access the + * semantic relations storage. The function sets the case ID of each 'QmitkSemanticRelationsStorageInspector'. + * + * @param caseID A case ID as string + */ + virtual void SetCaseID(const mitk::SemanticTypes::CaseID& caseID); + /** + * @brief Extends the base class to allow setting the current lesion which can be used to show on which images + * the lesion is visible. The function sets the lesion of each 'QmitkSemanticRelationsStorageInspector'. + * + * @param lesion The selected lesion + */ + virtual void SetLesion(const mitk::SemanticTypes::Lesion& lesion); + +}; + +#endif // QMITKSEMANTICRELATIONSNODESELECTIONDIALOG_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.cpp new file mode 100644 index 0000000000..2c5a87bcf4 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.cpp @@ -0,0 +1,273 @@ +/*=================================================================== + +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 "QmitkSemanticRelationsView.h" +#include "QmitkDataNodeAddToSemanticRelationsAction.h" +#include "QmitkDataNodeRemoveFromSemanticRelationsAction.h" + +// semantic relations module +#include +#include +#include +#include + +// mitk qt widgets module +#include +#include + +// mitk multi label module +#include + +// berry +#include +#include + +// qt +#include +#include + +const std::string QmitkSemanticRelationsView::VIEW_ID = "org.mitk.views.semanticrelations"; + +void QmitkSemanticRelationsView::SetFocus() +{ + // nothing here +} + +void QmitkSemanticRelationsView::CreateQtPartControl(QWidget* parent) +{ + // create GUI widgets + m_Controls.setupUi(parent); + + m_LesionInfoWidget = new QmitkLesionInfoWidget(GetDataStorage(), parent); + m_Controls.gridLayout->addWidget(m_LesionInfoWidget); + + m_PatientTableInspector = new QmitkPatientTableInspector(parent); + m_PatientTableInspector->SetDataStorage(GetDataStorage()); + m_Controls.gridLayout->addWidget(m_PatientTableInspector); + + QGridLayout* dndDataNodeWidgetLayout = new QGridLayout; + dndDataNodeWidgetLayout->addWidget(m_PatientTableInspector, 0, 0); + dndDataNodeWidgetLayout->setContentsMargins(0, 0, 0, 0); + + m_DnDDataNodeWidget = new QmitkDnDDataNodeWidget(parent); + m_DnDDataNodeWidget->setLayout(dndDataNodeWidgetLayout); + + m_Controls.gridLayout->addWidget(m_DnDDataNodeWidget); + + m_ContextMenu = new QmitkSemanticRelationsContextMenu(GetSite(), m_PatientTableInspector); + m_ContextMenu->SetDataStorage(GetDataStorage()); + + mitk::IRenderWindowPart* renderWindowPart = GetRenderWindowPart(); + if (nullptr != renderWindowPart) + { + RenderWindowPartActivated(renderWindowPart); + } + + SetUpConnections(); + + const auto& allCaseIDs = mitk::RelationStorage::GetAllCaseIDs(); + for (const auto& caseID : allCaseIDs) + { + AddToComboBox(caseID); + } +} + +void QmitkSemanticRelationsView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) +{ + // connect QmitkRenderWindows - underlying vtkRenderWindow is the same as "mitk::RenderingManager::GetInstance()->GetAllRegisteredRenderWindows()" + QHash windowMap = renderWindowPart->GetQmitkRenderWindows(); + QHash::Iterator it; + + mitk::BaseRenderer* baseRenderer = nullptr; + RenderWindowLayerUtilities::RendererVector controlledRenderer; + for (it = windowMap.begin(); it != windowMap.end(); ++it) + { + baseRenderer = mitk::BaseRenderer::GetInstance(it.value()->GetVtkRenderWindow()); + if (nullptr != baseRenderer) + { + controlledRenderer.push_back(baseRenderer); + } + } + + m_ContextMenu->SetControlledRenderer(controlledRenderer); +} + +void QmitkSemanticRelationsView::SetUpConnections() +{ + connect(m_Controls.caseIDComboBox, static_cast(&QComboBox::currentIndexChanged), this, &QmitkSemanticRelationsView::OnCaseIDSelectionChanged); + connect(m_LesionInfoWidget, &QmitkLesionInfoWidget::LesionSelectionChanged, this, &QmitkSemanticRelationsView::OnLesionSelectionChanged); + connect(m_PatientTableInspector, &QmitkPatientTableInspector::CurrentSelectionChanged, this, &QmitkSemanticRelationsView::OnDataNodeSelectionChanged); + connect(m_PatientTableInspector, &QmitkPatientTableInspector::DataNodeDoubleClicked, this, &QmitkSemanticRelationsView::OnDataNodeDoubleClicked); + connect(m_DnDDataNodeWidget, &QmitkDnDDataNodeWidget::NodesDropped, this, &QmitkSemanticRelationsView::OnNodesAdded); + + connect(m_PatientTableInspector, &QmitkPatientTableInspector::OnContextMenuRequested, m_ContextMenu, &QmitkSemanticRelationsContextMenu::OnContextMenuRequested); + connect(m_PatientTableInspector, &QmitkPatientTableInspector::OnNodeRemoved, this, &QmitkSemanticRelationsView::NodeRemoved); +} + +QItemSelectionModel* QmitkSemanticRelationsView::GetDataNodeSelectionModel() const +{ + return m_PatientTableInspector->GetSelectionModel(); +} + +void QmitkSemanticRelationsView::NodeRemoved(const mitk::DataNode* dataNode) +{ + if (nullptr == dataNode) + { + return; + } + + if (mitk::SemanticRelationsInference::InstanceExists(dataNode)) + { + // no observer needed for the integration; simply use a temporary instance for removing + mitk::SemanticRelationsIntegration semanticRelationsIntegration; + RemoveFromSemanticRelationsAction::Run(&semanticRelationsIntegration, dataNode); + mitk::SemanticTypes::CaseID caseID = mitk::GetCaseIDFromDataNode(dataNode); + RemoveFromComboBox(caseID); + } +} + +void QmitkSemanticRelationsView::OnLesionSelectionChanged(const mitk::SemanticTypes::Lesion& lesion) +{ + m_PatientTableInspector->SetLesion(lesion); +} + +void QmitkSemanticRelationsView::OnDataNodeSelectionChanged(const QList& dataNodeSelection) +{ + m_LesionInfoWidget->SetDataNodeSelection(dataNodeSelection); +} + +void QmitkSemanticRelationsView::OnDataNodeDoubleClicked(const mitk::DataNode* dataNode) +{ + if (nullptr == dataNode) + { + return; + } + + if (mitk::NodePredicates::GetImagePredicate()->CheckNode(dataNode)) + { + OpenInEditor(dataNode); + } + else if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) + { + JumpToPosition(dataNode); + } +} + +void QmitkSemanticRelationsView::OnCaseIDSelectionChanged(const QString& caseID) +{ + m_LesionInfoWidget->SetCaseID(caseID.toStdString()); + m_PatientTableInspector->SetCaseID(caseID.toStdString()); +} + +void QmitkSemanticRelationsView::OnNodesAdded(std::vector nodes) +{ + mitk::SemanticTypes::CaseID caseID = ""; + for (mitk::DataNode* dataNode : nodes) + { + // no observer needed for the integration; simply use a temporary instance for adding + mitk::SemanticRelationsIntegration semanticRelationsIntegration; + AddToSemanticRelationsAction::Run(&semanticRelationsIntegration, GetDataStorage(), dataNode); + caseID = mitk::GetCaseIDFromDataNode(dataNode); + AddToComboBox(caseID); + } +} + +void QmitkSemanticRelationsView::OnNodeRemoved(const mitk::DataNode* dataNode) +{ + NodeRemoved(dataNode); +} + +void QmitkSemanticRelationsView::AddToComboBox(const mitk::SemanticTypes::CaseID& caseID) +{ + int foundIndex = m_Controls.caseIDComboBox->findText(QString::fromStdString(caseID)); + if (-1 == foundIndex) + { + // add the caseID to the combo box, as it is not already contained + m_Controls.caseIDComboBox->addItem(QString::fromStdString(caseID)); + } +} + +void QmitkSemanticRelationsView::RemoveFromComboBox(const mitk::SemanticTypes::CaseID& caseID) +{ + std::vector allControlPoints = mitk::RelationStorage::GetAllControlPointsOfCase(caseID); + int foundIndex = m_Controls.caseIDComboBox->findText(QString::fromStdString(caseID)); + if (allControlPoints.empty() && -1 != foundIndex) + { + // caseID does not contain any control points and therefore no data + // remove the caseID, if it is still contained + m_Controls.caseIDComboBox->removeItem(foundIndex); + } +} + +void QmitkSemanticRelationsView::OpenInEditor(const mitk::DataNode* dataNode) +{ + auto renderWindowPart = GetRenderWindowPart(); + if (nullptr == renderWindowPart) + { + renderWindowPart = GetRenderWindowPart(mitk::WorkbenchUtil::IRenderWindowPartStrategy::BRING_TO_FRONT | mitk::WorkbenchUtil::IRenderWindowPartStrategy::OPEN); + if (nullptr == renderWindowPart) + { + // no render window available + return; + } + } + + auto image = dynamic_cast(dataNode->GetData()); + if (nullptr != image) + { + mitk::RenderingManager::GetInstance()->InitializeViews(image->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); + } +} + +void QmitkSemanticRelationsView::JumpToPosition(const mitk::DataNode* dataNode) +{ + if (nullptr == dataNode) + { + return; + } + + mitk::LabelSetImage* labelSetImage = dynamic_cast(dataNode->GetData()); + if (nullptr == labelSetImage) + { + return; + } + + unsigned int activeLayer = labelSetImage->GetActiveLayer(); + mitk::Label* activeLabel = labelSetImage->GetActiveLabel(activeLayer); + labelSetImage->UpdateCenterOfMass(activeLabel->GetValue(), activeLayer); + const mitk::Point3D& centerPosition = activeLabel->GetCenterOfMassCoordinates(); + if (centerPosition.GetVnlVector().max_value() > 0.0) + { + auto renderWindowPart = GetRenderWindowPart(); + if (nullptr == renderWindowPart) + { + renderWindowPart = GetRenderWindowPart(mitk::WorkbenchUtil::IRenderWindowPartStrategy::BRING_TO_FRONT | mitk::WorkbenchUtil::IRenderWindowPartStrategy::OPEN); + if (nullptr == renderWindowPart) + { + // no render window available + return; + } + } + + auto segmentation = dynamic_cast(dataNode->GetData()); + if (nullptr != segmentation) + { + renderWindowPart->SetSelectedPosition(centerPosition); + mitk::RenderingManager::GetInstance()->InitializeViews(segmentation->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); + } + } +} diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.h new file mode 100644 index 0000000000..725696107d --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.h @@ -0,0 +1,105 @@ +/*=================================================================== + +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 QMITKSEMANTICRELATIONSVIEW_H +#define QMITKSEMANTICRELATIONSVIEW_H + +// semantic relations plugin +#include "ui_QmitkSemanticRelationsControls.h" +#include "QmitkLesionInfoWidget.h" +#include "QmitkSemanticRelationsContextMenu.h" + +// semantic relations module +#include + +// semantic relations UI module +#include + +// mitk gui common plugin +#include + +// berry +#include + +// mitk qt +#include + +class QmitkDnDDataNodeWidget; +class QMenu; + +/* +* @brief The QmitkSemanticRelationsView is an MITK view to combine and show the widgets of the 'SemanticRelationsUI'-module and this semantic relations plugin. +* +* It allows the MITK user to see and modify the content of the SemanticRelations-session. +* A combo box is used to select and show the current patient. +*/ +class QmitkSemanticRelationsView : public QmitkAbstractView, public mitk::IRenderWindowPartListener +{ + Q_OBJECT + +public: + + static const std::string VIEW_ID; + +protected: + + virtual void SetFocus() override; + virtual void CreateQtPartControl(QWidget* parent) override; + + virtual void RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) override; + virtual void RenderWindowPartDeactivated(mitk::IRenderWindowPart*) override { } + +private Q_SLOTS: + + void OnLesionSelectionChanged(const mitk::SemanticTypes::Lesion&); + void OnDataNodeSelectionChanged(const QList&); + void OnDataNodeDoubleClicked(const mitk::DataNode*); + void OnCaseIDSelectionChanged(const QString&); + + void OnNodesAdded(std::vector); + void OnNodeRemoved(const mitk::DataNode*); + +private: + + void SetUpConnections(); + /** + * @brief Provide a QItemSelectionModel, which supports the data role 'QmitkDataNodeRole' (\see QmitkRenderWindowDataModel). + * + * The provided QItemSelectionModel is used in the QmitkAbstractView-base class as the selection model of + * the selection provider (\see QmitkAbstractView::SetSelectionProvider()). + * The default selection provider is a QmitkDataNodeSelectionProvider. Each time a selection in the provided + * QItemSeletionModel is changed, a selection changed event is fired. All plugins (views), that subclass the + * QmitkAbstractView will be informed about the selection changed via the OnSelectionChanged-function. + */ + virtual QItemSelectionModel* GetDataNodeSelectionModel() const override; + + virtual void NodeRemoved(const mitk::DataNode* dataNode) override; + + void AddToComboBox(const mitk::SemanticTypes::CaseID& caseID); + void RemoveFromComboBox(const mitk::SemanticTypes::CaseID& caseID); + + void OpenInEditor(const mitk::DataNode* dataNode); + void JumpToPosition(const mitk::DataNode* dataNode); + + Ui::QmitkSemanticRelationsControls m_Controls; + QmitkLesionInfoWidget* m_LesionInfoWidget; + QmitkPatientTableInspector* m_PatientTableInspector; + QmitkDnDDataNodeWidget* m_DnDDataNodeWidget; + + QmitkSemanticRelationsContextMenu* m_ContextMenu; +}; + +#endif // QMITKSEMANTICRELATIONSVIEW_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/mitkPluginActivator.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/mitkPluginActivator.cpp new file mode 100644 index 0000000000..9a343753d6 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/mitkPluginActivator.cpp @@ -0,0 +1,31 @@ +/*=================================================================== + +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 "mitkPluginActivator.h" +#include "QmitkSemanticRelationsView.h" +#include + +namespace mitk +{ + void SemanticRelationsActivator::start(ctkPluginContext *context) + { + mitk::PersistenceService::LoadModule(); + + BERRY_REGISTER_EXTENSION_CLASS(QmitkSemanticRelationsView, context) + } + + void SemanticRelationsActivator::stop(ctkPluginContext *context) { Q_UNUSED(context) } +} diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/mitkPluginActivator.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/mitkPluginActivator.h new file mode 100644 index 0000000000..db67eb7674 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/mitkPluginActivator.h @@ -0,0 +1,37 @@ +/*=================================================================== + +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 MITKPLUGINACTIVATOR_H +#define MITKPLUGINACTIVATOR_H + +#include + +namespace mitk +{ + class SemanticRelationsActivator : public QObject, public ctkPluginActivator + { + Q_OBJECT + Q_PLUGIN_METADATA(IID "org_mitk_gui_qt_semanticrelations") + Q_INTERFACES(ctkPluginActivator) + + public: + void start(ctkPluginContext* context) override; + void stop(ctkPluginContext* context) override; + + }; +} + +#endif // MITKPLUGINACTIVATOR_H