diff --git a/Modules/SemanticRelations/include/mitkRelationStorage.h b/Modules/SemanticRelations/include/mitkRelationStorage.h index 73fd14a149..fc32e13c82 100644 --- a/Modules/SemanticRelations/include/mitkRelationStorage.h +++ b/Modules/SemanticRelations/include/mitkRelationStorage.h @@ -1,88 +1,73 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKRELATIONSTORAGE_H #define MITKRELATIONSTORAGE_H // semantic relations module -#include "mitkDICOMHelper.h" #include "mitkSemanticTypes.h" -// mitk core -#include - namespace mitk { - class RelationStorage + namespace RelationStorage { - public: - SemanticTypes::LesionVector GetAllLesionsOfCase(const SemanticTypes::CaseID& caseID); SemanticTypes::Lesion GetRepresentedLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID); - std::vector GetAllSegmentationIDsOfCase(const SemanticTypes::CaseID& caseID); + SemanticTypes::IDVector GetAllSegmentationIDsOfCase(const SemanticTypes::CaseID& caseID); SemanticTypes::ControlPoint GetControlPointOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID); SemanticTypes::ControlPointVector GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID); SemanticTypes::ExaminationPeriodVector GetAllExaminationPeriodsOfCase(const SemanticTypes::CaseID& caseID); SemanticTypes::InformationType GetInformationTypeOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID); SemanticTypes::InformationTypeVector GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID); - std::vector GetAllImageIDsOfCase(const SemanticTypes::CaseID& caseID); + SemanticTypes::IDVector GetAllImageIDsOfCase(const SemanticTypes::CaseID& caseID); std::vector GetAllCaseIDs(); void AddCase(const SemanticTypes::CaseID& caseID); void AddImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageNodeID); void RemoveImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageNodeID); void AddSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationNodeID, const SemanticTypes::ID& parentDataNodeID); void RemoveSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationNodeID); 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 LinkDataToControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& dataNodeID, const SemanticTypes::ControlPoint& controlPoint); void UnlinkDataFromControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& dataNodeID); void RemoveControlPointFromCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); void AddExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod); void AddControlPointToExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod examinationPeriod); void RemoveControlPointFromExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod examinationPeriod); void RemoveExaminationPeriodFromCase(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 RemoveInformationTypeFromCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType informationType); - private: - - // access the storage and retrieve the case data, stored under the given case ID - PropertyList::Pointer GetStorageData(const SemanticTypes::CaseID& caseID); - - SemanticTypes::Lesion GenerateLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& lesionID); - - SemanticTypes::ControlPoint GenerateControlpoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& controlPointUID); - }; - + } // namespace RelationStorage } // namespace mitk #endif // MITKRELATIONSTORAGE_H diff --git a/Modules/SemanticRelations/include/mitkSemanticRelations.h b/Modules/SemanticRelations/include/mitkSemanticRelations.h index 0c1bae27db..b4b017be56 100644 --- a/Modules/SemanticRelations/include/mitkSemanticRelations.h +++ b/Modules/SemanticRelations/include/mitkSemanticRelations.h @@ -1,650 +1,647 @@ /*=================================================================== 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 "mitkRelationStorage.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: - // the relation storage serves as a storage accessor and can be sub-classed for custom demands - std::shared_ptr m_RelationStorage; 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/include/mitkSemanticRelationsInference.h b/Modules/SemanticRelations/include/mitkSemanticRelationsInference.h new file mode 100644 index 0000000000..857caff09e --- /dev/null +++ b/Modules/SemanticRelations/include/mitkSemanticRelationsInference.h @@ -0,0 +1,278 @@ +/*=================================================================== + +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 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. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionVector GetAllLesionsOfCase(const SemanticTypes::CaseID& caseID); + /** + * @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 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. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::Lesion GetRepresentedLesion(const DataNode* segmentationNode); + /** + * @brief Returns the lesion that is defined by the segmentation identified by the segmentation ID. + * + * @throw SemanticRelationException, if the segmentation does not represent an existing lesion (this can be checked via 'IsRepresentingALesion'). + * + * @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 The represented lesion. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::Lesion GetRepresentedLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID); + /** + * @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 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 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 '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. + */ + MITKSEMANTICRELATIONS_EXPORT bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); + /** + * @brief Return a vector of all segmentation IDs that identify 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 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 segmentations that define the given lesion. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::IDVector GetAllSegmentationIDsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); + /** + * @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. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPointVector GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID); + /** + * @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 the control point of a data node identified by the image ID. + * 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. + * + * @param caseID The current case identifier is defined by the given string. + * @param imageID The image node identifier is defined by the given string. + * @return The control point of the image node, defined by the image node ID. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint GetControlPointOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID); + /** + * @brief Return a vector of all image IDs that identify images that link to the given control point. + * If the control point is not referred to by any image, 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 IDs identifying images that link to the given control point. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::IDVector GetAllImageIDsOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); + /** + * @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. + */ + MITKSEMANTICRELATIONS_EXPORT bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); + /** + * @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. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ExaminationPeriodVector GetAllExaminationPeriodsOfCase(const SemanticTypes::CaseID& caseID); + /** + * @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 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. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::InformationTypeVector GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID); + /** + * @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 the information type of the given image. + * If the image does not contain any information type, an empty information type is returned. + * + * @param caseID The current case identifier is defined by the given string. + * @param imageID The image node identifier is defined by the given string. + * @return The information type of the image node, defined by the image node ID. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::InformationType GetInformationTypeOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID); + /** + * @brief Return a vector of all image IDs that identify images that are of the given information type. + * If the information type is not used by any image, an empty vector is returned. + * + * @pre The information type has to exist. + * @throw SemanticRelationException, if the information type does not exist (this can be checked via 'InstanceExists'). + * + * @param caseID The current case identifier is defined by the given string. + * @param informationType An information type. + * @return A vector of IDs identifying images that are of the given information type. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::IDVector GetAllImageIDsOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType); + /** + * @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. + */ + MITKSEMANTICRELATIONS_EXPORT bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType); + /** + * @brief Return a vector of all CaseIDs that are currently available. + * + * @return A vector of CaseIDs as strings. + */ + MITKSEMANTICRELATIONS_EXPORT std::vector GetAllCaseIDs(); + /** + * @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 SemanticRelationsInference +} // namespace mitk + +#endif // MITKSEMANTICRELATIONSINFERENCE_H diff --git a/Modules/SemanticRelations/include/mitkSemanticRelationsIntegration.h b/Modules/SemanticRelations/include/mitkSemanticRelationsIntegration.h new file mode 100644 index 0000000000..ae65f4309c --- /dev/null +++ b/Modules/SemanticRelations/include/mitkSemanticRelationsIntegration.h @@ -0,0 +1,311 @@ +/*=================================================================== + +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 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 UnlinkImageFromControlPoint(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: + /** + * @brief A vector that stores the currently registered observer of this observable subject. + */ + static std::vector m_ObserverVector; + /** + * @brief The class notifies (updates) the observer with a given case ID. + * The view's caseID was set before in the GUI. The parts of the view that observe changes in the semantic relations are only updated, + * if the given case ID is equal to the observer's current caseID and thus the observer currently shows the semantic information of the given case. + * + * @param caseID The caseID that identifies the currently active patient / case. + */ + virtual void NotifyObserver(const mitk::SemanticTypes::CaseID& caseID) const override; + /** + * @brief Remove all control points from the storage that are not referenced by any image anymore. + * This might happen if an image has been removed (and unlinked from the corresponding control point) + * or if the user sets a new control point for an image manually in the GUI. + * + * @param caseID The current case identifier is defined by the given string. + */ + void ClearControlPoints(const SemanticTypes::CaseID& caseID); + + }; +} // namespace mitk + +#endif // MITKSEMANTICRELATIONSINTEGRATION_H diff --git a/Modules/SemanticRelations/include/mitkSemanticTypes.h b/Modules/SemanticRelations/include/mitkSemanticTypes.h index 4cd7b61824..2f12a09fff 100644 --- a/Modules/SemanticRelations/include/mitkSemanticTypes.h +++ b/Modules/SemanticRelations/include/mitkSemanticTypes.h @@ -1,128 +1,129 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKSEMANTICTYPES_H #define MITKSEMANTICTYPES_H #define BOOST_DATE_TIME_NO_LIB #if defined(BOOST_ALL_DYN_LINK) #undef BOOST_ALL_DYN_LINK #endif // boost #include // c++ #include #include #include namespace mitk { namespace SemanticTypes { using ID = std::string; using CaseID = std::string; // an ID of the current case (e.g. the DICOM PatientID) using InformationType = std::string; /* * @brief The concept of a control point. */ struct ControlPoint { ID UID; boost::gregorian::date date; ControlPoint() { date = boost::gregorian::date(boost::gregorian::min_date_time); } // less comparison to sort containers of control points bool operator<(const ControlPoint& other) const { return date < other.date; } std::string ToString() const { std::stringstream controlPointAsString; if (date.is_not_a_date()) { return ""; } controlPointAsString << std::to_string(date.year()) << "-" << std::setfill('0') << std::setw(2) << std::to_string(date.month()) << "-" << std::setfill('0') << std::setw(2) << std::to_string(date.day()); return controlPointAsString.str(); } void SetDateFromString(const std::string& dateAsString) { date = boost::gregorian::from_undelimited_string(dateAsString); } int DistanceInDays(const ControlPoint& other) const { boost::gregorian::date_duration duration = date - other.date; return std::abs(duration.days()); } }; /** * @brief The concept of an examination period. * An examination period holds a vector of control point UIDs. * The semantic relation storage stores the UIDs such that * the represented control points are in chronological order. */ struct ExaminationPeriod { ID UID; std::string name = ""; std::vector controlPointUIDs; }; /* * @brief The concept of a lesion class. */ struct LesionClass { ID UID; std::string classType = ""; }; /* * @brief The concept of a lesion. */ struct Lesion { ID UID; std::string name = ""; LesionClass lesionClass; }; + using IDVector = std::vector; using LesionVector = std::vector; using LesionClassVector = std::vector; using ControlPointVector = std::vector; using ExaminationPeriodVector = std::vector; using InformationTypeVector = std::vector; } // namespace SemanticTypes } // namespace mitk #endif // MITKSEMANTICTYPES_H diff --git a/Modules/SemanticRelations/src/mitkRelationStorage.cpp b/Modules/SemanticRelations/src/mitkRelationStorage.cpp index fa0aced1d7..2a4327543a 100644 --- a/Modules/SemanticRelations/src/mitkRelationStorage.cpp +++ b/Modules/SemanticRelations/src/mitkRelationStorage.cpp @@ -1,1290 +1,1294 @@ /*=================================================================== 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 "mitkRelationStorage.h" -#include "mitkNodePredicates.h" + +// semantic relations module +#include "mitkDICOMHelper.h" // multi label module #include // mitk core #include #include // c++ #include #include -std::vector mitk::RelationStorage::GetAllLesionsOfCase(const SemanticTypes::CaseID& caseID) +namespace +{ + 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 property list is valid for a whole case and contains all the properties for the current case + // the persistence service may create a new property list with the given ID, if no property list is found + return persistenceService->GetPropertyList(const_cast(caseID)); + } + + 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 controlPointVectorValue = controlPointVectorProperty->GetValue(); + // a control point has to have exactly three integer values (year, month and day) + if (controlPointVectorValue.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(controlPointVectorValue[0], + controlPointVectorValue[1], + controlPointVectorValue[2]); + + return generatedControlPoint; + } +} + +mitk::SemanticTypes::LesionVector mitk::RelationStorage::GetAllLesionsOfCase(const SemanticTypes::CaseID& caseID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { - MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; - return std::vector(); + 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* vectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); if (nullptr == vectorProperty) { - MITK_INFO << "Could not find any lesion in the storage."; - return std::vector(); + MITK_DEBUG << "Could not find any lesion in the storage."; + return SemanticTypes::LesionVector(); } std::vector vectorValue = vectorProperty->GetValue(); - std::vector allLesionsOfCase; + SemanticTypes::LesionVector allLesionsOfCase; for (const auto& lesionID : vectorValue) { SemanticTypes::Lesion generatedLesion = GenerateLesion(caseID, lesionID); if (!generatedLesion.UID.empty()) { allLesionsOfCase.push_back(generatedLesion); } } return allLesionsOfCase; } mitk::SemanticTypes::Lesion mitk::RelationStorage::GetRepresentedLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { - MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + 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_INFO << "Could not find the segmentation node " << segmentationID << " in the storage."; + MITK_DEBUG << "Could not find the segmentation node " << segmentationID << " in the storage."; return SemanticTypes::Lesion(); } std::vector segmentationVectorValue = segmentationVectorProperty->GetValue(); // the lesion ID of a segmentation is the second value in the vector if (segmentationVectorValue.size() != 2) { - MITK_INFO << "Incorrect segmentation storage. Not two (2) IDs stored."; + MITK_DEBUG << "Incorrect segmentation storage. Not two (2) IDs stored."; return SemanticTypes::Lesion(); } std::string lesionID = segmentationVectorValue[1]; if (lesionID.empty()) { // segmentation does not refer to any lesion; return empty lesion return SemanticTypes::Lesion(); } return GenerateLesion(caseID, lesionID); } -std::vector mitk::RelationStorage::GetAllSegmentationIDsOfCase(const SemanticTypes::CaseID& caseID) +mitk::SemanticTypes::IDVector mitk::RelationStorage::GetAllSegmentationIDsOfCase(const SemanticTypes::CaseID& caseID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { - MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; - return std::vector(); + 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* allSegmentationsVectorProperty = dynamic_cast*>(propertyList->GetProperty("segmentations")); if (nullptr == allSegmentationsVectorProperty) { - MITK_INFO << "Could not find any segmentation in the storage."; - return std::vector(); + MITK_DEBUG << "Could not find any segmentation in the storage."; + return SemanticTypes::IDVector(); } return allSegmentationsVectorProperty->GetValue(); } mitk::SemanticTypes::ControlPoint mitk::RelationStorage::GetControlPointOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { - MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + 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* dataNodeVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); if (nullptr == dataNodeVectorProperty) { - MITK_INFO << "Could not find the data node " << imageID << " in the storage."; + MITK_DEBUG << "Could not find the data node " << imageID << " in the storage."; return SemanticTypes::ControlPoint(); } std::vector dataNodeVectorValue = dataNodeVectorProperty->GetValue(); SemanticTypes::ControlPoint controlPoint; // an image node has to have exactly two values (the information type and the ID of the control point) if (dataNodeVectorValue.size() != 2) { - MITK_INFO << "Incorrect data storage. Not two (2) values stored."; + MITK_DEBUG << "Incorrect data storage. Not two (2) values stored."; return SemanticTypes::ControlPoint(); } // the second value of the data node vector is the ID of the referenced control point std::string controlPointID = dataNodeVectorValue[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_INFO << "Could not find the control point " << controlPointID << " in the storage."; + MITK_DEBUG << "Could not find the control point " << controlPointID << " in the storage."; return SemanticTypes::ControlPoint(); } std::vector controlPointVectorValue = controlPointVectorProperty->GetValue(); // a control point has to have exactly three integer values (year, month and day) if (controlPointVectorValue.size() != 3) { - MITK_INFO << "Incorrect control point storage. Not three (3) values of the date are stored."; + 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(controlPointVectorValue[0], controlPointVectorValue[1], controlPointVectorValue[2]); return controlPoint; } -std::vector mitk::RelationStorage::GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID) +mitk::SemanticTypes::ControlPointVector mitk::RelationStorage::GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { - MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; - return std::vector(); + 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* vectorProperty = dynamic_cast*>(propertyList->GetProperty("controlpoints")); if (nullptr == vectorProperty) { - MITK_INFO << "Could not find any control points in the storage."; - return std::vector(); + MITK_DEBUG << "Could not find any control points in the storage."; + return SemanticTypes::ControlPointVector(); } std::vector vectorValue = vectorProperty->GetValue(); - std::vector allControlPointsOfCase; + SemanticTypes::ControlPointVector allControlPointsOfCase; for (const auto& controlPointUID : vectorValue) { SemanticTypes::ControlPoint generatedControlPoint = GenerateControlpoint(caseID, controlPointUID); if (!generatedControlPoint.UID.empty()) { allControlPointsOfCase.push_back(generatedControlPoint); } } return allControlPointsOfCase; } -std::vector mitk::RelationStorage::GetAllExaminationPeriodsOfCase(const SemanticTypes::CaseID& caseID) +mitk::SemanticTypes::ExaminationPeriodVector mitk::RelationStorage::GetAllExaminationPeriodsOfCase(const SemanticTypes::CaseID& caseID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { - MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; - return std::vector(); + 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 vectorProperty = dynamic_cast*>(propertyList->GetProperty("examinationperiods")); if (nullptr == vectorProperty) { - MITK_INFO << "Could not find any examination periods in the storage."; - return std::vector(); + MITK_DEBUG << "Could not find any examination periods in the storage."; + return SemanticTypes::ExaminationPeriodVector(); } std::vector vectorValue = vectorProperty->GetValue(); - std::vector allExaminationPeriods; + SemanticTypes::ExaminationPeriodVector allExaminationPeriods; for (const auto& examinationPeriodID : vectorValue) { // retrieve a vector property that contains the represented control point-IDs VectorProperty::Pointer examinationPeriodVectorProperty = dynamic_cast*>(propertyList->GetProperty(examinationPeriodID)); if (nullptr == examinationPeriodVectorProperty) { - MITK_INFO << "Could not find the examination period " << examinationPeriodID << " in the storage."; + MITK_DEBUG << "Could not find the examination period " << examinationPeriodID << " in the storage."; continue; } std::vector examinationPeriodVectorValue = examinationPeriodVectorProperty->GetValue(); // an examination period has an arbitrary number of vector values (name and control point UIDs) (at least one for the name) if (examinationPeriodVectorValue.empty()) { - MITK_INFO << "Incorrect examination period storage. At least one (1) value for the examination period name has to be stored."; + 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 = examinationPeriodVectorValue[0]; for (int i = 1; i < examinationPeriodVectorValue.size(); ++i) { generatedExaminationPeriod.controlPointUIDs.push_back(examinationPeriodVectorValue[i]); } allExaminationPeriods.push_back(generatedExaminationPeriod); } } return allExaminationPeriods; } mitk::SemanticTypes::InformationType mitk::RelationStorage::GetInformationTypeOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { - MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + 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 data node (0. information type 1. control point ID) VectorProperty* dataNodeVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); if (nullptr == dataNodeVectorProperty) { - MITK_INFO << "Could not find the image " << imageID << " in the storage."; + MITK_DEBUG << "Could not find the image " << imageID << " in the storage."; return SemanticTypes::InformationType(); } std::vector dataNodeVectorValue = dataNodeVectorProperty->GetValue(); // an image node has to have exactly two values (the information type and the ID of the control point) if (dataNodeVectorValue.size() != 2) { - MITK_INFO << "Incorrect data storage. Not two (2) values stored."; + MITK_DEBUG << "Incorrect data storage. Not two (2) values stored."; return SemanticTypes::InformationType(); } // the first value of the data node vector is the information type return dataNodeVectorValue[0]; } -std::vector mitk::RelationStorage::GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID) +mitk::SemanticTypes::InformationTypeVector mitk::RelationStorage::GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { - MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; - return std::vector(); + 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* informationTypeVectorProperty = dynamic_cast*>(propertyList->GetProperty("informationtypes")); if (nullptr == informationTypeVectorProperty) { - MITK_INFO << "Could not find any information types in the storage."; - return std::vector(); + MITK_DEBUG << "Could not find any information types in the storage."; + return SemanticTypes::InformationTypeVector(); } return informationTypeVectorProperty->GetValue(); } -std::vector mitk::RelationStorage::GetAllImageIDsOfCase(const SemanticTypes::CaseID& caseID) +mitk::SemanticTypes::IDVector mitk::RelationStorage::GetAllImageIDsOfCase(const SemanticTypes::CaseID& caseID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { - MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; - return std::vector(); + 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 image-IDs of the current case VectorProperty* allImagesVectorProperty = dynamic_cast*>(propertyList->GetProperty("images")); if (nullptr == allImagesVectorProperty) { - MITK_INFO << "Could not find any image in the storage."; - return std::vector(); + MITK_DEBUG << "Could not find any image in the storage."; + return SemanticTypes::InformationTypeVector(); } return allImagesVectorProperty->GetValue(); } std::vector mitk::RelationStorage::GetAllCaseIDs() { PERSISTENCE_GET_SERVICE_MACRO if (nullptr == persistenceService) { - MITK_INFO << "Persistence service could not be loaded"; + 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"; PropertyList::Pointer propertyList = persistenceService->GetPropertyList(listIdentifier); if (nullptr == propertyList) { - MITK_INFO << "Could not find the property list " << listIdentifier << " for the current MITK workbench / session."; + 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 VectorProperty* caseIDsVectorProperty = dynamic_cast*>(propertyList->GetProperty(listIdentifier)); if (nullptr == caseIDsVectorProperty) { - MITK_INFO << "Could not find the property " << listIdentifier << " for the " << listIdentifier << " property list."; + MITK_DEBUG << "Could not find the property " << listIdentifier << " for the " << listIdentifier << " property list."; return std::vector(); } return caseIDsVectorProperty->GetValue(); } void mitk::RelationStorage::AddCase(const SemanticTypes::CaseID& caseID) { PERSISTENCE_GET_SERVICE_MACRO if (nullptr == persistenceService) { - MITK_INFO << "Persistence service could not be loaded"; + 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_INFO << "Could not find the property list " << listIdentifier << " for the current MITK workbench / session."; + 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 caseIDsVectorValue; if (nullptr == caseIDsVectorProperty) { caseIDsVectorProperty = VectorProperty::New(); } else { caseIDsVectorValue = caseIDsVectorProperty->GetValue(); } auto existingCase = std::find(caseIDsVectorValue.begin(), caseIDsVectorValue.end(), caseID); if (existingCase != caseIDsVectorValue.end()) { return; } // add case to the "caseIDs" property list caseIDsVectorValue.push_back(caseID); caseIDsVectorProperty->SetValue(caseIDsVectorValue); propertyList->SetProperty(listIdentifier, caseIDsVectorProperty); } void mitk::RelationStorage::AddImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageNodeID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { - MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + 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 allImagesVectorProperty = dynamic_cast*>(propertyList->GetProperty("images")); std::vector allImagesIDs; if (nullptr == allImagesVectorProperty) { allImagesVectorProperty = VectorProperty::New(); } else { allImagesIDs = allImagesVectorProperty->GetValue(); } auto existingImage = std::find(allImagesIDs.begin(), allImagesIDs.end(), imageNodeID); if (existingImage != allImagesIDs.end()) { return; } // add image to the "images" property list allImagesIDs.push_back(imageNodeID); allImagesVectorProperty->SetValue(allImagesIDs); propertyList->SetProperty("images", allImagesVectorProperty); // add the image itself VectorProperty::Pointer imageNodeVectorProperty = VectorProperty::New(); // an image node has to have exactly two values (the information type and the ID of the control point) std::vector imageNodeVectorValue(2); imageNodeVectorProperty->SetValue(imageNodeVectorValue); propertyList->SetProperty(imageNodeID, imageNodeVectorProperty); } void mitk::RelationStorage::RemoveImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageNodeID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { - MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + 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 allImagesVectorProperty = dynamic_cast*>(propertyList->GetProperty("images")); if (nullptr == allImagesVectorProperty) { - MITK_INFO << "Could not find any images in the storage."; + 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 allImagesIDs = allImagesVectorProperty->GetValue(); allImagesIDs.erase(std::remove(allImagesIDs.begin(), allImagesIDs.end(), imageNodeID), allImagesIDs.end()); if (allImagesIDs.empty()) { // no more images stored -> remove the images property list propertyList->DeleteProperty("images"); } else { // or store the modified vector value allImagesVectorProperty->SetValue(allImagesIDs); } // remove the image instance itself propertyList->DeleteProperty(imageNodeID); } void mitk::RelationStorage::AddSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationNodeID, const SemanticTypes::ID& parentNodeID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { - MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + 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 allSegmentationsVectorProperty = dynamic_cast*>(propertyList->GetProperty("segmentations")); std::vector allSegmentationsIDs; if (nullptr == allSegmentationsVectorProperty) { allSegmentationsVectorProperty = VectorProperty::New(); } else { allSegmentationsIDs = allSegmentationsVectorProperty->GetValue(); } auto existingImage = std::find(allSegmentationsIDs.begin(), allSegmentationsIDs.end(), segmentationNodeID); if (existingImage != allSegmentationsIDs.end()) { return; } // add segmentation to the "segmentations" property list allSegmentationsIDs.push_back(segmentationNodeID); allSegmentationsVectorProperty->SetValue(allSegmentationsIDs); propertyList->SetProperty("segmentations", allSegmentationsVectorProperty); // add the segmentation itself VectorProperty::Pointer segmentationNodeVectorProperty = VectorProperty::New(); // a segmentation node has to have exactly two values (the ID of the referenced image and the ID of the referenced lesion) std::vector segmentationNodeVectorValue(2); segmentationNodeVectorValue[0] = parentNodeID; segmentationNodeVectorProperty->SetValue(segmentationNodeVectorValue); propertyList->SetProperty(segmentationNodeID, segmentationNodeVectorProperty); } void mitk::RelationStorage::RemoveSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationNodeID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { - MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + 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 allSegmentationsVectorProperty = dynamic_cast*>(propertyList->GetProperty("segmentations")); if (nullptr == allSegmentationsVectorProperty) { - MITK_INFO << "Could not find any segmentation in the storage."; + 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 allSegmentationsIDs = allSegmentationsVectorProperty->GetValue(); allSegmentationsIDs.erase(std::remove(allSegmentationsIDs.begin(), allSegmentationsIDs.end(), segmentationNodeID), allSegmentationsIDs.end()); if (allSegmentationsIDs.empty()) { // no more segmentations stored -> remove the segmentations property list propertyList->DeleteProperty("segmentations"); } else { // or store the modified vector value allSegmentationsVectorProperty->SetValue(allSegmentationsIDs); } // remove the lesion instance itself propertyList->DeleteProperty(segmentationNodeID); } void mitk::RelationStorage::AddLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { - MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + 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 lesionsVectorValue; if (nullptr == lesionsVectorProperty) { lesionsVectorProperty = VectorProperty::New(); } else { lesionsVectorValue = lesionsVectorProperty->GetValue(); } const auto& existingIndex = std::find(lesionsVectorValue.begin(), lesionsVectorValue.end(), lesion.UID); if (existingIndex != lesionsVectorValue.end()) { return; } // add the new lesion id from the given lesion to the vector of all current lesion IDs lesionsVectorValue.push_back(lesion.UID); // overwrite the current vector property with the new, extended string vector lesionsVectorProperty->SetValue(lesionsVectorValue); 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_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + 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_INFO << "Could not find any lesion in the storage."; + MITK_DEBUG << "Could not find any lesion in the storage."; return; } std::vector lesionVectorValue = lesionVectorProperty->GetValue(); const auto existingLesion = std::find(lesionVectorValue.begin(), lesionVectorValue.end(), lesion.UID); if (existingLesion != lesionVectorValue.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_INFO << "Could not find lesion " << lesion.UID << " in the storage. Cannot overwrite the lesion."; + 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_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + 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_INFO << "Could not find any lesion property in the storage."; + MITK_DEBUG << "Could not find any lesion property in the storage."; return; } std::vector lesionVectorValue = lesionVectorProperty->GetValue(); const auto existingLesion = std::find(lesionVectorValue.begin(), lesionVectorValue.end(), lesion.UID); if (existingLesion != lesionVectorValue.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_INFO << "Could not find the segmentation node " << segmentationID << " in the storage. Cannot link segmentation to lesion."; + MITK_DEBUG << "Could not find the segmentation node " << segmentationID << " in the storage. Cannot link segmentation to lesion."; return; } std::vector segmentationVectorValue = segmentationVectorProperty->GetValue(); if (segmentationVectorValue.size() != 2) { - MITK_INFO << "Incorrect segmentation storage. Not two (2) IDs stored."; + MITK_DEBUG << "Incorrect segmentation storage. Not two (2) IDs stored."; return; } // the lesion ID of a segmentation is the second value in the vector segmentationVectorValue[1] = lesion.UID; segmentationVectorProperty->SetValue(segmentationVectorValue); return; } - MITK_INFO << "Could not find lesion " << lesion.UID << " in the storage. Cannot link segmentation to lesion."; + 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_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + 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_INFO << "Could not find the segmentation node " << segmentationID << " in the storage. Cannot unlink lesion from segmentation."; + MITK_DEBUG << "Could not find the segmentation node " << segmentationID << " in the storage. Cannot unlink lesion from segmentation."; return; } std::vector segmentationVectorValue = segmentationVectorProperty->GetValue(); // a segmentation has to have exactly two values (the ID of the linked image and the ID of the lesion) if (segmentationVectorValue.size() != 2) { - MITK_INFO << "Incorrect data storage. Not two (2) values stored."; + MITK_DEBUG << "Incorrect data storage. Not two (2) values stored."; return; } // the second value of the data node vector is the ID of the referenced lesion // set the lesion reference to an empty string for removal segmentationVectorValue[1] = ""; segmentationVectorProperty->SetValue(segmentationVectorValue); } void mitk::RelationStorage::RemoveLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { - MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + 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_INFO << "Could not find any lesion property in the storage."; + MITK_DEBUG << "Could not find any lesion property in the storage."; return; } // remove the lesion reference from the list of all lesions of the current case std::vector lesionVectorValue = lesionVectorProperty->GetValue(); lesionVectorValue.erase(std::remove(lesionVectorValue.begin(), lesionVectorValue.end(), lesion.UID), lesionVectorValue.end()); if (lesionVectorValue.empty()) { // no more lesions stored -> remove the lesions property list propertyList->DeleteProperty("lesions"); } else { // or store the modified vector value lesionVectorProperty->SetValue(lesionVectorValue); } // 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_INFO << "Lesion " << lesion.UID << " not found (already removed?). Cannot remove the lesion."; + 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_INFO << "Incorrect lesion data storage. Not two (2) strings of the lesion UID and the lesion name are stored."; + 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_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + 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_INFO << "Lesion class " << lesionClassID << " not found (already removed?). Cannot remove the lesion class."; + 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 lesionVectorValue = lesionVectorProperty->GetValue(); const auto existingLesionClass = std::find_if(lesionVectorValue.begin(), lesionVectorValue.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 == lesionVectorValue.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_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + 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 controlPointVectorProperty = dynamic_cast*>(propertyList->GetProperty("controlpoints")); std::vector controlPointVectorValue; if (nullptr == controlPointVectorProperty) { controlPointVectorProperty = VectorProperty::New(); } else { controlPointVectorValue = controlPointVectorProperty->GetValue(); } const auto existingControlPoint = std::find(controlPointVectorValue.begin(), controlPointVectorValue.end(), controlPoint.UID); if (existingControlPoint != controlPointVectorValue.end()) { return; } // add the new control point UID from the given control point to the vector of all current control point UIDs controlPointVectorValue.push_back(controlPoint.UID); // overwrite the current vector property with the new, extended string vector controlPointVectorProperty->SetValue(controlPointVectorValue); propertyList->SetProperty("controlpoints", controlPointVectorProperty); // 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::LinkDataToControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& dataNodeID, const SemanticTypes::ControlPoint& controlPoint) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { - MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + 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* controlPointVectorProperty = dynamic_cast*>(propertyList->GetProperty("controlpoints")); if (nullptr == controlPointVectorProperty) { - MITK_INFO << "Could not find any control point property in the storage."; + MITK_DEBUG << "Could not find any control point property in the storage."; return; } std::vector controlPointVectorValue = controlPointVectorProperty->GetValue(); const auto existingControlPoint = std::find(controlPointVectorValue.begin(), controlPointVectorValue.end(), controlPoint.UID); if (existingControlPoint != controlPointVectorValue.end()) { // set / overwrite the control point reference of the given data // retrieve a vector property that contains the referenced ID of a data node (0. information type 1. control point ID) VectorProperty* dataNodeVectorProperty = dynamic_cast*>(propertyList->GetProperty(dataNodeID)); if (nullptr == dataNodeVectorProperty) { - MITK_INFO << "Could not find the data node " << dataNodeID << " in the storage. Cannot link data to control point."; + MITK_DEBUG << "Could not find the data node " << dataNodeID << " in the storage. Cannot link data to control point."; return; } std::vector dataNodeVectorValue = dataNodeVectorProperty->GetValue(); // an image node has to have exactly two values (the information type and the ID of the control point) if (dataNodeVectorValue.size() != 2) { - MITK_INFO << "Incorrect data storage. Not two (2) values stored."; + MITK_DEBUG << "Incorrect data storage. Not two (2) values stored."; return; } // the second value of the data node vector is the ID of the referenced control point dataNodeVectorValue[1] = controlPoint.UID; dataNodeVectorProperty->SetValue(dataNodeVectorValue); return; } - MITK_INFO << "Could not find control point " << controlPoint.UID << " in the storage. Cannot link data to control point."; + MITK_DEBUG << "Could not find control point " << controlPoint.UID << " in the storage. Cannot link data to control point."; } void mitk::RelationStorage::UnlinkDataFromControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& dataNodeID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { - MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + 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* dataNodeVectorProperty = dynamic_cast*>(propertyList->GetProperty(dataNodeID)); if (nullptr == dataNodeVectorProperty) { - MITK_INFO << "Could not find the date " << dataNodeID << " in the storage. Cannot unlink control point from date."; + MITK_DEBUG << "Could not find the date " << dataNodeID << " in the storage. Cannot unlink control point from date."; return; } std::vector dataNodeVectorValue = dataNodeVectorProperty->GetValue(); // a data node has to have exactly two values (the information type and the ID of the control point) if (dataNodeVectorValue.size() != 2) { - MITK_INFO << "Incorrect data storage. Not two (2) values stored."; + MITK_DEBUG << "Incorrect data storage. Not two (2) values stored."; return; } // the second value of the data node vector is the ID of the referenced control point // set the control point reference to an empty string for removal dataNodeVectorValue[1] = ""; dataNodeVectorProperty->SetValue(dataNodeVectorValue); } void mitk::RelationStorage::RemoveControlPointFromCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { - MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + 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* allControlPointsVectorProperty = dynamic_cast*>(propertyList->GetProperty("controlpoints")); if (nullptr == allControlPointsVectorProperty) { - MITK_INFO << "Could not find any control point property in the storage."; + MITK_DEBUG << "Could not find any control point property in the storage."; return; } // remove the control point reference from the list of all control points of the current case std::vector currentControlPointVectorValue = allControlPointsVectorProperty->GetValue(); currentControlPointVectorValue.erase(std::remove(currentControlPointVectorValue.begin(), currentControlPointVectorValue.end(), controlPoint.UID), currentControlPointVectorValue.end()); allControlPointsVectorProperty->SetValue(currentControlPointVectorValue); // 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_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + 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 vectorProperty = dynamic_cast*>(propertyList->GetProperty("examinationperiods")); std::vector examinationPeriodsVectorValue; if (nullptr == vectorProperty) { vectorProperty = VectorProperty::New(); } else { examinationPeriodsVectorValue = vectorProperty->GetValue(); } const auto& existingIndex = std::find(examinationPeriodsVectorValue.begin(), examinationPeriodsVectorValue.end(), examinationPeriod.UID); if (existingIndex != examinationPeriodsVectorValue.end()) { return; } // add the new examination period id from the given examination period to the vector of all current examination period UIDs examinationPeriodsVectorValue.push_back(examinationPeriod.UID); // overwrite the current vector property with the new, extended string vector vectorProperty->SetValue(examinationPeriodsVectorValue); propertyList->SetProperty("examinationperiods", vectorProperty); // add the examination period with the UID as the key and the name as as the vector value std::vector examinationPeriodData; examinationPeriodData.push_back(examinationPeriod.name); VectorProperty::Pointer newExaminationPeriodVectorProperty = VectorProperty::New(); newExaminationPeriodVectorProperty->SetValue(examinationPeriodData); propertyList->SetProperty(examinationPeriod.UID, newExaminationPeriodVectorProperty); } void mitk::RelationStorage::AddControlPointToExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod examinationPeriod) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { - MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + 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_INFO << "Could not find the examination period " << examinationPeriod.UID << " in the storage. Cannot add the control point to the examination period."; + 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 controlPointUIDsVectorValue = controlPointUIDsVectorProperty->GetValue(); // store the control point UID controlPointUIDsVectorValue.push_back(controlPoint.UID); // sort the vector according to the date of the control points referenced by the UIDs - auto lambda = [&caseID, this](const SemanticTypes::ID& leftControlPointUID, const SemanticTypes::ID& rightControlPointUID) + 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(controlPointUIDsVectorValue.begin(), controlPointUIDsVectorValue.end(), lambda); // store the modified and sorted control point UID vector of this examination period controlPointUIDsVectorProperty->SetValue(controlPointUIDsVectorValue); } 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_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + 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_INFO << "Could not find examination period " << examinationPeriod.UID << " in the storage. Cannot add the control point to the examination period."; + MITK_DEBUG << "Could not find examination period " << examinationPeriod.UID << " in the storage. Cannot add the control point to the examination period."; return; } std::vector controlPointUIDsVectorValue = controlPointUIDsVectorProperty->GetValue(); // an examination period has an arbitrary number of vector values (name and control point UIDs) (at least one for the name) if (controlPointUIDsVectorValue.size() < 2) { - MITK_INFO << "Incorrect examination period storage. At least one (1) control point ID has to be stored."; + MITK_DEBUG << "Incorrect examination period storage. At least one (1) control point ID has to be stored."; return; } else { controlPointUIDsVectorValue.erase(std::remove(controlPointUIDsVectorValue.begin(), controlPointUIDsVectorValue.end(), controlPoint.UID), controlPointUIDsVectorValue.end()); if (controlPointUIDsVectorValue.size() < 2) { RemoveExaminationPeriodFromCase(caseID, examinationPeriod); } else { // store the modified vector value controlPointUIDsVectorProperty->SetValue(controlPointUIDsVectorValue); } } } void mitk::RelationStorage::RemoveExaminationPeriodFromCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod examinationPeriod) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { - MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + 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 vectorProperty = dynamic_cast*>(propertyList->GetProperty("examinationperiods")); if (nullptr == vectorProperty) { - MITK_INFO << "Could not find any examination periods in the storage."; + MITK_DEBUG << "Could not find any examination periods in the storage."; return; } std::vector examinationPeriodVectorValue = vectorProperty->GetValue(); examinationPeriodVectorValue.erase(std::remove(examinationPeriodVectorValue.begin(), examinationPeriodVectorValue.end(), examinationPeriod.UID), examinationPeriodVectorValue.end()); if (examinationPeriodVectorValue.empty()) { // no more examination periods stored -> remove the examination period property list propertyList->DeleteProperty("examinationperiods"); } else { // or store the modified vector value vectorProperty->SetValue(examinationPeriodVectorValue); } } 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_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + 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 informationTypeVectorProperty = dynamic_cast*>(propertyList->GetProperty("informationtypes")); std::vector informationTypeVectorValue; if (nullptr == informationTypeVectorProperty) { informationTypeVectorProperty = VectorProperty::New(); } else { informationTypeVectorValue = informationTypeVectorProperty->GetValue(); } const auto existingInformationType = std::find(informationTypeVectorValue.begin(), informationTypeVectorValue.end(), informationType); if (existingInformationType == informationTypeVectorValue.end()) { // at first: add the information type to the storage informationTypeVectorValue.push_back(informationType); informationTypeVectorProperty->SetValue(informationTypeVectorValue); propertyList->SetProperty("informationtypes", informationTypeVectorProperty); } // 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* imageNodeVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); if (nullptr == imageNodeVectorProperty) { - MITK_INFO << "Could not find the image " << imageID << " in the storage. Cannot add information type to image."; + MITK_DEBUG << "Could not find the image " << imageID << " in the storage. Cannot add information type to image."; return; } std::vector imageNodeVectorValue = imageNodeVectorProperty->GetValue(); // an image node has to have exactly two values (the information type and the ID of the control point) if (imageNodeVectorValue.size() != 2) { - MITK_INFO << "Incorrect data storage. Not two (2) values stored."; + MITK_DEBUG << "Incorrect data storage. Not two (2) values stored."; return; } // the first value of the data node vector is the information type imageNodeVectorValue[0] = informationType; imageNodeVectorProperty->SetValue(imageNodeVectorValue); } void mitk::RelationStorage::RemoveInformationTypeFromImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { - MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + 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* imageNodeVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); if (nullptr == imageNodeVectorProperty) { - MITK_INFO << "Could not find the image " << imageID << " in the storage. Cannot remove information type from image."; + MITK_DEBUG << "Could not find the image " << imageID << " in the storage. Cannot remove information type from image."; return; } std::vector imageNodeVectorValue = imageNodeVectorProperty->GetValue(); // an image node has to have exactly two values (the information type and the ID of the control point) if (imageNodeVectorValue.size() != 2) { - MITK_INFO << "Incorrect data storage. Not two (2) values stored."; + MITK_DEBUG << "Incorrect data storage. Not two (2) values stored."; return; } // the first value of the data node vector is the information type // set the information type to an empty string for removal imageNodeVectorValue[0] = ""; imageNodeVectorProperty->SetValue(imageNodeVectorValue); } void mitk::RelationStorage::RemoveInformationTypeFromCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType informationType) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { - MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + 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* informationTypeVectorProperty = dynamic_cast*>(propertyList->GetProperty("informationtypes")); if (nullptr == informationTypeVectorProperty) { - MITK_INFO << "Could not find any information type property in the storage."; + MITK_DEBUG << "Could not find any information type property in the storage."; return; } std::vector informationTypeVectorValue = informationTypeVectorProperty->GetValue(); informationTypeVectorValue.erase(std::remove(informationTypeVectorValue.begin(), informationTypeVectorValue.end(), informationType), informationTypeVectorValue.end()); if (informationTypeVectorValue.empty()) { // no more information types stored -> remove the information types property list propertyList->DeleteProperty("informationtypes"); } else { // or store the modified vector value informationTypeVectorProperty->SetValue(informationTypeVectorValue); } } - -mitk::PropertyList::Pointer mitk::RelationStorage::GetStorageData(const SemanticTypes::CaseID& caseID) -{ - // access the storage - PERSISTENCE_GET_SERVICE_MACRO - if (nullptr == persistenceService) - { - MITK_INFO << "Persistence service could not be loaded"; - return nullptr; - } - - // the property list is valid for a whole case and contains all the properties for the current case - // the persistence service may create a new property list with the given ID, if no property list is found - return persistenceService->GetPropertyList(const_cast(caseID)); -} - -mitk::SemanticTypes::Lesion mitk::RelationStorage::GenerateLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& lesionID) -{ - PropertyList::Pointer propertyList = GetStorageData(caseID); - if (nullptr == propertyList) - { - MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; - return SemanticTypes::Lesion(); - } - - VectorProperty* lesionDataProperty = dynamic_cast*>(propertyList->GetProperty(lesionID)); - if (nullptr == lesionDataProperty) - { - MITK_INFO << "Lesion " << lesionID << " not found. Lesion can not be retrieved."; - return 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_INFO << "Incorrect lesion data storage. Not two (2) strings of the lesion name and the lesion UID are stored."; - return SemanticTypes::Lesion(); - } - - // the lesion class ID is stored as the second property - std::string lesionClassID = lesionData[1]; - StringProperty* lesionClassProperty = dynamic_cast(propertyList->GetProperty(lesionClassID)); - if (nullptr != lesionClassProperty) - { - SemanticTypes::LesionClass generatedLesionClass; - generatedLesionClass.UID = lesionClassID; - generatedLesionClass.classType = lesionClassProperty->GetValue(); - - SemanticTypes::Lesion generatedLesion; - generatedLesion.UID = lesionID; - generatedLesion.name = lesionData[0]; - generatedLesion.lesionClass = generatedLesionClass; - - return generatedLesion; - } - - MITK_INFO << "Incorrect lesion class storage. Lesion " << lesionID << " can not be retrieved."; - return SemanticTypes::Lesion(); -} - -mitk::SemanticTypes::ControlPoint mitk::RelationStorage::GenerateControlpoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& controlPointUID) -{ - PropertyList::Pointer propertyList = GetStorageData(caseID); - if (nullptr == propertyList) - { - MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; - return SemanticTypes::ControlPoint(); - } - - // 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(controlPointUID)); - if (nullptr == controlPointVectorProperty) - { - MITK_INFO << "Could not find the control point " << controlPointUID << " in the storage."; - return SemanticTypes::ControlPoint(); - } - - std::vector controlPointVectorValue = controlPointVectorProperty->GetValue(); - // a control point has to have exactly three integer values (year, month and day) - if (controlPointVectorValue.size() != 3) - { - MITK_INFO << "Incorrect control point storage. Not three (3) values of the date are stored."; - return SemanticTypes::ControlPoint(); - } - - // set the values of the control point - SemanticTypes::ControlPoint generatedControlPoint; - generatedControlPoint.UID = controlPointUID; - generatedControlPoint.date = boost::gregorian::date(controlPointVectorValue[0], - controlPointVectorValue[1], - controlPointVectorValue[2]); - - return generatedControlPoint; -} diff --git a/Modules/SemanticRelations/src/mitkSemanticRelations.cpp b/Modules/SemanticRelations/src/mitkSemanticRelations.cpp index 8519e502b9..72f247f5f0 100644 --- a/Modules/SemanticRelations/src/mitkSemanticRelations.cpp +++ b/Modules/SemanticRelations/src/mitkSemanticRelations.cpp @@ -1,1058 +1,1060 @@ /*=================================================================== 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 "mitkSemanticRelations.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) { - m_RelationStorage = std::make_shared(); + // 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 m_RelationStorage->GetAllLesionsOfCase(caseID); + 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 = m_RelationStorage->GetRepresentedLesion(caseID, segmentationID); + 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 = m_RelationStorage->GetAllImageIDsOfCase(caseID); + 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 = m_RelationStorage->GetAllSegmentationIDsOfCase(caseID); + 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 = m_RelationStorage->GetAllSegmentationIDsOfCase(caseID); + 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 = m_RelationStorage->GetAllImageIDsOfCase(caseID); + 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 m_RelationStorage->GetAllControlPointsOfCase(caseID); + 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 m_RelationStorage->GetControlPointOfImage(caseID, dataNodeID); + 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 m_RelationStorage->GetAllExaminationPeriodsOfCase(caseID); + 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 m_RelationStorage->GetAllInformationTypesOfCase(caseID); + 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 m_RelationStorage->GetInformationTypeOfImage(caseID, imageID); + 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 m_RelationStorage->GetAllCaseIDs(); + 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); - m_RelationStorage->AddCase(caseID); - m_RelationStorage->AddImage(caseID, nodeID); + 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); - m_RelationStorage->RemoveImage(caseID, nodeID); + 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 { - m_RelationStorage->AddLesion(caseID, lesion); + RelationStorage::AddLesion(caseID, lesion); NotifyObserver(caseID); } } void mitk::SemanticRelations::OverwriteLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { if (InstanceExists(caseID, lesion)) { - m_RelationStorage->OverwriteLesion(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 - m_RelationStorage->RemoveLesion(caseID, lesion); + 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); - m_RelationStorage->AddSegmentation(caseID, segmentationNodeID, parentNodeID); + 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); - m_RelationStorage->LinkSegmentationToLesion(caseID, segmentationID, 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::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); - m_RelationStorage->UnlinkSegmentationFromLesion(caseID, segmentationID); + 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); - m_RelationStorage->RemoveSegmentation(caseID, segmentationNodeID); + 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."; } - m_RelationStorage->AddControlPoint(caseID, controlPoint); + 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); - m_RelationStorage->LinkDataToControlPoint(caseID, dataID, controlPoint); + 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 = m_RelationStorage->GetControlPointOfImage(caseID, dataID); - m_RelationStorage->UnlinkDataFromControlPoint(caseID, dataID); + 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 { - m_RelationStorage->AddExaminationPeriod(caseID, examinationPeriod); + 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."; } - m_RelationStorage->AddControlPointToExaminationPeriod(caseID, controlPoint, examinationPeriod); + 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); - m_RelationStorage->AddInformationTypeToImage(caseID, imageID, informationType); + 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 = m_RelationStorage->GetInformationTypeOfImage(caseID, imageID); - m_RelationStorage->RemoveInformationTypeFromImage(caseID, imageID); + SemanticTypes::InformationType originalInformationType = RelationStorage::GetInformationTypeOfImage(caseID, imageID); + RelationStorage::RemoveInformationTypeFromImage(caseID, imageID); // check for further references to the removed information type - std::vector allImageIDsVectorValue = m_RelationStorage->GetAllImageIDsOfCase(caseID); + std::vector allImageIDsVectorValue = RelationStorage::GetAllImageIDsOfCase(caseID); for (const auto otherImageID : allImageIDsVectorValue) { - SemanticTypes::InformationType otherInformationType = m_RelationStorage->GetInformationTypeOfImage(caseID, otherImageID); + 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 - m_RelationStorage->RemoveInformationTypeFromCase(caseID, originalInformationType); + 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); - m_RelationStorage->RemoveControlPointFromExaminationPeriod(caseID, controlPoint, examinationPeriod); - m_RelationStorage->RemoveControlPointFromCase(caseID, controlPoint); + RelationStorage::RemoveControlPointFromExaminationPeriod(caseID, controlPoint, examinationPeriod); + RelationStorage::RemoveControlPointFromCase(caseID, controlPoint); } } diff --git a/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp b/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp new file mode 100644 index 0000000000..9f3bdf1d59 --- /dev/null +++ b/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp @@ -0,0 +1,394 @@ +/*=================================================================== + +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::LesionVector mitk::SemanticRelationsInference::GetAllLesionsOfCase(const SemanticTypes::CaseID& caseID) +{ + return RelationStorage::GetAllLesionsOfCase(caseID); +} + +mitk::SemanticTypes::LesionClassVector mitk::SemanticRelationsInference::GetAllLesionClassesOfCase(const SemanticTypes::CaseID& caseID) +{ + 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::Lesion mitk::SemanticRelationsInference::GetRepresentedLesion(const DataNode* segmentationNode) +{ + if (nullptr == segmentationNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; + } + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); + SemanticTypes::ID segmentationID = GetIDFromDataNode(segmentationNode); + SemanticTypes::Lesion representedLesion = 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; + } +} + +mitk::SemanticTypes::Lesion mitk::SemanticRelationsInference::GetRepresentedLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID) +{ + 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 ID " << segmentationID; + } + else + { + return representedLesion; + } +} + +bool mitk::SemanticRelationsInference::IsRepresentingALesion(const DataNode* segmentationNode) +{ + try + { + SemanticTypes::Lesion representedLesion = GetRepresentedLesion(segmentationNode); + return true; + } + catch (const Exception&) + { + return false; + } +} + +bool mitk::SemanticRelationsInference::IsRepresentingALesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID) +{ + try + { + SemanticTypes::Lesion representedLesion = GetRepresentedLesion(caseID, segmentationID); + return true; + } + catch (const Exception&) + { + return false; + } +} + +bool mitk::SemanticRelationsInference::IsLesionPresentOnSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ID& segmentationID) +{ + try + { + const auto representedLesion = GetRepresentedLesion(caseID, segmentationID); + return lesion.UID == representedLesion.UID; + } + catch (const SemanticRelationException&) + { + return false; + } +} + +bool mitk::SemanticRelationsInference::InstanceExists(const DataNode* dataNode) +{ + try + { + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); + SemanticTypes::ID dataNodeID = GetIDFromDataNode(dataNode); + + if (NodePredicates::GetImagePredicate()->CheckNode(dataNode)) + { + std::vector allImageIDsOfCase = RelationStorage::GetAllImageIDsOfCase(caseID); + return std::find(allImageIDsOfCase.begin(), allImageIDsOfCase.end(), dataNodeID) != allImageIDsOfCase.end(); + } + else if (NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) + { + std::vector allSegmentationIDsOfCase = RelationStorage::GetAllSegmentationIDsOfCase(caseID); + return std::find(allSegmentationIDsOfCase.begin(), allSegmentationIDsOfCase.end(), dataNodeID) != allSegmentationIDsOfCase.end(); + } + else + { + return false; + } + } + catch (const SemanticRelationException&) + { + return false; + } +} + +bool mitk::SemanticRelationsInference::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) +{ + SemanticTypes::LesionVector allLesions = GetAllLesionsOfCase(caseID); + + // filter all lesions: check for equality with the given lesion using a lambda function + auto lambda = [&lesion](const SemanticTypes::Lesion& currentLesion) { return currentLesion.UID == lesion.UID; }; + const auto existingLesion = std::find_if(allLesions.begin(), allLesions.end(), lambda); + + if (existingLesion != allLesions.end()) + { + return true; + } + else + { + return false; + } +} + +mitk::SemanticTypes::IDVector mitk::SemanticRelationsInference::GetAllSegmentationIDsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) +{ + if (InstanceExists(caseID, lesion)) + { + // lesion exists, retrieve all case segmentations from the storage + SemanticTypes::IDVector allSegmentationIDs = RelationStorage::GetAllSegmentationIDsOfCase(caseID); + + // filter all segmentationIDs: check for semantic relation with the given lesion using a lambda function + auto lambda = [&lesion, caseID](const SemanticTypes::ID& segmentationID) + { + try + { + SemanticTypes::Lesion representedLesion = GetRepresentedLesion(caseID, segmentationID); + return lesion.UID != representedLesion.UID; + } + catch (const SemanticRelationException&) + { + return true; + } + }; + + allSegmentationIDs.erase(std::remove_if(allSegmentationIDs.begin(), allSegmentationIDs.end(), lambda), allSegmentationIDs.end()); + + return allSegmentationIDs; + } + else + { + mitkThrowException(SemanticRelationException) << "Could not find an existing lesion instance for the given caseID " << caseID << " and lesion " << lesion.UID << "."; + } +} + +mitk::SemanticTypes::ControlPointVector mitk::SemanticRelationsInference::GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID) +{ + return RelationStorage::GetAllControlPointsOfCase(caseID); +} + +mitk::SemanticTypes::ControlPoint mitk::SemanticRelationsInference::GetControlPointOfImage(const DataNode* imageNode) +{ + if (nullptr == imageNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid data node."; + } + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); + SemanticTypes::ID dataNodeID = GetIDFromDataNode(imageNode); + return GetControlPointOfImage(caseID, dataNodeID); +} + +mitk::SemanticTypes::ControlPoint mitk::SemanticRelationsInference::GetControlPointOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID) +{ + return RelationStorage::GetControlPointOfImage(caseID, imageID); +} + +mitk::SemanticTypes::IDVector mitk::SemanticRelationsInference::GetAllImageIDsOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) +{ + if (InstanceExists(caseID, controlPoint)) + { + // control point exists, retrieve all imageIDs from the storage + SemanticTypes::IDVector allImageIDs = RelationStorage::GetAllImageIDsOfCase(caseID); + + // filter all imageIDs to remove the ones with a different control point using a lambda function + auto lambda = [&controlPoint, &caseID](const SemanticTypes::ID& imageID) + { + return controlPoint.UID != RelationStorage::GetControlPointOfImage(caseID, imageID).UID; + }; + + allImageIDs.erase(std::remove_if(allImageIDs.begin(), allImageIDs.end(), lambda), allImageIDs.end()); + + return allImageIDs; + } + else + { + mitkThrowException(SemanticRelationException) << "Could not find an existing control point instance for the given caseID " << caseID << " and control point " << controlPoint.UID << "."; + } +} + +bool mitk::SemanticRelationsInference::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) +{ + 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::SemanticRelationsInference::GetAllExaminationPeriodsOfCase(const SemanticTypes::CaseID& caseID) +{ + return RelationStorage::GetAllExaminationPeriodsOfCase(caseID); +} + +bool mitk::SemanticRelationsInference::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) +{ + 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::SemanticRelationsInference::GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID) +{ + return RelationStorage::GetAllInformationTypesOfCase(caseID); +} + +mitk::SemanticTypes::InformationType mitk::SemanticRelationsInference::GetInformationTypeOfImage(const DataNode* imageNode) +{ + if (nullptr == imageNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid image data node."; + } + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); + SemanticTypes::ID imageID = GetIDFromDataNode(imageNode); + return GetInformationTypeOfImage(caseID, imageID); +} + +mitk::SemanticTypes::InformationType mitk::SemanticRelationsInference::GetInformationTypeOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID) +{ + return RelationStorage::GetInformationTypeOfImage(caseID, imageID); +} + +mitk::SemanticTypes::IDVector mitk::SemanticRelationsInference::GetAllImageIDsOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) +{ + if (InstanceExists(caseID, informationType)) + { + // information type exists, retrieve all imageIDs from the storage + SemanticTypes::IDVector allImageIDs = RelationStorage::GetAllImageIDsOfCase(caseID); + + // filter all images to remove the ones with a different information type using a lambda function + auto lambda = [&informationType, caseID](const SemanticTypes::ID& imageID) + { + return informationType != RelationStorage::GetInformationTypeOfImage(caseID, imageID); + }; + + allImageIDs.erase(std::remove_if(allImageIDs.begin(), allImageIDs.end(), lambda), allImageIDs.end()); + + return allImageIDs; + } + else + { + mitkThrowException(SemanticRelationException) << "Could not find an existing information type for the given caseID " << caseID << " and information type " << informationType; + } +} + +bool mitk::SemanticRelationsInference::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) +{ + 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::SemanticRelationsInference::GetAllCaseIDs() +{ + return RelationStorage::GetAllCaseIDs(); +} + +void mitk::SemanticRelationsInference::ClearControlPoints(const SemanticTypes::CaseID& caseID) +{ + SemanticTypes::ControlPointVector allControlPointsOfCase = GetAllControlPointsOfCase(caseID); + + std::vector allImageIDsVectorValue = RelationStorage::GetAllImageIDsOfCase(caseID); + SemanticTypes::ControlPointVector referencedControlPoints; + for (const auto& imageID : allImageIDsVectorValue) + { + auto controlPointOfImage = 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 = GetAllExaminationPeriodsOfCase(caseID); + for (const auto& controlPoint : nonReferencedControlPoints) + { + const auto& examinationPeriod = FindExaminationPeriod(controlPoint, allExaminationPeriods); + RelationStorage::RemoveControlPointFromExaminationPeriod(caseID, controlPoint, examinationPeriod); + RelationStorage::RemoveControlPointFromCase(caseID, controlPoint); + } +} diff --git a/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp b/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp new file mode 100644 index 0000000000..24cd6c93c3 --- /dev/null +++ b/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp @@ -0,0 +1,446 @@ +/*=================================================================== + +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 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::SemanticRelationsIntegration::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); + UnlinkImageFromControlPoint(imageNode); + RelationStorage::RemoveImage(caseID, nodeID); + NotifyObserver(caseID); +} + +void mitk::SemanticRelationsIntegration::AddLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) +{ + if (SemanticRelationsInference::InstanceExists(caseID, lesion)) + { + mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to add already exists for the given case."; + } + else + { + RelationStorage::AddLesion(caseID, lesion); + NotifyObserver(caseID); + } +} + +void mitk::SemanticRelationsIntegration::OverwriteLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) +{ + if (SemanticRelationsInference::InstanceExists(caseID, lesion)) + { + RelationStorage::OverwriteLesion(caseID, lesion); + NotifyObserver(caseID); + } + else + { + mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to overwrite does not exist for the given case."; + } +} + +void mitk::SemanticRelationsIntegration::AddLesionAndLinkSegmentation(const DataNode* segmentationNode, const SemanticTypes::Lesion& lesion) +{ + if (nullptr == segmentationNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; + } + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); + AddLesion(caseID, lesion); + LinkSegmentationToLesion(segmentationNode, lesion); + NotifyObserver(caseID); +} + +void mitk::SemanticRelationsIntegration::RemoveLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) +{ + if (SemanticRelationsInference::InstanceExists(caseID, lesion)) + { + SemanticTypes::IDVector allSegmentationIDsOfLesion = SemanticRelationsInference::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."; + } + + // 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::SemanticRelationsIntegration::LinkSegmentationToLesion(const DataNode* segmentationNode, const SemanticTypes::Lesion& lesion) +{ + if (nullptr == segmentationNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; + } + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); + if (SemanticRelationsInference::InstanceExists(caseID, lesion)) + { + SemanticTypes::ID segmentationID = GetIDFromDataNode(segmentationNode); + RelationStorage::LinkSegmentationToLesion(caseID, segmentationID, lesion); + NotifyObserver(caseID); + } + else + { + mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to link does not exist for the given case."; + } +} + +void mitk::SemanticRelationsIntegration::UnlinkSegmentationFromLesion(const DataNode* segmentationNode) +{ + if (nullptr == segmentationNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; + } + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); + SemanticTypes::ID segmentationID = GetIDFromDataNode(segmentationNode); + RelationStorage::UnlinkSegmentationFromLesion(caseID, segmentationID); + NotifyObserver(caseID); +} + +void mitk::SemanticRelationsIntegration::RemoveSegmentation(const DataNode* segmentationNode) +{ + if (nullptr == segmentationNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; + } + + // continue with a valid data node + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); + SemanticTypes::ID segmentationNodeID = GetIDFromDataNode(segmentationNode); + RelationStorage::RemoveSegmentation(caseID, segmentationNodeID); + NotifyObserver(caseID); +} + +void mitk::SemanticRelationsIntegration::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 = RelationStorage::GetAllControlPointsOfCase(caseID); + // need to check if an already existing control point fits/contains the user control point + SemanticTypes::ControlPoint existingControlPoint = FindExistingControlPoint(controlPoint, allControlPoints); + if (!existingControlPoint.UID.empty()) + { + try + { + // found an already existing control point + 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 = RelationStorage::GetAllExaminationPeriodsOfCase(caseID); + SemanticTypes::ExaminationPeriod examinationPeriod = FindExaminationPeriod(closestControlPoint, allExaminationPeriods); + if (examinationPeriod.UID.empty()) + { + // no closest control point (exceed threshold) or no examination period found + // create a new examination period for this control point and add it to the storage + examinationPeriod.UID = UIDGeneratorBoost::GenerateUID(); + examinationPeriod.name = "New examination period " + std::to_string(allExaminationPeriods.size()); + AddExaminationPeriod(caseID, examinationPeriod); + } + + // add the control point to the (newly created or found / close) examination period + AddControlPointToExaminationPeriod(caseID, controlPoint, examinationPeriod); + } + catch (const SemanticRelationException&) + { + mitkThrowException(SemanticRelationException) << "The data can not be linked. Inconsistency in the semantic relations storage assumed."; + } + } + + ClearControlPoints(caseID); + NotifyObserver(caseID); +} + +void mitk::SemanticRelationsIntegration::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 (SemanticRelationsInference::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::SemanticRelationsIntegration::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 (SemanticRelationsInference::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::SemanticRelationsIntegration::UnlinkImageFromControlPoint(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::SemanticRelationsIntegration::AddExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) +{ + if (SemanticRelationsInference::InstanceExists(caseID, examinationPeriod)) + { + mitkThrowException(SemanticRelationException) << "The examination period " << examinationPeriod.UID << " to add already exists for the given case."; + } + else + { + RelationStorage::AddExaminationPeriod(caseID, examinationPeriod); + } +} + +void mitk::SemanticRelationsIntegration::AddControlPointToExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod& examinationPeriod) +{ + if (!SemanticRelationsInference::InstanceExists(caseID, controlPoint)) + { + mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to add does not exist for the given case."; + } + + if (!SemanticRelationsInference::InstanceExists(caseID, examinationPeriod)) + { + mitkThrowException(SemanticRelationException) << "The examination period " << examinationPeriod.UID << " does not exist for the given case. \n Use 'AddExaminationPeriod' before."; + } + + RelationStorage::AddControlPointToExaminationPeriod(caseID, controlPoint, examinationPeriod); +} + +void mitk::SemanticRelationsIntegration::SetInformationType(const DataNode* imageNode, const SemanticTypes::InformationType& informationType) +{ + RemoveInformationTypeFromImage(imageNode); + AddInformationTypeToImage(imageNode, informationType); + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); + NotifyObserver(caseID); +} + +void mitk::SemanticRelationsIntegration::AddInformationTypeToImage(const DataNode* imageNode, const SemanticTypes::InformationType& informationType) +{ + if (nullptr == imageNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid image data node."; + } + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); + SemanticTypes::ID imageID = GetIDFromDataNode(imageNode); + RelationStorage::AddInformationTypeToImage(caseID, imageID, informationType); +} + +void mitk::SemanticRelationsIntegration::RemoveInformationTypeFromImage(const DataNode* imageNode) +{ + if (nullptr == imageNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid image data node."; + } + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); + SemanticTypes::ID imageID = GetIDFromDataNode(imageNode); + SemanticTypes::InformationType originalInformationType = RelationStorage::GetInformationTypeOfImage(caseID, imageID); + RelationStorage::RemoveInformationTypeFromImage(caseID, imageID); + + // check for further references to the removed information type + std::vector allImageIDsVectorValue = RelationStorage::GetAllImageIDsOfCase(caseID); + for (const auto& otherImageID : allImageIDsVectorValue) + { + SemanticTypes::InformationType otherInformationType = RelationStorage::GetInformationTypeOfImage(caseID, otherImageID); + if (otherInformationType == originalInformationType) + { + // found the information type in another image -> cannot remove the information type from the case + return; + } + } + + // given information type was not referred by any other image of the case -> the information type can be removed from the case + RelationStorage::RemoveInformationTypeFromCase(caseID, originalInformationType); +} + +/************************************************************************/ +/* private functions */ +/************************************************************************/ +void mitk::SemanticRelationsIntegration::NotifyObserver(const SemanticTypes::CaseID& caseID) const +{ + for (auto& observer : m_ObserverVector) + { + observer->Update(caseID); + } +} + +void mitk::SemanticRelationsIntegration::ClearControlPoints(const SemanticTypes::CaseID& caseID) +{ + SemanticTypes::ControlPointVector allControlPointsOfCase = RelationStorage::GetAllControlPointsOfCase(caseID); + + std::vector allImageIDsVectorValue = RelationStorage::GetAllImageIDsOfCase(caseID); + SemanticTypes::ControlPointVector referencedControlPoints; + for (const auto& imageID : allImageIDsVectorValue) + { + auto controlPointOfImage = RelationStorage::GetControlPointOfImage(caseID, imageID); + referencedControlPoints.push_back(controlPointOfImage); + } + + std::sort(allControlPointsOfCase.begin(), allControlPointsOfCase.end()); + std::sort(referencedControlPoints.begin(), referencedControlPoints.end()); + + SemanticTypes::ControlPointVector nonReferencedControlPoints; + std::set_difference(allControlPointsOfCase.begin(), allControlPointsOfCase.end(), + referencedControlPoints.begin(), referencedControlPoints.end(), + std::inserter(nonReferencedControlPoints, nonReferencedControlPoints.begin())); + + auto allExaminationPeriods = RelationStorage::GetAllExaminationPeriodsOfCase(caseID); + for (const auto& controlPoint : nonReferencedControlPoints) + { + const auto& examinationPeriod = FindExaminationPeriod(controlPoint, allExaminationPeriods); + RelationStorage::RemoveControlPointFromExaminationPeriod(caseID, controlPoint, examinationPeriod); + RelationStorage::RemoveControlPointFromCase(caseID, controlPoint); + } +}