diff --git a/Modules/SemanticRelations/CMakeLists.txt b/Modules/SemanticRelations/CMakeLists.txt index 72db95214f..aa6819b240 100644 --- a/Modules/SemanticRelations/CMakeLists.txt +++ b/Modules/SemanticRelations/CMakeLists.txt @@ -1,7 +1,7 @@ MITK_CREATE_MODULE( - DEPENDS MitkSceneSerializationBase MitkDICOMReader MitkMultilabel MitkPersistence + DEPENDS MitkSceneSerializationBase MitkDICOMReader MitkMultilabel MitkPersistence MitkImageStatisticsUI ) if(BUILD_TESTING) ADD_SUBDIRECTORY(Test) endif(BUILD_TESTING) diff --git a/Modules/SemanticRelations/include/mitkControlPointManager.h b/Modules/SemanticRelations/include/mitkControlPointManager.h index c1343ebd8f..d51ac37f9f 100644 --- a/Modules/SemanticRelations/include/mitkControlPointManager.h +++ b/Modules/SemanticRelations/include/mitkControlPointManager.h @@ -1,93 +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 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. + * @param caseID The current case identifier is defined by the given string. + * @param controlPointUID The control point UID as string. * * @return The control point with its UID and the date. */ - MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint GetControlPointByUID(const SemanticTypes::ID& controlPointUID, const SemanticTypes::ControlPointVector& allControlPoints); + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint GetControlPointByUID(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& controlPointUID); /** * @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. + * @param caseID The current case identifier is defined by the given string. + * @param controlPoint The control point to check for existence. + * + * @return The existing control point. */ - MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint FindExistingControlPoint(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ControlPointVector& allControlPoints); + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint FindExistingControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); /** - * @brief Returns an already existing close control point from the given vector of control points. This closest control point has a date + * @brief Returns an already existing close control point from the given vector of control points. This closest control point has a * 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. + * @param caseID The current case identifier is defined by the given string. + * @param controlPoint The control point to check for distance. + * + * @return The closest control point. */ - MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint FindClosestControlPoint(const SemanticTypes::ControlPoint& controlPoint, SemanticTypes::ControlPointVector& allControlPoints); + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint FindClosestControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); /** * @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. + * @param caseID The current case identifier is defined by the given string. + * @param controlPoint The control point of which the examination period should be found. + * + * @return The examination period that contains the given control point. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ExaminationPeriod FindContainingExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); + /** + * @brief Return the examination period to which the given data node belongs. + * The control point is used to find an already existing or the closest control point in the semantic relations storage. + * If such a control point is found, the 'FindClosestControlPoint'-function with this control point as an argument is used + * to actually find the corresponding examination period. + * + * @param caseID The current case identifier is defined by the given string. + * @param controlPoint The control point of which the examination period should be found. + * + * @return The examination period that fits the given data node. */ - MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ExaminationPeriod FindExaminationPeriod(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriodVector& allExaminationPeriods); + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ExaminationPeriod FindFittingExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); + /** + * @brief Return the examination period to which the given data node belongs. + * The DICOM date of the data node is used to find an already existing or the closest control point in the semantic relations storage. + * If such a control point is found, the 'FindFittingExaminationPeriod'-function with this control point as an argument is used + * to actually find the corresponding examination period. + * + * @param datanode A data node pointer, whose date should be included in the newly generated control point. + * + * @return The examination period that contains the given data node. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ExaminationPeriod FindFittingExaminationPeriod(const DataNode* dataNode); /** * @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. + * @param caseID The current case identifier is defined by the given string. + * @param allExaminationPeriods The examination periods to sort. */ - MITKSEMANTICRELATIONS_EXPORT void SortExaminationPeriods(SemanticTypes::ExaminationPeriodVector& allExaminationPeriods, const SemanticTypes::ControlPointVector& allControlPoints); + MITKSEMANTICRELATIONS_EXPORT void SortAllExaminationPeriods(const SemanticTypes::CaseID& caseID, SemanticTypes::ExaminationPeriodVector& allExaminationPeriods); + } // namespace mitk #endif // MITKCONTROLPOINTMANAGER_H diff --git a/Modules/SemanticRelations/include/mitkISemanticRelationsObservable.h b/Modules/SemanticRelations/include/mitkISemanticRelationsObservable.h index 08e50b37ac..a9c78e0f07 100644 --- a/Modules/SemanticRelations/include/mitkISemanticRelationsObservable.h +++ b/Modules/SemanticRelations/include/mitkISemanticRelationsObservable.h @@ -1,57 +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. ===================================================================*/ #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. + * @param 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. + * @param 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. + * @param caseID A caseID that identifies the currently active patient / case. */ - virtual void NotifyObserver(const mitk::SemanticTypes::CaseID& caseID) const = 0; + virtual void NotifyObserver(const 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 index 3fd8651456..d7df63e1f5 100644 --- a/Modules/SemanticRelations/include/mitkISemanticRelationsObserver.h +++ b/Modules/SemanticRelations/include/mitkISemanticRelationsObserver.h @@ -1,43 +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. + * @param 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 index 7f1beec103..a2818d3422 100644 --- a/Modules/SemanticRelations/include/mitkLesionData.h +++ b/Modules/SemanticRelations/include/mitkLesionData.h @@ -1,63 +1,68 @@ /*=================================================================== 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 namespace mitk { /** * @brief This class holds the data of each lesion in the lesion tree view. + * The data is the lesion itself with its UID, name and lesion class + * as well as two vectors for + * - lesion presence: bool value for each control-point + * inside the semantic relations storage + * - lesion volume: double value for each control-point - information type pair + * inside the semantic relations storage * */ 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 #endif // MITKLESIONDATA_H diff --git a/Modules/SemanticRelations/include/mitkLesionManager.h b/Modules/SemanticRelations/include/mitkLesionManager.h index 790382bc0e..976d8f43de 100644 --- a/Modules/SemanticRelations/include/mitkLesionManager.h +++ b/Modules/SemanticRelations/include/mitkLesionManager.h @@ -1,78 +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 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. +* to generate new and 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. + * @brief Compute and store lesion presence for all available control points and information types. * * @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); + MITKSEMANTICRELATIONS_EXPORT void ComputeLesionPresence(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 index 554653b21e..201e9e3838 100644 --- a/Modules/SemanticRelations/include/mitkNodePredicates.h +++ b/Modules/SemanticRelations/include/mitkNodePredicates.h @@ -1,47 +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(); + MITKSEMANTICRELATIONS_EXPORT 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(); + MITKSEMANTICRELATIONS_EXPORT NodePredicateAnd::Pointer GetSegmentationPredicate(); } // namespace NodePredicates } // namespace mitk #endif // MITKNODEPREDICATES_H diff --git a/Modules/SemanticRelations/include/mitkRelationStorage.h b/Modules/SemanticRelations/include/mitkRelationStorage.h index 08f6aecc12..0bfc45cbd3 100644 --- a/Modules/SemanticRelations/include/mitkRelationStorage.h +++ b/Modules/SemanticRelations/include/mitkRelationStorage.h @@ -1,82 +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 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); + MITKSEMANTICRELATIONS_EXPORT void RenameExaminationPeriod(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/mitkSemanticRelationsDataStorageAccess.h b/Modules/SemanticRelations/include/mitkSemanticRelationsDataStorageAccess.h index 07b2854aaf..e02de8ba66 100644 --- a/Modules/SemanticRelations/include/mitkSemanticRelationsDataStorageAccess.h +++ b/Modules/SemanticRelations/include/mitkSemanticRelationsDataStorageAccess.h @@ -1,140 +1,187 @@ /*=================================================================== 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. + * @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 specified by the given vector of image IDs. + * + * @pre The data storage member has to be valid (!nullptr). + * @throw SemanticRelationException, if the data storage member is invalid (==nullptr). + * + * @param imageIDs A vector of image IDs that represent the images in the data storage. + * + * @return A vector of data nodes representing images. + */ + DataNodeVector GetAllImagesByID(const SemanticTypes::IDVector& imageIDs) 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. + * @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. + * @brief Return a vector of all image nodes that are defined with the given control point and the given information type. * * @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. + * + * @return A vector of image nodes that are defined with the given control point and the given information type. */ 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. + * @brief Return a vector of all image nodes that are defined with the given information type and the given examination period. + * The function uses the 'SemanticRelationsInference::GetAllImageIDsOfExaminationPeriod'-function to retrieve the imageIDs of the examination period and + * then compares the information type of all these images against the given information type. + * + * @param informationType An information type that identifies the corresponding information type instance. + * @param examinationPeriod An examination period that identifies the corresponding examination period 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::InformationType& informationType, const SemanticTypes::ExaminationPeriod& examinationPeriod) const; + /** + * @brief Return a vector of all segmentation nodes that are defined with the given control point and the given information type. * 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. + * + * @return A vector of segmentation nodes that are defined with the given control point and the given information type. */ DataNodeVector GetAllSpecificSegmentations(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const; + /** + * @brief Return the single segmentation node that is defined with the given information type, the given control point and is representing the given lesion. + * The function uses the 'GetAllSpecificSegmentations'-function to retrieve the specific segmentations and then checks for the represented lesion. + * + * @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). + * The lesion has to exist for the given case. + * @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') or + * if the lesion does not exist for the given case (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. + * @param lesion A lesion with a UID that identifies the corresponding lesion instance. + * + * @return A single segmentation node that is defined with the given information type, the given control point and is representing the given lesion. + */ + DataNode::Pointer GetSpecificSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, + const SemanticTypes::InformationType& informationType, const SemanticTypes::Lesion& lesion) const; private: WeakPointer m_DataStorage; }; } // namespace mitk #endif // MITKSEMANTICRELATIONSDATASTORAGEACCESS_H diff --git a/Modules/SemanticRelations/include/mitkSemanticRelationsInference.h b/Modules/SemanticRelations/include/mitkSemanticRelationsInference.h index fa9faeda4d..404edad885 100644 --- a/Modules/SemanticRelations/include/mitkSemanticRelationsInference.h +++ b/Modules/SemanticRelations/include/mitkSemanticRelationsInference.h @@ -1,292 +1,351 @@ /*=================================================================== 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. + * + * @return A vector of lesions. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionVector GetAllLesionsOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); /** + * @brief Returns a vector of all lesions 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) lesion: + * Only those lesions are returned for which the image of the associated segmentation is of the given information type. + * If the information type instance does not exist, an empty vector is returned. + * + * @return A vector of lesions. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionVector GetAllLesionsOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType); + /** + * @brief Returns a vector of all lesions that are valid for the given case, given a specific control point and a specific information type. + * + * @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. + * @param informationType A specific information type which has to be available at a returned (found) lesion: + * Only those lesions are returned for which the image of the associated segmentation is of the given information type. + * If the information type instance does not exist, an empty vector is returned. + * + * @return A vector of lesions. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionVector GetAllSpecificLesions(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType); + /** * @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. + * + * @return A vector of IDs identifying images that are related to the given lesion. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::IDVector GetAllImageIDsOfLesion(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 examination period. + * If the examination period is not used by and image, an empty vector is returned. + * + * @pre The UID of the examination period has to exist for an examination period instance. + * @throw SemanticRelationException, if UID of the examination period does not exist for an examination period instance (this can be checked via 'InstanceExists'). + * + * @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 A vector of IDs identifying images that are related to the given examination period. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::IDVector GetAllImageIDsOfExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod); + /** * @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. + * @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 index 2babb30592..66f41802e2 100644 --- a/Modules/SemanticRelations/include/mitkSemanticRelationsIntegration.h +++ b/Modules/SemanticRelations/include/mitkSemanticRelationsIntegration.h @@ -1,314 +1,324 @@ /*=================================================================== 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 Rename an already existing examination period instance. + * + * @pre The UID of the examination period has to exist for an examination period instance. + * @throw SemanticRelationException, if the UID of the examination period does not exist for an examination period instance (this can be checked via 'InstanceExists'). + * + * @param caseID The current case identifier is defined by the given string. + * @param lesion The examination period instance that renames an existing examination period. + */ + void RenameExaminationPeriod(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/src/mitkControlPointManager.cpp b/Modules/SemanticRelations/src/mitkControlPointManager.cpp index 98f1486379..01c041f871 100644 --- a/Modules/SemanticRelations/src/mitkControlPointManager.cpp +++ b/Modules/SemanticRelations/src/mitkControlPointManager.cpp @@ -1,179 +1,228 @@ /*=================================================================== 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 "mitkRelationStorage.h" #include "mitkSemanticRelationException.h" #include "mitkUIDGeneratorBoost.h" // mitk core #include mitk::SemanticTypes::ControlPoint mitk::GenerateControlPoint(const DataNode* datanode) { SemanticTypes::ControlPoint controlPoint; try { controlPoint = GetDICOMDateFromDataNode(datanode); } catch (SemanticRelationException& e) { - mitkReThrow(e) << "Cannot generate a control point from the DICOM tag of the given data node"; + mitkReThrow(e) << "Cannot generate a control point from the DICOM tag of the given data node."; } controlPoint.UID = UIDGeneratorBoost::GenerateUID(); return controlPoint; } -mitk::SemanticTypes::ControlPoint mitk::GetControlPointByUID(const SemanticTypes::ID& controlPointUID, const std::vector& allControlPoints) +mitk::SemanticTypes::ControlPoint mitk::GetControlPointByUID(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& controlPointUID) { auto lambda = [&controlPointUID](const SemanticTypes::ControlPoint& currentControlPoint) { return currentControlPoint.UID == controlPointUID; }; + SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); 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) +mitk::SemanticTypes::ControlPoint mitk::FindExistingControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { + SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); 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) +mitk::SemanticTypes::ControlPoint mitk::FindClosestControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { + SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); 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; + SemanticTypes::ControlPointVector::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) +mitk::SemanticTypes::ExaminationPeriod mitk::FindContainingExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { + SemanticTypes::ExaminationPeriodVector allExaminationPeriods = RelationStorage::GetAllExaminationPeriodsOfCase(caseID); 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) +mitk::SemanticTypes::ExaminationPeriod mitk::FindFittingExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { - auto lambda = [allControlPoints](const SemanticTypes::ExaminationPeriod& leftExaminationPeriod, const SemanticTypes::ExaminationPeriod& rightExaminationPeriod) + SemanticTypes::ExaminationPeriod specificExaminationPeriod; + SemanticTypes::ControlPoint specificControlPoint; + // find the closest control point + SemanticTypes::ControlPoint existingControlPoint = FindExistingControlPoint(caseID, controlPoint); + if (!existingControlPoint.UID.empty()) + { + specificControlPoint = existingControlPoint; + } + else + { + auto closestControlPoint = FindClosestControlPoint(caseID, controlPoint); + if (!closestControlPoint.UID.empty()) + { + specificControlPoint = closestControlPoint; + } + } + + // find the containing examination period + return FindContainingExaminationPeriod(caseID, specificControlPoint); +} + +mitk::SemanticTypes::ExaminationPeriod mitk::FindFittingExaminationPeriod(const DataNode* dataNode) +{ + SemanticTypes::CaseID caseID = ""; + SemanticTypes::ControlPoint controlPoint; + try + { + caseID = GetCaseIDFromDataNode(dataNode); + controlPoint = GetDICOMDateFromDataNode(dataNode); + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot find an examination period."; + } + + return FindFittingExaminationPeriod(caseID, controlPoint); +} + +void mitk::SortAllExaminationPeriods(const SemanticTypes::CaseID& caseID, SemanticTypes::ExaminationPeriodVector& allExaminationPeriods) +{ + SemanticTypes::ControlPointVector controlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); + // sort the vector of control points for the timeline + std::sort(controlPoints.begin(), controlPoints.end()); + + auto lambda = [&caseID](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); + const auto& leftControlPoint = GetControlPointByUID(caseID, leftUID); + const auto& rightControlPoint = GetControlPointByUID(caseID, rightUID); 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 index 7be974ef13..21e2f1fc21 100644 --- a/Modules/SemanticRelations/src/mitkDICOMHelper.cpp +++ b/Modules/SemanticRelations/src/mitkDICOMHelper.cpp @@ -1,190 +1,190 @@ /*=================================================================== 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; } std::string mitk::GetCaseIDDICOMProperty() { // 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 PatientName (0x0010, 0x0010) is used return GeneratePropertyNameForDICOMTag(0x0010, 0x0010); } std::string mitk::GetNodeIDDICOMProperty() { // extract suitable DICOM tag to use as the data node id // DICOM tag "0x0020, 0x000e" is SeriesInstanceUID return GeneratePropertyNameForDICOMTag(0x0020, 0x000e); } std::string mitk::GetDateDICOMProperty() { // extract suitable DICOM tag to use as the data node id // DICOM tag "0x0008, 0x0022" is AcquisitionDate return GeneratePropertyNameForDICOMTag(0x0008, 0x0022); } std::string mitk::GetModalityDICOMProperty() { // extract suitable DICOM tag to use as the information type // DICOM tag "0x0008, 0x0060" is Modality return GeneratePropertyNameForDICOMTag(0x0008, 0x0060); } -mitk::SemanticTypes::CaseID mitk::GetCaseIDFromDataNode(const mitk::DataNode* dataNode) +mitk::SemanticTypes::CaseID mitk::GetCaseIDFromDataNode(const DataNode* dataNode) { if (nullptr == dataNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } - mitk::BaseData* baseData = dataNode->GetData(); + BaseData* baseData = dataNode->GetData(); if (nullptr == baseData) { mitkThrowException(SemanticRelationException) << "No valid base data."; } - mitk::BaseProperty* dicomTag = baseData->GetProperty(GetCaseIDDICOMProperty().c_str()); + BaseProperty* dicomTag = baseData->GetProperty(GetCaseIDDICOMProperty().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) +mitk::SemanticTypes::ID mitk::GetIDFromDataNode(const DataNode* dataNode) { if (nullptr == dataNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } - mitk::BaseData* baseData = dataNode->GetData(); + BaseData* baseData = dataNode->GetData(); if (nullptr == baseData) { mitkThrowException(SemanticRelationException) << "No valid base data."; } - mitk::BaseProperty* dicomTag = baseData->GetProperty(GetNodeIDDICOMProperty().c_str()); + BaseProperty* dicomTag = baseData->GetProperty(GetNodeIDDICOMProperty().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) +mitk::SemanticTypes::ControlPoint mitk::GetDICOMDateFromDataNode(const DataNode* dataNode) { if (nullptr == dataNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } - mitk::BaseData* baseData = dataNode->GetData(); + BaseData* baseData = dataNode->GetData(); if (nullptr == baseData) { mitkThrowException(SemanticRelationException) << "No valid base data."; } - mitk::BaseProperty* acquisitionDateProperty = baseData->GetProperty(GetDateDICOMProperty().c_str()); + BaseProperty* acquisitionDateProperty = baseData->GetProperty(GetDateDICOMProperty().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) +mitk::SemanticTypes::InformationType mitk::GetDICOMModalityFromDataNode(const DataNode* dataNode) { if (nullptr == dataNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } - mitk::BaseData* baseData = dataNode->GetData(); + BaseData* baseData = dataNode->GetData(); if (nullptr == baseData) { mitkThrowException(SemanticRelationException) << "No valid base data."; } - mitk::BaseProperty* dicomTag = baseData->GetProperty(GetModalityDICOMProperty().c_str()); + BaseProperty* dicomTag = baseData->GetProperty(GetModalityDICOMProperty().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 index 172b364f65..d2c8ced6e4 100644 --- a/Modules/SemanticRelations/src/mitkLesionData.cpp +++ b/Modules/SemanticRelations/src/mitkLesionData.cpp @@ -1,43 +1,38 @@ /*=================================================================== 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 index a9e83d88e2..6c623ad2c9 100644 --- a/Modules/SemanticRelations/src/mitkLesionManager.cpp +++ b/Modules/SemanticRelations/src/mitkLesionManager.cpp @@ -1,123 +1,106 @@ /*=================================================================== 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 "mitkRelationStorage.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.UID = 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.UID = 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) +void mitk::ComputeLesionPresence(LesionData& lesionData, const SemanticTypes::CaseID& caseID) { std::vector lesionPresence; - std::vector lesionVolume; - SemanticTypes::Lesion lesion = lesionData.GetLesion(); + auto lesion = lesionData.GetLesion(); bool presence = false; - double volume = 0.0; - - SemanticTypes::ControlPointVector controlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); + auto controlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); // sort the vector of control points for the timeline std::sort(controlPoints.begin(), controlPoints.end()); - for (const auto& controlPoint : controlPoints) + auto informationTypes = RelationStorage::GetAllInformationTypesOfCase(caseID); + for (const auto& informationType : informationTypes) { - try + for (const auto& controlPoint : controlPoints) { - presence = SemanticRelationsInference::IsLesionPresentAtControlPoint(caseID, lesion, controlPoint); + try + { + presence = SemanticRelationsInference::IsLesionPresentAtControlPoint(caseID, lesion, controlPoint); + } + catch (SemanticRelationException&) + { + presence = false; + } + + lesionPresence.push_back(presence); } - catch (SemanticRelationException& e) - { - mitkReThrow(e) << "Cannot determine the lesion presence for generating additional lesion data."; - } - - lesionPresence.push_back(presence); - volume = GetLesionVolume(caseID, lesion, controlPoint); - lesionVolume.push_back(volume); } 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/mitkRelationStorage.cpp b/Modules/SemanticRelations/src/mitkRelationStorage.cpp index 0a1f1c9e40..32156b5760 100644 --- a/Modules/SemanticRelations/src/mitkRelationStorage.cpp +++ b/Modules/SemanticRelations/src/mitkRelationStorage.cpp @@ -1,1539 +1,1571 @@ /*=================================================================== 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::RenameExaminationPeriod(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 data of the given examination period + VectorProperty* examinationPeriodDataVectorProperty = dynamic_cast*>(propertyList->GetProperty(examinationPeriod.UID)); + if (nullptr == examinationPeriodDataVectorProperty) + { + MITK_DEBUG << "Could not find examination period " << examinationPeriod.UID << " in the storage. Cannot rename the examination period."; + return; + } + + std::vector examinationPeriodDataVectorPropertyValue = examinationPeriodDataVectorProperty->GetValue(); + // an examination period has an arbitrary number of vector values (name and control point UIDs) (at least one for the name) + if (examinationPeriodDataVectorPropertyValue.size() < 1) + { + MITK_DEBUG << "Incorrect examination period storage. At least one (1) name has to be stored."; + return; + } + else + { + // set the first vector value - the name + examinationPeriodDataVectorPropertyValue[0] = examinationPeriod.name; + // store the modified vector value + examinationPeriodDataVectorProperty->SetValue(examinationPeriodDataVectorPropertyValue); + } +} + 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 index e020f5706b..e064de7ef5 100644 --- a/Modules/SemanticRelations/src/mitkSemanticRelationsDataStorageAccess.cpp +++ b/Modules/SemanticRelations/src/mitkSemanticRelationsDataStorageAccess.cpp @@ -1,236 +1,342 @@ /*=================================================================== 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; + SemanticTypes::IDVector allSegmentationIDsOfCase = RelationStorage::GetAllSegmentationIDsOfCase(caseID); + DataNodeVector 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(); - std::string caseID; - std::string segmentationID; + SemanticTypes::CaseID currentCaseID; + SemanticTypes::ID segmentationID; try { // find the corresponding segmentation node for the given segmentation ID - caseID = GetCaseIDFromDataNode(segmentationNode); + currentCaseID = GetCaseIDFromDataNode(segmentationNode); segmentationID = GetIDFromDataNode(segmentationNode); } catch (SemanticRelationException&) { // 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; } - if (caseID == caseID && (std::find(allSegmentationIDsOfCase.begin(), allSegmentationIDsOfCase.end(), segmentationID) != allSegmentationIDsOfCase.end())) + if (caseID == currentCaseID && (std::find(allSegmentationIDsOfCase.begin(), allSegmentationIDsOfCase.end(), segmentationID) != allSegmentationIDsOfCase.end())) { // found current image node in the storage, add it to the return vector allSegmentationsOfCase.push_back(segmentationNode); } } 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; + SemanticTypes::IDVector allImageIDsOfCase = RelationStorage::GetAllImageIDsOfCase(caseID); + DataNodeVector 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(); - std::string caseID; - std::string imageID; + SemanticTypes::CaseID currentCaseID; + SemanticTypes::ID imageID; try { // find the corresponding image node for the given segmentation ID - caseID = GetCaseIDFromDataNode(imageNode); + currentCaseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException&) { // found an image node that is not stored in the semantic relations // this image node does not have any DICOM information --> exception thrown // continue with the next image to compare IDs continue; } - if (caseID == caseID && (std::find(allImageIDsOfCase.begin(), allImageIDsOfCase.end(), imageID) != allImageIDsOfCase.end())) + if (caseID == currentCaseID && (std::find(allImageIDsOfCase.begin(), allImageIDsOfCase.end(), imageID) != 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::GetAllImagesByID(const SemanticTypes::IDVector& imageIDs) const +{ + if (m_DataStorage.IsExpired()) + { + mitkThrowException(SemanticRelationException) << "Not a valid data storage."; + } + + DataNodeVector 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(); + + SemanticTypes::CaseID currentCaseID; + SemanticTypes::ID imageID; + try + { + // find the corresponding image node for the given segmentation ID + imageID = GetIDFromDataNode(imageNode); + } + catch (SemanticRelationException&) + { + // found an image node that is not stored in the semantic relations + // this image node does not have any DICOM information --> exception thrown + // continue with the next image to compare IDs + continue; + } + + if (std::find(imageIDs.begin(), imageIDs.end(), imageID) != imageIDs.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; + 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::GetAllSpecificImages(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType, const SemanticTypes::ExaminationPeriod& examinationPeriod) const +{ + if (SemanticRelationsInference::InstanceExists(caseID, informationType)) + { + if (SemanticRelationsInference::InstanceExists(caseID, examinationPeriod)) + { + // examination period exists, information type exists, retrieve all imageIDs from the storage + auto allImageIDsOfExaminationPeriod = SemanticRelationsInference::GetAllImageIDsOfExaminationPeriod(caseID, examinationPeriod); + // filter all images to remove the ones with a different information type using a lambda function + auto lambda = [&caseID, &informationType, this](SemanticTypes::ID imageID) + { + return (informationType != RelationStorage::GetInformationTypeOfImage(caseID, imageID)); + }; + + allImageIDsOfExaminationPeriod.erase(std::remove_if(allImageIDsOfExaminationPeriod.begin(), allImageIDsOfExaminationPeriod.end(), lambda), allImageIDsOfExaminationPeriod.end()); + + auto allImagesOfExaminationPeriod = GetAllImagesByID(allImageIDsOfExaminationPeriod); + return allImagesOfExaminationPeriod; + } + else + { + mitkThrowException(SemanticRelationException) << "Could not find an existing examination period for the given caseID " << caseID << " and examination period " << examinationPeriod.name << "."; } } else { - mitkThrowException(SemanticRelationException) << "Could not find an existing control point for the given caseID " << caseID << " and control point " << controlPoint.UID; + mitkThrowException(SemanticRelationException) << "Could not find an existing information type for the given caseID " << caseID << " and information type " << informationType << "."; } } 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 allSpecificImages; + try + { + allSpecificImages = GetAllSpecificImages(caseID, controlPoint, informationType); + + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot get the specific segmentation."; + } + 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; } + +mitk::DataNode::Pointer mitk::SemanticRelationsDataStorageAccess::GetSpecificSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, + const SemanticTypes::InformationType& informationType, const SemanticTypes::Lesion& lesion) const +{ + if (m_DataStorage.IsExpired()) + { + mitkThrow() << "Not a valid data storage."; + } + + DataNodeVector allSpecificSegmentations; + try + { + allSpecificSegmentations = GetAllSpecificSegmentations(caseID, controlPoint, informationType); + + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot get the specific segmentation."; + } + + for (const auto& segmentationNode : allSpecificSegmentations) + { + SemanticTypes::Lesion representedLesion = SemanticRelationsInference::GetLesionOfSegmentation(segmentationNode); + if (representedLesion.UID == lesion.UID) + { + return segmentationNode; + } + } + + return mitk::DataNode::Pointer(); +} diff --git a/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp b/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp index 21dfdb3301..a974ed4571 100644 --- a/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp +++ b/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp @@ -1,528 +1,591 @@ /*=================================================================== 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 = ""; SemanticTypes::ID segmentationID = ""; try { caseID = GetCaseIDFromDataNode(segmentationNode); segmentationID = GetIDFromDataNode(segmentationNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot get the lesion of the given segmentation data node."; } 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 = ""; SemanticTypes::ID imageID = ""; try { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot get all lesions of the given image data node."; } 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; } +mitk::SemanticTypes::LesionVector mitk::SemanticRelationsInference::GetAllLesionsOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) +{ + 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 information type using a lambda function + auto lambda = [&caseID, &informationType](const SemanticTypes::Lesion& lesion) + { + return !SpecificImageExists(caseID, lesion, informationType); + }; + + allLesions.erase(std::remove_if(allLesions.begin(), allLesions.end(), lambda), allLesions.end()); + + return allLesions; +} + +mitk::SemanticTypes::LesionVector mitk::SemanticRelationsInference::GetAllSpecificLesions(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) +{ + auto allLesionsOfControlPoint = GetAllLesionsOfControlPoint(caseID, controlPoint); + auto allLesionsOfInformationType = GetAllLesionsOfInformationType(caseID, informationType); + SemanticTypes::LesionVector allLesionsIntersection; + + 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(allLesionsOfControlPoint.begin(), allLesionsOfControlPoint.end(), lessThan); + std::sort(allLesionsOfInformationType.begin(), allLesionsOfInformationType.end(), lessThan); + SemanticTypes::IDVector allImageIDsIntersection; + // set_intersection removes duplicated nodes + std::set_intersection(allLesionsOfControlPoint.begin(), allLesionsOfControlPoint.end(), + allLesionsOfInformationType.begin(), allLesionsOfInformationType.end(), + std::back_inserter(allLesionsIntersection), equal); + + return allLesionsIntersection; +} + bool mitk::SemanticRelationsInference::IsRepresentingALesion(const DataNode* segmentationNode) { SemanticTypes::Lesion representedLesion; try { representedLesion = GetLesionOfSegmentation(segmentationNode); } catch (const SemanticRelationException&) { return false; } return !representedLesion.UID.empty(); } bool mitk::SemanticRelationsInference::IsRepresentingALesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID) { SemanticTypes::Lesion representedLesion = RelationStorage::GetLesionOfSegmentation(caseID, segmentationID); return !representedLesion.UID.empty(); } bool mitk::SemanticRelationsInference::IsLesionPresent(const SemanticTypes::Lesion& lesion, const DataNode* dataNode) { SemanticTypes::CaseID caseID = ""; SemanticTypes::ID dataNodeID = ""; try { caseID = GetCaseIDFromDataNode(dataNode); dataNodeID = GetIDFromDataNode(dataNode); } catch (const SemanticRelationException&) { return false; } if (NodePredicates::GetImagePredicate()->CheckNode(dataNode)) { return IsLesionPresentOnImage(caseID, lesion, dataNodeID); } if (NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { return IsLesionPresentOnSegmentation(caseID, lesion, dataNodeID); } return false; } bool mitk::SemanticRelationsInference::IsLesionPresentOnImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ID& imageID) { SemanticTypes::IDVector allImageIDsOfLesion; try { allImageIDsOfLesion = GetAllImageIDsOfLesion(caseID, lesion); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot get all image IDs of the given lesion to determine the lesion presence."; } 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; try { allImageIDsOfLesion = GetAllImageIDsOfLesion(caseID, lesion); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot get all image IDs of the given lesion to determine the lesion presence."; } for (const auto& imageIDOfLesion : allImageIDsOfLesion) { - auto imageControlPoint = mitk::RelationStorage::GetControlPointOfImage(caseID, imageIDOfLesion); + auto imageControlPoint = RelationStorage::GetControlPointOfImage(caseID, imageIDOfLesion); if (imageControlPoint.date == controlPoint.date) { return true; } } return false; } bool mitk::SemanticRelationsInference::InstanceExists(const DataNode* dataNode) { SemanticTypes::CaseID caseID = ""; SemanticTypes::ID dataNodeID = ""; try { caseID = GetCaseIDFromDataNode(dataNode); dataNodeID = GetIDFromDataNode(dataNode); } catch (const SemanticRelationException&) { return false; } if (NodePredicates::GetImagePredicate()->CheckNode(dataNode)) { - std::vector allImageIDsOfCase = RelationStorage::GetAllImageIDsOfCase(caseID); + SemanticTypes::IDVector allImageIDsOfCase = RelationStorage::GetAllImageIDsOfCase(caseID); return std::find(allImageIDsOfCase.begin(), allImageIDsOfCase.end(), dataNodeID) != allImageIDsOfCase.end(); } if (NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { - std::vector allSegmentationIDsOfCase = RelationStorage::GetAllSegmentationIDsOfCase(caseID); + SemanticTypes::IDVector allSegmentationIDsOfCase = RelationStorage::GetAllSegmentationIDsOfCase(caseID); return std::find(allSegmentationIDsOfCase.begin(), allSegmentationIDsOfCase.end(), dataNodeID) != allSegmentationIDsOfCase.end(); } 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); return existingLesion != allLesions.end(); } 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::IDVector mitk::SemanticRelationsInference::GetAllImageIDsOfExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) +{ + if (!InstanceExists(caseID, examinationPeriod)) + { + mitkThrowException(SemanticRelationException) << "Could not find an existing examination period for the given caseID " << caseID << " and examination period " << examinationPeriod.name << "."; + } + + SemanticTypes::IDVector allImageIDsOfExaminationPeriod; + // 1. get all control point UIDs of the examination period + // 2. get all images of each control points to find all images of the examination period + SemanticTypes::ControlPoint controlPoint; + for (const auto& controlPointUID : examinationPeriod.controlPointUIDs) + { + controlPoint = GetControlPointByUID(caseID, controlPointUID); + auto allImageIDsOfControlPoint = RelationStorage::GetAllImageIDsOfControlPoint(caseID, controlPoint); + allImageIDsOfExaminationPeriod.insert(allImageIDsOfExaminationPeriod.end(), allImageIDsOfControlPoint.begin(), allImageIDsOfControlPoint.end()); + } + + return allImageIDsOfExaminationPeriod; +} + mitk::SemanticTypes::ControlPoint mitk::SemanticRelationsInference::GetControlPointOfImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } SemanticTypes::CaseID caseID = ""; SemanticTypes::ID imageID = ""; try { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot get the control point of the given image data node."; } return RelationStorage::GetControlPointOfImage(caseID, imageID); } 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 = ""; SemanticTypes::ID imageID = ""; try { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot get the information type of the given image data node."; } 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 index 938e31c3b2..7173530030 100644 --- a/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp +++ b/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp @@ -1,601 +1,610 @@ /*=================================================================== 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; SemanticTypes::ID imageID; SemanticTypes::InformationType informationType; SemanticTypes::ControlPoint controlPoint; try // retrieve information { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); informationType = GetDICOMModalityFromDataNode(imageNode); controlPoint = GenerateControlPoint(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot add the given image data node."; } try // add and set information { RelationStorage::AddCase(caseID); RelationStorage::AddImage(caseID, imageID); AddInformationTypeToImage(imageNode, informationType); SetControlPointOfImage(imageNode, controlPoint); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot add the given image data node."; } } void mitk::SemanticRelationsIntegration::RemoveImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID; SemanticTypes::ID imageID; try // retrieve information { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot remove the given image data node."; } try { RemoveInformationTypeFromImage(imageNode); UnlinkImageFromControlPoint(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot remove the given image data node."; } RelationStorage::RemoveImage(caseID, imageID); 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."; } 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; try { caseID = GetCaseIDFromDataNode(segmentationNode); AddLesion(caseID, lesion); LinkSegmentationToLesion(segmentationNode, lesion); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot add given lesion and link the given segmentation data node."; } 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; SemanticTypes::ID segmentationNodeID; SemanticTypes::ID parentNodeID; try { caseID = GetCaseIDFromDataNode(segmentationNode); segmentationNodeID = GetIDFromDataNode(segmentationNode); parentNodeID = GetIDFromDataNode(parentNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot add the given segmentation data node."; } 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; SemanticTypes::ID segmentationID; try { caseID = GetCaseIDFromDataNode(segmentationNode); segmentationID = GetIDFromDataNode(segmentationNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot link the given segmentation data node to the given lesion."; } if (SemanticRelationsInference::InstanceExists(caseID, lesion)) { 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; SemanticTypes::ID segmentationID; try { caseID = GetCaseIDFromDataNode(segmentationNode); segmentationID = GetIDFromDataNode(segmentationNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot unlink the given segmentation data node from its lesion."; } 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; SemanticTypes::ID segmentationNodeID; try { caseID = GetCaseIDFromDataNode(segmentationNode); segmentationNodeID = GetIDFromDataNode(segmentationNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot remove the given segmentation data node."; } try { UnlinkSegmentationFromLesion(segmentationNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot remove the given segmentation data node."; } 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; try { caseID = GetCaseIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot set the given control point for the given image data node."; } 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); + SemanticTypes::ControlPoint existingControlPoint = FindExistingControlPoint(caseID, controlPoint); try { if (!existingControlPoint.UID.empty()) { // found an already existing control point LinkImageToControlPoint(imageNode, existingControlPoint, false); } else { - 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); + SemanticTypes::ControlPoint closestControlPoint = FindClosestControlPoint(caseID, controlPoint); + SemanticTypes::ExaminationPeriod examinationPeriod = FindContainingExaminationPeriod(caseID, closestControlPoint); 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()); + examinationPeriod.name = "New examination period"; AddExaminationPeriod(caseID, examinationPeriod); } + // added a new control point + AddControlPointAndLinkImage(imageNode, controlPoint, false); // add the control point to the (newly created or found / close) examination period AddControlPointToExaminationPeriod(caseID, controlPoint, examinationPeriod); } } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot set the given control point for the given image data node."; } 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; try { caseID = GetCaseIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot add the given control point and link the given image data node."; } 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); try { LinkImageToControlPoint(imageNode, controlPoint, checkConsistence); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot add the given control point and link the given image data node."; } } 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; SemanticTypes::ID imageID; try { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot link the image data node to the given control point."; } if (SemanticRelationsInference::InstanceExists(caseID, controlPoint)) { RelationStorage::LinkImageToControlPoint(caseID, imageID, 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 = ""; SemanticTypes::ID imageID = ""; try { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot unlink the given image data node from its control point."; } - SemanticTypes::ControlPoint controlPoint = RelationStorage::GetControlPointOfImage(caseID, imageID); RelationStorage::UnlinkImageFromControlPoint(caseID, imageID); 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::RenameExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) +{ + if (SemanticRelationsInference::InstanceExists(caseID, examinationPeriod)) + { + RelationStorage::RenameExaminationPeriod(caseID, examinationPeriod); + NotifyObserver(caseID); + } + else + { + mitkThrowException(SemanticRelationException) << "The examination period " << examinationPeriod.UID << " to overwrite does not exist for the given case."; + } +} + 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) { SemanticTypes::CaseID caseID; try { caseID = GetCaseIDFromDataNode(imageNode); RemoveInformationTypeFromImage(imageNode); AddInformationTypeToImage(imageNode, informationType); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot set the given information type for the given image data node."; } 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 = ""; SemanticTypes::ID imageID = ""; try { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot add the given information type to the given image data node."; } 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 = ""; SemanticTypes::ID imageID = ""; try { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot remove the information type from the given image data node."; } 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); + SemanticTypes::IDVector 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::IDVector allImageIDsVectorValue = RelationStorage::GetAllImageIDsOfCase(caseID); SemanticTypes::ControlPointVector referencedControlPoints; for (const auto& imageID : allImageIDsVectorValue) { - auto controlPointOfImage = RelationStorage::GetControlPointOfImage(caseID, imageID); + SemanticTypes::ControlPoint 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); + const SemanticTypes::ExaminationPeriod& examinationPeriod = FindContainingExaminationPeriod(caseID, controlPoint); RelationStorage::RemoveControlPointFromExaminationPeriod(caseID, controlPoint, examinationPeriod); RelationStorage::RemoveControlPoint(caseID, controlPoint); } } diff --git a/Modules/SemanticRelationsUI/files.cmake b/Modules/SemanticRelationsUI/files.cmake index 8208ef2d16..a10162f616 100644 --- a/Modules/SemanticRelationsUI/files.cmake +++ b/Modules/SemanticRelationsUI/files.cmake @@ -1,32 +1,36 @@ 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 + QmitkStatisticsCalculator.cpp + QmitkStatisticsTreeModel.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/QmitkStatisticsCalculator.h + include/QmitkStatisticsTreeModel.h include/QmitkTableItemThumbnailDelegate.h ) set(UI_FILES src/QmitkPatientTableInspector.ui ) diff --git a/Modules/SemanticRelationsUI/include/QmitkLesionTreeItem.h b/Modules/SemanticRelationsUI/include/QmitkLesionTreeItem.h index 4c9c1afe8f..306454f0f7 100644 --- a/Modules/SemanticRelationsUI/include/QmitkLesionTreeItem.h +++ b/Modules/SemanticRelationsUI/include/QmitkLesionTreeItem.h @@ -1,122 +1,124 @@ /*=================================================================== 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 +* @brief This class is used by custom tree models to create their tree items. +* It provides functions to traverse and modify the tree. +* Additionally it holds some 'LesionData' that is used to display lesion properties inside a tree view. */ 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()); /** * @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 index d159ec0405..f7653ec75d 100644 --- a/Modules/SemanticRelationsUI/include/QmitkLesionTreeModel.h +++ b/Modules/SemanticRelationsUI/include/QmitkLesionTreeModel.h @@ -1,100 +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 QMITKLESIONTREEMODEL_H #define QMITKLESIONTREEMODEL_H // mitk semantic relations UI #include "MitkSemanticRelationsUIExports.h" #include "QmitkAbstractSemanticRelationsStorageModel.h" #include "QmitkLesionTreeItem.h" -// c++ -#include - /* -* @brief +* @brief The 'QmitkLesionTreeModel' is a subclass of 'QmitkAbstractSemanticRelationsStorageModel' and provides +* functionality to serve as a tree model. +* The tree model creates a new top-level tree item for each lesion that is stored inside the semantic relations storage. +* Each lesion tree item contains lesion data that can be display inside a tree view. The lesion data +* consists of a lesion with with its UID, name and lesion class. The name or UID is used for the top-level tree items. +* Additionally the lesion data contains two vectors which define the lesion presence (bool) and the lesion volume (double) +* for each control-point - information type pair. The lesion presence will be used inside this model for the tree items. +* The volume is used inside another tree model. +* +* The model holds the last segmentation that is added to the data storage to support the process of defining a new lesion +* (and linking it with the latest segmentation) (see 'NodeAdded'). +* Furthermore the model is able to accept a 'QList' of currently selected data nodes and to use it to change the background +* color of each lesion tree item that is connected to this data node(s). This helps to see which lesion is already found and +* defined for a given (set of) data node(s). */ 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); ////////////////////////////////////////////////////////////////////////// // 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 ////////////////////////////////////////////////////////////////////////// const mitk::DataNode* GetLastSegmentation() const; 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; const mitk::DataNode* m_LastSegmentation; std::shared_ptr m_RootItem; mitk::SemanticTypes::ControlPointVector m_ControlPoints; mitk::SemanticTypes::LesionVector m_CurrentLesions; }; #endif // QMITKLESIONTREEMODEL_H diff --git a/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h b/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h index 16a38ef02b..966f97a35e 100644 --- a/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h +++ b/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h @@ -1,134 +1,133 @@ /*=================================================================== 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::SemanticTypes::InformationTypeVector m_InformationTypes; mitk::SemanticRelationsDataStorageAccess::DataNodeVector m_CurrentDataNodes; std::string m_SelectedNodeType; QStandardItemModel* m_HeaderModel; }; #endif // QMITKPATIENTTABLEMODEL_H diff --git a/Modules/SemanticRelationsUI/include/QmitkStatisticsCalculator.h b/Modules/SemanticRelationsUI/include/QmitkStatisticsCalculator.h new file mode 100644 index 0000000000..9861660b0a --- /dev/null +++ b/Modules/SemanticRelationsUI/include/QmitkStatisticsCalculator.h @@ -0,0 +1,80 @@ +/*=================================================================== + +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 QMITKSTATISTICSCALCULATOR_H +#define QMITKSTATISTICSCALCULATOR_H + +// mitk semantic relations UI +#include "MitkSemanticRelationsUIExports.h" + +// mitk semantic relations module +#include +#include + +// mitk core +#include +#include + +// mitk image statistics ui module +#include + +/* +* @brief This class provides functions to compute the lesion volume of a given lesion. +* A lesion can be defined by a specific segmentation at each control-point - information type pair. +* This segmentation and its parent image will be used inside the private 'GetSegmentationMaskVolume' function. +* This function in turn uses the image statistics module (the 'ImageStatisticsContainerManager') to retrieve +* the specific statistics values from a statistics node. However, if the statistics are not found, +* a new 'QmitkImageStatisticsCalculationJob' is started. If the job is finished and the statistics are calculated, +* a new statistics node is added to the data storage (or an existing statistics data node is updated). +*/ +class MITKSEMANTICRELATIONSUI_EXPORT QmitkStatisticsCalculator : public QObject +{ + Q_OBJECT + +public: + + QmitkStatisticsCalculator(); + ~QmitkStatisticsCalculator(); + + void SetDataStorage(mitk::DataStorage* dataStorage) { m_DataStorage = dataStorage; } + /** + * @brief Compute and store lesion volume for all available control points and information types. + * + * @param lesionData The lesion data that holds the lesion and will hold the additional lesion data. + * @param caseID The current case ID. + */ + void ComputeLesionVolume(mitk::LesionData& lesionData, const mitk::SemanticTypes::CaseID& caseID); + +private: + + /** + * @brief + * + * + */ + double GetSegmentationMaskVolume(mitk::DataNode::Pointer imageNode, mitk::DataNode::Pointer segmentationNode); + + void OnStatisticsCalculationEnds(); + + QmitkImageStatisticsCalculationJob* m_CalculationJob; + mitk::WeakPointer m_DataStorage; + mitk::DataNode::Pointer m_ImageNode; + mitk::DataNode::Pointer m_SegmentationNode; + double m_MaskVolume; + +}; + +#endif // QMITKSTATISTICSCALCULATOR_H diff --git a/Modules/SemanticRelationsUI/include/QmitkLesionTreeModel.h b/Modules/SemanticRelationsUI/include/QmitkStatisticsTreeModel.h similarity index 55% copy from Modules/SemanticRelationsUI/include/QmitkLesionTreeModel.h copy to Modules/SemanticRelationsUI/include/QmitkStatisticsTreeModel.h index d159ec0405..1c54bde693 100644 --- a/Modules/SemanticRelationsUI/include/QmitkLesionTreeModel.h +++ b/Modules/SemanticRelationsUI/include/QmitkStatisticsTreeModel.h @@ -1,100 +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 +#ifndef QMITKSTATISTICSTREEMODEL_H +#define QMITKSTATISTICSTREEMODEL_H // mitk semantic relations UI #include "MitkSemanticRelationsUIExports.h" #include "QmitkAbstractSemanticRelationsStorageModel.h" #include "QmitkLesionTreeItem.h" - -// c++ -#include +#include "QmitkStatisticsCalculator.h" /* -* @brief +* @brief The 'QmitkStatisticsTreeModel' is a subclass of 'QmitkAbstractSemanticRelationsStorageModel' and provides +* functionality to serve as a tree model. +* The tree model creates a new top-level tree item for each lesion that is stored inside the semantic relations storage. +* The top-level lesion tree items hold child items for each information type. +* Each lesion tree item contains lesion data that can be display inside a tree view. The lesion data +* consists of a lesion with with its UID, name and lesion class. The name or UID is used for the top-level tree items. +* Additionally the lesion data contains two vectors which define the lesion presence (bool) and the lesion volume (double) +* for each control-point - information type pair. The lesion volume will be used inside this model for the child items. +* The presence is used inside another tree model. +* +* The model uses the 'QmitkStatisticsCalculator' to start the lesion volume calculation for each lesion. +* This calculator is able to find an existing lesion volume or to trigger the computation of the required statistics. +* If the required statistics are newly computed and added as a statistics container to the data storage, +* this model will be notified about this event (see 'NodeAdded', 'NodeChanged' and 'NodeRemoved') and will update +* its lesion tree items. */ -class MITKSEMANTICRELATIONSUI_EXPORT QmitkLesionTreeModel : public QmitkAbstractSemanticRelationsStorageModel +class MITKSEMANTICRELATIONSUI_EXPORT QmitkStatisticsTreeModel : public QmitkAbstractSemanticRelationsStorageModel { - Q_OBJECT - + Q_OBJECT + public: /** * @brief Initialize the root item of the model. The root item does not have a parent item. */ - QmitkLesionTreeModel(QObject* parent = nullptr); + QmitkStatisticsTreeModel(QObject* parent = nullptr); ////////////////////////////////////////////////////////////////////////// // 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 ////////////////////////////////////////////////////////////////////////// - const mitk::DataNode* GetLastSegmentation() const; - protected: - - // the following functions have to be overridden but are not implemented in this model + + virtual void DataStorageChanged() override; 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 { } + 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; - const mitk::DataNode* m_LastSegmentation; + std::unique_ptr m_StatisticsCalculator; std::shared_ptr m_RootItem; mitk::SemanticTypes::ControlPointVector m_ControlPoints; + mitk::SemanticTypes::InformationTypeVector m_InformationTypes; mitk::SemanticTypes::LesionVector m_CurrentLesions; }; -#endif // QMITKLESIONTREEMODEL_H +#endif // QMITKSTATISTICSTREEMODEL_H diff --git a/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp b/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp index 9e3ff1aed8..be4259127b 100644 --- a/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp @@ -1,289 +1,296 @@ /*=================================================================== 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 #include // qt #include QmitkLesionTreeModel::QmitkLesionTreeModel(QObject* parent/* = nullptr*/) : QmitkAbstractSemanticRelationsStorageModel(parent) , m_RootItem(std::make_shared(mitk::LesionData())) { // 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 "N/A"; } return QVariant(lesionPresence.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(); } const mitk::DataNode* QmitkLesionTreeModel::GetLastSegmentation() const { return m_LastSegmentation; } void QmitkLesionTreeModel::NodeAdded(const mitk::DataNode* dataNode) { if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { m_LastSegmentation = dataNode; } } 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) { + if (m_DataStorage.IsExpired()) + { + return; + } + + auto dataStorage = m_DataStorage.Lock(); + // create new lesion tree item data and modify it according to the control point data mitk::LesionData lesionData(lesion); - mitk::GenerateAdditionalLesionData(lesionData, m_CaseID); + mitk::ComputeLesionPresence(lesionData, m_CaseID); - // add the 1. level lesion item to the root item + // add the top-level lesion item to the root item std::shared_ptr newLesionTreeItem = std::make_shared(lesionData); m_RootItem->AddChild(newLesionTreeItem); } 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/QmitkPatientTableModel.cpp b/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp index 80b857e1f7..93a04931e8 100644 --- a/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp @@ -1,342 +1,350 @@ /*=================================================================== 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(); + return m_ExaminationPeriods.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())) + || index.column() < 0 || index.column() >= static_cast(m_ExaminationPeriods.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); + + // sort all examination periods for the timeline + mitk::SortAllExaminationPeriods(m_CaseID, m_ExaminationPeriods); + + // rename examination periods according to their new order + std::string examinationPeriodName = "Baseline"; + for (int i = 0; i < m_ExaminationPeriods.size(); ++i) + { + auto& examinationPeriod = m_ExaminationPeriods.at(i); + examinationPeriod.name = examinationPeriodName; + mitk::RelationStorage::RenameExaminationPeriod(m_CaseID, examinationPeriod); + examinationPeriodName = "Follow-up " + std::to_string(i); + } // 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; } // set the lesion presence for the current node bool lesionPresence = mitk::SemanticRelationsInference::IsLesionPresent(m_Lesion, dataNode); SetLesionPresenceOfNode(dataNode, lesionPresence); } } 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 + auto examinationPeriod = m_ExaminationPeriods.at(index.column()); + auto currentInformationType = m_InformationTypes.at(index.row()); + auto controlPointsOfExaminationPeriod = examinationPeriod.controlPointUIDs; + for (const auto& controlPointUID : controlPointsOfExaminationPeriod) { - std::vector filteredDataNodes; - if ("Image" == m_SelectedNodeType) - { - filteredDataNodes = m_SemanticRelationsDataStorageAccess->GetAllSpecificImages(m_CaseID, currentControlPoint, currentInformationType); - } - else if ("Segmentation" == m_SelectedNodeType) + auto currentControlPoint = mitk::GetControlPointByUID(m_CaseID, controlPointUID); + try { - filteredDataNodes = m_SemanticRelationsDataStorageAccess->GetAllSpecificSegmentations(m_CaseID, currentControlPoint, currentInformationType); + 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()) + { + // try next control point + continue; + } + else + { + // found a specific image + return filteredDataNodes.front(); + } } - - if (filteredDataNodes.empty()) + catch (const mitk::SemanticRelationException&) { return nullptr; } - return filteredDataNodes.front(); - } - catch (const mitk::SemanticRelationException&) - { - return nullptr; } + // could not find a specif image + return nullptr; } diff --git a/Modules/SemanticRelationsUI/src/QmitkStatisticsCalculator.cpp b/Modules/SemanticRelationsUI/src/QmitkStatisticsCalculator.cpp new file mode 100644 index 0000000000..ea1892430e --- /dev/null +++ b/Modules/SemanticRelationsUI/src/QmitkStatisticsCalculator.cpp @@ -0,0 +1,237 @@ +/*=================================================================== + +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 "QmitkStatisticsCalculator.h" + +// semantic relations module +#include +#include +#include +#include +#include + +// mitk image statistics module +#include +#include +#include +#include + +QmitkStatisticsCalculator::QmitkStatisticsCalculator() + : m_CalculationJob(nullptr) + , m_DataStorage(nullptr) + , m_MaskVolume(0.0) +{ + m_CalculationJob = new QmitkImageStatisticsCalculationJob(); + + connect(m_CalculationJob, &QmitkImageStatisticsCalculationJob::finished, this, + &QmitkStatisticsCalculator::OnStatisticsCalculationEnds, Qt::QueuedConnection); +} + +QmitkStatisticsCalculator::~QmitkStatisticsCalculator() +{ + if (!m_CalculationJob->isFinished()) + { + m_CalculationJob->terminate(); + m_CalculationJob->wait(); + } + m_CalculationJob->deleteLater(); +} + +void QmitkStatisticsCalculator::ComputeLesionVolume(mitk::LesionData& lesionData, const mitk::SemanticTypes::CaseID& caseID) +{ + if (m_DataStorage.IsExpired()) + { + return; + } + + auto dataStorage = m_DataStorage.Lock(); + + std::vector lesionVolume; + mitk::SemanticTypes::Lesion lesion = lesionData.GetLesion(); + double volume = 0.0; + + mitk::SemanticTypes::ControlPointVector controlPoints = mitk::RelationStorage::GetAllControlPointsOfCase(caseID); + // sort the vector of control points for the timeline + std::sort(controlPoints.begin(), controlPoints.end()); + mitk::SemanticTypes::InformationTypeVector informationTypes = mitk::RelationStorage::GetAllInformationTypesOfCase(caseID); + for (const auto& informationType : informationTypes) + { + for (const auto& controlPoint : controlPoints) + { + mitk::SemanticRelationsDataStorageAccess semanticRelationsDataStorageAccess(dataStorage); + mitk::DataNode::Pointer specificImage; + mitk::DataNode::Pointer specificSegmentation; + try + { + specificSegmentation = semanticRelationsDataStorageAccess.GetSpecificSegmentation(caseID, controlPoint, informationType, lesion); + if (nullptr == specificSegmentation) + { + volume = 0.0; + } + else + { + // get parent node of the specific segmentation node with the node predicate + auto parentNodes = dataStorage->GetSources(specificSegmentation, mitk::NodePredicates::GetImagePredicate(), false); + for (auto it = parentNodes->Begin(); it != parentNodes->End(); ++it) + { + specificImage = it->Value(); + } + + volume = GetSegmentationMaskVolume(specificImage, specificSegmentation); + } + } + catch (mitk::SemanticRelationException&) + { + volume = 0.0; + } + + lesionVolume.push_back(volume); + } + } + + lesionData.SetLesionVolume(lesionVolume); +} + +double QmitkStatisticsCalculator::GetSegmentationMaskVolume(mitk::DataNode::Pointer imageNode, mitk::DataNode::Pointer segmentationNode) +{ + m_MaskVolume = 0.0; + + if (m_DataStorage.IsExpired()) + { + return m_MaskVolume; + } + + auto dataStorage = m_DataStorage.Lock(); + + if (imageNode.IsNull() || segmentationNode.IsNull()) + { + return m_MaskVolume; + } + + m_ImageNode = imageNode; + m_SegmentationNode = segmentationNode; + + auto image = dynamic_cast(m_ImageNode->GetData()); + auto segmentation = dynamic_cast(m_SegmentationNode->GetData()); + if (nullptr == image || nullptr == segmentation) + { + return m_MaskVolume; + } + + // all nodes and images are valid, retrieve statistics + mitk::ImageStatisticsContainer::ConstPointer imageStatistics = mitk::ImageStatisticsContainerManager::GetImageStatistics(dataStorage, image, segmentation); + + bool imageStatisticsOlderThanInputs = false; + if (imageStatistics && (imageStatistics->GetMTime() < image->GetMTime() || (imageStatistics->GetMTime() < segmentation->GetMTime()))) + { + imageStatisticsOlderThanInputs = true; + } + // statistics need to be (re)computed + if (!imageStatistics || imageStatisticsOlderThanInputs) + { + m_CalculationJob->Initialize(image, segmentation, nullptr); + try + { + m_CalculationJob->start(); + return m_MaskVolume; + } + catch (const std::exception&) + { + return m_MaskVolume; + } + } + + // use a valid statistics object to get the volume of the image-segmentation pair + mitk::ImageStatisticsContainer::ImageStatisticsObject statisticsObject; + try + { + statisticsObject = imageStatistics->GetStatisticsForTimeStep(0); + } + catch (mitk::Exception&) + { + return m_MaskVolume; + } + try + { + if (statisticsObject.HasStatistic(mitk::ImageStatisticsConstants::VOLUME())) + { + auto valueVariant = statisticsObject.GetValueNonConverted(mitk::ImageStatisticsConstants::VOLUME()); + m_MaskVolume = boost::get(valueVariant); + } + } + catch (mitk::Exception&) + { + return m_MaskVolume; + } + + return m_MaskVolume; +} + +void QmitkStatisticsCalculator::OnStatisticsCalculationEnds() +{ + // taken from 'QmitkImageStatisticsView' (see measurementtoolbox plugin) + if (m_DataStorage.IsExpired()) + { + return; + } + + auto dataStorage = m_DataStorage.Lock(); + + if (m_CalculationJob->GetStatisticsUpdateSuccessFlag()) + { + auto statistic = m_CalculationJob->GetStatisticsData(); + auto image = m_CalculationJob->GetStatisticsImage(); + mitk::BaseData::ConstPointer mask = nullptr; + auto imageRule = mitk::StatisticsToImageRelationRule::New(); + imageRule->Connect(statistic, image); + + if (m_CalculationJob->GetMaskImage()) + { + auto maskRule = mitk::StatisticsToMaskRelationRule::New(); + mask = m_CalculationJob->GetMaskImage(); + maskRule->Connect(statistic, mask); + } + + auto imageStatistics = mitk::ImageStatisticsContainerManager::GetImageStatistics(dataStorage, image, mask); + + // if statistics base data already exist: add to existing node + if (nullptr != imageStatistics) + { + auto allDataNodes = dataStorage->GetAll()->CastToSTLConstContainer(); + for (auto node : allDataNodes) + { + auto nodeData = node->GetData(); + if (nullptr != nodeData && nodeData->GetUID() == imageStatistics->GetUID()) + { + node->SetData(statistic); + } + } + } + // statistics base data does not exist: add new node + else + { + auto statisticsNodeName = m_ImageNode->GetName(); + if (m_SegmentationNode) + { + statisticsNodeName += "_" + m_SegmentationNode->GetName(); + } + statisticsNodeName += "_statistics"; + auto statisticsNode = mitk::CreateImageStatisticsNode(statistic, statisticsNodeName); + dataStorage->Add(statisticsNode); + } + } +} diff --git a/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp b/Modules/SemanticRelationsUI/src/QmitkStatisticsTreeModel.cpp similarity index 54% copy from Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp copy to Modules/SemanticRelationsUI/src/QmitkStatisticsTreeModel.cpp index 9e3ff1aed8..af1f4bf2b5 100644 --- a/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkStatisticsTreeModel.cpp @@ -1,289 +1,272 @@ /*=================================================================== 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" +#include "QmitkStatisticsTreeModel.h" // semantic relations module #include #include -#include #include #include #include -// qt -#include - -QmitkLesionTreeModel::QmitkLesionTreeModel(QObject* parent/* = nullptr*/) +QmitkStatisticsTreeModel::QmitkStatisticsTreeModel(QObject* parent/* = nullptr*/) : QmitkAbstractSemanticRelationsStorageModel(parent) , m_RootItem(std::make_shared(mitk::LesionData())) { - // nothing here + m_StatisticsCalculator = std::make_unique(); } ////////////////////////////////////////////////////////////////////////// // overridden virtual functions from QAbstractItemModel ////////////////////////////////////////////////////////////////////////// -QModelIndex QmitkLesionTreeModel::index(int row, int column, const QModelIndex& itemIndex) const +QModelIndex QmitkStatisticsTreeModel::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 +QModelIndex QmitkStatisticsTreeModel::parent(const QModelIndex& itemIndex) const { if (!itemIndex.isValid()) { return QModelIndex(); } - auto parentItem = GetItemByIndex(itemIndex)->GetParent(); - if (parentItem.expired()) + auto parentItemWeakPtr = GetItemByIndex(itemIndex)->GetParent(); + if (parentItemWeakPtr.expired()) { return QModelIndex(); } - auto sharedParent = parentItem.lock(); - if (sharedParent == m_RootItem) + auto parentItem = parentItemWeakPtr.lock(); + if (parentItem == m_RootItem) { return QModelIndex(); } - return createIndex(sharedParent->GetRow(), 0, sharedParent.get()); + return createIndex(parentItem->GetRow(), 0, parentItem.get()); } -int QmitkLesionTreeModel::rowCount(const QModelIndex& itemIndex/* = QModelIndex()*/) const +int QmitkStatisticsTreeModel::rowCount(const QModelIndex& itemIndex/* = QModelIndex()*/) const { return GetItemByIndex(itemIndex)->ChildCount(); } -int QmitkLesionTreeModel::columnCount(const QModelIndex&/* itemIndex = QModelIndex() */) const +int QmitkStatisticsTreeModel::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 +QVariant QmitkStatisticsTreeModel::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 + // parent exists and is the root item -> top level item 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); } + } + // parent is not the root item -> volume item + else + { + // display role fills the first columns with the information type + if (0 == index.column()) + { + if (index.row() < static_cast(m_InformationTypes.size())) + { + return QString::fromStdString(m_InformationTypes.at(index.row())); + } + return "N/A"; + } 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())) + // display role fills other columns with the lesion volume info + const auto lesionVolume = currentItem->GetData().GetLesionVolume(); + if ((index.column() - 1) * index.row() < static_cast(lesionVolume.size())) { - return ""; + return QVariant(lesionVolume.at(index.row()*m_ControlPoints.size() + (index.column() - 1))); } - - return QVariant(lesionPresence.at(index.column() - 1)); + return "N/A"; } } } - 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 +QVariant QmitkStatisticsTreeModel::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(); } -const mitk::DataNode* QmitkLesionTreeModel::GetLastSegmentation() const +void QmitkStatisticsTreeModel::DataStorageChanged() { - return m_LastSegmentation; + if (!m_DataStorage.IsExpired()) + { + auto dataStorage = m_DataStorage.Lock(); + m_SemanticRelationsDataStorageAccess = std::make_unique(dataStorage); + m_StatisticsCalculator->SetDataStorage(dataStorage); + UpdateModelData(); + } } -void QmitkLesionTreeModel::NodeAdded(const mitk::DataNode* dataNode) +void QmitkStatisticsTreeModel::NodeAdded(const mitk::DataNode*) { - if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) - { - m_LastSegmentation = dataNode; - } + emit beginResetModel(); + UpdateModelData(); + emit endResetModel(); +} + +void QmitkStatisticsTreeModel::NodeChanged(const mitk::DataNode*) +{ + emit beginResetModel(); + UpdateModelData(); + emit endResetModel(); } -void QmitkLesionTreeModel::SetData() +void QmitkStatisticsTreeModel::NodeRemoved(const mitk::DataNode*) +{ + emit beginResetModel(); + UpdateModelData(); + emit endResetModel(); +} + +void QmitkStatisticsTreeModel::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()); + // get all information types points of current case + m_InformationTypes = mitk::RelationStorage::GetAllInformationTypesOfCase(m_CaseID); + SetLesionData(); - SetSelectedDataNodesPresence(); } -void QmitkLesionTreeModel::SetLesionData() +void QmitkStatisticsTreeModel::SetLesionData() { m_CurrentLesions = mitk::RelationStorage::GetAllLesionsOfCase(m_CaseID); for (auto& lesion : m_CurrentLesions) { AddLesion(lesion); } } -void QmitkLesionTreeModel::AddLesion(const mitk::SemanticTypes::Lesion& lesion) +void QmitkStatisticsTreeModel::AddLesion(const mitk::SemanticTypes::Lesion& lesion) { + if (m_DataStorage.IsExpired()) + { + return; + } + + auto dataStorage = m_DataStorage.Lock(); + // create new lesion tree item data and modify it according to the control point data mitk::LesionData lesionData(lesion); - mitk::GenerateAdditionalLesionData(lesionData, m_CaseID); + m_StatisticsCalculator->ComputeLesionVolume(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); -} - -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 + auto informationTypeSize = m_InformationTypes.size(); + for (int i = 0; i < informationTypeSize; ++i) { - m_DataNodePresence.insert(std::make_pair(lesion->UID, dataNodePresence)); + std::shared_ptr volumeItem = std::make_shared(lesionData); + newLesionTreeItem->AddChild(volumeItem); } } -QmitkLesionTreeItem* QmitkLesionTreeModel::GetItemByIndex(const QModelIndex& index) const +QmitkLesionTreeItem* QmitkStatisticsTreeModel::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/Plugins/org.mitk.gui.qt.semanticrelations/files.cmake b/Plugins/org.mitk.gui.qt.semanticrelations/files.cmake index a44132c424..2191a732b8 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/files.cmake +++ b/Plugins/org.mitk.gui.qt.semanticrelations/files.cmake @@ -1,55 +1,59 @@ set(SRC_CPP_FILES QmitkDataNodeAddToSemanticRelationsAction.cpp ) set(INTERNAL_CPP_FILES mitkPluginActivator.cpp QmitkAbstractSemanticRelationsAction.cpp QmitkDataNodeRemoveFromSemanticRelationsAction.cpp QmitkDataNodeUnlinkFromLesionAction.cpp QmitkDataNodeSetControlPointAction.cpp QmitkDataNodeSetInformationTypeAction.cpp QmitkDataSetOpenInAction.cpp QmitkFocusOnLesionAction.cpp QmitkLabelSetJumpToAction.cpp QmitkLesionInfoWidget.cpp QmitkSemanticRelationsContextMenu.cpp QmitkSemanticRelationsNodeSelectionDialog.cpp + QmitkSemanticRelationsStatisticsView.cpp QmitkSemanticRelationsView.cpp ) set(UI_FILES src/internal/QmitkLesionInfoWidgetControls.ui src/internal/QmitkSemanticRelationsControls.ui + src/internal/QmitkSemanticRelationsStatisticsControls.ui ) set(MOC_H_FILES src/QmitkDataNodeAddToSemanticRelationsAction.h src/internal/mitkPluginActivator.h src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.h src/internal/QmitkDataNodeUnlinkFromLesionAction.h src/internal/QmitkDataNodeSetControlPointAction.h src/internal/QmitkDataNodeSetInformationTypeAction.h src/internal/QmitkDataSetOpenInAction.h src/internal/QmitkFocusOnLesionAction.h src/internal/QmitkLabelSetJumpToAction.h src/internal/QmitkLesionInfoWidget.h src/internal/QmitkSemanticRelationsContextMenu.h src/internal/QmitkSemanticRelationsNodeSelectionDialog.h + src/internal/QmitkSemanticRelationsStatisticsView.h src/internal/QmitkSemanticRelationsView.h ) set(CACHED_RESOURCE_FILES resources/SemanticRelations_48.png + resources/SemanticRelationsStatistics_48.png plugin.xml ) set(QRC_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.semanticrelations/plugin.xml b/Plugins/org.mitk.gui.qt.semanticrelations/plugin.xml index be0ea613f6..618c2e82f1 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/plugin.xml +++ b/Plugins/org.mitk.gui.qt.semanticrelations/plugin.xml @@ -1,11 +1,18 @@ + icon="resources/SemanticRelations_48.png" + category="Semantic Relations"/> + + diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/resources/SemanticRelationsStatistics_48.png b/Plugins/org.mitk.gui.qt.semanticrelations/resources/SemanticRelationsStatistics_48.png new file mode 100644 index 0000000000..182a722caf Binary files /dev/null and b/Plugins/org.mitk.gui.qt.semanticrelations/resources/SemanticRelationsStatistics_48.png differ diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/QmitkDataNodeAddToSemanticRelationsAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/QmitkDataNodeAddToSemanticRelationsAction.cpp index 31c17047e8..04c81dcffe 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/QmitkDataNodeAddToSemanticRelationsAction.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/QmitkDataNodeAddToSemanticRelationsAction.cpp @@ -1,207 +1,275 @@ /*=================================================================== 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" +#include "internal/QmitkDataNodeRemoveFromSemanticRelationsAction.h" // semantic relations module +#include #include #include +#include #include #include +#include #include // mitk gui common plugin #include // mitk core #include // qt #include // namespace that contains the concrete action namespace AddToSemanticRelationsAction { - void Run(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataStorage* dataStorage, const mitk::DataNode* dataNode) + void Run(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, mitk::DataStorage* dataStorage, const mitk::DataNode* dataNode) { if (nullptr == dataNode) { return; } if (mitk::NodePredicates::GetImagePredicate()->CheckNode(dataNode)) { - AddImage(semanticRelationsIntegration, dataNode); + AddImage(semanticRelationsIntegration, dataStorage, dataNode); } else if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { AddSegmentation(semanticRelationsIntegration, dataStorage, dataNode); } } - void AddImage(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataNode* image) + void AddImage(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, mitk::DataStorage* dataStorage, const mitk::DataNode* image) { if (nullptr == image) { return; } + mitk::SemanticTypes::InformationType informationType; + mitk::SemanticTypes::ExaminationPeriod examinationPeriod; + mitk::SemanticRelationsDataStorageAccess::DataNodeVector allSpecificImages; + try + { + mitk::SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(image); + informationType = GetDICOMModalityFromDataNode(image); + // see if the examination period - information type cell is already taken + examinationPeriod = FindFittingExaminationPeriod(image); + auto semanticRelationsDataStorageAccess = mitk::SemanticRelationsDataStorageAccess(dataStorage); + try + { + allSpecificImages = semanticRelationsDataStorageAccess.GetAllSpecificImages(caseID, informationType, examinationPeriod); + } + catch (const mitk::SemanticRelationException&) + { + // just continue since an exception means that there is no specific image + } + } + catch (const mitk::SemanticRelationException& e) + { + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox(QMessageBox::Warning, + "Could not add the selected image.", + "The program wasn't able to correctly add the selected images.\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); + msgBox.exec(); + } + + if (!allSpecificImages.empty()) + { + // examination period - information type cell is already taken + // ask if cell should be overwritten + QMessageBox::StandardButton answerButton = + QMessageBox::question(nullptr, + "Specific image already exists.", + QString::fromStdString("Force overwriting existing image " + informationType + " at " + examinationPeriod.name + "?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + if (answerButton == QMessageBox::Yes) + { + try + { + // remove already existent images at specific cell + for (const auto& specificImage : allSpecificImages) + { + RemoveFromSemanticRelationsAction::Run(semanticRelationsIntegration, dataStorage, specificImage); + } + } + catch (const mitk::SemanticRelationException& e) + { + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox(QMessageBox::Warning, + "Could not add the selected image.", + "The program wasn't able to correctly add the selected images.\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); + msgBox.exec(); + } + } + else + { + // else case is: no overwriting + return; + } + } + + // specific image does not exist or has been removed; adding the image should work try { - // add the image to the semantic relations storage semanticRelationsIntegration->AddImage(image); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox(QMessageBox::Warning, "Could not add the selected image.", "The program wasn't able to correctly add the selected images.\n" - "Reason:\n" + QString::fromStdString(exceptionMessage.str())); + "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); msgBox.exec(); - return; } } - void AddSegmentation(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataStorage* dataStorage, const mitk::DataNode* segmentation) + void AddSegmentation(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, 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); if (parentNodes->empty()) { // segmentation without corresponding image will not be added - std::stringstream exceptionMessage; exceptionMessage << "No parent image found"; QMessageBox msgBox(QMessageBox::Warning, "Could not add the selected segmentation.", "The program wasn't able to correctly add the selected segmentation.\n" - "Reason:\n" + QString::fromStdString(exceptionMessage.str())); + "Reason: No parent image found"); msgBox.exec(); return; } // check for already existing, identifying base properties auto caseIDPropertyName = mitk::GetCaseIDDICOMProperty(); auto nodeIDPropertyName = mitk::GetNodeIDDICOMProperty(); mitk::BaseProperty* caseIDProperty = baseData->GetProperty(caseIDPropertyName.c_str()); mitk::BaseProperty* nodeIDProperty = baseData->GetProperty(nodeIDPropertyName.c_str()); if (nullptr == caseIDProperty || nullptr == nodeIDProperty) { MITK_INFO << "No DICOM tags for case and node identification found. Transferring DICOM tags from the parent node to the selected segmentation node."; mitk::SemanticTypes::CaseID caseID; mitk::SemanticTypes::ID nodeID; try { caseID = mitk::GetCaseIDFromDataNode(parentNodes->front()); nodeID = mitk::GetIDFromDataNode(parentNodes->front()); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox(QMessageBox::Warning, "Could not add the selected segmentation.", "The program wasn't able to correctly add the selected segmentation.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.exec(); return; } // transfer DICOM tags to the segmentation node baseData->SetProperty(caseIDPropertyName, mitk::TemporoSpatialStringProperty::New(caseID)); // add UID to distinguish between different segmentations of the same parent node baseData->SetProperty(nodeIDPropertyName, mitk::TemporoSpatialStringProperty::New(nodeID + mitk::UIDGeneratorBoost::GenerateUID())); } // add the parent node if not already existent if (!mitk::SemanticRelationsInference::InstanceExists(parentNodes->front())) { - AddImage(semanticRelationsIntegration, parentNodes->front()); + AddImage(semanticRelationsIntegration, dataStorage, parentNodes->front()); } 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(QMessageBox::Warning, "Could not add the selected segmentation.", "The program wasn't able to correctly add the selected segmentation.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); 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/QmitkDataNodeAddToSemanticRelationsAction.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/QmitkDataNodeAddToSemanticRelationsAction.h index e7aada0ebd..94acc3a444 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/QmitkDataNodeAddToSemanticRelationsAction.h +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/QmitkDataNodeAddToSemanticRelationsAction.h @@ -1,57 +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 "internal/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); + MITK_GUI_SEMANTICRELATIONS_EXPORT void Run(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, mitk::DataStorage* dataStorage, const mitk::DataNode* image); + + void AddImage(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, mitk::DataStorage* dataStorage, const mitk::DataNode* image); + void AddSegmentation(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, 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 index 5f2b2ca4dc..86ae166272 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.cpp @@ -1,147 +1,147 @@ /*=================================================================== 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::DataStorage* dataStorage, const mitk::DataNode* dataNode) + void Run(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, mitk::DataStorage* dataStorage, const mitk::DataNode* dataNode) { if (nullptr == dataNode) { return; } if (mitk::NodePredicates::GetImagePredicate()->CheckNode(dataNode)) { RemoveImage(semanticRelationsIntegration, dataStorage, dataNode); } else if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { RemoveSegmentation(semanticRelationsIntegration, dataNode); } } - void RemoveImage(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataStorage* dataStorage, const mitk::DataNode* image) + void RemoveImage(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, mitk::DataStorage* dataStorage, const mitk::DataNode* image) { if (nullptr == image) { return; } try { // remove each corresponding segmentation from the semantic relations storage mitk::DataStorage::SetOfObjects::ConstPointer childNodes = dataStorage->GetDerivations(image, mitk::NodePredicates::GetSegmentationPredicate(), false); for (auto it = childNodes->Begin(); it != childNodes->End(); ++it) { RemoveSegmentation(semanticRelationsIntegration, it->Value()); } // 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*/) { if (nullptr == m_SemanticRelationsIntegration) { return; } if (m_DataStorage.IsExpired()) { return; } auto dataNode = GetSelectedNode(); RemoveFromSemanticRelationsAction::Run(m_SemanticRelationsIntegration.get(), m_DataStorage.Lock(),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 index 7b2fbd75fd..4aebf291f7 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.h +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.h @@ -1,57 +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::DataStorage* dataStorage, const mitk::DataNode* dataNode); + MITK_GUI_SEMANTICRELATIONS_EXPORT void Run(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, mitk::DataStorage* dataStorage, const mitk::DataNode* dataNode); - void RemoveImage(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataStorage* dataStorage, const mitk::DataNode* image); + void RemoveImage(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, mitk::DataStorage* dataStorage, 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 index 825a5abf7f..37c192b029 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetControlPointAction.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetControlPointAction.cpp @@ -1,102 +1,180 @@ /*=================================================================== 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" +#include "QmitkDataNodeRemoveFromSemanticRelationsAction.h" // semantic relations module +#include #include +#include #include #include #include // semantic relations UI module #include "QmitkControlPointDialog.h" -// mitk gui common plugin -#include - // qt #include +#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; } + if (m_DataStorage.IsExpired()) + { + 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()); + mitk::SemanticTypes::InformationType informationType; + mitk::SemanticTypes::ExaminationPeriod examinationPeriod; + mitk::SemanticRelationsDataStorageAccess::DataNodeVector allSpecificImages; + try + { + mitk::SemanticTypes::CaseID caseID = mitk::GetCaseIDFromDataNode(dataNode); + informationType = mitk::SemanticRelationsInference::GetInformationTypeOfImage(dataNode); + // see if the examination period - information type cell is already taken + examinationPeriod = mitk::FindFittingExaminationPeriod(caseID, controlPoint); + auto semanticRelationsDataStorageAccess = mitk::SemanticRelationsDataStorageAccess(m_DataStorage.Lock()); + try + { + allSpecificImages = semanticRelationsDataStorageAccess.GetAllSpecificImages(caseID, informationType, examinationPeriod); + } + catch (const mitk::SemanticRelationException&) + { + // just continue since an exception means that there is no specific image + } + } + catch (const mitk::SemanticRelationException& e) + { + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox(QMessageBox::Warning, + "Could not set the control point.", + "The program wasn't able to correctly set the control point.\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); + msgBox.exec(); + } + + if (!allSpecificImages.empty()) + { + // examination period - information type cell is already taken + // ask if cell should be overwritten + QMessageBox::StandardButton answerButton = + QMessageBox::question(nullptr, + "Specific image already exists.", + QString::fromStdString("Force overwriting existing image " + informationType + " at " + examinationPeriod.name + "?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + if (answerButton == QMessageBox::Yes) + { + try + { + // remove already existent images at specific cell + for (const auto& specificImage : allSpecificImages) + { + RemoveFromSemanticRelationsAction::Run(m_SemanticRelationsIntegration.get(), m_DataStorage.Lock(), specificImage); + } + } + catch (const mitk::SemanticRelationException& e) + { + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox(QMessageBox::Warning, + "Could not set the control point.", + "The program wasn't able to correctly set the control point.\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); + msgBox.exec(); + } + } + else + { + // else case is: no overwriting + return; + } + } + + // specific image does not exist or has been removed; setting the control point should work try { m_SemanticRelationsIntegration->UnlinkImageFromControlPoint(dataNode); m_SemanticRelationsIntegration->SetControlPointOfImage(dataNode, controlPoint); } - catch (const mitk::SemanticRelationException&) + catch (const mitk::SemanticRelationException& e) { - return; + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox(QMessageBox::Warning, + "Could not set the control point.", + "The program wasn't able to correctly set the control point.\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); + msgBox.exec(); } } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetInformationTypeAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetInformationTypeAction.cpp index 64ea29b0e6..4075650f6f 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetInformationTypeAction.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetInformationTypeAction.cpp @@ -1,91 +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 "QmitkDataNodeSetInformationTypeAction.h" +#include "QmitkDataNodeRemoveFromSemanticRelationsAction.h" // semantic relations module +#include +#include +#include #include #include -// mitk gui common plugin -#include - // qt #include +#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; } + if (m_DataStorage.IsExpired()) + { + 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; } + mitk::SemanticTypes::InformationType informationType = inputDialog->textValue().toStdString(); + + mitk::SemanticTypes::ExaminationPeriod examinationPeriod; + mitk::SemanticTypes::ControlPoint controlPoint; + mitk::SemanticRelationsDataStorageAccess::DataNodeVector allSpecificImages; try { - m_SemanticRelationsIntegration->SetInformationType(dataNode, inputDialog->textValue().toStdString()); + mitk::SemanticTypes::CaseID caseID = mitk::GetCaseIDFromDataNode(dataNode); + controlPoint = mitk::SemanticRelationsInference::GetControlPointOfImage(dataNode); + // see if the examination period - information type cell is already taken + examinationPeriod = mitk::FindFittingExaminationPeriod(caseID, controlPoint); + auto semanticRelationsDataStorageAccess = mitk::SemanticRelationsDataStorageAccess(m_DataStorage.Lock()); + try + { + allSpecificImages = semanticRelationsDataStorageAccess.GetAllSpecificImages(caseID, informationType, examinationPeriod); + } + catch (const mitk::SemanticRelationException&) + { + // just continue since an exception means that there is no specific image + } } - catch (const mitk::SemanticRelationException&) + catch (const mitk::SemanticRelationException& e) { - return; + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox(QMessageBox::Warning, + "Could not set the information type.", + "The program wasn't able to correctly set the information type.\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); + msgBox.exec(); + } + + if (!allSpecificImages.empty()) + { + // examination period - information type cell is already taken + // ask if cell should be overwritten + QMessageBox::StandardButton answerButton = + QMessageBox::question(nullptr, + "Specific image already exists.", + QString::fromStdString("Force overwriting existing image " + informationType + " at " + examinationPeriod.name + "?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + if (answerButton == QMessageBox::Yes) + { + try + { + // remove already existent images at specific cell + for (const auto& specificImage : allSpecificImages) + { + RemoveFromSemanticRelationsAction::Run(m_SemanticRelationsIntegration.get(), m_DataStorage.Lock(), specificImage); + } + } + catch (const mitk::SemanticRelationException& e) + { + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox(QMessageBox::Warning, + "Could not set the information type.", + "The program wasn't able to correctly set the information type.\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); + msgBox.exec(); + } + } + else + { + // else case is: no overwriting + return; + } + } + + try + { + m_SemanticRelationsIntegration->SetInformationType(dataNode, informationType); + } + catch (const mitk::SemanticRelationException& e) + { + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox(QMessageBox::Warning, + "Could not set the information type.", + "The program wasn't able to correctly set the information type.\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); + msgBox.exec(); } } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsStatisticsControls.ui b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsStatisticsControls.ui new file mode 100644 index 0000000000..330e1d12c1 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsStatisticsControls.ui @@ -0,0 +1,40 @@ + + + QmitkSemanticRelationsStatisticsControls + + + + 0 + 0 + 350 + 300 + + + + Semantic relations plugin + + + + + + + + + + 0 + 0 + + + + Currently selected patient: + + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsStatisticsView.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsStatisticsView.cpp new file mode 100644 index 0000000000..cf5a0f862e --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsStatisticsView.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 "QmitkSemanticRelationsStatisticsView.h" + +// semantic relations module +#include +#include + +const std::string QmitkSemanticRelationsStatisticsView::VIEW_ID = "org.mitk.views.semanticrelationsstatistics"; + +QmitkSemanticRelationsStatisticsView::~QmitkSemanticRelationsStatisticsView() +{ + auto semanticRelationsIntegration = std::make_unique(); + semanticRelationsIntegration->RemoveObserver(this); +} + +void QmitkSemanticRelationsStatisticsView::Update(const mitk::SemanticTypes::CaseID& caseID) +{ + AddToComboBox(caseID); +} + +void QmitkSemanticRelationsStatisticsView::SetFocus() +{ + // nothing here +} + +void QmitkSemanticRelationsStatisticsView::CreateQtPartControl(QWidget* parent) +{ + // create GUI elements + m_Controls.setupUi(parent); + + m_StatisticsTreeModel = new QmitkStatisticsTreeModel(parent); + m_StatisticsTreeModel->SetDataStorage(GetDataStorage()); + m_Controls.statisticsTreeView->setModel(m_StatisticsTreeModel); + + auto semanticRelationsIntegration = std::make_unique(); + semanticRelationsIntegration->AddObserver(this); + + SetUpConnections(); + const auto& allCaseIDs = mitk::RelationStorage::GetAllCaseIDs(); + for (const auto& caseID : allCaseIDs) + { + AddToComboBox(caseID); + } +} + +void QmitkSemanticRelationsStatisticsView::SetUpConnections() +{ + connect(m_Controls.caseIDComboBox, static_cast(&QComboBox::currentIndexChanged), this, &QmitkSemanticRelationsStatisticsView::OnCaseIDSelectionChanged); + connect(m_StatisticsTreeModel, &QmitkStatisticsTreeModel::ModelUpdated, this, &QmitkSemanticRelationsStatisticsView::OnModelUpdated); +} + +void QmitkSemanticRelationsStatisticsView::OnCaseIDSelectionChanged(const QString& caseID) +{ + m_StatisticsTreeModel->SetCaseID(caseID.toStdString()); +} + +void QmitkSemanticRelationsStatisticsView::OnModelUpdated() +{ + m_Controls.statisticsTreeView->expandAll(); + int columns = m_Controls.statisticsTreeView->model()->columnCount(); + for (int i = 0; i < columns; ++i) + { + m_Controls.statisticsTreeView->resizeColumnToContents(i); + } +} + +void QmitkSemanticRelationsStatisticsView::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)); + } +} \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsStatisticsView.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsStatisticsView.h new file mode 100644 index 0000000000..64c6ec3c0d --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsStatisticsView.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 QMITKSEMANTICRELATIONSSTATISTICSVIEW_H +#define QMITKSEMANTICRELATIONSSTATISTICSVIEW_H + +// semantic relations plugin +#include "ui_QmitkSemanticRelationsStatisticsControls.h" + +// semantic relations module +#include + +// semantic relations ui module +#include "QmitkStatisticsTreeModel.h" + +// mitk qt gui common plugin +#include + +/* +* @brief The QmitkSemanticRelationsStatisticsView is an MITK view to combine and show the statistics tree view of the 'SemanticRelationsUI'-module. +* It observes the semantic relations storage and displays the currently available case IDs in a combo box. +* A 'QmitkStatisticsTreeModel' is created and set as the model of a QTreeView. +*/ +class QmitkSemanticRelationsStatisticsView : public QmitkAbstractView, public mitk::ISemanticRelationsObserver +{ + Q_OBJECT + +public: + + static const std::string VIEW_ID; + + virtual ~QmitkSemanticRelationsStatisticsView() override; + + /* + * @brief Update the view with the data from the semantic relations. + * + * Overridden from 'ISemanticRelationsObserver'. + * In order for the Update-function to be called, this view has to be added as an observer of SemanticRelation + * (e.g. m_SemanticRelations->AddObserver(this);) + * + * @par caseID The current case ID to identify the currently active patient / case. + */ + virtual void Update(const mitk::SemanticTypes::CaseID& caseID) override; + +protected: + + virtual void SetFocus() override; + virtual void CreateQtPartControl(QWidget* parent) override; + +private Q_SLOTS: + + void OnCaseIDSelectionChanged(const QString&); + void OnModelUpdated(); + +private: + + void SetUpConnections(); + void AddToComboBox(const mitk::SemanticTypes::CaseID& caseID); + + Ui::QmitkSemanticRelationsStatisticsControls m_Controls; + + QmitkStatisticsTreeModel* m_StatisticsTreeModel; +}; + +#endif // QMITKSEMANTICRELATIONSSTATISTICSVIEW_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.h index 4bca6dd829..3c41a3f2bf 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.h +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.h @@ -1,110 +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 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 +// mitk qt gui common plugin #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; virtual void RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) override; virtual void RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart) override; virtual void RenderWindowPartInputChanged(mitk::IRenderWindowPart* renderWindowPart) override; protected: virtual void SetFocus() override; virtual void CreateQtPartControl(QWidget* parent) 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 SetControlledRenderer(); Ui::QmitkSemanticRelationsControls m_Controls; mitk::IRenderWindowPart* m_RenderWindowPart; 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 index 9a343753d6..d05c04421d 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/mitkPluginActivator.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/mitkPluginActivator.cpp @@ -1,31 +1,34 @@ /*=================================================================== 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 "QmitkSemanticRelationsStatisticsView.h" #include "QmitkSemanticRelationsView.h" + #include namespace mitk { void SemanticRelationsActivator::start(ctkPluginContext *context) { mitk::PersistenceService::LoadModule(); BERRY_REGISTER_EXTENSION_CLASS(QmitkSemanticRelationsView, context) + BERRY_REGISTER_EXTENSION_CLASS(QmitkSemanticRelationsStatisticsView, context) } void SemanticRelationsActivator::stop(ctkPluginContext *context) { Q_UNUSED(context) } }