diff --git a/Modules/SemanticRelations/files.cmake b/Modules/SemanticRelations/files.cmake index 3fbf24cd98..b5e1bddd7a 100644 --- a/Modules/SemanticRelations/files.cmake +++ b/Modules/SemanticRelations/files.cmake @@ -1,12 +1,14 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkControlPointManager.cpp mitkDICOMHelper.cpp mitkLesionData.cpp mitkLesionManager.cpp mitkNodePredicates.cpp mitkRelationStorage.cpp - mitkSemanticRelations.cpp + mitkSemanticRelationsDataStorageAccess.cpp + mitkSemanticRelationsInference.cpp + mitkSemanticRelationsIntegration.cpp mitkUIDGeneratorBoost.cpp ) diff --git a/Modules/SemanticRelations/include/mitkLesionManager.h b/Modules/SemanticRelations/include/mitkLesionManager.h index f637e7eccd..790382bc0e 100644 --- a/Modules/SemanticRelations/include/mitkLesionManager.h +++ b/Modules/SemanticRelations/include/mitkLesionManager.h @@ -1,80 +1,78 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKLESIONMANAGER_H #define MITKLESIONMANAGER_H #include // semantic relations module -#include "mitkSemanticRelations.h" #include "mitkSemanticTypes.h" #include "mitkLesionData.h" // mitk core #include /* * @brief Provides helper functions that are needed to work with lesions. * * These functions help to generate new lesions, check for existing lesions or provide functionality * to find existing lesion class types. */ namespace mitk { typedef std::vector LesionClassVector; /** * @brief Generate a new lesion and lesion class with UIDs and the given string as lesion class type. * * @param lesionClassType The lesion class type as string. Default parameter is "". */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::Lesion GenerateNewLesion(const std::string& lesionClassType = ""); /** * @brief Generate a new lesion class with UID and the given string as lesion class type. * * @param lesionClassType The lesion class type as string. Default parameter is "". */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionClass GenerateNewLesionClass(const std::string& lesionClassType = ""); /** * @brief Find and return a whole lesion including its lesion class given a specific lesion UID. * * @param lesionUID The lesion UID as string. * @param allLesions All currently known lesions of a specific case. * * @return The lesion with its UID and the lesion class. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::Lesion GetLesionByUID(const SemanticTypes::ID& lesionUID, const std::vector& allLesions); /** * @brief Find and return the whole lesion class including its UID given a specific lesion class type. * * @param lesionClassType The lesion class type as string. * @param allLesionClasses All currently known lesion classes of a specific case. * * @return The lesion class with its UID and the class type. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionClass FindExistingLesionClass(const std::string& lesionClassType, const std::vector& allLesionClasses); /** * @brief Generate and store additional lesion data such as lesion presence and lesion volume for each control point. * * @param lesionData The lesion data that holds the lesion and will hold the additional lesion data. * @param caseID The current case ID. - * @param semanticRelations An instance of the semantic relations to retrieve the additional data. */ - MITKSEMANTICRELATIONS_EXPORT void GenerateAdditionalLesionData(LesionData& lesionData, const SemanticTypes::CaseID& caseID, std::shared_ptr semanticRelations); + MITKSEMANTICRELATIONS_EXPORT void GenerateAdditionalLesionData(LesionData& lesionData, const SemanticTypes::CaseID& caseID); } // namespace mitk #endif // MITKLESIONMANAGER_H diff --git a/Modules/SemanticRelations/include/mitkSemanticRelations.h b/Modules/SemanticRelations/include/mitkSemanticRelations.h deleted file mode 100644 index b4b017be56..0000000000 --- a/Modules/SemanticRelations/include/mitkSemanticRelations.h +++ /dev/null @@ -1,647 +0,0 @@ -/*=================================================================== - -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 MITKSEMANTICRELATIONS_H -#define MITKSEMANTICRELATIONS_H - -#include - -// semantic relations module -#include "mitkDICOMHelper.h" -#include "mitkISemanticRelationsObservable.h" -#include "mitkISemanticRelationsObserver.h" -#include "mitkSemanticTypes.h" - -// mitk core -#include -#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 the BlackSwan context the case is identified with the DICOM PatientID. - * - * In order for most functions to work the case ID has to be defined in the model. If not, - * the functions do nothing. - */ - class MITKSEMANTICRELATIONS_EXPORT SemanticRelations : public ISemanticRelationsObservable - { - public: - - SemanticRelations(DataStorage* dataStorage); - ~SemanticRelations(); - - using DataNodeVector = std::vector ; - - /************************************************************************/ - /* 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 get instances / attributes */ - /************************************************************************/ - /** - * @brief Returns a vector of all lesions that are currently available for the given case. - * The lesions may be marked by a segmentation or may be empty - with no connection to a specific image / segmentation of the case data. - * If no lesions are stored for the current case, an empty vector is returned. - * - * @param caseID The current case identifier is defined by the given string. - * @return A vector of lesions. - */ - SemanticTypes::LesionVector GetAllLesionsOfCase(const SemanticTypes::CaseID& caseID) const; - /** - * @brief - * - * - */ - SemanticTypes::LesionClassVector GetAllLesionClassesOfCase(const SemanticTypes::CaseID& caseID) const; - /** - * @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. - */ - SemanticTypes::LesionVector GetAllLesionsOfCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) const; - /** - * @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 segmentation nodes 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). - * @pre The image node has to have associated segmentation nodes (child nodes) in order to reference a lesion. - * - * @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. - */ - SemanticTypes::LesionVector GetAllLesionsInImage(const DataNode* imageNode) const; - /** - * @brief Returns the lesion that is defined by the given segmentation data. - * - * @pre The given segmentation data node has to be valid (!nullptr). - * @throw SemanticRelationException, if the given segmentation data node 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. - */ - SemanticTypes::Lesion GetRepresentedLesion(const DataNode* segmentationNode) const; - /** - * @brief Check if the given lesion is present on the given data node. - * For this the given data node is checked against the image / segmentation predicate. If the node is a - * segmentation, the represented lesion of the segmentation is simply compared against the member lesion. - * If the node is an image its derived segmentations are retrieved. For each derived segmentation - * the represented lesion is compared against the member lesion. - * - * @pre The given data node has to be valid (!nullptr). - * @throw SemanticRelationException, if the given data node is invalid (==nullptr). - * - * @param dataNode The data node identifier is extracted from the given data node. - * The data node or it's derivations are used for a function call to 'GetRepresentedLesion'. - * @return True, if the lesion is present on the given data node; false otherwise. - */ - bool IsLesionPresentOnDataNode(const SemanticTypes::Lesion& lesion, const mitk::DataNode* dataNode) const; - /** - * @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. - */ - bool IsRepresentingALesion(const DataNode* segmentationNode) const; - /** - * @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. - */ - bool InstanceExists(const DataNode* dataNode) const; - /** - * @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. - */ - mitk::SemanticRelations::DataNodeVector GetAllSegmentationsOfCase(const SemanticTypes::CaseID& caseID) const; - /** - * @brief Return a vector of all segmentations that define the given lesion. These segmentations don't have to be linked to the same image. - * If the lesion is not referred to by any segmentation, an empty vector is returned. - * - * @pre The data storage member has to be valid (!nullptr). - * @throw SemanticRelationException, if the data storage member is invalid (==nullptr). - * @pre The UID of the lesion has to exist for a lesion instance. - * @throw SemanticRelationException, if UID of the lesion does not exist for a lesion instance (this can be checked via 'InstanceExists'). - * - * @param caseID The current case identifier is defined by the given string. - * @param lesion A Lesion with a UID that identifies the corresponding lesion instance. - * @return A vector of data nodes representing segmentations that define the given lesion. - */ - DataNodeVector GetAllSegmentationsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const; - /** - * @brief Return a vector of all images that are currently available for the given case. - * - * @pre The data storage member has to be valid (!nullptr). - * @throw SemanticRelationException, if the data storage member is invalid (==nullptr). - * - * @param caseID The current case identifier is defined by the given string. - * @return A vector of data nodes representing images. - */ - DataNodeVector GetAllImagesOfCase(const SemanticTypes::CaseID& caseID) const; - /** - * @brief Return a vector of all images that are connected to those segmentations that are linked to the given lesion. - * If the lesion is not referred to by any segmentation, an empty vector is returned. - * - * @pre The UID of the lesion has to exist for a lesion instance. - * @throw SemanticRelationException, if UID of the lesion does not exist for a lesion instance (this can be checked via 'InstanceExists'). - * - * @param caseID The current case identifier is defined by the given string. - * @param lesion A Lesion with a UID that identifies the corresponding lesion instance. - * @return A vector of data nodes representing images on which the lesions are visible. - */ - DataNodeVector GetAllImagesOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const; - /** - * @brief Check if the given lesion instance exists. - * This function can be used before calling 'GetAllSegmentationsOfLesion' in order to avoid a possible exception. - * 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. - */ - bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const; - /** - * @brief Return a vector of all control points that are valid for the given case. - * - * @param caseID The current case identifier is defined by the given string. - * @return A vector of control points. - */ - SemanticTypes::ControlPointVector GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID) const; - /** - * @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. - */ - SemanticTypes::ControlPointVector GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const; - /** - * @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. - */ - SemanticTypes::ControlPointVector GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) const; - /** - * @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. - */ - SemanticTypes::ControlPoint GetControlPointOfData(const DataNode* dataNode) const; - /** - * @brief Return a vector of all image nodes that link to the given control point. - * If the control point is not referred to by any data node, an empty vector is returned. - * - * @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 exist for a control point instance (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. - * @return A vector of image nodes that link to the given control point. - */ - DataNodeVector GetAllImagesOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) const; - /** - * @brief Check if the given control point instance exists. - * This function can be used before calling 'GetAllDataOfControlPoint' in order to avoid a possible exception. - * 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. - */ - bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) const; - /** - * @brief Return a vector of all examination periods nodes that are valid for the given case. - * - * @param caseID The current case identifier is defined by the given string. - * @return A vector of examination periods. - */ - SemanticTypes::ExaminationPeriodVector GetAllExaminationPeriodsOfCase(const SemanticTypes::CaseID& caseID) const; - /** - * @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. - */ - bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) const; - /** - * @brief Return a vector of all information types that are valid for the given case. - * - * @param caseID The current case identifier is defined by the given string. - * @return A vector of information types. - */ - SemanticTypes::InformationTypeVector GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID) const; - /** - * @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. - */ - SemanticTypes::InformationTypeVector GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) const; - /** - * @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. - */ - SemanticTypes::InformationType GetInformationTypeOfImage(const DataNode* imageNode) const; - /** - * @brief Return a vector of all image nodes that are defined with the given information type. - * - * @pre The information type has to exist for the given case (and is therefore used by at least one data node). - * @throw SemanticRelationException, 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 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. - */ - DataNodeVector GetAllImagesOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) const; - /** - * @brief Return a vector of all image nodes that are defined with the given information type and with the given control point. - * - * @pre The UID of the control point has to exist for a control point instance. - * The information type has to exist for the given case (and is therefore used by at least one data node). - * @throw SemanticRelationException, if the UID of the control point does not exist for a control point instance (this can be checked via 'InstanceExists') or - * if the information type is not used by any data node (this can be checked via 'InstanceExists'). - * - * @param caseID The current case identifier is defined by the given string. - * @param controlPoint A control point with a UID that identifies the corresponding control point instance. - * @param informationType An information type that identifies the corresponding information type instance. - * @return A vector of image nodes that are defined with the given information type with the given control point. - */ - DataNodeVector GetAllSpecificImages(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const; - /** - * @brief Return a vector of all segmentation nodes that are defined with the given information type and with the given control point. - * The function uses the 'GetAllSpecificImages'-function to retrieve the specific images and then searches for the derived nodes (segmentation child nodes). - * - * @pre The UID of the control point has to exist for a control point instance. - * The information type has to exist for the given case (and is therefore used by at least one data node). - * @throw SemanticRelationException, if the UID of the control point does not exist for a control point instance (this can be checked via 'InstanceExists') or - * if the information type is not used by any data node (this can be checked via 'InstanceExists'). - * - * @param caseID The current case identifier is defined by the given string. - * @param controlPoint A control point with a UID that identifies the corresponding control point instance. - * @param informationType An information type that identifies the corresponding information type instance. - * @return A vector of segmentation nodes that are defined with the given information type with the given control point. - */ - DataNodeVector GetAllSpecificSegmentations(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const; - /** - * @brief Check if the given information type exists. - * This function can be used before calling 'GetAllDataOfInformationType' in order to avoid a possible exception. - * - * @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. - */ - bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) const; - /** - * @brief Return a vector of all CaseIDs that are currently available. - * - * @return A vector of CaseIDs as strings. - */ - std::vector GetAllCaseIDs() const; - - /************************************************************************/ - /* 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 data. 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 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 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 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 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 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 data node. - * - * @pre The given data node has to be valid (!nullptr). - * @throw SemanticRelationException, if the given data node is invalid (==nullptr). - */ - void SetControlPointOfData(const DataNode* dataNode, 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 data. - * This function combines adding a control point and linking it, since a control point with no associated data is not allowed. - * - * @pre The given data node has to be valid (!nullptr). - * @throw SemanticRelationException, if the given 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 data node (if parameter 'checkConsistence = true'). - * @throw SemanticRelationException, if the given control point does not contain the date of the given data node and 'checkConsistence = true' (this can be checked via 'ControlPointManager::InsideControlPoint'). - * - * @param dataNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. - * @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 data node actually lies inside the control point to link. - */ - void AddControlPointAndLinkData(const DataNode* dataNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence = true); - /** - * @brief Link the given data to an already existing control point. - * - * @pre The given data node has to be valid (!nullptr). - * @throw SemanticRelationException, if the given 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 data node (if parameter 'checkConsistence = true'). - * @throw SemanticRelationException, if the given control point does not contain the date of the given data node and 'checkConsistence = true' (this can be checked via 'ControlPointManager::InsideControlPoint'). - * - * @param dataNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. - * @param controlPoint The control point instance to link. - * @param checkConsistence If true, the function checks, whether the date of the data node actually lies inside the control point to link. - */ - void LinkDataToControlPoint(const DataNode* dataNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence = true); - /** - * @brief Unlink the given image from the linked control point. - * If data is unlinked from a control point, the function needs to check whether the control point is still linked to any other data: - * - if not, the control point instance will be removed (has to be removed since a control point with no associated data 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 dataNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. - */ - void UnlinkDataFromControlPoint(const DataNode* dataNode); - /** - * @brief Add an examination period instance to the set of already existing examination periods - with no connection to a specific control point. - * - * @pre The UID of the examination period must not already exist for an examination period instance. - * @throw SemanticRelationException, if the UID of the examination period already exists for a examination period instance (this can be checked via 'InstanceExists'). - * - * @param caseID The current case identifier is defined by the given string. - * @param examinationPeriod The examination period to add. - */ - void AddExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod); - /** - * @brief Add a control point to the vector of control point UIDs of an existing examination period. - * - * @pre The UID of the control point has to exist for a control point instance. - * @throw SemanticRelationException, if the UID of the control point does not exists for a control point instance (this can be checked via 'InstanceExists'). - * @pre The UID of the examination period must not already exist for an examination period instance. - * @throw SemanticRelationException, if the UID of the examination period already exists for a examination period instance (this can be checked via 'InstanceExists'). - * - * @param caseID The current case identifier is defined by the given string. - * @param controlPoint The control point instance to add to the examination period. - * @param examinationPeriod The examination period to which the control point should be added. - */ - void AddControlPointToExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod& examinationPeriod); - /** - * @brief Set (and possibly overwrite) the information type of the given image. - * An already associated information type might be removed if is not referenced by any other image: - * - * @pre The given image data node has to be valid (!nullptr). - * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). - * @post If the information type instance did not exist before, it is now added. - * - * @param imageNode The current case identifier is extracted from the given 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 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 data node, which contains DICOM information about the case. - */ - void RemoveInformationTypeFromImage(const DataNode* imageNode); - - private: - - WeakPointer m_DataStorage; - - /** - * @brief A vector that stores the currently registered observer of this observable subject. - */ - static std::vector m_ObserverVector; - /** - * @brief The SemanticRelations, as an example of an observable subject, 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 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. - */ - bool ControlPointContainsLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ControlPoint& controlPoint) const; - /** - * @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. - */ - bool ControlPointContainsInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType, const SemanticTypes::ControlPoint& controlPoint) const; - /** - * @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 // MITKSEMANTICRELATIONS_H diff --git a/Modules/SemanticRelations/src/mitkLesionManager.cpp b/Modules/SemanticRelations/src/mitkLesionManager.cpp index 7764bbe5ba..fc459ad32e 100644 --- a/Modules/SemanticRelations/src/mitkLesionManager.cpp +++ b/Modules/SemanticRelations/src/mitkLesionManager.cpp @@ -1,144 +1,122 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // semantic relations module #include "mitkLesionManager.h" #include "mitkSemanticRelationException.h" +#include "mitkSemanticRelationsInference.h" +#include "mitkRelationStorage.h" #include "mitkUIDGeneratorBoost.h" -bool GetLesionPresence(const mitk::SemanticTypes::CaseID& caseID, std::shared_ptr semanticRelations, const mitk::SemanticTypes::Lesion& lesion, const mitk::SemanticTypes::ControlPoint& controlPoint); - -double GetLesionVolume(const mitk::SemanticTypes::CaseID& caseID, std::shared_ptr semanticRelations, const mitk::SemanticTypes::Lesion& lesion, const mitk::SemanticTypes::ControlPoint& controlPoint); +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/* = ""*/) { mitk::SemanticTypes::Lesion lesion; lesion.UID = mitk::UIDGeneratorBoost::GenerateUID(); lesion.name = "New lesion"; lesion.lesionClass = mitk::SemanticTypes::LesionClass(); lesion.lesionClass.UID = mitk::UIDGeneratorBoost::GenerateUID(); lesion.lesionClass.classType = lesionClassType; return lesion; } mitk::SemanticTypes::LesionClass mitk::GenerateNewLesionClass(const std::string& lesionClassType/* = ""*/) { mitk::SemanticTypes::LesionClass lesionClass; lesionClass.UID = mitk::UIDGeneratorBoost::GenerateUID(); lesionClass.classType = lesionClassType; return lesionClass; } mitk::SemanticTypes::Lesion mitk::GetLesionByUID(const SemanticTypes::ID& lesionUID, const std::vector& allLesions) { auto lambda = [&lesionUID](const SemanticTypes::Lesion& currentLesion) { return currentLesion.UID == lesionUID; }; const auto existingLesion = std::find_if(allLesions.begin(), allLesions.end(), lambda); mitk::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); mitk::SemanticTypes::LesionClass lesionClass; if (existingLesionClass != allLesionClasses.end()) { lesionClass = *existingLesionClass; } return lesionClass; } -void mitk::GenerateAdditionalLesionData(LesionData& lesionData, const SemanticTypes::CaseID& caseID, std::shared_ptr semanticRelations) +void mitk::GenerateAdditionalLesionData(LesionData& lesionData, const SemanticTypes::CaseID& caseID) { std::vector lesionPresence; std::vector lesionVolume; SemanticTypes::Lesion lesion = lesionData.GetLesion(); bool presence = false; double volume = 0.0; try { - std::vector controlPoints = semanticRelations->GetAllControlPointsOfCase(caseID); + SemanticTypes::ControlPointVector controlPoints = mitk::RelationStorage::GetAllControlPointsOfCase(caseID); for (const auto& controlPoint : controlPoints) { - presence = GetLesionPresence(caseID, semanticRelations, lesion, controlPoint); + presence = mitk::SemanticRelationsInference::IsLesionPresentAtControlPoint(caseID, lesion, controlPoint); lesionPresence.push_back(presence); - volume = GetLesionVolume(caseID, semanticRelations, lesion, controlPoint); + volume = GetLesionVolume(caseID, lesion, controlPoint); lesionVolume.push_back(volume); } } catch (const mitk::SemanticRelationException&) { return; } lesionData.SetLesionPresence(lesionPresence); lesionData.SetLesionVolume(lesionVolume); } -bool GetLesionPresence(const mitk::SemanticTypes::CaseID& caseID, std::shared_ptr semanticRelations, const mitk::SemanticTypes::Lesion& lesion, const mitk::SemanticTypes::ControlPoint& controllPoint) -{ - try - { - mitk::SemanticRelations::DataNodeVector allImagesOfLesion = semanticRelations->GetAllImagesOfLesion(caseID, lesion); - for (const auto& image : allImagesOfLesion) - { - auto imageControlPoint = semanticRelations->GetControlPointOfData(image); - if (imageControlPoint.date == controllPoint.date) - { - return true; - } - } - } - catch (const mitk::SemanticRelationException&) - { - return false; - } - - return false; -} - -double GetLesionVolume(const mitk::SemanticTypes::CaseID& caseID, std::shared_ptr semanticRelations, const mitk::SemanticTypes::Lesion& lesion, const mitk::SemanticTypes::ControlPoint& controlPoint) +double GetLesionVolume(const mitk::SemanticTypes::CaseID& caseID, const mitk::SemanticTypes::Lesion& lesion, const mitk::SemanticTypes::ControlPoint& controlPoint) { - bool presence = GetLesionPresence(caseID, semanticRelations, lesion, controlPoint); + bool presence = mitk::SemanticRelationsInference::IsLesionPresentAtControlPoint(caseID, lesion, controlPoint); if (presence) { return 1.0; } else { return 0.0; } } diff --git a/Modules/SemanticRelations/src/mitkSemanticRelations.cpp b/Modules/SemanticRelations/src/mitkSemanticRelations.cpp deleted file mode 100644 index 72f247f5f0..0000000000 --- a/Modules/SemanticRelations/src/mitkSemanticRelations.cpp +++ /dev/null @@ -1,1060 +0,0 @@ -/*=================================================================== - -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 "mitkSemanticRelations.h" - -// semantic relations module -#include "mitkControlPointManager.h" -#include "mitkNodePredicates.h" -#include "mitkRelationStorage.h" -#include "mitkSemanticRelationException.h" -#include "mitkUIDGeneratorBoost.h" - -// multi label module -#include - -// c++ -#include -#include - -std::vector mitk::SemanticRelations::m_ObserverVector; - -mitk::SemanticRelations::SemanticRelations(DataStorage* dataStorage) - : m_DataStorage(dataStorage) -{ - // nothing here -} - -mitk::SemanticRelations::~SemanticRelations() -{ - // nothing here -} - -void mitk::SemanticRelations::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::SemanticRelations::RemoveObserver(ISemanticRelationsObserver* observer) -{ - m_ObserverVector.erase(std::remove(m_ObserverVector.begin(), m_ObserverVector.end(), observer), m_ObserverVector.end()); -} - -/************************************************************************/ -/* functions to get instances / attributes */ -/************************************************************************/ - -mitk::SemanticTypes::LesionVector mitk::SemanticRelations::GetAllLesionsOfCase(const SemanticTypes::CaseID& caseID) const -{ - return RelationStorage::GetAllLesionsOfCase(caseID); -} - -mitk::SemanticTypes::LesionVector mitk::SemanticRelations::GetAllLesionsOfCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) const -{ - SemanticTypes::LesionVector allLesions = 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, this](const SemanticTypes::Lesion& lesion) { return !ControlPointContainsLesion(caseID, lesion, controlPoint); }; - allLesions.erase(std::remove_if(allLesions.begin(), allLesions.end(), lambda), allLesions.end()); - - return allLesions; -} - -mitk::SemanticTypes::LesionClassVector mitk::SemanticRelations::GetAllLesionClassesOfCase(const SemanticTypes::CaseID& caseID) const -{ - SemanticTypes::LesionVector allLesionsOfCase = 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::LesionVector mitk::SemanticRelations::GetAllLesionsInImage(const DataNode* imageNode) const -{ - if (nullptr == imageNode) - { - mitkThrowException(SemanticRelationException) << "Not a valid image data node."; - } - - if (m_DataStorage.IsExpired()) - { - mitkThrowException(SemanticRelationException) << "Not a valid data storage."; - } - - SemanticTypes::LesionVector allLesionsInImage; - // get child nodes of the current node with the segmentation predicate - DataStorage::SetOfObjects::ConstPointer segmentationNodes = m_DataStorage.Lock()->GetDerivations(imageNode, NodePredicates::GetSegmentationPredicate(), false); - for (auto it = segmentationNodes->Begin(); it != segmentationNodes->End(); ++it) - { - DataNode* segmentationNode = it->Value(); - try - { - SemanticTypes::Lesion representedLesion = GetRepresentedLesion(segmentationNode); - allLesionsInImage.push_back(representedLesion); - } - catch (const SemanticRelationException&) - { - continue; - } - } - return allLesionsInImage; -} - -mitk::SemanticTypes::Lesion mitk::SemanticRelations::GetRepresentedLesion(const DataNode* segmentationNode) const -{ - if (nullptr == segmentationNode) - { - mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; - } - - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); - SemanticTypes::ID segmentationID = GetIDFromDataNode(segmentationNode); - SemanticTypes::Lesion representedLesion = RelationStorage::GetRepresentedLesion(caseID, segmentationID); - - if (representedLesion.UID.empty()) - { - mitkThrowException(SemanticRelationException) << "Could not find a represented lesion instance for the given segmentation node " << segmentationNode->GetName(); - } - else - { - return representedLesion; - } -} - -bool mitk::SemanticRelations::IsLesionPresentOnDataNode(const SemanticTypes::Lesion& lesion, const mitk::DataNode* dataNode) const -{ - if (nullptr == dataNode) - { - mitkThrowException(SemanticRelationException) << "Not a valid data node."; - } - - if (m_DataStorage.IsExpired()) - { - mitkThrowException(SemanticRelationException) << "Not a valid data storage."; - } - - try - { - if (NodePredicates::GetImagePredicate()->CheckNode(dataNode)) - { - // get segmentations of the image node with the segmentation predicate - DataStorage::SetOfObjects::ConstPointer segmentations = m_DataStorage.Lock()->GetDerivations(dataNode, mitk::NodePredicates::GetSegmentationPredicate(), false); - for (auto it = segmentations->Begin(); it != segmentations->End(); ++it) - { - const auto representedLesion = GetRepresentedLesion(it.Value()); - return lesion.UID == representedLesion.UID; - } - } - else if (NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) - { - const auto representedLesion = GetRepresentedLesion(dataNode); - return lesion.UID == representedLesion.UID; - } - } - catch (const SemanticRelationException&) - { - return false; - } - - return false; -} - -bool mitk::SemanticRelations::IsRepresentingALesion(const DataNode* segmentationNode) const -{ - try - { - SemanticTypes::Lesion representedLesion = GetRepresentedLesion(segmentationNode); - return true; - } - catch (const Exception&) - { - return false; - } -} - -bool mitk::SemanticRelations::InstanceExists(const DataNode* dataNode) const -{ - try - { - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); - SemanticTypes::ID dataNodeID = GetIDFromDataNode(dataNode); - - if (NodePredicates::GetImagePredicate()->CheckNode(dataNode)) - { - std::vector allImageIDsOfCase = RelationStorage::GetAllImageIDsOfCase(caseID); - return std::find(allImageIDsOfCase.begin(), allImageIDsOfCase.end(), dataNodeID) != allImageIDsOfCase.end(); - } - else if (NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) - { - std::vector allSegmentationIDsOfCase = RelationStorage::GetAllSegmentationIDsOfCase(caseID); - return std::find(allSegmentationIDsOfCase.begin(), allSegmentationIDsOfCase.end(), dataNodeID) != allSegmentationIDsOfCase.end(); - } - else - { - return false; - } - } - catch (const SemanticRelationException&) - { - return false; - } -} - -mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllSegmentationsOfCase(const SemanticTypes::CaseID& caseID) const -{ - if (m_DataStorage.IsExpired()) - { - mitkThrowException(SemanticRelationException) << "Not a valid data storage."; - } - - std::vector allSegmentationIDsOfCase = RelationStorage::GetAllSegmentationIDsOfCase(caseID); - std::vector allSegmentationsOfCase; - // get all segmentation nodes of the current data storage - // only those nodes are respected, that are currently held in the data storage - DataStorage::SetOfObjects::ConstPointer segmentationNodes = m_DataStorage.Lock()->GetSubset(NodePredicates::GetSegmentationPredicate()); - for (auto it = segmentationNodes->Begin(); it != segmentationNodes->End(); ++it) - { - DataNode* segmentationNode = it->Value(); - try - { - // find the corresponding segmentation node for the given segmentation ID - std::string nodeCaseID = GetCaseIDFromDataNode(segmentationNode); - std::string nodeSegmentationID = GetIDFromDataNode(segmentationNode); - if (nodeCaseID == caseID && (std::find(allSegmentationIDsOfCase.begin(), allSegmentationIDsOfCase.end(), nodeSegmentationID) != allSegmentationIDsOfCase.end())) - { - // found current image node in the storage, add it to the return vector - allSegmentationsOfCase.push_back(segmentationNode); - } - } - catch (const std::exception&) - { - // found a segmentation node that is not stored in the semantic relations - // this segmentation node does not have any DICOM information --> exception thrown - // continue with the next segmentation to compare IDs - continue; - } - } - - return allSegmentationsOfCase; -} - -mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllSegmentationsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const -{ - if (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 = GetRepresentedLesion(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::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllImagesOfCase(const SemanticTypes::CaseID& caseID) const -{ - if (m_DataStorage.IsExpired()) - { - mitkThrowException(SemanticRelationException) << "Not a valid data storage."; - } - - std::vector allImageIDsOfCase = RelationStorage::GetAllImageIDsOfCase(caseID); - std::vector allImagesOfCase; - // get all image nodes of the current data storage - // only those nodes are respected, that are currently held in the data storage - DataStorage::SetOfObjects::ConstPointer imageNodes = m_DataStorage.Lock()->GetSubset(NodePredicates::GetImagePredicate()); - for (auto it = imageNodes->Begin(); it != imageNodes->End(); ++it) - { - DataNode* imageNode = it->Value(); - // find the corresponding image node for the given segmentation ID - std::string nodeCaseID = GetCaseIDFromDataNode(imageNode); - std::string nodeImageID = GetIDFromDataNode(imageNode); - if (nodeCaseID == caseID && (std::find(allImageIDsOfCase.begin(), allImageIDsOfCase.end(), nodeImageID) != allImageIDsOfCase.end())) - { - // found current image node in the storage, add it to the return vector - allImagesOfCase.push_back(imageNode); - } - } - - return allImagesOfCase; -} - -mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::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; -} - -bool mitk::SemanticRelations::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const -{ - SemanticTypes::LesionVector allLesions = GetAllLesionsOfCase(caseID); - - // filter all lesions: check for equality with the given lesion using a lambda function - auto lambda = [&lesion](const SemanticTypes::Lesion& currentLesion) { return currentLesion.UID == lesion.UID; }; - const auto existingLesion = std::find_if(allLesions.begin(), allLesions.end(), lambda); - - if (existingLesion != allLesions.end()) - { - return true; - } - else - { - return false; - } -} - -mitk::SemanticTypes::ControlPointVector mitk::SemanticRelations::GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID) const -{ - return RelationStorage::GetAllControlPointsOfCase(caseID); -} - -mitk::SemanticTypes::ControlPointVector mitk::SemanticRelations::GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const -{ - SemanticTypes::ControlPointVector allControlPoints = 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, this](const SemanticTypes::ControlPoint& controlPoint) { return !ControlPointContainsLesion(caseID, lesion, controlPoint); }; - allControlPoints.erase(std::remove_if(allControlPoints.begin(), allControlPoints.end(), lambda), allControlPoints.end()); - - return allControlPoints; -} - -mitk::SemanticTypes::ControlPointVector mitk::SemanticRelations::GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) const -{ - SemanticTypes::ControlPointVector allControlPoints = 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, this](const SemanticTypes::ControlPoint& controlPoint) { return !ControlPointContainsInformationType(caseID, informationType, controlPoint); }; - allControlPoints.erase(std::remove_if(allControlPoints.begin(), allControlPoints.end(), lambda), allControlPoints.end()); - - return allControlPoints; -} - -mitk::SemanticTypes::ControlPoint mitk::SemanticRelations::GetControlPointOfData(const DataNode* imageNode) const -{ - if (nullptr == imageNode) - { - mitkThrowException(SemanticRelationException) << "Not a valid data node."; - } - - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); - SemanticTypes::ID dataNodeID = GetIDFromDataNode(imageNode); - return RelationStorage::GetControlPointOfImage(caseID, dataNodeID); -} - -mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllImagesOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) const -{ - if (InstanceExists(caseID, controlPoint)) - { - // control point exists, retrieve all images from the storage - DataNodeVector allImagesOfControlPoint = GetAllImagesOfCase(caseID); - - // filter all images to remove the ones with a different control point using a lambda function - auto lambda = [&controlPoint, this](DataNode::Pointer imageNode) - { - return controlPoint.UID != GetControlPointOfData(imageNode).UID; - }; - - allImagesOfControlPoint.erase(std::remove_if(allImagesOfControlPoint.begin(), allImagesOfControlPoint.end(), lambda), allImagesOfControlPoint.end()); - - return allImagesOfControlPoint; - } - else - { - mitkThrowException(SemanticRelationException) << "Could not find an existing control point instance for the given caseID " << caseID << " and control point " << controlPoint.UID << "."; - } -} - -bool mitk::SemanticRelations::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) const -{ - SemanticTypes::ControlPointVector allControlPoints = 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; - } -} - -mitk::SemanticTypes::ExaminationPeriodVector mitk::SemanticRelations::GetAllExaminationPeriodsOfCase(const SemanticTypes::CaseID& caseID) const -{ - return RelationStorage::GetAllExaminationPeriodsOfCase(caseID); -} - -bool mitk::SemanticRelations::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) const -{ - SemanticTypes::ExaminationPeriodVector allExaminationPeriods = 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::InformationTypeVector mitk::SemanticRelations::GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID) const -{ - return RelationStorage::GetAllInformationTypesOfCase(caseID); -} - -mitk::SemanticTypes::InformationTypeVector mitk::SemanticRelations::GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) const -{ - SemanticTypes::InformationTypeVector allInformationTypes = 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, this](const SemanticTypes::InformationType& informationType) { return !ControlPointContainsInformationType(caseID, informationType, controlPoint); }; - allInformationTypes.erase(std::remove_if(allInformationTypes.begin(), allInformationTypes.end(), lambda), allInformationTypes.end()); - - return allInformationTypes; -} - -mitk::SemanticTypes::InformationType mitk::SemanticRelations::GetInformationTypeOfImage(const DataNode* imageNode) const -{ - if (nullptr == imageNode) - { - mitkThrowException(SemanticRelationException) << "Not a valid image data node."; - } - - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); - SemanticTypes::ID imageID = GetIDFromDataNode(imageNode); - return RelationStorage::GetInformationTypeOfImage(caseID, imageID); -} - -mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllImagesOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) const -{ - if (InstanceExists(caseID, informationType)) - { - // information type exists, retrieve all images from the storage - DataNodeVector allImagesOfInformationType = GetAllImagesOfCase(caseID); - - // filter all images to remove the ones with a different information type using a lambda function - auto lambda = [&informationType, this](DataNode::Pointer imageNode) - { - return informationType != GetInformationTypeOfImage(imageNode); - }; - - allImagesOfInformationType.erase(std::remove_if(allImagesOfInformationType.begin(), allImagesOfInformationType.end(), lambda), allImagesOfInformationType.end()); - - return allImagesOfInformationType; - } - else - { - mitkThrowException(SemanticRelationException) << "Could not find an existing information type for the given caseID " << caseID << " and information type " << informationType; - } -} - -mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllSpecificImages(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const -{ - if (InstanceExists(caseID, controlPoint)) - { - if (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 != GetInformationTypeOfImage(imageNode)) || (controlPoint.date != GetControlPointOfData(imageNode).date); - }; - - allImagesOfCase.erase(std::remove_if(allImagesOfCase.begin(), allImagesOfCase.end(), lambda), allImagesOfCase.end()); - - return allImagesOfCase; - } - else - { - mitkThrowException(SemanticRelationException) << "Could not find an existing information type for the given caseID " << caseID << " and information type " << informationType; - } - } - else - { - mitkThrowException(SemanticRelationException) << "Could not find an existing control point for the given caseID " << caseID << " and control point " << controlPoint.UID; - } -} - -mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllSpecificSegmentations(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const -{ - if (m_DataStorage.IsExpired()) - { - mitkThrow() << "Not a valid data storage."; - } - - DataNodeVector allSpecificImages = GetAllSpecificImages(caseID, controlPoint, informationType); - DataNodeVector allSpecificSegmentations; - for (const auto& imageNode : allSpecificImages) - { - DataStorage::SetOfObjects::ConstPointer segmentationNodes = m_DataStorage.Lock()->GetDerivations(imageNode, NodePredicates::GetSegmentationPredicate(), false); - for (auto it = segmentationNodes->Begin(); it != segmentationNodes->End(); ++it) - { - allSpecificSegmentations.push_back(it->Value()); - } - } - - return allSpecificSegmentations; -} - -bool mitk::SemanticRelations::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) const -{ - SemanticTypes::InformationTypeVector allInformationTypes = 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; - } -} - -std::vector mitk::SemanticRelations::GetAllCaseIDs() const -{ - return RelationStorage::GetAllCaseIDs(); -} - -/************************************************************************/ -/* functions to add / remove instances / attributes */ -/************************************************************************/ - -void mitk::SemanticRelations::AddImage(const DataNode* imageNode) -{ - if (nullptr == imageNode) - { - mitkThrowException(SemanticRelationException) << "Not a valid data node."; - } - - // continue with a valid data node - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); - SemanticTypes::ID nodeID = GetIDFromDataNode(imageNode); - - RelationStorage::AddCase(caseID); - RelationStorage::AddImage(caseID, nodeID); - - SemanticTypes::InformationType informationType = GetDICOMModalityFromDataNode(imageNode); - AddInformationTypeToImage(imageNode, informationType); - - // set the correct control point for this image - SemanticTypes::ControlPoint controlPoint = GenerateControlPoint(imageNode); - SetControlPointOfData(imageNode, controlPoint); -} - -void mitk::SemanticRelations::RemoveImage(const DataNode* imageNode) -{ - if (nullptr == imageNode) - { - mitkThrowException(SemanticRelationException) << "Not a valid data node."; - } - - // continue with a valid data node - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); - SemanticTypes::ID nodeID = GetIDFromDataNode(imageNode); - - RemoveInformationTypeFromImage(imageNode); - UnlinkDataFromControlPoint(imageNode); - RelationStorage::RemoveImage(caseID, nodeID); - NotifyObserver(caseID); -} - -void mitk::SemanticRelations::AddLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) -{ - if (InstanceExists(caseID, lesion)) - { - mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to add already exists for the given case."; - } - else - { - RelationStorage::AddLesion(caseID, lesion); - NotifyObserver(caseID); - } -} - -void mitk::SemanticRelations::OverwriteLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) -{ - if (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::SemanticRelations::AddLesionAndLinkSegmentation(const DataNode* segmentationNode, const SemanticTypes::Lesion& lesion) -{ - if (nullptr == segmentationNode) - { - mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; - } - - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); - AddLesion(caseID, lesion); - LinkSegmentationToLesion(segmentationNode, lesion); - NotifyObserver(caseID); -} - -void mitk::SemanticRelations::RemoveLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) -{ - if (InstanceExists(caseID, lesion)) - { - DataNodeVector allSegmentationsOfLesion = GetAllSegmentationsOfLesion(caseID, lesion); - if (allSegmentationsOfLesion.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::SemanticRelations::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."; - } - - // continue with a valid data node - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); - SemanticTypes::ID segmentationNodeID = GetIDFromDataNode(segmentationNode); - SemanticTypes::ID parentNodeID = GetIDFromDataNode(parentNode); - - RelationStorage::AddSegmentation(caseID, segmentationNodeID, parentNodeID); - NotifyObserver(caseID); -} - -void mitk::SemanticRelations::LinkSegmentationToLesion(const DataNode* segmentationNode, const SemanticTypes::Lesion& lesion) -{ - if (nullptr == segmentationNode) - { - mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; - } - - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); - if (InstanceExists(caseID, lesion)) - { - SemanticTypes::ID segmentationID = GetIDFromDataNode(segmentationNode); - RelationStorage::LinkSegmentationToLesion(caseID, segmentationID, lesion); - NotifyObserver(caseID); - } - else - { - mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to link does not exist for the given case."; - } -} - -void mitk::SemanticRelations::UnlinkSegmentationFromLesion(const DataNode* segmentationNode) -{ - if (nullptr == segmentationNode) - { - mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; - } - - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); - SemanticTypes::ID segmentationID = GetIDFromDataNode(segmentationNode); - RelationStorage::UnlinkSegmentationFromLesion(caseID, segmentationID); - NotifyObserver(caseID); -} - -void mitk::SemanticRelations::RemoveSegmentation(const DataNode* segmentationNode) -{ - if (nullptr == segmentationNode) - { - mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; - } - - // continue with a valid data node - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); - SemanticTypes::ID segmentationNodeID = GetIDFromDataNode(segmentationNode); - RelationStorage::RemoveSegmentation(caseID, segmentationNodeID); - NotifyObserver(caseID); -} - -void mitk::SemanticRelations::SetControlPointOfData(const DataNode* dataNode, const SemanticTypes::ControlPoint& controlPoint) -{ - if (nullptr == dataNode) - { - mitkThrowException(SemanticRelationException) << "Not a valid data node."; - } - - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); - SemanticTypes::ControlPointVector allControlPoints = GetAllControlPointsOfCase(caseID); - // need to check if an already existing control point fits/contains the user control point - SemanticTypes::ControlPoint existingControlPoint = FindExistingControlPoint(controlPoint, allControlPoints); - if (!existingControlPoint.UID.empty()) - { - try - { - // found an already existing control point - LinkDataToControlPoint(dataNode, existingControlPoint, false); - } - catch (const SemanticRelationException&) - { - mitkThrowException(SemanticRelationException) << "The data can not be linked. Inconsistency in the semantic relations storage assumed."; - } - } - else - { - try - { - AddControlPointAndLinkData(dataNode, 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 = GetAllExaminationPeriodsOfCase(caseID); - SemanticTypes::ExaminationPeriod examinationPeriod = FindExaminationPeriod(closestControlPoint, allExaminationPeriods); - if (examinationPeriod.UID.empty()) - { - // no closest control point (exceed threshold) or no examination period found - // create a new examination period for this control point and add it to the storage - examinationPeriod.UID = UIDGeneratorBoost::GenerateUID(); - examinationPeriod.name = "New examination period " + std::to_string(allExaminationPeriods.size()); - AddExaminationPeriod(caseID, examinationPeriod); - } - - // add the control point to the (newly created or found / close) examination period - AddControlPointToExaminationPeriod(caseID, controlPoint, examinationPeriod); - } - catch (const SemanticRelationException&) - { - mitkThrowException(SemanticRelationException) << "The data can not be linked. Inconsistency in the semantic relations storage assumed."; - } - } - - ClearControlPoints(caseID); - NotifyObserver(caseID); -} - -void mitk::SemanticRelations::AddControlPointAndLinkData(const DataNode* dataNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence) -{ - if (nullptr == dataNode) - { - mitkThrowException(SemanticRelationException) << "Not a valid data node."; - } - - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); - if (InstanceExists(caseID, controlPoint)) - { - mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to add already exists for the given case. \n Use 'LinkDataToControlPoint' instead."; - } - - RelationStorage::AddControlPoint(caseID, controlPoint); - LinkDataToControlPoint(dataNode, controlPoint, checkConsistence); -} - -void mitk::SemanticRelations::LinkDataToControlPoint(const DataNode* dataNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence) -{ - if (nullptr == dataNode) - { - mitkThrowException(SemanticRelationException) << "Not a valid data node."; - } - - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); - if (InstanceExists(caseID, controlPoint)) - { - SemanticTypes::ID dataID = GetIDFromDataNode(dataNode); - RelationStorage::LinkDataToControlPoint(caseID, dataID, controlPoint); - } - else - { - mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to link does not exist for the given case."; - } -} - -void mitk::SemanticRelations::UnlinkDataFromControlPoint(const DataNode* dataNode) -{ - if (nullptr == dataNode) - { - mitkThrowException(SemanticRelationException) << "Not a valid data node."; - } - - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); - SemanticTypes::ID dataID = GetIDFromDataNode(dataNode); - SemanticTypes::ControlPoint controlPoint = RelationStorage::GetControlPointOfImage(caseID, dataID); - RelationStorage::UnlinkDataFromControlPoint(caseID, dataID); - ClearControlPoints(caseID); -} - -void mitk::SemanticRelations::AddExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) -{ - if (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::SemanticRelations::AddControlPointToExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod& examinationPeriod) -{ - if (!InstanceExists(caseID, controlPoint)) - { - mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to add does not exist for the given case."; - } - - if (!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::SemanticRelations::SetInformationType(const DataNode* imageNode, const SemanticTypes::InformationType& informationType) -{ - RemoveInformationTypeFromImage(imageNode); - AddInformationTypeToImage(imageNode, informationType); - - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); - NotifyObserver(caseID); -} - -void mitk::SemanticRelations::AddInformationTypeToImage(const DataNode* imageNode, const SemanticTypes::InformationType& informationType) -{ - if (nullptr == imageNode) - { - mitkThrowException(SemanticRelationException) << "Not a valid image data node."; - } - - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); - SemanticTypes::ID imageID = GetIDFromDataNode(imageNode); - RelationStorage::AddInformationTypeToImage(caseID, imageID, informationType); -} - -void mitk::SemanticRelations::RemoveInformationTypeFromImage(const DataNode* imageNode) -{ - if (nullptr == imageNode) - { - mitkThrowException(SemanticRelationException) << "Not a valid image data node."; - } - - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); - SemanticTypes::ID imageID = GetIDFromDataNode(imageNode); - SemanticTypes::InformationType originalInformationType = RelationStorage::GetInformationTypeOfImage(caseID, imageID); - RelationStorage::RemoveInformationTypeFromImage(caseID, imageID); - - // check for further references to the removed information type - std::vector allImageIDsVectorValue = RelationStorage::GetAllImageIDsOfCase(caseID); - for (const auto otherImageID : allImageIDsVectorValue) - { - SemanticTypes::InformationType otherInformationType = RelationStorage::GetInformationTypeOfImage(caseID, otherImageID); - if (otherInformationType == originalInformationType) - { - // found the information type in another image -> cannot remove the information type from the case - return; - } - } - - // given information type was not referred by any other image of the case -> the information type can be removed from the case - RelationStorage::RemoveInformationTypeFromCase(caseID, originalInformationType); -} - -/************************************************************************/ -/* private functions */ -/************************************************************************/ -void mitk::SemanticRelations::NotifyObserver(const SemanticTypes::CaseID& caseID) const -{ - for (auto& observer : m_ObserverVector) - { - observer->Update(caseID); - } -} - -bool mitk::SemanticRelations::ControlPointContainsLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ControlPoint& controlPoint) const -{ - DataNodeVector allImagesOfLesion; - try - { - allImagesOfLesion = GetAllImagesOfLesion(caseID, lesion); - } - catch (const SemanticRelationException&) - { - // error retrieving image data; lesion has to be outside the control point - return false; - } - - DataNodeVector allImagesOfControlPoint; - try - { - allImagesOfControlPoint = GetAllImagesOfControlPoint(caseID, controlPoint); - } - catch (const SemanticRelationException&) - { - // error retrieving control point data; lesion has to be outside the control point - return false; - } - - std::sort(allImagesOfLesion.begin(), allImagesOfLesion.end()); - std::sort(allImagesOfControlPoint.begin(), allImagesOfControlPoint.end()); - DataNodeVector allImagesIntersection; - // set intersection removes duplicated nodes, since 'GetAllImagesOfControlPoint' only contains at most one of each node - std::set_intersection(allImagesOfLesion.begin(), allImagesOfLesion.end(), - allImagesOfControlPoint.begin(), allImagesOfControlPoint.end(), - std::back_inserter(allImagesIntersection)); - - // if the vector of intersecting data is empty, the control point does not contain the lesion - return !allImagesIntersection.empty(); -} - -bool mitk::SemanticRelations::ControlPointContainsInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType, const SemanticTypes::ControlPoint& controlPoint) const -{ - DataNodeVector allImagesIntersection = GetAllSpecificImages(caseID, controlPoint, informationType); - return !allImagesIntersection.empty(); -} - -void mitk::SemanticRelations::ClearControlPoints(const SemanticTypes::CaseID& caseID) -{ - SemanticTypes::ControlPointVector allControlPointsOfCase = GetAllControlPointsOfCase(caseID); - - DataNodeVector allImagesOfCase; - try - { - allImagesOfCase = GetAllImagesOfCase(caseID); - } - catch (const SemanticRelationException&) - { - // error retrieving image data - return; - } - - SemanticTypes::ControlPointVector referencedControlPoints; - for (const auto& image : allImagesOfCase) - { - try - { - referencedControlPoints.push_back(GetControlPointOfData(image)); - } - catch (const SemanticRelationException&) - { - // error retrieving control point of data - continue; - } - } - - 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 = GetAllExaminationPeriodsOfCase(caseID); - for (const auto& controlPoint : nonReferencedControlPoints) - { - const auto& examinationPeriod = FindExaminationPeriod(controlPoint, allExaminationPeriods); - RelationStorage::RemoveControlPointFromExaminationPeriod(caseID, controlPoint, examinationPeriod); - RelationStorage::RemoveControlPointFromCase(caseID, controlPoint); - } -}