diff --git a/Modules/SemanticRelations/include/mitkSemanticRelationsInference.h b/Modules/SemanticRelations/include/mitkSemanticRelationsInference.h index 2bedb6909c..1c21322699 100644 --- a/Modules/SemanticRelations/include/mitkSemanticRelationsInference.h +++ b/Modules/SemanticRelations/include/mitkSemanticRelationsInference.h @@ -1,230 +1,344 @@ /*=================================================================== 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 data. + * @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 data node is invalid (==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 GetRepresentedLesion(const DataNode* segmentationNode); + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::Lesion GetLesionOfSegmentation(const DataNode* segmentationNode); + /** + * @brief Returns a vector of all lesions that are currently available for the current case and are connected to the given image (via a segmentation). + * If no lesions are stored for the current case, an empty vector is returned. If no segmentations are + * connected with the image node, no lesions for the specific image will be found and an empty vector is returned. + * + * @pre The given image data node has to be valid (!nullptr). + * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). + * + * @param imageNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. + * @return A vector of lesions. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionVector GetAllLesionsOfImage(const DataNode* imageNode); + /** + * @brief Returns a vector of all lesions that are valid for the given case, given a specific control point. + * + * @param caseID The current case identifier is defined by the given string. + * @param controlPoint A specific control point which has to be available at a returned (found) lesion: + * Only those lesions are returned for which the image of the associated segmentation is linked to the given control point. + * If the control point instance does not exist, an empty vector is returned. + * @return A vector of control points. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionVector GetAllLesionsOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); /** * @brief Check if the given segmentation refers to an existing lesion instance. * This function can be used before calling 'GetRepresentedLesion' in order to avoid a possible exception. * * @param segmentationNode The segmentation identifier is extracted from the given data node. * @return True, if the segmentation refers to an existing lesion; false otherwise. */ MITKSEMANTICRELATIONS_EXPORT bool IsRepresentingALesion(const DataNode* segmentationNode); /** * @brief Check if the segmentation identified by the given segmentation ID refers to an existing lesion instance. * This function can be used before calling 'GetRepresentedLesion' in order to avoid a possible exception. * * @param caseID The current case identifier is defined by the given string. * @param segmentationID The segmentation node identifier is defined by the given string. * @return True, if the segmentation refers to an existing lesion; false otherwise. */ MITKSEMANTICRELATIONS_EXPORT bool IsRepresentingALesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID); /** + * @brief Check if the given lesion is present on the given data node. + * The function receives the case- and the node-ID from the DICOM tags of the node itself. + * It uses node predicates to decide if the node is an image or a segmentation node. + * + * @param lesion A lesion with a UID that identifies the corresponding lesion instance. + * @param dataNode A data node to check. + * @return True, if the lesion is present on the data node; false otherwise. + */ + MITKSEMANTICRELATIONS_EXPORT bool IsLesionPresent(const SemanticTypes::Lesion& lesion, const DataNode* dataNode); + /** + * @brief Check if the given lesion is related to the image identified by the given image ID. + * Each lesion is represented by a segmentation which is connected to its parent image. + * + * @param caseID The current case identifier is defined by the given string. + * @param lesion A lesion with a UID that identifies the corresponding lesion instance. + * @param imageID The image node identifier is defined by the given string. + * @return True, if the lesion is related to image identified by the given image ID; false otherwise. + */ + MITKSEMANTICRELATIONS_EXPORT bool IsLesionPresentOnImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ID& imageID); + /** * @brief Check if the given lesion is present on the segmentation identified by the given segmentation ID. * * @param caseID The current case identifier is defined by the given string. - * @param lesion A Lesion with a UID that identifies the corresponding lesion instance. + * @param 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 'GetAllSegmentationsOfLesion' in order to avoid a possible exception. * This function can be used before calling 'AddLesionInstance' in order to avoid a possible exception. * * @param caseID The current case identifier is defined by the given string. - * @param lesion A Lesion with a UID that identifies the corresponding lesion instance. + * @param lesion A lesion with a UID that identifies the corresponding lesion instance. * @return True, if the lesion instance exists; false otherwise. */ MITKSEMANTICRELATIONS_EXPORT bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); /** + * @brief Return a vector of all image IDs that identify images that are related to the given lesion. + * Each lesion is represented by a segmentation which is connected to its parent image. + * If the lesion is not represented by any segmentation, an empty vector is returned. + * + * @pre The UID of the lesion has to exist for a lesion instance. + * @throw SemanticRelationException, if UID of the lesion does not exist for a lesion instance (this can be checked via 'InstanceExists'). + * + * @param caseID The current case identifier is defined by the given string. + * @param lesion A lesion with a UID that identifies the corresponding lesion instance. + * @return A vector of IDs identifying images that identify images that are related to the given lesion. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::IDVector GetAllImageIDsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); + /** * @brief Return a vector of all segmentation IDs that identify segmentations that define the given lesion. * These segmentations don't have to be linked to the same image. * If the lesion is not referred to by any segmentation, an empty vector is returned. * * @pre The UID of the lesion has to exist for a lesion instance. * @throw SemanticRelationException, if UID of the lesion does not exist for a lesion instance (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. - * @param lesion A Lesion with a UID that identifies the corresponding lesion instance. + * @param lesion A lesion with a UID that identifies the corresponding lesion instance. * @return A vector of IDs identifying segmentations that define the given lesion. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::IDVector GetAllSegmentationIDsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); /** * @brief Return 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 calling 'GetAllDataOfControlPoint' in order to avoid a possible exception. * This function can be used before adding, linking and unlinking control points to avoid a possible exception. * * @param caseID The current case identifier is defined by the given string. * @param controlPoint A control point with a UID that identifies the corresponding control point instance. * @return True, if the control point instance exists; false otherwise. */ MITKSEMANTICRELATIONS_EXPORT bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); /** * @brief Return a vector of all examination periods nodes that are valid for the given case. * * @param caseID The current case identifier is defined by the given string. * @return A vector of examination periods. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ExaminationPeriodVector GetAllExaminationPeriodsOfCase(const SemanticTypes::CaseID& caseID); /** * @brief Check if the given examination period instance exists. * This function can be used before calling 'AddExaminationPeriod' in order to avoid a possible exception. * * @param caseID The current case identifier is defined by the given string. * @param examinationPeriod An examination period with a UID that identifies the corresponding examination period instance. * @return True, if the examination period instance exists; false otherwise. */ MITKSEMANTICRELATIONS_EXPORT bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod); /** - * @brief Return a vector of all information types that are valid for the given case. - * - * @param caseID The current case identifier is defined by the given string. - * @return A vector of information types. - */ - MITKSEMANTICRELATIONS_EXPORT SemanticTypes::InformationTypeVector GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID); - /** * @brief Return the information type of the given image. * If the image does not contain any information type, an empty information type is returned. * * @pre The given image data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). * * @param imageNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. * @return The information type of the given data node. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::InformationType GetInformationTypeOfImage(const DataNode* imageNode); /** * @brief Return the information type of the given image. * If the image does not contain any information type, an empty information type is returned. * * @param caseID The current case identifier is defined by the given string. * @param imageID The image node identifier is defined by the given string. * @return The information type of the image node, defined by the image node ID. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::InformationType GetInformationTypeOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID); /** + * @brief Return a vector of all 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 Return a vector of all image IDs that identify images that are of the given information type. * If the information type is not used by any image, an empty vector is returned. * * @pre The information type has to exist. * @throw SemanticRelationException, if the information type does not exist (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param informationType An information type. * @return A vector of IDs identifying images that are of the given information type. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::IDVector GetAllImageIDsOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType); /** * @brief Check if the given information type exists. * This function can be used before calling 'GetAllDataOfInformationType' in order to avoid a possible exception. * * @param caseID The current case identifier is defined by the given string. * @param informationType An information type. * @return True, if the information type exists; false otherwise. */ MITKSEMANTICRELATIONS_EXPORT bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType); /** - * @brief Return a vector of all CaseIDs that are currently available. + * @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 A vector of CaseIDs as strings. + * @return True, if the given control point contains data that is related to the given information type; false otherwise. */ - MITKSEMANTICRELATIONS_EXPORT std::vector GetAllCaseIDs(); + MITKSEMANTICRELATIONS_EXPORT bool SpecificImageExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType, const SemanticTypes::ControlPoint& controlPoint); /** * @brief Remove all control points from the storage that are not referenced by any image anymore. * This might happen if an image has been removed (and unlinked from the corresponding control point) * or if the user sets a new control point for an image manually in the GUI. * * @param caseID The current case identifier is defined by the given string. */ - void ClearControlPoints(const SemanticTypes::CaseID& caseID); + MITKSEMANTICRELATIONS_EXPORT void ClearControlPoints(const SemanticTypes::CaseID& caseID); } // namespace SemanticRelationsInference } // namespace mitk #endif // MITKSEMANTICRELATIONSINFERENCE_H diff --git a/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp b/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp index f124b56c0e..3e6174fc76 100644 --- a/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp +++ b/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp @@ -1,322 +1,561 @@ /*=================================================================== 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::GetRepresentedLesion(const DataNode* segmentationNode) +mitk::SemanticTypes::Lesion mitk::SemanticRelationsInference::GetLesionOfSegmentation(const DataNode* segmentationNode) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); SemanticTypes::ID segmentationID = GetIDFromDataNode(segmentationNode); return RelationStorage::GetLesionOfSegmentation(caseID, segmentationID); } +mitk::SemanticTypes::LesionVector mitk::SemanticRelationsInference::GetAllLesionsOfImage(const DataNode* imageNode) +{ + if (nullptr == imageNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid image data node."; + } + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); + SemanticTypes::ID imageID = GetIDFromDataNode(imageNode); + + SemanticTypes::LesionVector allLesionsOfImage; + // 1. get all segmentations that are connected to the given image + // 2. get the lesion of each segmentation + // 3. guarantee uniqueness of lesions + SemanticTypes::IDVector allSegmentationIDsOfImage = RelationStorage::GetAllSegmentationIDsOfImage(caseID, imageID); + for (const auto& segmentationID : allSegmentationIDsOfImage) + { + // get represented lesion of the current segmentation + SemanticTypes::Lesion representedLesion = RelationStorage::GetLesionOfSegmentation(caseID, segmentationID); + if (!representedLesion.UID.empty()) + { + allLesionsOfImage.push_back(representedLesion); + } + } + + // remove duplicate entries + auto lessThan = [](const SemanticTypes::Lesion& lesionLeft, const SemanticTypes::Lesion& lesionRight) + { + return lesionLeft.UID < lesionRight.UID; + }; + + auto equal = [](const SemanticTypes::Lesion& lesionLeft, const SemanticTypes::Lesion& lesionRight) + { + return lesionLeft.UID == lesionRight.UID; + }; + + std::sort(allLesionsOfImage.begin(), allLesionsOfImage.end(), lessThan); + allLesionsOfImage.erase(std::unique(allLesionsOfImage.begin(), allLesionsOfImage.end(), equal), allLesionsOfImage.end()); + + return allLesionsOfImage; +} + +mitk::SemanticTypes::LesionVector mitk::SemanticRelationsInference::GetAllLesionsOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) +{ + SemanticTypes::LesionVector allLesions = RelationStorage::GetAllLesionsOfCase(caseID); + + // filter the lesions: use only those, where the associated data is connected to image data that refers to the given control point using a lambda function + auto lambda = [&caseID, &controlPoint](const SemanticTypes::Lesion& lesion) + { + return !SpecificImageExists(caseID, lesion, controlPoint); + }; + + allLesions.erase(std::remove_if(allLesions.begin(), allLesions.end(), lambda), allLesions.end()); + + return allLesions; +} + bool mitk::SemanticRelationsInference::IsRepresentingALesion(const DataNode* segmentationNode) { try { - SemanticTypes::Lesion representedLesion = GetRepresentedLesion(segmentationNode); + SemanticTypes::Lesion representedLesion = GetLesionOfSegmentation(segmentationNode); if (representedLesion.UID.empty()) { return false; } } catch (const Exception&) { return false; } return true; } bool mitk::SemanticRelationsInference::IsRepresentingALesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID) { SemanticTypes::Lesion representedLesion = RelationStorage::GetLesionOfSegmentation(caseID, segmentationID); if (representedLesion.UID.empty()) { return false; } return true; } +bool mitk::SemanticRelationsInference::IsLesionPresent(const SemanticTypes::Lesion& lesion, const DataNode* dataNode) +{ + try + { + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); + SemanticTypes::ID dataNodeID = GetIDFromDataNode(dataNode); + + if (NodePredicates::GetImagePredicate()->CheckNode(dataNode)) + { + return IsLesionPresentOnImage(caseID, lesion, dataNodeID); + } + + if (NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) + { + return IsLesionPresentOnSegmentation(caseID, lesion, dataNodeID); + } + + return false; + } + catch (const SemanticRelationException&) + { + return false; + } +} + +bool mitk::SemanticRelationsInference::IsLesionPresentOnImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ID& imageID) +{ + SemanticTypes::IDVector allImageIDsOfLesion = GetAllImageIDsOfLesion(caseID, lesion); + for (const auto& imageIDOfLesion : allImageIDsOfLesion) + { + if (imageIDOfLesion == imageID) + { + return true; + } + } + + return false; +} + bool mitk::SemanticRelationsInference::IsLesionPresentOnSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ID& segmentationID) { const auto representedLesion = RelationStorage::GetLesionOfSegmentation(caseID, segmentationID); return lesion.UID == representedLesion.UID; } +bool mitk::SemanticRelationsInference::IsLesionPresentAtControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ControlPoint& controlPoint) +{ + SemanticTypes::IDVector allImageIDsOfLesion = GetAllImageIDsOfLesion(caseID, lesion); + for (const auto& imageID : allImageIDsOfLesion) + { + auto imageControlPoint = mitk::RelationStorage::GetControlPointOfImage(caseID, imageID); + if (imageControlPoint.date == controlPoint.date) + { + return true; + } + } + + return false; +} + bool mitk::SemanticRelationsInference::InstanceExists(const DataNode* dataNode) { try { SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); SemanticTypes::ID dataNodeID = GetIDFromDataNode(dataNode); if (NodePredicates::GetImagePredicate()->CheckNode(dataNode)) { std::vector allImageIDsOfCase = RelationStorage::GetAllImageIDsOfCase(caseID); return std::find(allImageIDsOfCase.begin(), allImageIDsOfCase.end(), dataNodeID) != allImageIDsOfCase.end(); } else if (NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { std::vector allSegmentationIDsOfCase = RelationStorage::GetAllSegmentationIDsOfCase(caseID); return std::find(allSegmentationIDsOfCase.begin(), allSegmentationIDsOfCase.end(), dataNodeID) != allSegmentationIDsOfCase.end(); } else { return false; } } catch (const SemanticRelationException&) { return false; } } bool mitk::SemanticRelationsInference::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { SemanticTypes::LesionVector allLesions = RelationStorage::GetAllLesionsOfCase(caseID); // filter all lesions: check for equality with the given lesion using a lambda function auto lambda = [&lesion](const SemanticTypes::Lesion& currentLesion) { return currentLesion.UID == lesion.UID; }; const auto existingLesion = std::find_if(allLesions.begin(), allLesions.end(), lambda); if (existingLesion != allLesions.end()) { return true; } else { return false; } } -mitk::SemanticTypes::IDVector mitk::SemanticRelationsInference::GetAllSegmentationIDsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) +mitk::SemanticTypes::IDVector mitk::SemanticRelationsInference::GetAllImageIDsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { - if (InstanceExists(caseID, lesion)) + if (!InstanceExists(caseID, lesion)) { - // lesion exists, retrieve all case segmentations from the storage - SemanticTypes::IDVector allSegmentationIDs = RelationStorage::GetAllSegmentationIDsOfCase(caseID); + mitkThrowException(SemanticRelationException) << "Could not find an existing lesion instance for the given caseID " << caseID << " and lesion " << lesion.UID << "."; + } - // filter all segmentationIDs: check for semantic relation with the given lesion using a lambda function - auto lambda = [&lesion, caseID](const SemanticTypes::ID& segmentationID) + 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 = 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()) { - SemanticTypes::Lesion representedLesion = RelationStorage::GetLesionOfSegmentation(caseID, segmentationID); - return lesion.UID != representedLesion.UID; - }; + allImageIDsOfLesion.push_back(imageID); + } + } - allSegmentationIDs.erase(std::remove_if(allSegmentationIDs.begin(), allSegmentationIDs.end(), lambda), allSegmentationIDs.end()); + std::sort(allImageIDsOfLesion.begin(), allImageIDsOfLesion.end()); + allImageIDsOfLesion.erase(std::unique(allImageIDsOfLesion.begin(), allImageIDsOfLesion.end()), allImageIDsOfLesion.end()); - return allSegmentationIDs; - } - else + return allImageIDsOfLesion; +} + +mitk::SemanticTypes::IDVector mitk::SemanticRelationsInference::GetAllSegmentationIDsOfLesion(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 << "."; } + + // lesion exists, retrieve all case segmentations from the storage + SemanticTypes::IDVector allSegmentationIDs = RelationStorage::GetAllSegmentationIDsOfCase(caseID); + + // filter all segmentationIDs: check for semantic relation with the given lesion using a lambda function + auto lambda = [&lesion, caseID](const SemanticTypes::ID& segmentationID) + { + SemanticTypes::Lesion representedLesion = RelationStorage::GetLesionOfSegmentation(caseID, segmentationID); + return lesion.UID != representedLesion.UID; + }; + + allSegmentationIDs.erase(std::remove_if(allSegmentationIDs.begin(), allSegmentationIDs.end(), lambda), allSegmentationIDs.end()); + + return allSegmentationIDs; } mitk::SemanticTypes::ControlPoint mitk::SemanticRelationsInference::GetControlPointOfImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); SemanticTypes::ID dataNodeID = GetIDFromDataNode(imageNode); return RelationStorage::GetControlPointOfImage(caseID, dataNodeID); } +mitk::SemanticTypes::ControlPointVector mitk::SemanticRelationsInference::GetAllControlPointsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) +{ + SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); + + // filter the control points: use only those, where the associated image data has a segmentation that refers to the given lesion using a lambda function + auto lambda = [&caseID, &lesion](const SemanticTypes::ControlPoint& controlPoint) + { + return !SpecificImageExists(caseID, lesion, controlPoint); + }; + + allControlPoints.erase(std::remove_if(allControlPoints.begin(), allControlPoints.end(), lambda), allControlPoints.end()); + + return allControlPoints; +} + +mitk::SemanticTypes::ControlPointVector mitk::SemanticRelationsInference::GetAllControlPointsOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) +{ + SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); + + // filter the control points: use only those, where the associated image data refers to the given information type using a lambda function + auto lambda = [&caseID, &informationType](const SemanticTypes::ControlPoint& controlPoint) + { + return !SpecificImageExists(caseID, informationType, controlPoint); + }; + + allControlPoints.erase(std::remove_if(allControlPoints.begin(), allControlPoints.end(), lambda), allControlPoints.end()); + + return allControlPoints; +} + bool mitk::SemanticRelationsInference::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); // filter all control points: check for equality with the given control point using a lambda function auto lambda = [&controlPoint](const SemanticTypes::ControlPoint& currentControlPoint) { return currentControlPoint.UID == controlPoint.UID; }; const auto existingControlPoint = std::find_if(allControlPoints.begin(), allControlPoints.end(), lambda); if (existingControlPoint != allControlPoints.end()) { return true; } else { return false; } } mitk::SemanticTypes::ExaminationPeriodVector mitk::SemanticRelationsInference::GetAllExaminationPeriodsOfCase(const SemanticTypes::CaseID& caseID) { return RelationStorage::GetAllExaminationPeriodsOfCase(caseID); } bool mitk::SemanticRelationsInference::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) { SemanticTypes::ExaminationPeriodVector allExaminationPeriods = GetAllExaminationPeriodsOfCase(caseID); // filter all examination periods: check for equality with the given examination period using a lambda function auto lambda = [&examinationPeriod](const SemanticTypes::ExaminationPeriod& currentExaminationPeriod) { return currentExaminationPeriod.UID == examinationPeriod.UID; }; const auto existingExaminationPeriod = std::find_if(allExaminationPeriods.begin(), allExaminationPeriods.end(), lambda); if (existingExaminationPeriod != allExaminationPeriods.end()) { return true; } else { return false; } } -mitk::SemanticTypes::InformationTypeVector mitk::SemanticRelationsInference::GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID) -{ - return RelationStorage::GetAllInformationTypesOfCase(caseID); -} - mitk::SemanticTypes::InformationType mitk::SemanticRelationsInference::GetInformationTypeOfImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); SemanticTypes::ID imageID = GetIDFromDataNode(imageNode); return GetInformationTypeOfImage(caseID, imageID); } mitk::SemanticTypes::InformationType mitk::SemanticRelationsInference::GetInformationTypeOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID) { return RelationStorage::GetInformationTypeOfImage(caseID, imageID); } -mitk::SemanticTypes::IDVector mitk::SemanticRelationsInference::GetAllImageIDsOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) +mitk::SemanticTypes::InformationTypeVector mitk::SemanticRelationsInference::GetAllInformationTypesOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { - if (InstanceExists(caseID, informationType)) + 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) { - // information type exists, retrieve all imageIDs from the storage - SemanticTypes::IDVector allImageIDs = RelationStorage::GetAllImageIDsOfCase(caseID); + return !SpecificImageExists(caseID, informationType, controlPoint); + }; - // filter all images to remove the ones with a different information type using a lambda function - auto lambda = [&informationType, caseID](const SemanticTypes::ID& imageID) - { - return informationType != RelationStorage::GetInformationTypeOfImage(caseID, imageID); - }; + allInformationTypes.erase(std::remove_if(allInformationTypes.begin(), allInformationTypes.end(), lambda), allInformationTypes.end()); - allImageIDs.erase(std::remove_if(allImageIDs.begin(), allImageIDs.end(), lambda), allImageIDs.end()); + return allInformationTypes; +} - return allImageIDs; - } - else +mitk::SemanticTypes::IDVector mitk::SemanticRelationsInference::GetAllImageIDsOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) +{ + if (InstanceExists(caseID, informationType)) { mitkThrowException(SemanticRelationException) << "Could not find an existing information type for the given caseID " << caseID << " and information type " << informationType; } + + // information type exists, retrieve all imageIDs from the storage + SemanticTypes::IDVector allImageIDs = RelationStorage::GetAllImageIDsOfCase(caseID); + + // filter all images to remove the ones with a different information type using a lambda function + auto lambda = [&informationType, caseID](const SemanticTypes::ID& imageID) + { + return informationType != RelationStorage::GetInformationTypeOfImage(caseID, imageID); + }; + + allImageIDs.erase(std::remove_if(allImageIDs.begin(), allImageIDs.end(), lambda), allImageIDs.end()); + + return allImageIDs; } bool mitk::SemanticRelationsInference::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) { - SemanticTypes::InformationTypeVector allInformationTypes = GetAllInformationTypesOfCase(caseID); + 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; } } -std::vector mitk::SemanticRelationsInference::GetAllCaseIDs() +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) { - return RelationStorage::GetAllCaseIDs(); + 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(); } void mitk::SemanticRelationsInference::ClearControlPoints(const SemanticTypes::CaseID& caseID) { SemanticTypes::ControlPointVector allControlPointsOfCase = RelationStorage::GetAllControlPointsOfCase(caseID); std::vector allImageIDsVectorValue = RelationStorage::GetAllImageIDsOfCase(caseID); SemanticTypes::ControlPointVector referencedControlPoints; for (const auto& imageID : allImageIDsVectorValue) { auto controlPointOfImage = RelationStorage::GetControlPointOfImage(caseID, imageID); referencedControlPoints.push_back(controlPointOfImage); } std::sort(allControlPointsOfCase.begin(), allControlPointsOfCase.end()); std::sort(referencedControlPoints.begin(), referencedControlPoints.end()); SemanticTypes::ControlPointVector nonReferencedControlPoints; std::set_difference(allControlPointsOfCase.begin(), allControlPointsOfCase.end(), referencedControlPoints.begin(), referencedControlPoints.end(), std::inserter(nonReferencedControlPoints, nonReferencedControlPoints.begin())); auto allExaminationPeriods = GetAllExaminationPeriodsOfCase(caseID); for (const auto& controlPoint : nonReferencedControlPoints) { const auto& examinationPeriod = FindExaminationPeriod(controlPoint, allExaminationPeriods); RelationStorage::RemoveControlPointFromExaminationPeriod(caseID, controlPoint, examinationPeriod); RelationStorage::RemoveControlPointFromCase(caseID, controlPoint); } }