diff --git a/Modules/SemanticRelations/include/mitkControlPointManager.h b/Modules/SemanticRelations/include/mitkControlPointManager.h index cf8f3f570d..aa6eb760ae 100644 --- a/Modules/SemanticRelations/include/mitkControlPointManager.h +++ b/Modules/SemanticRelations/include/mitkControlPointManager.h @@ -1,118 +1,122 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKCONTROLPOINTMANAGER_H #define MITKCONTROLPOINTMANAGER_H #include // semantic relations module #include "mitkSemanticTypes.h" // mitk core #include namespace mitk { /** * @brief Provides helper functions that are needed to work with control points. * * These functions help to generate new control points, check for overlapping / containing control points or provide functionality * to find a fitting control point or even extend an already existing control point. */ /** * @brief Generates a control point from a given data node. * The date is extracted from the data node by using the 'DICOMHelper::GetDICOMDateFromDataNode'-function. * * @param datanode A data node pointer, whose date should be included in the newly generated control point. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint GenerateControlPoint(const mitk::DataNode* datanode); /** * @brief Find and return a whole control point including its date given a specific control point UID. * * @param controlPointUID The control point UID as string. * @param allControlPoints All currently known control points of a specific case. * * @return The control point with its UID and the date. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint GetControlPointByUID(const SemanticTypes::ID& controlPointUID, const SemanticTypes::ControlPointVector& allControlPoints); /** * @brief Returns an already existing control point from the given vector of control points. This existing control point has the * the same date (year, month, day) as the given single control point. * If no existing control point can be found an empty control point is returned. * * @param controlPoint The control point to check for existence. * @param allControlPoints The vector of already existing control points. + * + * @return The existing control point. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint FindExistingControlPoint(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ControlPointVector& allControlPoints); /** * @brief Returns an already existing close control point from the given vector of control points. This closest control point has a date * date that is within a certain distance-in-days to the given control point. * If no closest control point can be found within the distance threshold an empty control point is returned. * * @param controlPoint The control point to check for distance. * @param allControlPoints The vector of already existing control points. + * + * @return The closest control point. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint FindClosestControlPoint(const SemanticTypes::ControlPoint& controlPoint, SemanticTypes::ControlPointVector& allControlPoints); /** * @brief Returns the examination period to which the given control point belongs. * Each examination point holds a vector of control point UIDs so that the UID of the given control point can be compared against the UIDs of the vector. * An empty examination period is returned if, * - the given vector of examination periods is empty * - the examination periods do not contain any control point UIDs * - the UID of the given control point is not contained in any examination period * * @param controlPoint The control point of which the examination period should be found. * @param allExaminationPeriods All currently known examination periods of a specific case. * * @return The examination period that contains the given control point. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ExaminationPeriod FindContainingExaminationPeriod(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriodVector& allExaminationPeriods); /** * @brief Return the examination period to which the given data node belongs. * The control point is used to find an already existing or the closest control point in the semantic relations storage. * If such a control point is found, the 'FindClosestControlPoint'-function with this control point as an argument is used * to actually find the corresponding examination period. * * @param caseID The current case identifier is defined by the given string. * @param controlPoint The control point of which the examination period should be found. * * @return The examination period that fits the given data node. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ExaminationPeriod FindFittingExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); /** * @brief Return the examination period to which the given data node belongs. * The DICOM date of the data node is used to find an already existing or the closest control point in the semantic relations storage. * If such a control point is found, the 'FindFittingExaminationPeriod'-function with this control point as an argument is used * to actually find the corresponding examination period. * * @param datanode A data node pointer, whose date should be included in the newly generated control point. * * @return The examination period that contains the given data node. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ExaminationPeriod FindFittingExaminationPeriod(const DataNode* dataNode); /** * @brief Sort the given vector of examination periods. * Each examination period has a vector of control point UIDs (stored in chronological order). * The examination periods can be sorted by comparing the first control points of the examination periods. * * @param allExaminationPeriods All currently known examination periods of a specific case. * @param allControlPoints All currently known control points of a specific case. */ MITKSEMANTICRELATIONS_EXPORT void SortExaminationPeriods(SemanticTypes::ExaminationPeriodVector& allExaminationPeriods, const SemanticTypes::ControlPointVector& allControlPoints); } // namespace mitk #endif // MITKCONTROLPOINTMANAGER_H diff --git a/Modules/SemanticRelations/include/mitkISemanticRelationsObservable.h b/Modules/SemanticRelations/include/mitkISemanticRelationsObservable.h index 08e50b37ac..a9c78e0f07 100644 --- a/Modules/SemanticRelations/include/mitkISemanticRelationsObservable.h +++ b/Modules/SemanticRelations/include/mitkISemanticRelationsObservable.h @@ -1,57 +1,55 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKISEMANTICRELATIONSOBSERVABLE_H #define MITKISEMANTICRELATIONSOBSERVABLE_H #include "mitkISemanticRelationsObserver.h" -#include "mitkSemanticTypes.h" - namespace mitk { /* * @brief This interface declares three functions each observable subject has to implement * in order to be observed in the 'Observer pattern' sense. * The concrete observable class has to store its observer. */ class ISemanticRelationsObservable { public: /* * @brief Adds the given concrete observer to a container that holds all currently registered observer. * - * @par observer The concrete observer to register. + * @param observer The concrete observer to register. */ virtual void AddObserver(ISemanticRelationsObserver* observer) = 0; /* * @brief Removes the given concrete observer from the container that holds all currently registered observer. * - * @par observer The concrete observer to unregister. + * @param observer The concrete observer to unregister. */ virtual void RemoveObserver(ISemanticRelationsObserver* observer) = 0; /* * @brief Updates all concrete observer in the container that holds all currently registered observer. * The caseID can be used to only update the observer, if the caseID fulfills a certain condition. * - * @par caseID A caseID that identifies the currently active patient / case. + * @param caseID A caseID that identifies the currently active patient / case. */ - virtual void NotifyObserver(const mitk::SemanticTypes::CaseID& caseID) const = 0; + virtual void NotifyObserver(const SemanticTypes::CaseID& caseID) const = 0; }; // class ISemanticRelationsObservable } // namespace mitk #endif // MITKISEMANTICRELATIONSOBSERVABLE_H diff --git a/Modules/SemanticRelations/include/mitkISemanticRelationsObserver.h b/Modules/SemanticRelations/include/mitkISemanticRelationsObserver.h index 3fd8651456..d7df63e1f5 100644 --- a/Modules/SemanticRelations/include/mitkISemanticRelationsObserver.h +++ b/Modules/SemanticRelations/include/mitkISemanticRelationsObserver.h @@ -1,43 +1,43 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKISEMANTICRELATIONSOBSERVER_H #define MITKISEMANTICRELATIONSOBSERVER_H #include "mitkSemanticTypes.h" namespace mitk { /* * @brief This interface declares a functions each observer has to implement * in order to be notified in the 'Observer pattern' sense. */ class ISemanticRelationsObserver { public: /* * @brief Updates the concrete observer. * The caseID can be used to get access to a certain patient (case), * whose data should be used for updating. * - * @par caseID The current case ID to identify the currently active patient / case. + * @param caseID The current case ID to identify the currently active patient / case. */ virtual void Update(const mitk::SemanticTypes::CaseID& caseID) = 0; }; // class ISemanticRelationsObserver } // namespace mitk #endif // MITKISEMANTICRELATIONSOBSERVER_H diff --git a/Modules/SemanticRelations/include/mitkLesionData.h b/Modules/SemanticRelations/include/mitkLesionData.h index b16f5e8c55..a2818d3422 100644 --- a/Modules/SemanticRelations/include/mitkLesionData.h +++ b/Modules/SemanticRelations/include/mitkLesionData.h @@ -1,69 +1,68 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKLESIONDATA_H #define MITKLESIONDATA_H #include // mitk semantic relations module #include "mitkSemanticTypes.h" // c++ #include namespace mitk { /** * @brief This class holds the data of each lesion in the lesion tree view. * The data is the lesion itself with its UID, name and lesion class * as well as two vectors for * - lesion presence: bool value for each control-point * inside the semantic relations storage * - lesion volume: double value for each control-point - information type pair * inside the semantic relations storage * */ class MITKSEMANTICRELATIONS_EXPORT LesionData { public: /** * @brief sets the data members to their initial values */ LesionData(const SemanticTypes::Lesion& lesion = SemanticTypes::Lesion()); - ~LesionData(); SemanticTypes::Lesion GetLesion() const { return m_Lesion; }; SemanticTypes::ID GetLesionUID() const { return m_Lesion.UID; } std::string GetLesionName() const { return m_Lesion.name; } const std::vector& GetLesionPresence() const { return m_LesionPresence; }; const std::vector& GetLesionVolume() const { return m_LesionVolume; }; void SetLesion(const SemanticTypes::Lesion& lesion); void SetLesionPresence(const std::vector& lesionPresence); void SetLesionVolume(const std::vector& lesionVolume); private: SemanticTypes::Lesion m_Lesion; std::vector m_LesionPresence; std::vector m_LesionVolume; }; } // end namespace #endif // MITKLESIONDATA_H diff --git a/Modules/SemanticRelations/include/mitkLesionManager.h b/Modules/SemanticRelations/include/mitkLesionManager.h index 3dd48cd50e..976d8f43de 100644 --- a/Modules/SemanticRelations/include/mitkLesionManager.h +++ b/Modules/SemanticRelations/include/mitkLesionManager.h @@ -1,75 +1,75 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKLESIONMANAGER_H #define MITKLESIONMANAGER_H #include // semantic relations module -#include "mitkSemanticTypes.h" #include "mitkLesionData.h" /* * @brief Provides helper functions that are needed to work with lesions. * * These functions help to generate new lesions, check for existing lesions or provide functionality * to generate new and find existing lesion class types. */ namespace mitk { typedef std::vector LesionClassVector; /** * @brief Generate a new lesion and lesion class with UIDs and the given string as lesion class type. * * @param lesionClassType The lesion class type as string. Default parameter is "". */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::Lesion GenerateNewLesion(const std::string& lesionClassType = ""); /** * @brief Generate a new lesion class with UID and the given string as lesion class type. * * @param lesionClassType The lesion class type as string. Default parameter is "". */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionClass GenerateNewLesionClass(const std::string& lesionClassType = ""); /** * @brief Find and return a whole lesion including its lesion class given a specific lesion UID. * * @param lesionUID The lesion UID as string. * @param allLesions All currently known lesions of a specific case. * * @return The lesion with its UID and the lesion class. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::Lesion GetLesionByUID(const SemanticTypes::ID& lesionUID, const std::vector& allLesions); /** * @brief Find and return the whole lesion class including its UID given a specific lesion class type. * * @param lesionClassType The lesion class type as string. * @param allLesionClasses All currently known lesion classes of a specific case. * * @return The lesion class with its UID and the class type. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionClass FindExistingLesionClass(const std::string& lesionClassType, const std::vector& allLesionClasses); /** * @brief Compute and store lesion presence for all available control points and information types. * * @param lesionData The lesion data that holds the lesion and will hold the additional lesion data. * @param caseID The current case ID. */ MITKSEMANTICRELATIONS_EXPORT void ComputeLesionPresence(LesionData& lesionData, const SemanticTypes::CaseID& caseID); + } // namespace mitk #endif // MITKLESIONMANAGER_H diff --git a/Modules/SemanticRelations/include/mitkNodePredicates.h b/Modules/SemanticRelations/include/mitkNodePredicates.h index 554653b21e..201e9e3838 100644 --- a/Modules/SemanticRelations/include/mitkNodePredicates.h +++ b/Modules/SemanticRelations/include/mitkNodePredicates.h @@ -1,47 +1,47 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKNODEPREDICATES_H #define MITKNODEPREDICATES_H #include // mitk core #include namespace mitk { namespace NodePredicates { /* * @brief Helper function to get a node predicate that can be used to filter images. * * The images are of type 'mitk::Image' but must not be 'helper objects' or 'segmentation nodes'. * For the definition of 'segmentation nodes' see 'GetSegmentationPredicate'. */ - MITKSEMANTICRELATIONS_EXPORT mitk::NodePredicateAnd::Pointer GetImagePredicate(); + MITKSEMANTICRELATIONS_EXPORT NodePredicateAnd::Pointer GetImagePredicate(); /* * @brief Helper function to get a node predicate that can be used to filter segmentations. * * The segmentations are of type 'mitk::LabelSetImage' or nodes that have their 'binary' property set to true. * Segmentations must not be 'helper objects'. */ - MITKSEMANTICRELATIONS_EXPORT mitk::NodePredicateAnd::Pointer GetSegmentationPredicate(); + MITKSEMANTICRELATIONS_EXPORT NodePredicateAnd::Pointer GetSegmentationPredicate(); } // namespace NodePredicates } // namespace mitk #endif // MITKNODEPREDICATES_H diff --git a/Modules/SemanticRelations/include/mitkSemanticRelationsDataStorageAccess.h b/Modules/SemanticRelations/include/mitkSemanticRelationsDataStorageAccess.h index 65f8f321e7..e02de8ba66 100644 --- a/Modules/SemanticRelations/include/mitkSemanticRelationsDataStorageAccess.h +++ b/Modules/SemanticRelations/include/mitkSemanticRelationsDataStorageAccess.h @@ -1,178 +1,187 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKSEMANTICRELATIONSDATASTORAGEACCESS_H #define MITKSEMANTICRELATIONSDATASTORAGEACCESS_H #include // semantic relations module #include "mitkSemanticTypes.h" // mitk core #include #include namespace mitk { /** * @brief The API provides functions to query and manipulate image relations and instances, * that are helpful during follow-up examination, like control-points (time period), * types of the images or lesions that may be visible on multiple images. * * The class is able to generate IDs from given data nodes using DICOM information. * These IDs are used to identify the corresponding instances of a specific case. * The case can also be directly identified by the given case ID. * * In order for most functions to work the case ID has to be used as a parameter. * If not, these functions do nothing. */ class MITKSEMANTICRELATIONS_EXPORT SemanticRelationsDataStorageAccess { public: using DataNodeVector = std::vector; SemanticRelationsDataStorageAccess(DataStorage* dataStorage); /************************************************************************/ /* functions to get instances / attributes */ /************************************************************************/ /** * @brief Return a vector of all segmentations that are currently available for the given case. * The segmentations may be connected / not connected to a lesion of the case. * If no segmentations are stored for the current case, an empty vector is returned. * * @pre The data storage member has to be valid (!nullptr). * @throw SemanticRelationException, if the data storage member is invalid (==nullptr). * * @param caseID The current case identifier is defined by the given string. + * * @return A vector of data nodes representing segmentations. */ DataNodeVector GetAllSegmentationsOfCase(const SemanticTypes::CaseID& caseID) const; /** * @brief Return a vector of all segmentations that define the given lesion. These segmentations don't have to be linked to the same image. * If the lesion is not referred to by any segmentation, an empty vector is returned. * * @pre The data storage member has to be valid (!nullptr). * @throw SemanticRelationException, if the data storage member is invalid (==nullptr). * @pre The UID of the lesion has to exist for a lesion instance. * @throw SemanticRelationException, if UID of the lesion does not exist for a lesion instance (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param lesion A lesion with a UID that identifies the corresponding lesion instance. + * * @return A vector of data nodes representing segmentations that define the given lesion. */ DataNodeVector GetAllSegmentationsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const; /** * @brief Return a vector of all images that are currently available for the given case. * * @pre The data storage member has to be valid (!nullptr). * @throw SemanticRelationException, if the data storage member is invalid (==nullptr). * * @param caseID The current case identifier is defined by the given string. + * * @return A vector of data nodes representing images. */ DataNodeVector GetAllImagesOfCase(const SemanticTypes::CaseID& caseID) const; /** * @brief Return a vector of all images that are specified by the given vector of image IDs. * * @pre The data storage member has to be valid (!nullptr). * @throw SemanticRelationException, if the data storage member is invalid (==nullptr). * * @param imageIDs A vector of image IDs that represent the images in the data storage. + * * @return A vector of data nodes representing images. */ DataNodeVector GetAllImagesByID(const SemanticTypes::IDVector& imageIDs) const; /** * @brief Return a vector of all images that are connected to those segmentations that are linked to the given lesion. * If the lesion is not referred to by any segmentation, an empty vector is returned. * * @pre The UID of the lesion has to exist for a lesion instance. * @throw SemanticRelationException, if UID of the lesion does not exist for a lesion instance (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param lesion A lesion with a UID that identifies the corresponding lesion instance. + * * @return A vector of data nodes representing images on which the lesions are visible. */ DataNodeVector GetAllImagesOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const; /** * @brief Return a vector of all image nodes that are defined with the given control point and the given information type. * * @pre The UID of the control point has to exist for a control point instance. * The information type has to exist for the given case (and is therefore used by at least one data node). * @throw SemanticRelationException, if the UID of the control point does not exist for a control point instance (this can be checked via 'InstanceExists') or * if the information type is not used by any data node (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param controlPoint A control point with a UID that identifies the corresponding control point instance. * @param informationType An information type that identifies the corresponding information type instance. + * * @return A vector of image nodes that are defined with the given control point and the given information type. */ DataNodeVector GetAllSpecificImages(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const; /** * @brief Return a vector of all image nodes that are defined with the given information type and the given examination period. * The function uses the 'SemanticRelationsInference::GetAllImageIDsOfExaminationPeriod'-function to retrieve the imageIDs of the examination period and * then compares the information type of all these images against the given information type. * * @param informationType An information type that identifies the corresponding information type instance. * @param examinationPeriod An examination period that identifies the corresponding examination period instance. + * * @return A vector of image nodes that are defined with the given information type with the given control point. */ DataNodeVector GetAllSpecificImages(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType, const SemanticTypes::ExaminationPeriod& examinationPeriod) const; /** * @brief Return a vector of all segmentation nodes that are defined with the given control point and the given information type. * The function uses the 'GetAllSpecificImages'-function to retrieve the specific images and then searches for the derived nodes (segmentation child nodes). * * @pre The UID of the control point has to exist for a control point instance. * The information type has to exist for the given case (and is therefore used by at least one data node). * @throw SemanticRelationException, if the UID of the control point does not exist for a control point instance (this can be checked via 'InstanceExists') or * if the information type is not used by any data node (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param controlPoint A control point with a UID that identifies the corresponding control point instance. * @param informationType An information type that identifies the corresponding information type instance. + * * @return A vector of segmentation nodes that are defined with the given control point and the given information type. */ DataNodeVector GetAllSpecificSegmentations(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const; /** * @brief Return the single segmentation node that is defined with the given information type, the given control point and is representing the given lesion. * The function uses the 'GetAllSpecificSegmentations'-function to retrieve the specific segmentations and then checks for the represented lesion. * * @pre The UID of the control point has to exist for a control point instance. * The information type has to exist for the given case (and is therefore used by at least one data node). * The lesion has to exist for the given case. * @throw SemanticRelationException, if the UID of the control point does not exist for a control point instance (this can be checked via 'InstanceExists') or * if the information type is not used by any data node (this can be checked via 'InstanceExists') or * if the lesion does not exist for the given case (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param controlPoint A control point with a UID that identifies the corresponding control point instance. * @param informationType An information type that identifies the corresponding information type instance. * @param lesion A lesion with a UID that identifies the corresponding lesion instance. + * * @return A single segmentation node that is defined with the given information type, the given control point and is representing the given lesion. */ DataNode::Pointer GetSpecificSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType, const SemanticTypes::Lesion& lesion) const; private: WeakPointer m_DataStorage; }; } // namespace mitk #endif // MITKSEMANTICRELATIONSDATASTORAGEACCESS_H diff --git a/Modules/SemanticRelations/include/mitkSemanticRelationsInference.h b/Modules/SemanticRelations/include/mitkSemanticRelationsInference.h index d4e06f310d..404edad885 100644 --- a/Modules/SemanticRelations/include/mitkSemanticRelationsInference.h +++ b/Modules/SemanticRelations/include/mitkSemanticRelationsInference.h @@ -1,327 +1,351 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKSEMANTICRELATIONSINFERENCE_H #define MITKSEMANTICRELATIONSINFERENCE_H #include // semantic relations module #include "mitkSemanticTypes.h" // mitk core #include namespace mitk { /** * @brief The API provides functions to query image relations and instances * that are helpful during follow-up examination, like control-points (time period), * types of the images or lesions that may be visible on multiple images. * * The class is able to generate IDs from given data nodes using DICOM information. * These IDs are used to identify the corresponding instances of a specific case. * The case can also be directly identified by the given case ID. * * In order for most functions to work the case ID has to be used as a parameter. * If not, these functions do nothing. */ namespace SemanticRelationsInference { /************************************************************************/ /* functions to get instances / attributes */ /************************************************************************/ /** * @brief Return a vector of lesion classes that are currently available for the given case. * * @param caseID The current case identifier is defined by the given string. + * * @return A vector of lesion classes. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionClassVector GetAllLesionClassesOfCase(const SemanticTypes::CaseID& caseID); /** * @brief Return the lesion that is defined by the given segmentation. * * @pre The given segmentation data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given segmentation is invalid (==nullptr). * @pre The segmentation data node has to represent a lesion. If not, the retrieved lesion will be empty, which leads to an exception. * @throw SemanticRelationException, if the segmentation does not represent an existing lesion (this can be checked via 'IsRepresentingALesion'). * * @param segmentationNode The segmentation identifier is extracted from the given data node. + * * @return The represented lesion. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::Lesion GetLesionOfSegmentation(const DataNode* segmentationNode); /** * @brief Returns a vector of all lesions that are currently available for the current case and are connected to the given image (via a segmentation). * If no lesions are stored for the current case, an empty vector is returned. If no segmentations are * connected with the image node, no lesions for the specific image will be found and an empty vector is returned. * * @pre The given image data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). * * @param imageNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. + * * @return A vector of lesions. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionVector GetAllLesionsOfImage(const DataNode* imageNode); /** * @brief Returns a vector of all lesions that are valid for the given case, given a specific control point. * * @param caseID The current case identifier is defined by the given string. * @param controlPoint A specific control point which has to be available at a returned (found) lesion: * Only those lesions are returned for which the image of the associated segmentation is linked to the given control point. * If the control point instance does not exist, an empty vector is returned. + * * @return A vector of lesions. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionVector GetAllLesionsOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); /** * @brief Returns a vector of all lesions that are valid for the given case, given a specific information type. * * @param caseID The current case identifier is defined by the given string. * @param informationType A specific information type which has to be available at a returned (found) lesion: * Only those lesions are returned for which the image of the associated segmentation is of the given information type. * If the information type instance does not exist, an empty vector is returned. + * * @return A vector of lesions. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionVector GetAllLesionsOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType); /** * @brief Returns a vector of all lesions that are valid for the given case, given a specific control point and a specific information type. * * @param caseID The current case identifier is defined by the given string. * @param controlPoint A specific control point which has to be available at a returned (found) lesion: * Only those lesions are returned for which the image of the associated segmentation is linked to the given control point. * If the control point instance does not exist, an empty vector is returned. * @param informationType A specific information type which has to be available at a returned (found) lesion: * Only those lesions are returned for which the image of the associated segmentation is of the given information type. * If the information type instance does not exist, an empty vector is returned. + * * @return A vector of lesions. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionVector GetAllSpecificLesions(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType); /** * @brief Check if the given segmentation refers to an existing lesion instance. * This function can be used before calling 'GetRepresentedLesion' in order to avoid a possible exception. * * @param segmentationNode The segmentation identifier is extracted from the given data node. + * * @return True, if the segmentation refers to an existing lesion; false otherwise. */ MITKSEMANTICRELATIONS_EXPORT bool IsRepresentingALesion(const DataNode* segmentationNode); /** * @brief Check if the segmentation identified by the given segmentation ID refers to an existing lesion instance. * This function can be used before calling 'GetRepresentedLesion' in order to avoid a possible exception. * * @param caseID The current case identifier is defined by the given string. * @param segmentationID The segmentation node identifier is defined by the given string. + * * @return True, if the segmentation refers to an existing lesion; false otherwise. */ MITKSEMANTICRELATIONS_EXPORT bool IsRepresentingALesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID); /** * @brief Check if the given lesion is present on the given data node. * The function receives the case- and the node-ID from the DICOM tags of the node itself. * It uses node predicates to decide if the node is an image or a segmentation node. * * @param lesion A lesion with a UID that identifies the corresponding lesion instance. * @param dataNode A data node to check. + * * @return True, if the lesion is present on the data node; false otherwise. */ MITKSEMANTICRELATIONS_EXPORT bool IsLesionPresent(const SemanticTypes::Lesion& lesion, const DataNode* dataNode); /** * @brief Check if the given lesion is related to the image identified by the given image ID. * Each lesion is represented by a segmentation which is connected to its parent image. * * @param caseID The current case identifier is defined by the given string. * @param lesion A lesion with a UID that identifies the corresponding lesion instance. * @param imageID The image node identifier is defined by the given string. + * * @return True, if the lesion is related to image identified by the given image ID; false otherwise. */ MITKSEMANTICRELATIONS_EXPORT bool IsLesionPresentOnImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ID& imageID); /** * @brief Check if the given lesion is present on the segmentation identified by the given segmentation ID. * * @param caseID The current case identifier is defined by the given string. * @param lesion A lesion with a UID that identifies the corresponding lesion instance. * @param segmentationID The segmentation node identifier is defined by the given string. + * * @return True, if the lesion is present on the segmentation identified by the given segmentation ID; false otherwise. */ MITKSEMANTICRELATIONS_EXPORT bool IsLesionPresentOnSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ID& segmentationID); /** * @brief Check if the given lesion is present at the given control point. * * @param caseID The current case identifier is defined by the given string. * @param lesion A lesion with a UID that identifies the corresponding lesion instance. * @param controlPoint A control point with a UID that identifies the corresponding control point instance. + * * @return True, if the lesion is present at the given control point; false otherwise. */ MITKSEMANTICRELATIONS_EXPORT bool IsLesionPresentAtControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ControlPoint& controlPoint); /** * @brief Check if the given data node exists in the relation storage. * The function receives the case- and the node-ID from the DICOM tags of the node itself. * It uses node predicates to decide if the node is an image or a segmentation node and searches * through the corresponding relations. * * @param dataNode A data node to check. + * * @return True, if the data node exists; false otherwise. */ MITKSEMANTICRELATIONS_EXPORT bool InstanceExists(const DataNode* dataNode); /** * @brief Check if the given lesion instance exists. * This function can be used before calling 'AddLesionInstance' in order to avoid a possible exception. * * @param caseID The current case identifier is defined by the given string. * @param lesion A lesion with a UID that identifies the corresponding lesion instance. + * * @return True, if the lesion instance exists; false otherwise. */ MITKSEMANTICRELATIONS_EXPORT bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); /** * @brief Return a vector of all image IDs that identify images that are related to the given lesion. * Each lesion is represented by a segmentation which is connected to its parent image. * If the lesion is not represented by any segmentation, an empty vector is returned. * * @pre The UID of the lesion has to exist for a lesion instance. * @throw SemanticRelationException, if UID of the lesion does not exist for a lesion instance (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param lesion A lesion with a UID that identifies the corresponding lesion instance. + * * @return A vector of IDs identifying images that are related to the given lesion. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::IDVector GetAllImageIDsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); /** * @brief Return a vector of all image IDs that identify images that are related to the given examination period. * If the examination period is not used by and image, an empty vector is returned. * * @pre The UID of the examination period has to exist for an examination period instance. * @throw SemanticRelationException, if UID of the examination period does not exist for an examination period instance (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param examinationPeriod An examination period with a UID that identifies the corresponding examination period instance. + * * @return A vector of IDs identifying images that are related to the given examination period. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::IDVector GetAllImageIDsOfExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod); /** * @brief Return the control point of a data node. * If the data node is not linked to a control point or the data node refers to a non-existing control point, * a control point with an empty UID is returned. * * @pre The given image data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). * * @param dataNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. + * * @return The control point of the given data node. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint GetControlPointOfImage(const DataNode* dataNode); /** * @brief Return a vector of all control points that are valid for the given case, given a specific lesion * * @param caseID The current case identifier is defined by the given string. * @param lesion A specific lesion which has to be available at a returned (found) control point: * Only those control points are returned for which an associated data has a segmentation that references the given lesion. * If the lesion does not exists, an empty vector is returned. + * * @return A vector of control points. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPointVector GetAllControlPointsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); /** * @brief Return a vector of all control points that are valid for the given case, given a specific information type. * * @param caseID The current case identifier is defined by the given string. * @param informationType A specific information type which has to be available at a returned (found) control point: * Only those control points are returned for which an associated data has the given information type. * If the information type instance does not exists, an empty vector is returned. + * * @return A vector of control points. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPointVector GetAllControlPointsOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType); /** * @brief Check if the given control point instance exists. * This function can be used before adding, linking and unlinking control points to avoid a possible exception. * * @param caseID The current case identifier is defined by the given string. * @param controlPoint A control point with a UID that identifies the corresponding control point instance. + * * @return True, if the control point instance exists; false otherwise. */ MITKSEMANTICRELATIONS_EXPORT bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); /** * @brief Check if the given examination period instance exists. * This function can be used before calling 'AddExaminationPeriod' in order to avoid a possible exception. * * @param caseID The current case identifier is defined by the given string. * @param examinationPeriod An examination period with a UID that identifies the corresponding examination period instance. + * * @return True, if the examination period instance exists; false otherwise. */ MITKSEMANTICRELATIONS_EXPORT bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod); /** * @brief Return the information type of the given image. * If the image does not contain any information type, an empty information type is returned. * * @pre The given image data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). * * @param imageNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. + * * @return The information type of the given data node. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::InformationType GetInformationTypeOfImage(const DataNode* imageNode); /** * @brief Return a vector of all information types that are valid for the given case, given a specific control point. * * @param caseID The current case identifier is defined by the given string. * @param controlPoint A specific control point which has to be available at a returned (found) information type: * Only those information types are returned for which an associated data is linked to the given control point. * If the control point instance does not exist, an empty vector is returned. + * * @return A vector of information types. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::InformationTypeVector GetAllInformationTypesOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); /** * @brief Check if the given information type exists. * * @param caseID The current case identifier is defined by the given string. * @param informationType An information type. + * * @return True, if the information type exists; false otherwise. */ MITKSEMANTICRELATIONS_EXPORT bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType); /** * @brief Determine if the given information type contains images, which are connected to segmentations that represent the given lesion. * If the lesion or the information type are not correctly stored, the function returns false. * * @param caseID The current case identifier is defined by the given string. * @param lesion A Lesion with a UID that identifies the corresponding lesion instance. * @param informationType An information type that identifies the corresponding information type instance. * * @return True, if the given information type contains data that is related to the given lesion; false otherwise. */ MITKSEMANTICRELATIONS_EXPORT bool SpecificImageExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::InformationType& informationType); /** * @brief Determine if the given control point contains images, which are connected to segmentations that represent the given lesion. * If the lesion or the control point are not correctly stored, the function returns false. * * @param caseID The current case identifier is defined by the given string. * @param lesion A Lesion with a UID that identifies the corresponding lesion instance. * @param controlPoint A control point with a UID that identifies the corresponding control point instance. * * @return True, if the given control point contains data that is related to the given lesion; false otherwise. */ MITKSEMANTICRELATIONS_EXPORT bool SpecificImageExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ControlPoint& controlPoint); /** * @brief Determine if the given control point contains images, which refer to the given information type. * If the information type or the control point are not correctly stored, the function returns false. * * @param caseID The current case identifier is defined by the given string. * @param informationType An information type that identifies the corresponding information type instance. * @param controlPoint A control point with a UID that identifies the corresponding control point instance. * * @return True, if the given control point contains data that is related to the given information type; false otherwise. */ MITKSEMANTICRELATIONS_EXPORT bool SpecificImageExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType, const SemanticTypes::ControlPoint& controlPoint); } // namespace SemanticRelationsInference } // namespace mitk #endif // MITKSEMANTICRELATIONSINFERENCE_H diff --git a/Modules/SemanticRelations/src/mitkControlPointManager.cpp b/Modules/SemanticRelations/src/mitkControlPointManager.cpp index 7b0aee79eb..52d2ee7f4e 100644 --- a/Modules/SemanticRelations/src/mitkControlPointManager.cpp +++ b/Modules/SemanticRelations/src/mitkControlPointManager.cpp @@ -1,222 +1,222 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // semantic relations module #include "mitkControlPointManager.h" #include "mitkDICOMHelper.h" #include "mitkRelationStorage.h" #include "mitkSemanticRelationException.h" #include "mitkUIDGeneratorBoost.h" // mitk core #include mitk::SemanticTypes::ControlPoint mitk::GenerateControlPoint(const DataNode* datanode) { SemanticTypes::ControlPoint controlPoint; try { controlPoint = GetDICOMDateFromDataNode(datanode); } catch (SemanticRelationException& e) { - mitkReThrow(e) << "Cannot generate a control point from the DICOM tag of the given data node"; + mitkReThrow(e) << "Cannot generate a control point from the DICOM tag of the given data node."; } controlPoint.UID = UIDGeneratorBoost::GenerateUID(); return controlPoint; } mitk::SemanticTypes::ControlPoint mitk::GetControlPointByUID(const SemanticTypes::ID& controlPointUID, const std::vector& allControlPoints) { auto lambda = [&controlPointUID](const SemanticTypes::ControlPoint& currentControlPoint) { return currentControlPoint.UID == controlPointUID; }; const auto existingControlPoint = std::find_if(allControlPoints.begin(), allControlPoints.end(), lambda); mitk::SemanticTypes::ControlPoint controlPoint; if (existingControlPoint != allControlPoints.end()) { controlPoint = *existingControlPoint; } return controlPoint; } mitk::SemanticTypes::ControlPoint mitk::FindExistingControlPoint(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ControlPointVector& allControlPoints) { for (const auto& currentControlPoint : allControlPoints) { if (controlPoint.date == currentControlPoint.date) { return currentControlPoint; } } return SemanticTypes::ControlPoint(); } mitk::SemanticTypes::ControlPoint mitk::FindClosestControlPoint(const SemanticTypes::ControlPoint& controlPoint, SemanticTypes::ControlPointVector& allControlPoints) { if (allControlPoints.empty()) { return SemanticTypes::ControlPoint(); } // sort the vector of control points for easier lookup std::sort(allControlPoints.begin(), allControlPoints.end()); // new control point does not match an existing control point // check if the control point is close to an already existing control point std::vector::const_iterator it; for (it = allControlPoints.begin(); it != allControlPoints.end(); ++it) { if (controlPoint.date < it->date) { break; } } SemanticTypes::ControlPoint nextControlPoint; SemanticTypes::ControlPoint previousControlPoint; if (it == allControlPoints.begin()) { // new date is smaller ("older") than the smallest already existing control point nextControlPoint = *it; } else if (it != allControlPoints.end()) { // new date is greater ("newer") than an already existing control point, // but smaller ("older") than another already existing control point nextControlPoint = *it; previousControlPoint = *(--it); } else { // new date is greater ("newer") than the greatest already existing control point previousControlPoint = *(--it); } // test distance to next and previous time period double distanceToNextExaminationPeriod = nextControlPoint.DistanceInDays(controlPoint); double distanceToPreviousExaminationPeriod = previousControlPoint.DistanceInDays(controlPoint); SemanticTypes::ControlPoint closestControlPoint; int closestDistanceInDays = 0; if (distanceToNextExaminationPeriod < distanceToPreviousExaminationPeriod) { // control point is closer to the next control point closestControlPoint = nextControlPoint; closestDistanceInDays = distanceToNextExaminationPeriod; } else { // control point is closer to the previous control point closestControlPoint = previousControlPoint; closestDistanceInDays = distanceToPreviousExaminationPeriod; } int THRESHOLD_DISTANCE_IN_DAYS = 30; if (closestDistanceInDays <= THRESHOLD_DISTANCE_IN_DAYS) { return closestControlPoint; } return SemanticTypes::ControlPoint(); } -mitk::SemanticTypes::ExaminationPeriod mitk::FindExaminationPeriod(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriodVector& allExaminationPeriods) +mitk::SemanticTypes::ExaminationPeriod mitk::FindContainingExaminationPeriod(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriodVector& allExaminationPeriods) { for (const auto& examinationPeriod : allExaminationPeriods) { for (const auto& UID : examinationPeriod.controlPointUIDs) { if (controlPoint.UID == UID) { return examinationPeriod; } } } return SemanticTypes::ExaminationPeriod(); } mitk::SemanticTypes::ExaminationPeriod mitk::FindFittingExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { SemanticTypes::ExaminationPeriod specificExaminationPeriod; SemanticTypes::ControlPoint specificControlPoint; // find the closest control point auto allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); auto existingControlPoint = FindExistingControlPoint(controlPoint, allControlPoints); if (!existingControlPoint.UID.empty()) { specificControlPoint = existingControlPoint; } else { auto closestControlPoint = FindClosestControlPoint(controlPoint, allControlPoints); if (!closestControlPoint.UID.empty()) { specificControlPoint = closestControlPoint; } } // find the containing examination period auto allExaminationPeriods = RelationStorage::GetAllExaminationPeriodsOfCase(caseID); return FindContainingExaminationPeriod(specificControlPoint, allExaminationPeriods); } mitk::SemanticTypes::ExaminationPeriod mitk::FindFittingExaminationPeriod(const DataNode* dataNode) { SemanticTypes::CaseID caseID = ""; SemanticTypes::ControlPoint controlPoint; try { caseID = GetCaseIDFromDataNode(dataNode); controlPoint = GetDICOMDateFromDataNode(dataNode); } catch (SemanticRelationException& e) { - mitkReThrow(e) << "Cannot find an examination period. "; + mitkReThrow(e) << "Cannot find an examination period."; } return FindFittingExaminationPeriod(caseID, controlPoint); } void mitk::SortExaminationPeriods(SemanticTypes::ExaminationPeriodVector& allExaminationPeriods, const SemanticTypes::ControlPointVector& allControlPoints) { auto lambda = [allControlPoints](const SemanticTypes::ExaminationPeriod& leftExaminationPeriod, const SemanticTypes::ExaminationPeriod& rightExaminationPeriod) { if (leftExaminationPeriod.controlPointUIDs.empty()) { return true; } if (rightExaminationPeriod.controlPointUIDs.empty()) { return false; } const auto leftUID = leftExaminationPeriod.controlPointUIDs.front(); const auto rightUID = rightExaminationPeriod.controlPointUIDs.front(); const auto& leftControlPoint = GetControlPointByUID(leftUID, allControlPoints); const auto& rightControlPoint = GetControlPointByUID(rightUID, allControlPoints); return leftControlPoint.date < rightControlPoint.date; }; std::sort(allExaminationPeriods.begin(), allExaminationPeriods.end(), lambda); } diff --git a/Modules/SemanticRelations/src/mitkDICOMHelper.cpp b/Modules/SemanticRelations/src/mitkDICOMHelper.cpp index 7be974ef13..21e2f1fc21 100644 --- a/Modules/SemanticRelations/src/mitkDICOMHelper.cpp +++ b/Modules/SemanticRelations/src/mitkDICOMHelper.cpp @@ -1,190 +1,190 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // semantic relations module #include "mitkDICOMHelper.h" #include "mitkSemanticRelationException.h" #include "mitkUIDGeneratorBoost.h" // mitk core #include // c++ #include mitk::SemanticTypes::ControlPoint GetControlPointFromString(const std::string& dateAsString) { // date expected to be YYYYMMDD (8 characters) if (dateAsString.size() != 8) { // string does not represent a DICOM date mitkThrowException(mitk::SemanticRelationException) << "Not a valid DICOM date format."; } mitk::SemanticTypes::ControlPoint controlPoint; controlPoint.SetDateFromString(dateAsString); return controlPoint; } std::string mitk::GetCaseIDDICOMProperty() { // extract suitable DICOM tag to use as the case id // two alternatives can be used: // - DICOM tag "0x0010, 0x0010" is PatientName // - DICOM tag "0x0010, 0x0020" is PatientID // in the current implementation the PatientName (0x0010, 0x0010) is used return GeneratePropertyNameForDICOMTag(0x0010, 0x0010); } std::string mitk::GetNodeIDDICOMProperty() { // extract suitable DICOM tag to use as the data node id // DICOM tag "0x0020, 0x000e" is SeriesInstanceUID return GeneratePropertyNameForDICOMTag(0x0020, 0x000e); } std::string mitk::GetDateDICOMProperty() { // extract suitable DICOM tag to use as the data node id // DICOM tag "0x0008, 0x0022" is AcquisitionDate return GeneratePropertyNameForDICOMTag(0x0008, 0x0022); } std::string mitk::GetModalityDICOMProperty() { // extract suitable DICOM tag to use as the information type // DICOM tag "0x0008, 0x0060" is Modality return GeneratePropertyNameForDICOMTag(0x0008, 0x0060); } -mitk::SemanticTypes::CaseID mitk::GetCaseIDFromDataNode(const mitk::DataNode* dataNode) +mitk::SemanticTypes::CaseID mitk::GetCaseIDFromDataNode(const DataNode* dataNode) { if (nullptr == dataNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } - mitk::BaseData* baseData = dataNode->GetData(); + BaseData* baseData = dataNode->GetData(); if (nullptr == baseData) { mitkThrowException(SemanticRelationException) << "No valid base data."; } - mitk::BaseProperty* dicomTag = baseData->GetProperty(GetCaseIDDICOMProperty().c_str()); + BaseProperty* dicomTag = baseData->GetProperty(GetCaseIDDICOMProperty().c_str()); if (nullptr == dicomTag) { mitkThrowException(SemanticRelationException) << "Not a valid DICOM property."; } std::string dicomTagAsString = dicomTag->GetValueAsString(); return dicomTagAsString; } -mitk::SemanticTypes::ID mitk::GetIDFromDataNode(const mitk::DataNode* dataNode) +mitk::SemanticTypes::ID mitk::GetIDFromDataNode(const DataNode* dataNode) { if (nullptr == dataNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } - mitk::BaseData* baseData = dataNode->GetData(); + BaseData* baseData = dataNode->GetData(); if (nullptr == baseData) { mitkThrowException(SemanticRelationException) << "No valid base data."; } - mitk::BaseProperty* dicomTag = baseData->GetProperty(GetNodeIDDICOMProperty().c_str()); + BaseProperty* dicomTag = baseData->GetProperty(GetNodeIDDICOMProperty().c_str()); if (nullptr == dicomTag) { mitkThrowException(SemanticRelationException) << "Not a valid DICOM property."; } std::string dicomTagAsString = dicomTag->GetValueAsString(); return dicomTagAsString; } -mitk::SemanticTypes::ControlPoint mitk::GetDICOMDateFromDataNode(const mitk::DataNode* dataNode) +mitk::SemanticTypes::ControlPoint mitk::GetDICOMDateFromDataNode(const DataNode* dataNode) { if (nullptr == dataNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } - mitk::BaseData* baseData = dataNode->GetData(); + BaseData* baseData = dataNode->GetData(); if (nullptr == baseData) { mitkThrowException(SemanticRelationException) << "No valid base data."; } - mitk::BaseProperty* acquisitionDateProperty = baseData->GetProperty(GetDateDICOMProperty().c_str()); + BaseProperty* acquisitionDateProperty = baseData->GetProperty(GetDateDICOMProperty().c_str()); if (nullptr == acquisitionDateProperty) { mitkThrowException(SemanticRelationException) << "Not a valid DICOM property."; } std::string acquisitionDateAsString = acquisitionDateProperty->GetValueAsString(); SemanticTypes::ControlPoint controlPoint; try { controlPoint = GetControlPointFromString(acquisitionDateAsString); } catch (SemanticRelationException &e) { mitkReThrow(e) << "Cannot retrieve a valid DICOM date."; } return controlPoint; } -mitk::SemanticTypes::InformationType mitk::GetDICOMModalityFromDataNode(const mitk::DataNode* dataNode) +mitk::SemanticTypes::InformationType mitk::GetDICOMModalityFromDataNode(const DataNode* dataNode) { if (nullptr == dataNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } - mitk::BaseData* baseData = dataNode->GetData(); + BaseData* baseData = dataNode->GetData(); if (nullptr == baseData) { mitkThrowException(SemanticRelationException) << "No valid base data."; } - mitk::BaseProperty* dicomTag = baseData->GetProperty(GetModalityDICOMProperty().c_str()); + BaseProperty* dicomTag = baseData->GetProperty(GetModalityDICOMProperty().c_str()); if (nullptr == dicomTag) { mitkThrowException(SemanticRelationException) << "Not a valid DICOM property."; } std::string dicomTagAsString = dicomTag->GetValueAsString(); return dicomTagAsString; } std::string mitk::TrimDICOM(const std::string& identifier) { if (identifier.empty()) { return identifier; } // leading whitespace std::size_t first = identifier.find_first_not_of(' '); if (std::string::npos == first) { return ""; } // trailing whitespace std::size_t last = identifier.find_last_not_of(' '); return identifier.substr(first, last - first + 1); } diff --git a/Modules/SemanticRelations/src/mitkLesionData.cpp b/Modules/SemanticRelations/src/mitkLesionData.cpp index 172b364f65..d2c8ced6e4 100644 --- a/Modules/SemanticRelations/src/mitkLesionData.cpp +++ b/Modules/SemanticRelations/src/mitkLesionData.cpp @@ -1,43 +1,38 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // semantic relations module #include "mitkLesionData.h" mitk::LesionData::LesionData(const SemanticTypes::Lesion& lesion/* = SemanticTypes::Lesion()*/) { m_Lesion = lesion; } -mitk::LesionData::~LesionData() -{ - // nothing here -} - void mitk::LesionData::SetLesion(const SemanticTypes::Lesion& lesion) { m_Lesion = lesion; } void mitk::LesionData::SetLesionPresence(const std::vector& lesionPresence) { m_LesionPresence = lesionPresence; } void mitk::LesionData::SetLesionVolume(const std::vector& lesionVolume) { m_LesionVolume = lesionVolume; } diff --git a/Modules/SemanticRelations/src/mitkLesionManager.cpp b/Modules/SemanticRelations/src/mitkLesionManager.cpp index 9134b03540..6c623ad2c9 100644 --- a/Modules/SemanticRelations/src/mitkLesionManager.cpp +++ b/Modules/SemanticRelations/src/mitkLesionManager.cpp @@ -1,106 +1,106 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // semantic relations module #include "mitkLesionManager.h" #include "mitkRelationStorage.h" #include "mitkSemanticRelationException.h" #include "mitkSemanticRelationsInference.h" #include "mitkUIDGeneratorBoost.h" mitk::SemanticTypes::Lesion mitk::GenerateNewLesion(const std::string& lesionClassType/* = ""*/) { SemanticTypes::Lesion lesion; - lesion.UID = mitk::UIDGeneratorBoost::GenerateUID(); + lesion.UID = UIDGeneratorBoost::GenerateUID(); lesion.name = "New lesion"; lesion.lesionClass = GenerateNewLesionClass(lesionClassType); return lesion; } mitk::SemanticTypes::LesionClass mitk::GenerateNewLesionClass(const std::string& lesionClassType/* = ""*/) { SemanticTypes::LesionClass lesionClass; - lesionClass.UID = mitk::UIDGeneratorBoost::GenerateUID(); + lesionClass.UID = UIDGeneratorBoost::GenerateUID(); lesionClass.classType = lesionClassType; return lesionClass; } mitk::SemanticTypes::Lesion mitk::GetLesionByUID(const SemanticTypes::ID& lesionUID, const std::vector& allLesions) { auto lambda = [&lesionUID](const SemanticTypes::Lesion& currentLesion) { return currentLesion.UID == lesionUID; }; const auto existingLesion = std::find_if(allLesions.begin(), allLesions.end(), lambda); SemanticTypes::Lesion lesion; if (existingLesion != allLesions.end()) { lesion = *existingLesion; } return lesion; } mitk::SemanticTypes::LesionClass mitk::FindExistingLesionClass(const std::string& lesionClassType, const std::vector& allLesionClasses) { auto lambda = [&lesionClassType](const SemanticTypes::LesionClass& currentLesionClass) { return currentLesionClass.classType == lesionClassType; }; const auto existingLesionClass = std::find_if(allLesionClasses.begin(), allLesionClasses.end(), lambda); SemanticTypes::LesionClass lesionClass; if (existingLesionClass != allLesionClasses.end()) { lesionClass = *existingLesionClass; } return lesionClass; } void mitk::ComputeLesionPresence(LesionData& lesionData, const SemanticTypes::CaseID& caseID) { std::vector lesionPresence; - SemanticTypes::Lesion lesion = lesionData.GetLesion(); + auto lesion = lesionData.GetLesion(); bool presence = false; - SemanticTypes::ControlPointVector controlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); + auto controlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); // sort the vector of control points for the timeline std::sort(controlPoints.begin(), controlPoints.end()); - SemanticTypes::InformationTypeVector informationTypes = mitk::RelationStorage::GetAllInformationTypesOfCase(caseID); + auto informationTypes = RelationStorage::GetAllInformationTypesOfCase(caseID); for (const auto& informationType : informationTypes) { for (const auto& controlPoint : controlPoints) { try { presence = SemanticRelationsInference::IsLesionPresentAtControlPoint(caseID, lesion, controlPoint); } catch (SemanticRelationException&) { presence = false; } lesionPresence.push_back(presence); } } lesionData.SetLesionPresence(lesionPresence); } diff --git a/Modules/SemanticRelations/src/mitkSemanticRelationsDataStorageAccess.cpp b/Modules/SemanticRelations/src/mitkSemanticRelationsDataStorageAccess.cpp index decd9c8593..e064de7ef5 100644 --- a/Modules/SemanticRelations/src/mitkSemanticRelationsDataStorageAccess.cpp +++ b/Modules/SemanticRelations/src/mitkSemanticRelationsDataStorageAccess.cpp @@ -1,342 +1,342 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSemanticRelationsDataStorageAccess.h" // semantic relations module #include "mitkControlPointManager.h" #include "mitkDICOMHelper.h" #include "mitkNodePredicates.h" #include "mitkRelationStorage.h" #include "mitkSemanticRelationException.h" #include "mitkSemanticRelationsInference.h" // c++ #include #include mitk::SemanticRelationsDataStorageAccess::SemanticRelationsDataStorageAccess(DataStorage* dataStorage) : m_DataStorage(dataStorage) { // nothing here } /************************************************************************/ /* functions to get instances / attributes */ /************************************************************************/ mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllSegmentationsOfCase(const SemanticTypes::CaseID& caseID) const { if (m_DataStorage.IsExpired()) { mitkThrowException(SemanticRelationException) << "Not a valid data storage."; } - std::vector allSegmentationIDsOfCase = RelationStorage::GetAllSegmentationIDsOfCase(caseID); - std::vector allSegmentationsOfCase; + SemanticTypes::IDVector allSegmentationIDsOfCase = RelationStorage::GetAllSegmentationIDsOfCase(caseID); + DataNodeVector allSegmentationsOfCase; // get all segmentation nodes of the current data storage // only those nodes are respected, that are currently held in the data storage DataStorage::SetOfObjects::ConstPointer segmentationNodes = m_DataStorage.Lock()->GetSubset(NodePredicates::GetSegmentationPredicate()); for (auto it = segmentationNodes->Begin(); it != segmentationNodes->End(); ++it) { DataNode* segmentationNode = it->Value(); - std::string caseID; - std::string segmentationID; + SemanticTypes::CaseID currentCaseID; + SemanticTypes::ID segmentationID; try { // find the corresponding segmentation node for the given segmentation ID - caseID = GetCaseIDFromDataNode(segmentationNode); + currentCaseID = GetCaseIDFromDataNode(segmentationNode); segmentationID = GetIDFromDataNode(segmentationNode); } catch (SemanticRelationException&) { // found a segmentation node that is not stored in the semantic relations // this segmentation node does not have any DICOM information --> exception thrown // continue with the next segmentation to compare IDs continue; } - if (caseID == caseID && (std::find(allSegmentationIDsOfCase.begin(), allSegmentationIDsOfCase.end(), segmentationID) != allSegmentationIDsOfCase.end())) + if (caseID == currentCaseID && (std::find(allSegmentationIDsOfCase.begin(), allSegmentationIDsOfCase.end(), segmentationID) != allSegmentationIDsOfCase.end())) { // found current image node in the storage, add it to the return vector allSegmentationsOfCase.push_back(segmentationNode); } } return allSegmentationsOfCase; } mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllSegmentationsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const { if (SemanticRelationsInference::InstanceExists(caseID, lesion)) { // lesion exists, retrieve all case segmentations from the storage DataNodeVector allSegmentationsOfLesion = GetAllSegmentationsOfCase(caseID); // filter all segmentations: check for semantic relation with the given lesion using a lambda function auto lambda = [&lesion, this](DataNode::Pointer segmentation) { try { SemanticTypes::Lesion representedLesion = SemanticRelationsInference::GetLesionOfSegmentation(segmentation); return lesion.UID != representedLesion.UID; } catch (const SemanticRelationException&) { return true; } }; allSegmentationsOfLesion.erase(std::remove_if(allSegmentationsOfLesion.begin(), allSegmentationsOfLesion.end(), lambda), allSegmentationsOfLesion.end()); return allSegmentationsOfLesion; } else { mitkThrowException(SemanticRelationException) << "Could not find an existing lesion instance for the given caseID " << caseID << " and lesion " << lesion.UID << "."; } } mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllImagesOfCase(const SemanticTypes::CaseID& caseID) const { if (m_DataStorage.IsExpired()) { mitkThrowException(SemanticRelationException) << "Not a valid data storage."; } - std::vector allImageIDsOfCase = RelationStorage::GetAllImageIDsOfCase(caseID); - std::vector allImagesOfCase; + SemanticTypes::IDVector allImageIDsOfCase = RelationStorage::GetAllImageIDsOfCase(caseID); + DataNodeVector allImagesOfCase; // get all image nodes of the current data storage // only those nodes are respected, that are currently held in the data storage DataStorage::SetOfObjects::ConstPointer imageNodes = m_DataStorage.Lock()->GetSubset(NodePredicates::GetImagePredicate()); for (auto it = imageNodes->Begin(); it != imageNodes->End(); ++it) { DataNode* imageNode = it->Value(); - std::string caseID; - std::string imageID; + SemanticTypes::CaseID currentCaseID; + SemanticTypes::ID imageID; try { // find the corresponding image node for the given segmentation ID - caseID = GetCaseIDFromDataNode(imageNode); + currentCaseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException&) { // found an image node that is not stored in the semantic relations // this image node does not have any DICOM information --> exception thrown // continue with the next image to compare IDs continue; } - if (caseID == caseID && (std::find(allImageIDsOfCase.begin(), allImageIDsOfCase.end(), imageID) != allImageIDsOfCase.end())) + if (caseID == currentCaseID && (std::find(allImageIDsOfCase.begin(), allImageIDsOfCase.end(), imageID) != allImageIDsOfCase.end())) { // found current image node in the storage, add it to the return vector allImagesOfCase.push_back(imageNode); } } return allImagesOfCase; } mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllImagesByID(const SemanticTypes::IDVector& imageIDs) const { if (m_DataStorage.IsExpired()) { mitkThrowException(SemanticRelationException) << "Not a valid data storage."; } DataNodeVector allImagesOfCase; // get all image nodes of the current data storage // only those nodes are respected, that are currently held in the data storage DataStorage::SetOfObjects::ConstPointer imageNodes = m_DataStorage.Lock()->GetSubset(NodePredicates::GetImagePredicate()); for (auto it = imageNodes->Begin(); it != imageNodes->End(); ++it) { DataNode* imageNode = it->Value(); SemanticTypes::CaseID currentCaseID; SemanticTypes::ID imageID; try { // find the corresponding image node for the given segmentation ID imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException&) { // found an image node that is not stored in the semantic relations // this image node does not have any DICOM information --> exception thrown // continue with the next image to compare IDs continue; } if (std::find(imageIDs.begin(), imageIDs.end(), imageID) != imageIDs.end()) { // found current image node in the storage, add it to the return vector allImagesOfCase.push_back(imageNode); } } return allImagesOfCase; } mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllImagesOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const { if (m_DataStorage.IsExpired()) { mitkThrowException(SemanticRelationException) << "Not a valid data storage."; } DataNodeVector allImagesOfLesion; // 1. get all segmentations that define the lesion // 2. retrieve the parent node (source) of the found segmentation node DataNodeVector allSegmentationsOfLesion = GetAllSegmentationsOfLesion(caseID, lesion); for (const auto& segmentationNode : allSegmentationsOfLesion) { // get parent node of the current segmentation node with the node predicate DataStorage::SetOfObjects::ConstPointer parentNodes = m_DataStorage.Lock()->GetSources(segmentationNode, NodePredicates::GetImagePredicate(), false); for (auto it = parentNodes->Begin(); it != parentNodes->End(); ++it) { DataNode::Pointer dataNode = it->Value(); allImagesOfLesion.push_back(it->Value()); } } std::sort(allImagesOfLesion.begin(), allImagesOfLesion.end()); allImagesOfLesion.erase(std::unique(allImagesOfLesion.begin(), allImagesOfLesion.end()), allImagesOfLesion.end()); return allImagesOfLesion; } mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllSpecificImages(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const { if (SemanticRelationsInference::InstanceExists(caseID, controlPoint)) { if (SemanticRelationsInference::InstanceExists(caseID, informationType)) { // control point exists, information type exists, retrieve all images from the storage DataNodeVector allImagesOfCase = GetAllImagesOfCase(caseID); // filter all images to remove the ones with a different control point and information type using a lambda function auto lambda = [&controlPoint, &informationType, this](DataNode::Pointer imageNode) { return (informationType != SemanticRelationsInference::GetInformationTypeOfImage(imageNode)) || (controlPoint.date != SemanticRelationsInference::GetControlPointOfImage(imageNode).date); }; allImagesOfCase.erase(std::remove_if(allImagesOfCase.begin(), allImagesOfCase.end(), lambda), allImagesOfCase.end()); return allImagesOfCase; } else { - mitkThrowException(SemanticRelationException) << "Could not find an existing information type for the given caseID " << caseID << " and information type " << informationType; + mitkThrowException(SemanticRelationException) << "Could not find an existing information type for the given caseID " << caseID << " and information type " << informationType << "."; } } else { - mitkThrowException(SemanticRelationException) << "Could not find an existing control point for the given caseID " << caseID << " and control point " << controlPoint.UID; + mitkThrowException(SemanticRelationException) << "Could not find an existing control point for the given caseID " << caseID << " and control point " << controlPoint.UID << "."; } } mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllSpecificImages(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType, const SemanticTypes::ExaminationPeriod& examinationPeriod) const { if (SemanticRelationsInference::InstanceExists(caseID, informationType)) { if (SemanticRelationsInference::InstanceExists(caseID, examinationPeriod)) { // examination period exists, information type exists, retrieve all imageIDs from the storage auto allImageIDsOfExaminationPeriod = SemanticRelationsInference::GetAllImageIDsOfExaminationPeriod(caseID, examinationPeriod); // filter all images to remove the ones with a different information type using a lambda function auto lambda = [&caseID, &informationType, this](SemanticTypes::ID imageID) { return (informationType != RelationStorage::GetInformationTypeOfImage(caseID, imageID)); }; allImageIDsOfExaminationPeriod.erase(std::remove_if(allImageIDsOfExaminationPeriod.begin(), allImageIDsOfExaminationPeriod.end(), lambda), allImageIDsOfExaminationPeriod.end()); auto allImagesOfExaminationPeriod = GetAllImagesByID(allImageIDsOfExaminationPeriod); return allImagesOfExaminationPeriod; } else { - mitkThrowException(SemanticRelationException) << "Could not find an existing examination period for the given caseID " << caseID << " and examination period " << examinationPeriod.name; + mitkThrowException(SemanticRelationException) << "Could not find an existing examination period for the given caseID " << caseID << " and examination period " << examinationPeriod.name << "."; } } else { - mitkThrowException(SemanticRelationException) << "Could not find an existing information type for the given caseID " << caseID << " and information type " << informationType; + mitkThrowException(SemanticRelationException) << "Could not find an existing information type for the given caseID " << caseID << " and information type " << informationType << "."; } } mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllSpecificSegmentations(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const { if (m_DataStorage.IsExpired()) { mitkThrow() << "Not a valid data storage."; } DataNodeVector allSpecificImages; try { allSpecificImages = GetAllSpecificImages(caseID, controlPoint, informationType); } catch (SemanticRelationException& e) { - mitkReThrow(e) << "Cannot get the specific segmentation"; + mitkReThrow(e) << "Cannot get the specific segmentation."; } DataNodeVector allSpecificSegmentations; for (const auto& imageNode : allSpecificImages) { DataStorage::SetOfObjects::ConstPointer segmentationNodes = m_DataStorage.Lock()->GetDerivations(imageNode, NodePredicates::GetSegmentationPredicate(), false); for (auto it = segmentationNodes->Begin(); it != segmentationNodes->End(); ++it) { allSpecificSegmentations.push_back(it->Value()); } } return allSpecificSegmentations; } mitk::DataNode::Pointer mitk::SemanticRelationsDataStorageAccess::GetSpecificSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType, const SemanticTypes::Lesion& lesion) const { if (m_DataStorage.IsExpired()) { mitkThrow() << "Not a valid data storage."; } DataNodeVector allSpecificSegmentations; try { allSpecificSegmentations = GetAllSpecificSegmentations(caseID, controlPoint, informationType); } catch (SemanticRelationException& e) { - mitkReThrow(e) << "Cannot get the specific segmentation"; + mitkReThrow(e) << "Cannot get the specific segmentation."; } for (const auto& segmentationNode : allSpecificSegmentations) { SemanticTypes::Lesion representedLesion = SemanticRelationsInference::GetLesionOfSegmentation(segmentationNode); if (representedLesion.UID == lesion.UID) { return segmentationNode; } } return mitk::DataNode::Pointer(); } diff --git a/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp b/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp index 3cd01a1b11..5b043474c1 100644 --- a/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp +++ b/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp @@ -1,592 +1,592 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSemanticRelationsInference.h" // semantic relations module #include "mitkControlPointManager.h" #include "mitkDICOMHelper.h" #include "mitkNodePredicates.h" #include "mitkRelationStorage.h" #include "mitkSemanticRelationException.h" /************************************************************************/ /* functions to get instances / attributes */ /************************************************************************/ mitk::SemanticTypes::LesionClassVector mitk::SemanticRelationsInference::GetAllLesionClassesOfCase(const SemanticTypes::CaseID& caseID) { SemanticTypes::LesionVector allLesionsOfCase = RelationStorage::GetAllLesionsOfCase(caseID); SemanticTypes::LesionClassVector allLesionClassesOfCase; for (const auto& lesion : allLesionsOfCase) { allLesionClassesOfCase.push_back(lesion.lesionClass); } // remove duplicate entries auto lessThan = [](const SemanticTypes::LesionClass& lesionClassLeft, const SemanticTypes::LesionClass& lesionClassRight) { return lesionClassLeft.UID < lesionClassRight.UID; }; auto equal = [](const SemanticTypes::LesionClass& lesionClassLeft, const SemanticTypes::LesionClass& lesionClassRight) { return lesionClassLeft.UID == lesionClassRight.UID; }; std::sort(allLesionClassesOfCase.begin(), allLesionClassesOfCase.end(), lessThan); allLesionClassesOfCase.erase(std::unique(allLesionClassesOfCase.begin(), allLesionClassesOfCase.end(), equal), allLesionClassesOfCase.end()); return allLesionClassesOfCase; } mitk::SemanticTypes::Lesion mitk::SemanticRelationsInference::GetLesionOfSegmentation(const DataNode* segmentationNode) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } SemanticTypes::CaseID caseID = ""; SemanticTypes::ID segmentationID = ""; try { caseID = GetCaseIDFromDataNode(segmentationNode); segmentationID = GetIDFromDataNode(segmentationNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot get the lesion of the given segmentation data node."; } return RelationStorage::GetLesionOfSegmentation(caseID, segmentationID); } mitk::SemanticTypes::LesionVector mitk::SemanticRelationsInference::GetAllLesionsOfImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID = ""; SemanticTypes::ID imageID = ""; try { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot get all lesions of the given image data node."; } SemanticTypes::LesionVector allLesionsOfImage; // 1. get all segmentations that are connected to the given image // 2. get the lesion of each segmentation // 3. guarantee uniqueness of lesions SemanticTypes::IDVector allSegmentationIDsOfImage = RelationStorage::GetAllSegmentationIDsOfImage(caseID, imageID); for (const auto& segmentationID : allSegmentationIDsOfImage) { // get represented lesion of the current segmentation SemanticTypes::Lesion representedLesion = RelationStorage::GetLesionOfSegmentation(caseID, segmentationID); if (!representedLesion.UID.empty()) { allLesionsOfImage.push_back(representedLesion); } } // remove duplicate entries auto lessThan = [](const SemanticTypes::Lesion& lesionLeft, const SemanticTypes::Lesion& lesionRight) { return lesionLeft.UID < lesionRight.UID; }; auto equal = [](const SemanticTypes::Lesion& lesionLeft, const SemanticTypes::Lesion& lesionRight) { return lesionLeft.UID == lesionRight.UID; }; std::sort(allLesionsOfImage.begin(), allLesionsOfImage.end(), lessThan); allLesionsOfImage.erase(std::unique(allLesionsOfImage.begin(), allLesionsOfImage.end(), equal), allLesionsOfImage.end()); return allLesionsOfImage; } mitk::SemanticTypes::LesionVector mitk::SemanticRelationsInference::GetAllLesionsOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { SemanticTypes::LesionVector allLesions = RelationStorage::GetAllLesionsOfCase(caseID); // filter the lesions: use only those, where the associated data is connected to image data that refers to the given control point using a lambda function auto lambda = [&caseID, &controlPoint](const SemanticTypes::Lesion& lesion) { return !SpecificImageExists(caseID, lesion, controlPoint); }; allLesions.erase(std::remove_if(allLesions.begin(), allLesions.end(), lambda), allLesions.end()); return allLesions; } mitk::SemanticTypes::LesionVector mitk::SemanticRelationsInference::GetAllLesionsOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) { SemanticTypes::LesionVector allLesions = RelationStorage::GetAllLesionsOfCase(caseID); // filter the lesions: use only those, where the associated data is connected to image data that refers to the given information type using a lambda function auto lambda = [&caseID, &informationType](const SemanticTypes::Lesion& lesion) { return !SpecificImageExists(caseID, lesion, informationType); }; allLesions.erase(std::remove_if(allLesions.begin(), allLesions.end(), lambda), allLesions.end()); return allLesions; } mitk::SemanticTypes::LesionVector mitk::SemanticRelationsInference::GetAllSpecificLesions(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) { auto allLesionsOfControlPoint = GetAllLesionsOfControlPoint(caseID, controlPoint); auto allLesionsOfInformationType = GetAllLesionsOfInformationType(caseID, informationType); SemanticTypes::LesionVector allLesionsIntersection; auto lessThan = [](const SemanticTypes::Lesion& lesionLeft, const SemanticTypes::Lesion& lesionRight) { return lesionLeft.UID < lesionRight.UID; }; auto equal = [](const SemanticTypes::Lesion& lesionLeft, const SemanticTypes::Lesion& lesionRight) { return lesionLeft.UID == lesionRight.UID; }; std::sort(allLesionsOfControlPoint.begin(), allLesionsOfControlPoint.end(), lessThan); std::sort(allLesionsOfInformationType.begin(), allLesionsOfInformationType.end(), lessThan); SemanticTypes::IDVector allImageIDsIntersection; // set_intersection removes duplicated nodes std::set_intersection(allLesionsOfControlPoint.begin(), allLesionsOfControlPoint.end(), allLesionsOfInformationType.begin(), allLesionsOfInformationType.end(), std::back_inserter(allLesionsIntersection), equal); return allLesionsIntersection; } bool mitk::SemanticRelationsInference::IsRepresentingALesion(const DataNode* segmentationNode) { SemanticTypes::Lesion representedLesion; try { representedLesion = GetLesionOfSegmentation(segmentationNode); } catch (const SemanticRelationException&) { return false; } return !representedLesion.UID.empty(); } bool mitk::SemanticRelationsInference::IsRepresentingALesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID) { SemanticTypes::Lesion representedLesion = RelationStorage::GetLesionOfSegmentation(caseID, segmentationID); return !representedLesion.UID.empty(); } bool mitk::SemanticRelationsInference::IsLesionPresent(const SemanticTypes::Lesion& lesion, const DataNode* dataNode) { SemanticTypes::CaseID caseID = ""; SemanticTypes::ID dataNodeID = ""; try { caseID = GetCaseIDFromDataNode(dataNode); dataNodeID = GetIDFromDataNode(dataNode); } catch (const SemanticRelationException&) { return false; } if (NodePredicates::GetImagePredicate()->CheckNode(dataNode)) { return IsLesionPresentOnImage(caseID, lesion, dataNodeID); } if (NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { return IsLesionPresentOnSegmentation(caseID, lesion, dataNodeID); } return false; } bool mitk::SemanticRelationsInference::IsLesionPresentOnImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ID& imageID) { SemanticTypes::IDVector allImageIDsOfLesion; try { allImageIDsOfLesion = GetAllImageIDsOfLesion(caseID, lesion); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot get all image IDs of the given lesion to determine the lesion presence."; } for (const auto& imageIDOfLesion : allImageIDsOfLesion) { if (imageIDOfLesion == imageID) { return true; } } return false; } bool mitk::SemanticRelationsInference::IsLesionPresentOnSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ID& segmentationID) { const auto representedLesion = RelationStorage::GetLesionOfSegmentation(caseID, segmentationID); return lesion.UID == representedLesion.UID; } bool mitk::SemanticRelationsInference::IsLesionPresentAtControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ControlPoint& controlPoint) { SemanticTypes::IDVector allImageIDsOfLesion; try { allImageIDsOfLesion = GetAllImageIDsOfLesion(caseID, lesion); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot get all image IDs of the given lesion to determine the lesion presence."; } for (const auto& imageIDOfLesion : allImageIDsOfLesion) { - auto imageControlPoint = mitk::RelationStorage::GetControlPointOfImage(caseID, imageIDOfLesion); + auto imageControlPoint = RelationStorage::GetControlPointOfImage(caseID, imageIDOfLesion); if (imageControlPoint.date == controlPoint.date) { return true; } } return false; } bool mitk::SemanticRelationsInference::InstanceExists(const DataNode* dataNode) { SemanticTypes::CaseID caseID = ""; SemanticTypes::ID dataNodeID = ""; try { caseID = GetCaseIDFromDataNode(dataNode); dataNodeID = GetIDFromDataNode(dataNode); } catch (const SemanticRelationException&) { return false; } if (NodePredicates::GetImagePredicate()->CheckNode(dataNode)) { - std::vector allImageIDsOfCase = RelationStorage::GetAllImageIDsOfCase(caseID); + SemanticTypes::IDVector allImageIDsOfCase = RelationStorage::GetAllImageIDsOfCase(caseID); return std::find(allImageIDsOfCase.begin(), allImageIDsOfCase.end(), dataNodeID) != allImageIDsOfCase.end(); } if (NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { - std::vector allSegmentationIDsOfCase = RelationStorage::GetAllSegmentationIDsOfCase(caseID); + SemanticTypes::IDVector allSegmentationIDsOfCase = RelationStorage::GetAllSegmentationIDsOfCase(caseID); return std::find(allSegmentationIDsOfCase.begin(), allSegmentationIDsOfCase.end(), dataNodeID) != allSegmentationIDsOfCase.end(); } return false; } bool mitk::SemanticRelationsInference::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { SemanticTypes::LesionVector allLesions = RelationStorage::GetAllLesionsOfCase(caseID); // filter all lesions: check for equality with the given lesion using a lambda function auto lambda = [&lesion](const SemanticTypes::Lesion& currentLesion) { return currentLesion.UID == lesion.UID; }; const auto existingLesion = std::find_if(allLesions.begin(), allLesions.end(), lambda); return existingLesion != allLesions.end(); } mitk::SemanticTypes::IDVector mitk::SemanticRelationsInference::GetAllImageIDsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { if (!InstanceExists(caseID, lesion)) { mitkThrowException(SemanticRelationException) << "Could not find an existing lesion instance for the given caseID " << caseID << " and lesion " << lesion.UID << "."; } SemanticTypes::IDVector allImageIDsOfLesion; // 1. get all segmentations that define the lesion // 2. get the parentID (imageID) of each segmentation // 3. guarantee uniqueness of image IDs SemanticTypes::IDVector allSegmentationIDsOfLesion = RelationStorage::GetAllSegmentationIDsOfLesion(caseID, lesion); for (const auto& segmentationID : allSegmentationIDsOfLesion) { // get parent ID of the current segmentation ID SemanticTypes::ID imageID = RelationStorage::GetImageIDOfSegmentation(caseID, segmentationID); if(!imageID.empty()) { allImageIDsOfLesion.push_back(imageID); } } std::sort(allImageIDsOfLesion.begin(), allImageIDsOfLesion.end()); allImageIDsOfLesion.erase(std::unique(allImageIDsOfLesion.begin(), allImageIDsOfLesion.end()), allImageIDsOfLesion.end()); return allImageIDsOfLesion; } mitk::SemanticTypes::IDVector mitk::SemanticRelationsInference::GetAllImageIDsOfExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) { if (!InstanceExists(caseID, examinationPeriod)) { mitkThrowException(SemanticRelationException) << "Could not find an existing examination period for the given caseID " << caseID << " and examination period " << examinationPeriod.name << "."; } SemanticTypes::IDVector allImageIDsOfExaminationPeriod; // 1. get all control point UIDs of the examination period // 2. get all images of each control points to find all images of the examination period auto allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); SemanticTypes::ControlPoint controlPoint; for (const auto& controlPointUID : examinationPeriod.controlPointUIDs) { controlPoint = GetControlPointByUID(controlPointUID, allControlPoints); auto allImageIDsOfControlPoint = RelationStorage::GetAllImageIDsOfControlPoint(caseID, controlPoint); allImageIDsOfExaminationPeriod.insert(allImageIDsOfExaminationPeriod.end(), allImageIDsOfControlPoint.begin(), allImageIDsOfControlPoint.end()); } return allImageIDsOfExaminationPeriod; } mitk::SemanticTypes::ControlPoint mitk::SemanticRelationsInference::GetControlPointOfImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } SemanticTypes::CaseID caseID = ""; SemanticTypes::ID imageID = ""; try { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot get the control point of the given image data node."; } return RelationStorage::GetControlPointOfImage(caseID, imageID); } mitk::SemanticTypes::ControlPointVector mitk::SemanticRelationsInference::GetAllControlPointsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); // filter the control points: use only those, where the associated image data has a segmentation that refers to the given lesion using a lambda function auto lambda = [&caseID, &lesion](const SemanticTypes::ControlPoint& controlPoint) { return !SpecificImageExists(caseID, lesion, controlPoint); }; allControlPoints.erase(std::remove_if(allControlPoints.begin(), allControlPoints.end(), lambda), allControlPoints.end()); return allControlPoints; } mitk::SemanticTypes::ControlPointVector mitk::SemanticRelationsInference::GetAllControlPointsOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) { SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); // filter the control points: use only those, where the associated image data refers to the given information type using a lambda function auto lambda = [&caseID, &informationType](const SemanticTypes::ControlPoint& controlPoint) { return !SpecificImageExists(caseID, informationType, controlPoint); }; allControlPoints.erase(std::remove_if(allControlPoints.begin(), allControlPoints.end(), lambda), allControlPoints.end()); return allControlPoints; } bool mitk::SemanticRelationsInference::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); // filter all control points: check for equality with the given control point using a lambda function auto lambda = [&controlPoint](const SemanticTypes::ControlPoint& currentControlPoint) { return currentControlPoint.UID == controlPoint.UID; }; const auto existingControlPoint = std::find_if(allControlPoints.begin(), allControlPoints.end(), lambda); if (existingControlPoint != allControlPoints.end()) { return true; } else { return false; } } bool mitk::SemanticRelationsInference::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) { SemanticTypes::ExaminationPeriodVector allExaminationPeriods = RelationStorage::GetAllExaminationPeriodsOfCase(caseID); // filter all examination periods: check for equality with the given examination period using a lambda function auto lambda = [&examinationPeriod](const SemanticTypes::ExaminationPeriod& currentExaminationPeriod) { return currentExaminationPeriod.UID == examinationPeriod.UID; }; const auto existingExaminationPeriod = std::find_if(allExaminationPeriods.begin(), allExaminationPeriods.end(), lambda); if (existingExaminationPeriod != allExaminationPeriods.end()) { return true; } else { return false; } } mitk::SemanticTypes::InformationType mitk::SemanticRelationsInference::GetInformationTypeOfImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID = ""; SemanticTypes::ID imageID = ""; try { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot get the information type of the given image data node."; } return RelationStorage::GetInformationTypeOfImage(caseID, imageID); } mitk::SemanticTypes::InformationTypeVector mitk::SemanticRelationsInference::GetAllInformationTypesOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { SemanticTypes::InformationTypeVector allInformationTypes = RelationStorage::GetAllInformationTypesOfCase(caseID); // filter the information types: use only those, where the associated data refers to the given control point using a lambda function auto lambda = [&caseID, &controlPoint](const SemanticTypes::InformationType& informationType) { return !SpecificImageExists(caseID, informationType, controlPoint); }; allInformationTypes.erase(std::remove_if(allInformationTypes.begin(), allInformationTypes.end(), lambda), allInformationTypes.end()); return allInformationTypes; } bool mitk::SemanticRelationsInference::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) { SemanticTypes::InformationTypeVector allInformationTypes = RelationStorage::GetAllInformationTypesOfCase(caseID); // filter all information types: check for equality with the given information type using a lambda function auto lambda = [&informationType](const SemanticTypes::InformationType& currentInformationType) { return currentInformationType == informationType; }; const auto existingInformationType = std::find_if(allInformationTypes.begin(), allInformationTypes.end(), lambda); if (existingInformationType != allInformationTypes.end()) { return true; } else { return false; } } bool mitk::SemanticRelationsInference::SpecificImageExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::InformationType& informationType) { SemanticTypes::IDVector allImageIDsOfLesion; try { allImageIDsOfLesion = GetAllImageIDsOfLesion(caseID, lesion); } catch (const SemanticRelationException&) { return false; } SemanticTypes::IDVector allImageIDsOfInformationType = RelationStorage::GetAllImageIDsOfInformationType(caseID, informationType); std::sort(allImageIDsOfLesion.begin(), allImageIDsOfLesion.end()); std::sort(allImageIDsOfInformationType.begin(), allImageIDsOfInformationType.end()); SemanticTypes::IDVector allImageIDsIntersection; // set_intersection removes duplicated nodes, since 'GetAllImageIDsOfInformationType' only contains at most one of each node std::set_intersection(allImageIDsOfLesion.begin(), allImageIDsOfLesion.end(), allImageIDsOfInformationType.begin(), allImageIDsOfInformationType.end(), std::back_inserter(allImageIDsIntersection)); // if the vector of intersecting image IDs is empty, the information type does not contain the lesion return !allImageIDsIntersection.empty(); } bool mitk::SemanticRelationsInference::SpecificImageExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ControlPoint& controlPoint) { SemanticTypes::IDVector allImageIDsOfLesion; try { allImageIDsOfLesion = GetAllImageIDsOfLesion(caseID, lesion); } catch (const SemanticRelationException&) { return false; } SemanticTypes::IDVector allImageIDsOfControlPoint = RelationStorage::GetAllImageIDsOfControlPoint(caseID, controlPoint); std::sort(allImageIDsOfLesion.begin(), allImageIDsOfLesion.end()); std::sort(allImageIDsOfControlPoint.begin(), allImageIDsOfControlPoint.end()); SemanticTypes::IDVector allImageIDsIntersection; // set_intersection removes duplicated nodes, since 'GetAllImageIDsOfControlPoint' only contains at most one of each node std::set_intersection(allImageIDsOfLesion.begin(), allImageIDsOfLesion.end(), allImageIDsOfControlPoint.begin(), allImageIDsOfControlPoint.end(), std::back_inserter(allImageIDsIntersection)); // if the vector of intersecting image IDs is empty, the control point does not contain the lesion return !allImageIDsIntersection.empty(); } bool mitk::SemanticRelationsInference::SpecificImageExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType, const SemanticTypes::ControlPoint& controlPoint) { SemanticTypes::IDVector allImageIDsOfInformationType = RelationStorage::GetAllImageIDsOfInformationType(caseID, informationType); SemanticTypes::IDVector allImageIDsOfControlPoint = RelationStorage::GetAllImageIDsOfControlPoint(caseID, controlPoint); std::sort(allImageIDsOfInformationType.begin(), allImageIDsOfInformationType.end()); std::sort(allImageIDsOfControlPoint.begin(), allImageIDsOfControlPoint.end()); SemanticTypes::IDVector allImageIDsIntersection; // set_intersection removes duplicated nodes std::set_intersection(allImageIDsOfInformationType.begin(), allImageIDsOfInformationType.end(), allImageIDsOfControlPoint.begin(), allImageIDsOfControlPoint.end(), std::back_inserter(allImageIDsIntersection)); // if the vector of intersecting image IDs is empty no image exists for the given information type and control point return !allImageIDsIntersection.empty(); } diff --git a/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp b/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp index d5477878d3..478cd4f00d 100644 --- a/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp +++ b/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp @@ -1,613 +1,612 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSemanticRelationsIntegration.h" // semantic relations module #include "mitkControlPointManager.h" #include "mitkDICOMHelper.h" #include "mitkNodePredicates.h" #include "mitkRelationStorage.h" #include "mitkSemanticRelationException.h" #include "mitkSemanticRelationsInference.h" #include "mitkUIDGeneratorBoost.h" // multi label module #include // c++ #include #include std::vector mitk::SemanticRelationsIntegration::m_ObserverVector; void mitk::SemanticRelationsIntegration::AddObserver(ISemanticRelationsObserver* observer) { std::vector::iterator existingObserver = std::find(m_ObserverVector.begin(), m_ObserverVector.end(), observer); if (existingObserver != m_ObserverVector.end()) { // no need to add the already existing observer return; } m_ObserverVector.push_back(observer); } void mitk::SemanticRelationsIntegration::RemoveObserver(ISemanticRelationsObserver* observer) { m_ObserverVector.erase(std::remove(m_ObserverVector.begin(), m_ObserverVector.end(), observer), m_ObserverVector.end()); } /************************************************************************/ /* functions to add / remove instances / attributes */ /************************************************************************/ void mitk::SemanticRelationsIntegration::AddImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID; SemanticTypes::ID imageID; SemanticTypes::InformationType informationType; SemanticTypes::ControlPoint controlPoint; try // retrieve information { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); informationType = GetDICOMModalityFromDataNode(imageNode); controlPoint = GenerateControlPoint(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot add the given image data node."; } try // add and set information { RelationStorage::AddCase(caseID); RelationStorage::AddImage(caseID, imageID); AddInformationTypeToImage(imageNode, informationType); SetControlPointOfImage(imageNode, controlPoint); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot add the given image data node."; } } void mitk::SemanticRelationsIntegration::RemoveImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID; SemanticTypes::ID imageID; try // retrieve information { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot remove the given image data node."; } try { RemoveInformationTypeFromImage(imageNode); UnlinkImageFromControlPoint(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot remove the given image data node."; } RelationStorage::RemoveImage(caseID, imageID); NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::AddLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { if (SemanticRelationsInference::InstanceExists(caseID, lesion)) { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to add already exists for the given case."; } RelationStorage::AddLesion(caseID, lesion); NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::OverwriteLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { if (SemanticRelationsInference::InstanceExists(caseID, lesion)) { RelationStorage::OverwriteLesion(caseID, lesion); NotifyObserver(caseID); } else { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to overwrite does not exist for the given case."; } } void mitk::SemanticRelationsIntegration::AddLesionAndLinkSegmentation(const DataNode* segmentationNode, const SemanticTypes::Lesion& lesion) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } SemanticTypes::CaseID caseID; try { caseID = GetCaseIDFromDataNode(segmentationNode); AddLesion(caseID, lesion); LinkSegmentationToLesion(segmentationNode, lesion); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot add given lesion and link the given segmentation data node."; } NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::RemoveLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { if (SemanticRelationsInference::InstanceExists(caseID, lesion)) { SemanticTypes::IDVector allSegmentationIDsOfLesion = RelationStorage::GetAllSegmentationIDsOfLesion(caseID, lesion); if (allSegmentationIDsOfLesion.empty()) { // no more segmentations are linked to the specific lesion // the lesion can be removed from the storage RelationStorage::RemoveLesion(caseID, lesion); NotifyObserver(caseID); } else { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to remove is still referred to by a segmentation node. Lesion will not be removed."; } } else { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to remove does not exist for the given case."; } } void mitk::SemanticRelationsIntegration::AddSegmentation(const DataNode* segmentationNode, const DataNode* parentNode) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } if (nullptr == parentNode) { mitkThrowException(SemanticRelationException) << "Not a valid parent data node."; } SemanticTypes::CaseID caseID; SemanticTypes::ID segmentationNodeID; SemanticTypes::ID parentNodeID; try { caseID = GetCaseIDFromDataNode(segmentationNode); segmentationNodeID = GetIDFromDataNode(segmentationNode); parentNodeID = GetIDFromDataNode(parentNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot add the given segmentation data node."; } RelationStorage::AddSegmentation(caseID, segmentationNodeID, parentNodeID); NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::LinkSegmentationToLesion(const DataNode* segmentationNode, const SemanticTypes::Lesion& lesion) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } SemanticTypes::CaseID caseID; SemanticTypes::ID segmentationID; try { caseID = GetCaseIDFromDataNode(segmentationNode); segmentationID = GetIDFromDataNode(segmentationNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot link the given segmentation data node to the given lesion."; } if (SemanticRelationsInference::InstanceExists(caseID, lesion)) { RelationStorage::LinkSegmentationToLesion(caseID, segmentationID, lesion); NotifyObserver(caseID); } else { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to link does not exist for the given case."; } } void mitk::SemanticRelationsIntegration::UnlinkSegmentationFromLesion(const DataNode* segmentationNode) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } SemanticTypes::CaseID caseID; SemanticTypes::ID segmentationID; try { caseID = GetCaseIDFromDataNode(segmentationNode); segmentationID = GetIDFromDataNode(segmentationNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot unlink the given segmentation data node from its lesion."; } RelationStorage::UnlinkSegmentationFromLesion(caseID, segmentationID); NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::RemoveSegmentation(const DataNode* segmentationNode) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } SemanticTypes::CaseID caseID; SemanticTypes::ID segmentationNodeID; try { caseID = GetCaseIDFromDataNode(segmentationNode); segmentationNodeID = GetIDFromDataNode(segmentationNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot remove the given segmentation data node."; } try { UnlinkSegmentationFromLesion(segmentationNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot remove the given segmentation data node."; } RelationStorage::RemoveSegmentation(caseID, segmentationNodeID); NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::SetControlPointOfImage(const DataNode* imageNode, const SemanticTypes::ControlPoint& controlPoint) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID; try { caseID = GetCaseIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot set the given control point for the given image data node."; } SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); // need to check if an already existing control point fits/contains the user control point SemanticTypes::ControlPoint existingControlPoint = FindExistingControlPoint(controlPoint, allControlPoints); try { if (!existingControlPoint.UID.empty()) { // found an already existing control point LinkImageToControlPoint(imageNode, existingControlPoint, false); } else { AddControlPointAndLinkImage(imageNode, controlPoint, false); // added a new control point // find closest control point to add the new control point to the correct examination period SemanticTypes::ControlPoint closestControlPoint = FindClosestControlPoint(controlPoint, allControlPoints); SemanticTypes::ExaminationPeriodVector allExaminationPeriods = RelationStorage::GetAllExaminationPeriodsOfCase(caseID); SemanticTypes::ExaminationPeriod examinationPeriod = FindContainingExaminationPeriod(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()); + examinationPeriod.name = "New examination period"; AddExaminationPeriod(caseID, examinationPeriod); } // add the control point to the (newly created or found / close) examination period AddControlPointToExaminationPeriod(caseID, controlPoint, examinationPeriod); } } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot set the given control point for the given image data node."; } ClearControlPoints(caseID); NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::AddControlPointAndLinkImage(const DataNode* imageNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID; try { caseID = GetCaseIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot add the given control point and link the given image data node."; } if (SemanticRelationsInference::InstanceExists(caseID, controlPoint)) { mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to add already exists for the given case. \n Use 'LinkImageToControlPoint' instead."; } RelationStorage::AddControlPoint(caseID, controlPoint); try { LinkImageToControlPoint(imageNode, controlPoint, checkConsistence); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot add the given control point and link the given image data node."; } } void mitk::SemanticRelationsIntegration::LinkImageToControlPoint(const DataNode* imageNode, const SemanticTypes::ControlPoint& controlPoint, bool /*checkConsistence*/) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID; SemanticTypes::ID imageID; try { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot link the image data node to the given control point."; } if (SemanticRelationsInference::InstanceExists(caseID, controlPoint)) { RelationStorage::LinkImageToControlPoint(caseID, imageID, controlPoint); } else { mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to link does not exist for the given case."; } } void mitk::SemanticRelationsIntegration::UnlinkImageFromControlPoint(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID = ""; SemanticTypes::ID imageID = ""; try { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot unlink the given image data node from its control point."; } - SemanticTypes::ControlPoint controlPoint = RelationStorage::GetControlPointOfImage(caseID, imageID); RelationStorage::UnlinkImageFromControlPoint(caseID, imageID); ClearControlPoints(caseID); } void mitk::SemanticRelationsIntegration::AddExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) { if (SemanticRelationsInference::InstanceExists(caseID, examinationPeriod)) { mitkThrowException(SemanticRelationException) << "The examination period " << examinationPeriod.UID << " to add already exists for the given case."; } else { RelationStorage::AddExaminationPeriod(caseID, examinationPeriod); } } void mitk::SemanticRelationsIntegration::RenameExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) { if (SemanticRelationsInference::InstanceExists(caseID, examinationPeriod)) { RelationStorage::RenameExaminationPeriod(caseID, examinationPeriod); NotifyObserver(caseID); } else { mitkThrowException(SemanticRelationException) << "The examination period " << examinationPeriod.UID << " to overwrite does not exist for the given case."; } } void mitk::SemanticRelationsIntegration::AddControlPointToExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod& examinationPeriod) { if (!SemanticRelationsInference::InstanceExists(caseID, controlPoint)) { mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to add does not exist for the given case."; } if (!SemanticRelationsInference::InstanceExists(caseID, examinationPeriod)) { mitkThrowException(SemanticRelationException) << "The examination period " << examinationPeriod.UID << " does not exist for the given case. \n Use 'AddExaminationPeriod' before."; } RelationStorage::AddControlPointToExaminationPeriod(caseID, controlPoint, examinationPeriod); } void mitk::SemanticRelationsIntegration::SetInformationType(const DataNode* imageNode, const SemanticTypes::InformationType& informationType) { SemanticTypes::CaseID caseID; try { caseID = GetCaseIDFromDataNode(imageNode); RemoveInformationTypeFromImage(imageNode); AddInformationTypeToImage(imageNode, informationType); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot set the given information type for the given image data node."; } NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::AddInformationTypeToImage(const DataNode* imageNode, const SemanticTypes::InformationType& informationType) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID = ""; SemanticTypes::ID imageID = ""; try { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot add the given information type to the given image data node."; } RelationStorage::AddInformationTypeToImage(caseID, imageID, informationType); } void mitk::SemanticRelationsIntegration::RemoveInformationTypeFromImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID = ""; SemanticTypes::ID imageID = ""; try { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot remove the information type from the given image data node."; } SemanticTypes::InformationType originalInformationType = RelationStorage::GetInformationTypeOfImage(caseID, imageID); RelationStorage::RemoveInformationTypeFromImage(caseID, imageID); // check for further references to the removed information type - std::vector allImageIDsVectorValue = RelationStorage::GetAllImageIDsOfCase(caseID); + SemanticTypes::IDVector allImageIDsVectorValue = RelationStorage::GetAllImageIDsOfCase(caseID); for (const auto& otherImageID : allImageIDsVectorValue) { SemanticTypes::InformationType otherInformationType = RelationStorage::GetInformationTypeOfImage(caseID, otherImageID); if (otherInformationType == originalInformationType) { // found the information type in another image -> cannot remove the information type from the case return; } } // given information type was not referred by any other image of the case -> the information type can be removed from the case RelationStorage::RemoveInformationType(caseID, originalInformationType); } /************************************************************************/ /* private functions */ /************************************************************************/ void mitk::SemanticRelationsIntegration::NotifyObserver(const SemanticTypes::CaseID& caseID) const { for (auto& observer : m_ObserverVector) { observer->Update(caseID); } } void mitk::SemanticRelationsIntegration::ClearControlPoints(const SemanticTypes::CaseID& caseID) { SemanticTypes::ControlPointVector allControlPointsOfCase = RelationStorage::GetAllControlPointsOfCase(caseID); - std::vector allImageIDsVectorValue = RelationStorage::GetAllImageIDsOfCase(caseID); + SemanticTypes::IDVector allImageIDsVectorValue = RelationStorage::GetAllImageIDsOfCase(caseID); SemanticTypes::ControlPointVector referencedControlPoints; for (const auto& imageID : allImageIDsVectorValue) { - auto controlPointOfImage = RelationStorage::GetControlPointOfImage(caseID, imageID); + SemanticTypes::ControlPoint controlPointOfImage = RelationStorage::GetControlPointOfImage(caseID, imageID); referencedControlPoints.push_back(controlPointOfImage); } std::sort(allControlPointsOfCase.begin(), allControlPointsOfCase.end()); std::sort(referencedControlPoints.begin(), referencedControlPoints.end()); SemanticTypes::ControlPointVector nonReferencedControlPoints; std::set_difference(allControlPointsOfCase.begin(), allControlPointsOfCase.end(), referencedControlPoints.begin(), referencedControlPoints.end(), std::inserter(nonReferencedControlPoints, nonReferencedControlPoints.begin())); - auto allExaminationPeriods = RelationStorage::GetAllExaminationPeriodsOfCase(caseID); + SemanticTypes::ExaminationPeriodVector allExaminationPeriods = RelationStorage::GetAllExaminationPeriodsOfCase(caseID); for (const auto& controlPoint : nonReferencedControlPoints) { - const auto& examinationPeriod = FindExaminationPeriod(controlPoint, allExaminationPeriods); + const SemanticTypes::ExaminationPeriod& examinationPeriod = FindContainingExaminationPeriod(controlPoint, allExaminationPeriods); RelationStorage::RemoveControlPointFromExaminationPeriod(caseID, controlPoint, examinationPeriod); RelationStorage::RemoveControlPoint(caseID, controlPoint); } }