diff --git a/Modules/SemanticRelations/include/mitkSemanticRelationsDataStorageAccess.h b/Modules/SemanticRelations/include/mitkSemanticRelationsDataStorageAccess.h new file mode 100644 index 0000000000..07b2854aaf --- /dev/null +++ b/Modules/SemanticRelations/include/mitkSemanticRelationsDataStorageAccess.h @@ -0,0 +1,140 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef MITKSEMANTICRELATIONSDATASTORAGEACCESS_H +#define MITKSEMANTICRELATIONSDATASTORAGEACCESS_H + +#include + +// semantic relations module +#include "mitkSemanticTypes.h" + +// mitk core +#include +#include + +namespace mitk +{ + /** + * @brief The API provides functions to query and manipulate image relations and instances, + * that are helpful during follow-up examination, like control-points (time period), + * types of the images or lesions that may be visible on multiple images. + * + * The class is able to generate IDs from given data nodes using DICOM information. + * These IDs are used to identify the corresponding instances of a specific case. + * The case can also be directly identified by the given case ID. + * + * In order for most functions to work the case ID has to be used as a parameter. + * If not, these functions do nothing. + */ + class MITKSEMANTICRELATIONS_EXPORT SemanticRelationsDataStorageAccess + { + public: + + using DataNodeVector = std::vector; + + SemanticRelationsDataStorageAccess(DataStorage* dataStorage); + ~SemanticRelationsDataStorageAccess(); + + /************************************************************************/ + /* functions to get instances / attributes */ + /************************************************************************/ + /** + * @brief Return a vector of all segmentations that are currently available for the given case. + * The segmentations may be connected / not connected to a lesion of the case. + * If no segmentations are stored for the current case, an empty vector is returned. + * + * @pre The data storage member has to be valid (!nullptr). + * @throw SemanticRelationException, if the data storage member is invalid (==nullptr). + * + * @param caseID The current case identifier is defined by the given string. + * @return A vector of data nodes representing segmentations. + */ + DataNodeVector GetAllSegmentationsOfCase(const SemanticTypes::CaseID& caseID) const; + /** + * @brief Return a vector of all segmentations that define the given lesion. These segmentations don't have to be linked to the same image. + * If the lesion is not referred to by any segmentation, an empty vector is returned. + * + * @pre The data storage member has to be valid (!nullptr). + * @throw SemanticRelationException, if the data storage member is invalid (==nullptr). + * @pre The UID of the lesion has to exist for a lesion instance. + * @throw SemanticRelationException, if UID of the lesion does not exist for a lesion instance (this can be checked via 'InstanceExists'). + * + * @param caseID The current case identifier is defined by the given string. + * @param lesion A Lesion with a UID that identifies the corresponding lesion instance. + * @return A vector of data nodes representing segmentations that define the given lesion. + */ + DataNodeVector GetAllSegmentationsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const; + /** + * @brief Return a vector of all images that are currently available for the given case. + * + * @pre The data storage member has to be valid (!nullptr). + * @throw SemanticRelationException, if the data storage member is invalid (==nullptr). + * + * @param caseID The current case identifier is defined by the given string. + * @return A vector of data nodes representing images. + */ + DataNodeVector GetAllImagesOfCase(const SemanticTypes::CaseID& caseID) const; + /** + * @brief Return a vector of all images that are connected to those segmentations that are linked to the given lesion. + * If the lesion is not referred to by any segmentation, an empty vector is returned. + * + * @pre The UID of the lesion has to exist for a lesion instance. + * @throw SemanticRelationException, if UID of the lesion does not exist for a lesion instance (this can be checked via 'InstanceExists'). + * + * @param caseID The current case identifier is defined by the given string. + * @param lesion A Lesion with a UID that identifies the corresponding lesion instance. + * @return A vector of data nodes representing images on which the lesions are visible. + */ + DataNodeVector GetAllImagesOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const; + /** + * @brief Return a vector of all image nodes that are defined with the given information type and with the given control point. + * + * @pre The UID of the control point has to exist for a control point instance. + * The information type has to exist for the given case (and is therefore used by at least one data node). + * @throw SemanticRelationException, if the UID of the control point does not exist for a control point instance (this can be checked via 'InstanceExists') or + * if the information type is not used by any data node (this can be checked via 'InstanceExists'). + * + * @param caseID The current case identifier is defined by the given string. + * @param controlPoint A control point with a UID that identifies the corresponding control point instance. + * @param informationType An information type that identifies the corresponding information type instance. + * @return A vector of image nodes that are defined with the given information type with the given control point. + */ + DataNodeVector GetAllSpecificImages(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const; + /** + * @brief Return a vector of all segmentation nodes that are defined with the given information type and with the given control point. + * The function uses the 'GetAllSpecificImages'-function to retrieve the specific images and then searches for the derived nodes (segmentation child nodes). + * + * @pre The UID of the control point has to exist for a control point instance. + * The information type has to exist for the given case (and is therefore used by at least one data node). + * @throw SemanticRelationException, if the UID of the control point does not exist for a control point instance (this can be checked via 'InstanceExists') or + * if the information type is not used by any data node (this can be checked via 'InstanceExists'). + * + * @param caseID The current case identifier is defined by the given string. + * @param controlPoint A control point with a UID that identifies the corresponding control point instance. + * @param informationType An information type that identifies the corresponding information type instance. + * @return A vector of segmentation nodes that are defined with the given information type with the given control point. + */ + DataNodeVector GetAllSpecificSegmentations(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const; + + private: + + WeakPointer m_DataStorage; + + }; +} // namespace mitk + +#endif // MITKSEMANTICRELATIONSDATASTORAGEACCESS_H diff --git a/Modules/SemanticRelations/src/mitkSemanticRelationsDataStorageAccess.cpp b/Modules/SemanticRelations/src/mitkSemanticRelationsDataStorageAccess.cpp new file mode 100644 index 0000000000..b02e89d69e --- /dev/null +++ b/Modules/SemanticRelations/src/mitkSemanticRelationsDataStorageAccess.cpp @@ -0,0 +1,218 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkSemanticRelationsDataStorageAccess.h" + +// semantic relations module +#include "mitkControlPointManager.h" +#include "mitkDICOMHelper.h" +#include "mitkNodePredicates.h" +#include "mitkRelationStorage.h" +#include "mitkSemanticRelationException.h" +#include "mitkSemanticRelationsInference.h" + +// c++ +#include +#include + +mitk::SemanticRelationsDataStorageAccess::SemanticRelationsDataStorageAccess(DataStorage* dataStorage) + : m_DataStorage(dataStorage) +{ + // nothing here +} + +mitk::SemanticRelationsDataStorageAccess::~SemanticRelationsDataStorageAccess() +{ + // nothing here +} + +/************************************************************************/ +/* functions to get instances / attributes */ +/************************************************************************/ + +mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllSegmentationsOfCase(const SemanticTypes::CaseID& caseID) const +{ + if (m_DataStorage.IsExpired()) + { + mitkThrowException(SemanticRelationException) << "Not a valid data storage."; + } + + std::vector allSegmentationIDsOfCase = RelationStorage::GetAllSegmentationIDsOfCase(caseID); + std::vector allSegmentationsOfCase; + // get all segmentation nodes of the current data storage + // only those nodes are respected, that are currently held in the data storage + DataStorage::SetOfObjects::ConstPointer segmentationNodes = m_DataStorage.Lock()->GetSubset(NodePredicates::GetSegmentationPredicate()); + for (auto it = segmentationNodes->Begin(); it != segmentationNodes->End(); ++it) + { + DataNode* segmentationNode = it->Value(); + try + { + // find the corresponding segmentation node for the given segmentation ID + std::string nodeCaseID = GetCaseIDFromDataNode(segmentationNode); + std::string nodeSegmentationID = GetIDFromDataNode(segmentationNode); + if (nodeCaseID == caseID && (std::find(allSegmentationIDsOfCase.begin(), allSegmentationIDsOfCase.end(), nodeSegmentationID) != allSegmentationIDsOfCase.end())) + { + // found current image node in the storage, add it to the return vector + allSegmentationsOfCase.push_back(segmentationNode); + } + } + catch (const std::exception&) + { + // found a segmentation node that is not stored in the semantic relations + // this segmentation node does not have any DICOM information --> exception thrown + // continue with the next segmentation to compare IDs + continue; + } + } + + return allSegmentationsOfCase; +} + +mitk::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; + // get all image nodes of the current data storage + // only those nodes are respected, that are currently held in the data storage + DataStorage::SetOfObjects::ConstPointer imageNodes = m_DataStorage.Lock()->GetSubset(NodePredicates::GetImagePredicate()); + for (auto it = imageNodes->Begin(); it != imageNodes->End(); ++it) + { + DataNode* imageNode = it->Value(); + // find the corresponding image node for the given segmentation ID + std::string nodeCaseID = GetCaseIDFromDataNode(imageNode); + std::string nodeImageID = GetIDFromDataNode(imageNode); + if (nodeCaseID == caseID && (std::find(allImageIDsOfCase.begin(), allImageIDsOfCase.end(), nodeImageID) != allImageIDsOfCase.end())) + { + // found current image node in the storage, add it to the return vector + allImagesOfCase.push_back(imageNode); + } + } + + return allImagesOfCase; +} + +mitk::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; + } + } + else + { + mitkThrowException(SemanticRelationException) << "Could not find an existing control point for the given caseID " << caseID << " and control point " << controlPoint.UID; + } +} + +mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllSpecificSegmentations(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const +{ + if (m_DataStorage.IsExpired()) + { + mitkThrow() << "Not a valid data storage."; + } + + DataNodeVector allSpecificImages = GetAllSpecificImages(caseID, controlPoint, informationType); + DataNodeVector allSpecificSegmentations; + for (const auto& imageNode : allSpecificImages) + { + DataStorage::SetOfObjects::ConstPointer segmentationNodes = m_DataStorage.Lock()->GetDerivations(imageNode, NodePredicates::GetSegmentationPredicate(), false); + for (auto it = segmentationNodes->Begin(); it != segmentationNodes->End(); ++it) + { + allSpecificSegmentations.push_back(it->Value()); + } + } + + return allSpecificSegmentations; +}