diff --git a/Modules/SemanticRelations/include/mitkControlPointManager.h b/Modules/SemanticRelations/include/mitkControlPointManager.h index aa6eb760ae..d51ac37f9f 100644 --- a/Modules/SemanticRelations/include/mitkControlPointManager.h +++ b/Modules/SemanticRelations/include/mitkControlPointManager.h @@ -1,122 +1,123 @@ /*=================================================================== 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. + * @param caseID The current case identifier is defined by the given string. + * @param controlPointUID The control point UID as string. * * @return The control point with its UID and the date. */ - MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint GetControlPointByUID(const SemanticTypes::ID& controlPointUID, const SemanticTypes::ControlPointVector& allControlPoints); + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint GetControlPointByUID(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& controlPointUID); /** * @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. + * @param caseID The current case identifier is defined by the given string. + * @param controlPoint The control point to check for existence. * * @return The existing control point. */ - MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint FindExistingControlPoint(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ControlPointVector& allControlPoints); + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint FindExistingControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); /** - * @brief Returns an already existing close control point from the given vector of control points. This closest control point has a date + * @brief Returns an already existing close control point from the given vector of control points. This closest control point has a * 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. + * @param caseID The current case identifier is defined by the given string. + * @param controlPoint The control point to check for distance. * * @return The closest control point. */ - MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint FindClosestControlPoint(const SemanticTypes::ControlPoint& controlPoint, SemanticTypes::ControlPointVector& allControlPoints); + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint FindClosestControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); /** * @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. + * @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 contains the given control point. */ - MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ExaminationPeriod FindContainingExaminationPeriod(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriodVector& allExaminationPeriods); + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ExaminationPeriod FindContainingExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); /** * @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. + * @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. + * @param caseID The current case identifier is defined by the given string. + * @param allExaminationPeriods The examination periods to sort. */ - MITKSEMANTICRELATIONS_EXPORT void SortExaminationPeriods(SemanticTypes::ExaminationPeriodVector& allExaminationPeriods, const SemanticTypes::ControlPointVector& allControlPoints); + MITKSEMANTICRELATIONS_EXPORT void SortAllExaminationPeriods(const SemanticTypes::CaseID& caseID, SemanticTypes::ExaminationPeriodVector& allExaminationPeriods); + } // namespace mitk #endif // MITKCONTROLPOINTMANAGER_H diff --git a/Modules/SemanticRelations/src/mitkControlPointManager.cpp b/Modules/SemanticRelations/src/mitkControlPointManager.cpp index 52d2ee7f4e..01c041f871 100644 --- a/Modules/SemanticRelations/src/mitkControlPointManager.cpp +++ b/Modules/SemanticRelations/src/mitkControlPointManager.cpp @@ -1,222 +1,228 @@ /*=================================================================== 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."; } controlPoint.UID = UIDGeneratorBoost::GenerateUID(); return controlPoint; } -mitk::SemanticTypes::ControlPoint mitk::GetControlPointByUID(const SemanticTypes::ID& controlPointUID, const std::vector& allControlPoints) +mitk::SemanticTypes::ControlPoint mitk::GetControlPointByUID(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& controlPointUID) { auto lambda = [&controlPointUID](const SemanticTypes::ControlPoint& currentControlPoint) { return currentControlPoint.UID == controlPointUID; }; + SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); 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) +mitk::SemanticTypes::ControlPoint mitk::FindExistingControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { + SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); 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) +mitk::SemanticTypes::ControlPoint mitk::FindClosestControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { + SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); 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; + SemanticTypes::ControlPointVector::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::FindContainingExaminationPeriod(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriodVector& allExaminationPeriods) +mitk::SemanticTypes::ExaminationPeriod mitk::FindContainingExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { + SemanticTypes::ExaminationPeriodVector allExaminationPeriods = RelationStorage::GetAllExaminationPeriodsOfCase(caseID); 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); + SemanticTypes::ControlPoint existingControlPoint = FindExistingControlPoint(caseID, controlPoint); if (!existingControlPoint.UID.empty()) { specificControlPoint = existingControlPoint; } else { - auto closestControlPoint = FindClosestControlPoint(controlPoint, allControlPoints); + auto closestControlPoint = FindClosestControlPoint(caseID, controlPoint); if (!closestControlPoint.UID.empty()) { specificControlPoint = closestControlPoint; } } // find the containing examination period - auto allExaminationPeriods = RelationStorage::GetAllExaminationPeriodsOfCase(caseID); - return FindContainingExaminationPeriod(specificControlPoint, allExaminationPeriods); + return FindContainingExaminationPeriod(caseID, specificControlPoint); } 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."; } return FindFittingExaminationPeriod(caseID, controlPoint); } -void mitk::SortExaminationPeriods(SemanticTypes::ExaminationPeriodVector& allExaminationPeriods, const SemanticTypes::ControlPointVector& allControlPoints) +void mitk::SortAllExaminationPeriods(const SemanticTypes::CaseID& caseID, SemanticTypes::ExaminationPeriodVector& allExaminationPeriods) { - auto lambda = [allControlPoints](const SemanticTypes::ExaminationPeriod& leftExaminationPeriod, const SemanticTypes::ExaminationPeriod& rightExaminationPeriod) + SemanticTypes::ControlPointVector controlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); + // sort the vector of control points for the timeline + std::sort(controlPoints.begin(), controlPoints.end()); + + auto lambda = [&caseID](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); + const auto& leftControlPoint = GetControlPointByUID(caseID, leftUID); + const auto& rightControlPoint = GetControlPointByUID(caseID, rightUID); return leftControlPoint.date < rightControlPoint.date; }; std::sort(allExaminationPeriods.begin(), allExaminationPeriods.end(), lambda); } diff --git a/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp b/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp index 5b043474c1..a974ed4571 100644 --- a/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp +++ b/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp @@ -1,592 +1,591 @@ /*=================================================================== 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 = 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)) { SemanticTypes::IDVector allImageIDsOfCase = RelationStorage::GetAllImageIDsOfCase(caseID); return std::find(allImageIDsOfCase.begin(), allImageIDsOfCase.end(), dataNodeID) != allImageIDsOfCase.end(); } if (NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { 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); + controlPoint = GetControlPointByUID(caseID, controlPointUID); 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 478cd4f00d..7173530030 100644 --- a/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp +++ b/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp @@ -1,612 +1,610 @@ /*=================================================================== 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); + SemanticTypes::ControlPoint existingControlPoint = FindExistingControlPoint(caseID, controlPoint); 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); + SemanticTypes::ControlPoint closestControlPoint = FindClosestControlPoint(caseID, controlPoint); + SemanticTypes::ExaminationPeriod examinationPeriod = FindContainingExaminationPeriod(caseID, closestControlPoint); 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"; AddExaminationPeriod(caseID, examinationPeriod); } + // added a new control point + AddControlPointAndLinkImage(imageNode, controlPoint, false); // 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."; } 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 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); SemanticTypes::IDVector allImageIDsVectorValue = RelationStorage::GetAllImageIDsOfCase(caseID); SemanticTypes::ControlPointVector referencedControlPoints; for (const auto& imageID : allImageIDsVectorValue) { 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())); - SemanticTypes::ExaminationPeriodVector allExaminationPeriods = RelationStorage::GetAllExaminationPeriodsOfCase(caseID); for (const auto& controlPoint : nonReferencedControlPoints) { - const SemanticTypes::ExaminationPeriod& examinationPeriod = FindContainingExaminationPeriod(controlPoint, allExaminationPeriods); + const SemanticTypes::ExaminationPeriod& examinationPeriod = FindContainingExaminationPeriod(caseID, controlPoint); RelationStorage::RemoveControlPointFromExaminationPeriod(caseID, controlPoint, examinationPeriod); RelationStorage::RemoveControlPoint(caseID, controlPoint); } } diff --git a/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h b/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h index e3efbb679f..966f97a35e 100644 --- a/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h +++ b/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h @@ -1,134 +1,133 @@ /*=================================================================== 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 QMITKPATIENTTABLEMODEL_H #define QMITKPATIENTTABLEMODEL_H // semantic relations UI module #include "QmitkAbstractSemanticRelationsStorageModel.h" // semantic relations module #include #include // mitk core #include // qt #include #include /** * @brief The QmitkPatientTableModel is a subclass of the QmitkAbstractSemanticRelationsStorageModel and holds the semantic relations data of the currently selected case. * * The QmitkPatientTableModel uses the 'data' function to return either the data node of a table cell or the thumbnail of the underlying image. * The horizontal header of the table shows the control points of the current case and the vertical header of the table shows the information types of the current case. * Using the 'GetFilteredData'-function of the SemanticRelations-class the model is able to retrieve the correct data node for each table entry. * * Additionally the model creates and holds the QPixmaps of the known data nodes in order to return a thumbnail, if needed. */ class QmitkPatientTableModel : public QmitkAbstractSemanticRelationsStorageModel { Q_OBJECT public: QmitkPatientTableModel(QObject* parent = nullptr); ~QmitkPatientTableModel(); ////////////////////////////////////////////////////////////////////////// // overridden functions from QAbstractItemModel ////////////////////////////////////////////////////////////////////////// virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; virtual QModelIndex parent(const QModelIndex& child) const override; virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override; virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; virtual Qt::ItemFlags flags(const QModelIndex& index) const override; ////////////////////////////////////////////////////////////////////////// /// end override ///////////////////////////////////////////////////////////////////////// void SetNodeType(const std::string& nodeType); protected: // the following functions have to be overridden... virtual void NodePredicateChanged() override; // but are not implemented in this model virtual void NodeAdded(const mitk::DataNode*) override { } virtual void NodeChanged(const mitk::DataNode*) override { } virtual void NodeRemoved(const mitk::DataNode*) override { } /** * @brief Overridden from 'QmitkAbstractSemanticRelationsStorageModel': This function retrieves all control points * and information types of the current case and stores them to define the header of the table. * Furthermore all images are retrieved and the pixmap of the images are generated and stored. */ virtual void SetData() override; private: void SetHeaderModel(); void SetPixmaps(); void SetLesionPresences(); /** * @brief The function uses the ID of the node to see if a pixmap was already set. If not, the given pixmap * is used and stored inside a member variable. If the pixmap was already set, it will be overwritten. * Using 'nullptr' as a pixmap will erase the entry for the given data node. * * @param dataNode The data node whose pixmap should be set * @param pixmapFromImage The pixmap that shows an image of the content of the data node */ void SetPixmapOfNode(const mitk::DataNode* dataNode, QPixmap* pixmapFromImage); /** * @brief The function uses the ID of the node to see if a lesion presence was already set. If not, the given * bool value is used and stored inside a member variable. If the lesion presence was already set, it * will be overwritten. * The function is used by the 'SetLesionPresences' function. * * @param dataNode The data node whose lesion presence should be set * @param lesionPresence The bool value that defines the lesion presence of the given data node */ void SetLesionPresenceOfNode(const mitk::DataNode* dataNode, bool lesionPresence); /** * @brief Returns the data node that is associated with the given table entry (index). * * The function uses the SemanticRelations-class and the current control point data and information type data to * filter the nodes of the current case. * The index is used to access the correct row in the table (information type) and the correct column in the table (control point). * * @par index The QModelIndex of the table entry */ mitk::DataNode* GetCurrentDataNode(const QModelIndex &index) const; std::map m_PixmapMap; std::map m_LesionPresence; - mitk::SemanticTypes::ControlPointVector m_ControlPoints; mitk::SemanticTypes::ExaminationPeriodVector m_ExaminationPeriods; mitk::SemanticTypes::InformationTypeVector m_InformationTypes; mitk::SemanticRelationsDataStorageAccess::DataNodeVector m_CurrentDataNodes; std::string m_SelectedNodeType; QStandardItemModel* m_HeaderModel; }; #endif // QMITKPATIENTTABLEMODEL_H diff --git a/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp b/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp index e620adbf6e..93a04931e8 100644 --- a/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp @@ -1,354 +1,350 @@ /*=================================================================== 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 UI module #include "QmitkPatientTableModel.h" #include "QmitkPatientTableHeaderView.h" #include "QmitkSemanticRelationsUIHelper.h" // semantic relations module #include #include #include #include #include #include #include // qt #include // c++ #include #include QmitkPatientTableModel::QmitkPatientTableModel(QObject* parent /*= nullptr*/) : QmitkAbstractSemanticRelationsStorageModel(parent) , m_SelectedNodeType("Image") { m_HeaderModel = new QStandardItemModel(this); } QmitkPatientTableModel::~QmitkPatientTableModel() { // nothing here } QModelIndex QmitkPatientTableModel::index(int row, int column, const QModelIndex& parent/* = QModelIndex()*/) const { if (hasIndex(row, column, parent)) { return createIndex(row, column); } return QModelIndex(); } QModelIndex QmitkPatientTableModel::parent(const QModelIndex& /*child*/) const { return QModelIndex(); } int QmitkPatientTableModel::rowCount(const QModelIndex& parent/* = QModelIndex()*/) const { if (parent.isValid()) { return 0; } return m_InformationTypes.size(); } int QmitkPatientTableModel::columnCount(const QModelIndex& parent/* = QModelIndex()*/) const { if (parent.isValid()) { return 0; } return m_ExaminationPeriods.size(); } QVariant QmitkPatientTableModel::data(const QModelIndex& index, int role/* = Qt::DisplayRole*/) const { // special role for returning the horizontal header if (QmitkPatientTableHeaderView::HorizontalHeaderDataRole == role) { return QVariant::fromValue(m_HeaderModel); } if (!index.isValid()) { return QVariant(); } if (index.row() < 0 || index.row() >= static_cast(m_InformationTypes.size()) || index.column() < 0 || index.column() >= static_cast(m_ExaminationPeriods.size())) { return QVariant(); } mitk::DataNode* dataNode = GetCurrentDataNode(index); if (nullptr == dataNode) { return QVariant(); } if (Qt::DecorationRole == role) { auto it = m_PixmapMap.find(dataNode); if (it != m_PixmapMap.end()) { return QVariant(it->second); } } if (QmitkDataNodeRole == role) { return QVariant::fromValue(mitk::DataNode::Pointer(dataNode)); } if (QmitkDataNodeRawPointerRole == role) { return QVariant::fromValue(dataNode); } if (Qt::BackgroundColorRole == role) { auto it = m_LesionPresence.find(dataNode); if (it != m_LesionPresence.end()) { return it->second ? QVariant(QColor(Qt::darkGreen)) : QVariant(QColor(Qt::transparent)); } return QVariant(QColor(Qt::transparent)); } return QVariant(); } QVariant QmitkPatientTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (Qt::Vertical == orientation && Qt::DisplayRole == role) { if (static_cast(m_InformationTypes.size()) > section) { mitk::SemanticTypes::InformationType currentInformationType = m_InformationTypes.at(section); return QVariant(QString::fromStdString(currentInformationType)); } } return QVariant(); } Qt::ItemFlags QmitkPatientTableModel::flags(const QModelIndex& index) const { Qt::ItemFlags flags; mitk::DataNode* dataNode = GetCurrentDataNode(index); if (nullptr != dataNode) { flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; } return flags; } void QmitkPatientTableModel::SetNodeType(const std::string& nodeType) { m_SelectedNodeType = nodeType; UpdateModelData(); } void QmitkPatientTableModel::NodePredicateChanged() { UpdateModelData(); } void QmitkPatientTableModel::SetData() { - // get all control points of current case - m_ControlPoints = mitk::RelationStorage::GetAllControlPointsOfCase(m_CaseID); - // sort the vector of control points for the timeline - std::sort(m_ControlPoints.begin(), m_ControlPoints.end()); - // get all examination periods of current case m_ExaminationPeriods = mitk::RelationStorage::GetAllExaminationPeriodsOfCase(m_CaseID); - // sort the vector of examination periods for the timeline - mitk::SortExaminationPeriods(m_ExaminationPeriods, m_ControlPoints); + + // sort all examination periods for the timeline + mitk::SortAllExaminationPeriods(m_CaseID, m_ExaminationPeriods); // rename examination periods according to their new order std::string examinationPeriodName = "Baseline"; for (int i = 0; i < m_ExaminationPeriods.size(); ++i) { auto& examinationPeriod = m_ExaminationPeriods.at(i); examinationPeriod.name = examinationPeriodName; mitk::RelationStorage::RenameExaminationPeriod(m_CaseID, examinationPeriod); examinationPeriodName = "Follow-up " + std::to_string(i); } // get all information types points of current case m_InformationTypes = mitk::RelationStorage::GetAllInformationTypesOfCase(m_CaseID); if ("Image" == m_SelectedNodeType) { m_CurrentDataNodes = m_SemanticRelationsDataStorageAccess->GetAllImagesOfCase(m_CaseID); } else if ("Segmentation" == m_SelectedNodeType) { m_CurrentDataNodes = m_SemanticRelationsDataStorageAccess->GetAllSegmentationsOfCase(m_CaseID); } SetHeaderModel(); SetPixmaps(); SetLesionPresences(); } void QmitkPatientTableModel::SetHeaderModel() { m_HeaderModel->clear(); QStandardItem* rootItem = new QStandardItem("Timeline"); QList standardItems; for (const auto& examinationPeriod : m_ExaminationPeriods) { QStandardItem* examinationPeriodItem = new QStandardItem(QString::fromStdString(examinationPeriod.name)); standardItems.push_back(examinationPeriodItem); rootItem->appendColumn(standardItems); standardItems.clear(); } m_HeaderModel->setItem(0, 0, rootItem); } void QmitkPatientTableModel::SetPixmaps() { m_PixmapMap.clear(); for (const auto& dataNode : m_CurrentDataNodes) { // set the pixmap for the current node QPixmap pixmapFromImage = QmitkSemanticRelationsUIHelper::GetPixmapFromImageNode(dataNode); SetPixmapOfNode(dataNode, &pixmapFromImage); } } void QmitkPatientTableModel::SetPixmapOfNode(const mitk::DataNode* dataNode, QPixmap* pixmapFromImage) { if (nullptr == dataNode) { return; } std::map::iterator iter = m_PixmapMap.find(dataNode); if (iter != m_PixmapMap.end()) { // key already existing if (nullptr != pixmapFromImage) { // overwrite already stored pixmap iter->second = pixmapFromImage->scaled(120, 120, Qt::IgnoreAspectRatio); } else { // remove key if no pixmap is given m_PixmapMap.erase(iter); } } else { m_PixmapMap.insert(std::make_pair(dataNode, pixmapFromImage->scaled(120, 120, Qt::IgnoreAspectRatio))); } } void QmitkPatientTableModel::SetLesionPresences() { m_LesionPresence.clear(); if (!mitk::SemanticRelationsInference::InstanceExists(m_CaseID, m_Lesion)) { return; } for (const auto& dataNode : m_CurrentDataNodes) { if (!mitk::SemanticRelationsInference::InstanceExists(dataNode)) { continue; } // set the lesion presence for the current node bool lesionPresence = mitk::SemanticRelationsInference::IsLesionPresent(m_Lesion, dataNode); SetLesionPresenceOfNode(dataNode, lesionPresence); } } void QmitkPatientTableModel::SetLesionPresenceOfNode(const mitk::DataNode* dataNode, bool lesionPresence) { std::map::iterator iter = m_LesionPresence.find(dataNode); if (iter != m_LesionPresence.end()) { // key already existing, overwrite already stored bool value iter->second = lesionPresence; } else { m_LesionPresence.insert(std::make_pair(dataNode, lesionPresence)); } } mitk::DataNode* QmitkPatientTableModel::GetCurrentDataNode(const QModelIndex& index) const { if (!index.isValid()) { return nullptr; } auto examinationPeriod = m_ExaminationPeriods.at(index.column()); auto currentInformationType = m_InformationTypes.at(index.row()); auto controlPointsOfExaminationPeriod = examinationPeriod.controlPointUIDs; for (const auto& controlPointUID : controlPointsOfExaminationPeriod) { - auto currentControlPoint = mitk::GetControlPointByUID(controlPointUID, m_ControlPoints); + auto currentControlPoint = mitk::GetControlPointByUID(m_CaseID, controlPointUID); try { std::vector filteredDataNodes; if ("Image" == m_SelectedNodeType) { filteredDataNodes = m_SemanticRelationsDataStorageAccess->GetAllSpecificImages(m_CaseID, currentControlPoint, currentInformationType); } else if ("Segmentation" == m_SelectedNodeType) { filteredDataNodes = m_SemanticRelationsDataStorageAccess->GetAllSpecificSegmentations(m_CaseID, currentControlPoint, currentInformationType); } if (filteredDataNodes.empty()) { // try next control point continue; } else { // found a specific image return filteredDataNodes.front(); } } catch (const mitk::SemanticRelationException&) { return nullptr; } } // could not find a specif image return nullptr; }