diff --git a/Modules/SemanticRelations/include/mitkControlPointManager.h b/Modules/SemanticRelations/include/mitkControlPointManager.h index 0ff5b1e0fd..c1343ebd8f 100644 --- a/Modules/SemanticRelations/include/mitkControlPointManager.h +++ b/Modules/SemanticRelations/include/mitkControlPointManager.h @@ -1,100 +1,93 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKCONTROLPOINTMANAGER_H #define MITKCONTROLPOINTMANAGER_H #include // semantic relations module #include "mitkSemanticTypes.h" // mitk core #include namespace mitk { /** * @brief Provides helper functions that are needed to work with control points. * * These functions help to generate new control points, check for overlapping / containing control points or provide functionality * to find a fitting control point or even extend an already existing control point. */ /** * @brief Generates a control point from a given data node. * The date is extracted from the data node by using the 'DICOMHelper::GetDICOMDateFromDataNode'-function. * * @param datanode A data node pointer, whose date should be included in the newly generated control point. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint GenerateControlPoint(const mitk::DataNode* datanode); /** * @brief Find and return a whole control point including its date given a specific control point UID. * * @param controlPointUID The control point UID as string. * @param allControlPoints All currently known control points of a specific case. * * @return The control point with its UID and the date. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint GetControlPointByUID(const SemanticTypes::ID& controlPointUID, const SemanticTypes::ControlPointVector& allControlPoints); /** - * @brief Returns a string that displays the given control point in the format "YYYY-MM-DD". - * This function is used in the GUI to display the control point as header in the "information-type - control-point"-matrix. - * - * @param controlPoint The control point to convert into a string. - */ - MITKSEMANTICRELATIONS_EXPORT std::string GetControlPointAsString(const SemanticTypes::ControlPoint& controlPoint); - /** * @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. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint FindExistingControlPoint(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ControlPointVector& allControlPoints); /** * @brief Returns an already existing close control point from the given vector of control points. This closest control point has a date * date that is within a certain distance-in-days to the given control point. * If no closest control point can be found within the distance threshold an empty control point is returned. * * @param controlPoint The control point to check for distance. * @param allControlPoints The vector of already existing control points. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint FindClosestControlPoint(const SemanticTypes::ControlPoint& controlPoint, SemanticTypes::ControlPointVector& allControlPoints); /** * @brief Returns the examination period to which the given control point belongs. * Each examination point holds a vector of control point UIDs so that the UID of the given control point can be compared against the UIDs of the vector. * An empty examination period is returned if, * - the given vector of examination periods is empty * - the examination periods do not contain any control point UIDs * - the UID of the given control point is not contained in any examination period * * @param controlPoint The control point of which the examination period should be found. * @param allExaminationPeriods All currently known examination periods of a specific case. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ExaminationPeriod FindExaminationPeriod(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriodVector& allExaminationPeriods); /** * @brief Sort the given vector of examination periods. * Each examination period has a vector of control point UIDs (stored in chronological order). * The examination periods can be sorted by comparing the first control points of the examination periods. * * @param allExaminationPeriods All currently known examination periods of a specific case. * @param allControlPoints All currently known control points of a specific case. */ MITKSEMANTICRELATIONS_EXPORT void SortExaminationPeriods(SemanticTypes::ExaminationPeriodVector& allExaminationPeriods, const SemanticTypes::ControlPointVector& allControlPoints); } // namespace mitk #endif // MITKCONTROLPOINTMANAGER_H diff --git a/Modules/SemanticRelations/include/mitkSemanticTypes.h b/Modules/SemanticRelations/include/mitkSemanticTypes.h index 69737022d0..4cd7b61824 100644 --- a/Modules/SemanticRelations/include/mitkSemanticTypes.h +++ b/Modules/SemanticRelations/include/mitkSemanticTypes.h @@ -1,110 +1,128 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKSEMANTICTYPES_H #define MITKSEMANTICTYPES_H +#define BOOST_DATE_TIME_NO_LIB +#if defined(BOOST_ALL_DYN_LINK) +#undef BOOST_ALL_DYN_LINK +#endif + +// boost +#include + // c++ #include #include #include namespace mitk { namespace SemanticTypes { using ID = std::string; using CaseID = std::string; // an ID of the current case (e.g. the DICOM PatientID) using InformationType = std::string; /* * @brief The concept of a control point. */ struct ControlPoint { ID UID; - int year = 0; - int month = 0; - int day = 0; + boost::gregorian::date date; - bool operator<(const ControlPoint& other) const + ControlPoint() { - return std::tie(year, month, day) < std::tie(other.year, other.month, other.day); + date = boost::gregorian::date(boost::gregorian::min_date_time); } - bool operator>(const ControlPoint& other) const - { - return std::tie(year, month, day) > std::tie(other.year, other.month, other.day); - } - bool operator==(const ControlPoint& other) const + + // less comparison to sort containers of control points + bool operator<(const ControlPoint& other) const { - return (!operator<(other) && !operator>(other)); + return date < other.date; } - bool operator!=(const ControlPoint& other) const + + std::string ToString() const { - return (operator<(other) || operator>(other)); + std::stringstream controlPointAsString; + if (date.is_not_a_date()) + { + return ""; + } + + controlPointAsString << std::to_string(date.year()) << "-" + << std::setfill('0') << std::setw(2) << std::to_string(date.month()) << "-" + << std::setfill('0') << std::setw(2) << std::to_string(date.day()); + + return controlPointAsString.str(); } - bool operator<=(const ControlPoint& other) const + + void SetDateFromString(const std::string& dateAsString) { - return (operator<(other) || operator==(other)); + date = boost::gregorian::from_undelimited_string(dateAsString); } - bool operator>=(const ControlPoint& other) const + + int DistanceInDays(const ControlPoint& other) const { - return (operator>(other) || operator==(other)); + boost::gregorian::date_duration duration = date - other.date; + return std::abs(duration.days()); } }; /** * @brief The concept of an examination period. * An examination period holds a vector of control point UIDs. * The semantic relation storage stores the UIDs such that * the represented control points are in chronological order. */ struct ExaminationPeriod { ID UID; std::string name = ""; std::vector controlPointUIDs; }; /* * @brief The concept of a lesion class. */ struct LesionClass { ID UID; std::string classType = ""; }; /* * @brief The concept of a lesion. */ struct Lesion { ID UID; std::string name = ""; LesionClass lesionClass; }; using LesionVector = std::vector; using LesionClassVector = std::vector; using ControlPointVector = std::vector; using ExaminationPeriodVector = std::vector; using InformationTypeVector = std::vector; } // namespace SemanticTypes } // namespace mitk #endif // MITKSEMANTICTYPES_H diff --git a/Modules/SemanticRelations/src/mitkControlPointManager.cpp b/Modules/SemanticRelations/src/mitkControlPointManager.cpp index 33ecb6fb75..e08a3fe44a 100644 --- a/Modules/SemanticRelations/src/mitkControlPointManager.cpp +++ b/Modules/SemanticRelations/src/mitkControlPointManager.cpp @@ -1,203 +1,170 @@ /*=================================================================== 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 "mitkUIDGeneratorBoost.h" // mitk core #include -double CalculateDistanceInDays(const mitk::SemanticTypes::ControlPoint& leftControlPoint, const mitk::SemanticTypes::ControlPoint& rightControlPoint); - mitk::SemanticTypes::ControlPoint mitk::GenerateControlPoint(const DataNode* datanode) { SemanticTypes::ControlPoint controlPoint = GetDICOMDateFromDataNode(datanode); controlPoint.UID = UIDGeneratorBoost::GenerateUID(); return controlPoint; } mitk::SemanticTypes::ControlPoint mitk::GetControlPointByUID(const SemanticTypes::ID& controlPointUID, const std::vector& allControlPoints) { auto lambda = [&controlPointUID](const SemanticTypes::ControlPoint& currentControlPoint) { return currentControlPoint.UID == controlPointUID; }; const auto existingControlPoint = std::find_if(allControlPoints.begin(), allControlPoints.end(), lambda); mitk::SemanticTypes::ControlPoint controlPoint; if (existingControlPoint != allControlPoints.end()) { controlPoint = *existingControlPoint; } return controlPoint; } -std::string mitk::GetControlPointAsString(const SemanticTypes::ControlPoint& controlPoint) -{ - std::stringstream controlPointAsString; - controlPointAsString << std::to_string(controlPoint.year) << "-" - << std::setfill('0') << std::setw(2) << std::to_string(controlPoint.month) << "-" - << std::setfill('0') << std::setw(2) << std::to_string(controlPoint.day); - - return controlPointAsString.str(); -} - mitk::SemanticTypes::ControlPoint mitk::FindExistingControlPoint(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ControlPointVector& allControlPoints) { for (const auto& currentControlPoint : allControlPoints) { - if (controlPoint == currentControlPoint) + if (controlPoint.date == currentControlPoint.date) { return currentControlPoint; } } return SemanticTypes::ControlPoint(); } mitk::SemanticTypes::ControlPoint mitk::FindClosestControlPoint(const SemanticTypes::ControlPoint& controlPoint, SemanticTypes::ControlPointVector& allControlPoints) { if (allControlPoints.empty()) { return SemanticTypes::ControlPoint(); } // sort the vector of control points for easier lookup std::sort(allControlPoints.begin(), allControlPoints.end()); // new control point does not match an existing control point // check if the control point is close to an already existing control point std::vector::const_iterator it; for (it = allControlPoints.begin(); it != allControlPoints.end(); ++it) { - if (controlPoint < *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 = CalculateDistanceInDays(nextControlPoint, controlPoint); - double distanceToPreviousExaminationPeriod = CalculateDistanceInDays(previousControlPoint, controlPoint); + double distanceToNextExaminationPeriod = nextControlPoint.DistanceInDays(controlPoint); + double distanceToPreviousExaminationPeriod = previousControlPoint.DistanceInDays(controlPoint); SemanticTypes::ControlPoint closestControlPoint; - double closestDistanceInDays = 0.0; + 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; } - double THRESHOLD_DISTANCE_IN_DAYS = 30.0; + int THRESHOLD_DISTANCE_IN_DAYS = 30; if (closestDistanceInDays < THRESHOLD_DISTANCE_IN_DAYS) { return closestControlPoint; } return SemanticTypes::ControlPoint(); } mitk::SemanticTypes::ExaminationPeriod mitk::FindExaminationPeriod(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriodVector& allExaminationPeriods) { for (const auto& examinationPeriod : allExaminationPeriods) { for (const auto& UID : examinationPeriod.controlPointUIDs) { if (controlPoint.UID == UID) { return examinationPeriod; } } } return SemanticTypes::ExaminationPeriod(); } void mitk::SortExaminationPeriods(SemanticTypes::ExaminationPeriodVector& allExaminationPeriods, const SemanticTypes::ControlPointVector& allControlPoints) { auto lambda = [allControlPoints](const SemanticTypes::ExaminationPeriod& leftExaminationPeriod, const SemanticTypes::ExaminationPeriod& rightExaminationPeriod) { if (leftExaminationPeriod.controlPointUIDs.empty()) { return true; } if (rightExaminationPeriod.controlPointUIDs.empty()) { return false; } const auto leftUID = leftExaminationPeriod.controlPointUIDs.front(); const auto rightUID = rightExaminationPeriod.controlPointUIDs.front(); const auto& leftControlPoint = GetControlPointByUID(leftUID, allControlPoints); const auto& rightControlPoint = GetControlPointByUID(rightUID, allControlPoints); - return leftControlPoint < rightControlPoint; + return leftControlPoint.date < rightControlPoint.date; }; std::sort(allExaminationPeriods.begin(), allExaminationPeriods.end(), lambda); } - -double CalculateDistanceInDays(const mitk::SemanticTypes::ControlPoint& leftControlPoint, const mitk::SemanticTypes::ControlPoint& rightControlPoint) -{ - std::tm leftTimeStructure = { 0, 0, 0, leftControlPoint.day, leftControlPoint.month - 1, leftControlPoint.year - 1900 }; - std::tm rightTimeStructure = { 0, 0, 0, rightControlPoint.day, rightControlPoint.month - 1, rightControlPoint.year - 1900 }; - - time_t leftTime = mktime(&leftTimeStructure); - time_t rightTime = mktime(&rightTimeStructure); - - if (leftTime == -1 || rightTime == -1) - { - // date is not initialized, no difference can be computed - return std::numeric_limits::max(); - } - - // compute distance here - double secondsPerDay = 60 * 60 * 24; - double timeDifferenceInDays = std::difftime(leftTime, rightTime) / secondsPerDay; - - return std::abs(timeDifferenceInDays); -} diff --git a/Modules/SemanticRelations/src/mitkDICOMHelper.cpp b/Modules/SemanticRelations/src/mitkDICOMHelper.cpp index c119edbeae..7dffde337f 100644 --- a/Modules/SemanticRelations/src/mitkDICOMHelper.cpp +++ b/Modules/SemanticRelations/src/mitkDICOMHelper.cpp @@ -1,163 +1,161 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // semantic relations module #include "mitkDICOMHelper.h" #include "mitkSemanticRelationException.h" #include "mitkUIDGeneratorBoost.h" // mitk core #include // c++ #include mitk::SemanticTypes::ControlPoint GetControlPointFromString(const std::string& dateAsString); mitk::SemanticTypes::CaseID mitk::GetCaseIDFromDataNode(const mitk::DataNode* dataNode) { if (nullptr == dataNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } mitk::BaseData* baseData = dataNode->GetData(); if (nullptr == baseData) { mitkThrowException(SemanticRelationException) << "No valid base data."; } // extract suitable DICOM tag to use as the case id // two alternatives can be used: // - DICOM tag "0x0010, 0x0010" is PatientName // - DICOM tag "0x0010, 0x0020" is PatientID // in the current implementation the PatientID (0x0010, 0x0010) is used mitk::BaseProperty* dicomTag = baseData->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str()); if (nullptr == dicomTag) { mitkThrowException(SemanticRelationException) << "Not a valid DICOM property."; } std::string dicomTagAsString = dicomTag->GetValueAsString(); return dicomTagAsString; } mitk::SemanticTypes::ID mitk::GetIDFromDataNode(const mitk::DataNode* dataNode) { if (nullptr == dataNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } mitk::BaseData* baseData = dataNode->GetData(); if (nullptr == baseData) { mitkThrowException(SemanticRelationException) << "No valid base data."; } // extract suitable DICOM tag to use as the data node id // DICOM tag "0x0020, 0x000e" is SeriesInstanceUID mitk::BaseProperty* dicomTag = baseData->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str()); if (nullptr == dicomTag) { mitkThrowException(SemanticRelationException) << "Not a valid DICOM property."; } std::string dicomTagAsString = dicomTag->GetValueAsString(); return dicomTagAsString; } mitk::SemanticTypes::ControlPoint mitk::GetDICOMDateFromDataNode(const mitk::DataNode* dataNode) { if (nullptr == dataNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } mitk::BaseData* baseData = dataNode->GetData(); if (nullptr == baseData) { mitkThrowException(SemanticRelationException) << "No valid base data."; } // extract suitable DICOM tag to use as the data node id // DICOM tag "0x0008, 0x0022" is AcquisitionDate mitk::BaseProperty* acquisitionDateProperty = baseData->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str()); if (nullptr == acquisitionDateProperty) { mitkThrowException(SemanticRelationException) << "Not a valid DICOM property."; } std::string acquisitionDateAsString = acquisitionDateProperty->GetValueAsString(); return GetControlPointFromString(acquisitionDateAsString); } mitk::SemanticTypes::InformationType mitk::GetDICOMModalityFromDataNode(const mitk::DataNode* dataNode) { if (nullptr == dataNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } mitk::BaseData* baseData = dataNode->GetData(); if (nullptr == baseData) { mitkThrowException(SemanticRelationException) << "No valid base data."; } // extract suitable DICOM tag to use as the information type // DICOM tag "0x0008, 0x0060" is Modality mitk::BaseProperty* dicomTag = baseData->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str()); if (nullptr == dicomTag) { mitkThrowException(SemanticRelationException) << "Not a valid DICOM property."; } std::string dicomTagAsString = dicomTag->GetValueAsString(); return dicomTagAsString; } std::string mitk::TrimDICOM(const std::string& identifier) { if (identifier.empty()) { return identifier; } // leading whitespace std::size_t first = identifier.find_first_not_of(' '); if (std::string::npos == first) { return ""; } // trailing whitespace std::size_t last = identifier.find_last_not_of(' '); return identifier.substr(first, last - first + 1); } mitk::SemanticTypes::ControlPoint GetControlPointFromString(const std::string& dateAsString) { - if (dateAsString.size() != 8) // string does not represent a DICOM date + // date expected to be YYYYMMDD (8 characters) + if (dateAsString.size() != 8) { + // string does not represent a DICOM date return mitk::SemanticTypes::ControlPoint(); } mitk::SemanticTypes::ControlPoint controlPoint; - - // date expected to be YYYYMMDD (8 characters) - controlPoint.year = std::strtoul(dateAsString.substr(0, 4).c_str(), nullptr, 10); - controlPoint.month = std::strtoul(dateAsString.substr(4, 2).c_str(), nullptr, 10); - controlPoint.day = std::strtoul(dateAsString.substr(6, 2).c_str(), nullptr, 10); + controlPoint.SetDateFromString(dateAsString); return controlPoint; } diff --git a/Modules/SemanticRelations/src/mitkLesionManager.cpp b/Modules/SemanticRelations/src/mitkLesionManager.cpp index 507812262d..7764bbe5ba 100644 --- a/Modules/SemanticRelations/src/mitkLesionManager.cpp +++ b/Modules/SemanticRelations/src/mitkLesionManager.cpp @@ -1,144 +1,144 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // semantic relations module #include "mitkLesionManager.h" #include "mitkSemanticRelationException.h" #include "mitkUIDGeneratorBoost.h" bool GetLesionPresence(const mitk::SemanticTypes::CaseID& caseID, std::shared_ptr semanticRelations, const mitk::SemanticTypes::Lesion& lesion, const mitk::SemanticTypes::ControlPoint& controlPoint); double GetLesionVolume(const mitk::SemanticTypes::CaseID& caseID, std::shared_ptr semanticRelations, const mitk::SemanticTypes::Lesion& lesion, const mitk::SemanticTypes::ControlPoint& controlPoint); mitk::SemanticTypes::Lesion mitk::GenerateNewLesion(const std::string& lesionClassType/* = ""*/) { mitk::SemanticTypes::Lesion lesion; lesion.UID = mitk::UIDGeneratorBoost::GenerateUID(); lesion.name = "New lesion"; lesion.lesionClass = mitk::SemanticTypes::LesionClass(); lesion.lesionClass.UID = mitk::UIDGeneratorBoost::GenerateUID(); lesion.lesionClass.classType = lesionClassType; return lesion; } mitk::SemanticTypes::LesionClass mitk::GenerateNewLesionClass(const std::string& lesionClassType/* = ""*/) { mitk::SemanticTypes::LesionClass lesionClass; lesionClass.UID = mitk::UIDGeneratorBoost::GenerateUID(); lesionClass.classType = lesionClassType; return lesionClass; } mitk::SemanticTypes::Lesion mitk::GetLesionByUID(const SemanticTypes::ID& lesionUID, const std::vector& allLesions) { auto lambda = [&lesionUID](const SemanticTypes::Lesion& currentLesion) { return currentLesion.UID == lesionUID; }; const auto existingLesion = std::find_if(allLesions.begin(), allLesions.end(), lambda); mitk::SemanticTypes::Lesion lesion; if (existingLesion != allLesions.end()) { lesion = *existingLesion; } return lesion; } mitk::SemanticTypes::LesionClass mitk::FindExistingLesionClass(const std::string& lesionClassType, const std::vector& allLesionClasses) { auto lambda = [&lesionClassType](const SemanticTypes::LesionClass& currentLesionClass) { return currentLesionClass.classType == lesionClassType; }; const auto existingLesionClass = std::find_if(allLesionClasses.begin(), allLesionClasses.end(), lambda); mitk::SemanticTypes::LesionClass lesionClass; if (existingLesionClass != allLesionClasses.end()) { lesionClass = *existingLesionClass; } return lesionClass; } void mitk::GenerateAdditionalLesionData(LesionData& lesionData, const SemanticTypes::CaseID& caseID, std::shared_ptr semanticRelations) { std::vector lesionPresence; std::vector lesionVolume; SemanticTypes::Lesion lesion = lesionData.GetLesion(); bool presence = false; double volume = 0.0; try { std::vector controlPoints = semanticRelations->GetAllControlPointsOfCase(caseID); for (const auto& controlPoint : controlPoints) { presence = GetLesionPresence(caseID, semanticRelations, lesion, controlPoint); lesionPresence.push_back(presence); volume = GetLesionVolume(caseID, semanticRelations, lesion, controlPoint); lesionVolume.push_back(volume); } } catch (const mitk::SemanticRelationException&) { return; } lesionData.SetLesionPresence(lesionPresence); lesionData.SetLesionVolume(lesionVolume); } bool GetLesionPresence(const mitk::SemanticTypes::CaseID& caseID, std::shared_ptr semanticRelations, const mitk::SemanticTypes::Lesion& lesion, const mitk::SemanticTypes::ControlPoint& controllPoint) { try { mitk::SemanticRelations::DataNodeVector allImagesOfLesion = semanticRelations->GetAllImagesOfLesion(caseID, lesion); for (const auto& image : allImagesOfLesion) { auto imageControlPoint = semanticRelations->GetControlPointOfData(image); - if (imageControlPoint == controllPoint) + if (imageControlPoint.date == controllPoint.date) { return true; } } } catch (const mitk::SemanticRelationException&) { return false; } return false; } double GetLesionVolume(const mitk::SemanticTypes::CaseID& caseID, std::shared_ptr semanticRelations, const mitk::SemanticTypes::Lesion& lesion, const mitk::SemanticTypes::ControlPoint& controlPoint) { bool presence = GetLesionPresence(caseID, semanticRelations, lesion, controlPoint); if (presence) { return 1.0; } else { return 0.0; } } diff --git a/Modules/SemanticRelations/src/mitkRelationStorage.cpp b/Modules/SemanticRelations/src/mitkRelationStorage.cpp index 42681d8667..87d3960499 100644 --- a/Modules/SemanticRelations/src/mitkRelationStorage.cpp +++ b/Modules/SemanticRelations/src/mitkRelationStorage.cpp @@ -1,1401 +1,1401 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // semantic relations module #include "mitkRelationStorage.h" #include "mitkNodePredicates.h" // multi label module #include // mitk core #include #include // c++ #include #include void mitk::RelationStorage::SetDataStorage(DataStorage::Pointer dataStorage) { if (m_DataStorage != dataStorage) { // set the new data storage m_DataStorage = dataStorage; } } std::vector mitk::RelationStorage::GetAllLesionsOfCase(const SemanticTypes::CaseID& caseID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return std::vector(); } // retrieve a vector property that contains the valid lesion-IDs for the current case mitk::VectorProperty* vectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); if (nullptr == vectorProperty) { MITK_INFO << "Could not find any lesion in the storage."; return std::vector(); } std::vector vectorValue = vectorProperty->GetValue(); std::vector allLesionsOfCase; for (const auto& lesionID : vectorValue) { SemanticTypes::Lesion generatedLesion = GenerateLesion(caseID, lesionID); if (!generatedLesion.UID.empty()) { allLesionsOfCase.push_back(generatedLesion); } } return allLesionsOfCase; } mitk::SemanticTypes::Lesion mitk::RelationStorage::GetRepresentedLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return SemanticTypes::Lesion(); } // retrieve a vector property that contains the referenced ID of a segmentation (0. image ID 1. lesion ID) mitk::VectorProperty* segmentationVectorProperty = dynamic_cast*>(propertyList->GetProperty(segmentationID)); if (nullptr == segmentationVectorProperty) { MITK_INFO << "Could not find the segmentation node " << segmentationID << " in the storage."; return SemanticTypes::Lesion(); } std::vector segmentationVectorValue = segmentationVectorProperty->GetValue(); // the lesion ID of a segmentation is the second value in the vector if (segmentationVectorValue.size() != 2) { MITK_INFO << "Incorrect segmentation storage. Not two (2) IDs stored."; return SemanticTypes::Lesion(); } else { std::string lesionID = segmentationVectorValue[1]; return GenerateLesion(caseID, lesionID); } } std::vector mitk::RelationStorage::GetAllSegmentationsOfCase(const SemanticTypes::CaseID& caseID) { if (m_DataStorage.IsNull()) { MITK_INFO << "No valid data storage found in the mitkPersistenceService-class. Segmentations of the current case can not be retrieved."; return std::vector(); } std::vector allSegmentationIDsOfCase = 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->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; } std::vector mitk::RelationStorage::GetAllSegmentationIDsOfCase(const SemanticTypes::CaseID& caseID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return std::vector(); } // retrieve a vector property that contains the valid segmentation-IDs for the current case mitk::VectorProperty* allSegmentationsVectorProperty = dynamic_cast*>(propertyList->GetProperty("segmentations")); if (nullptr == allSegmentationsVectorProperty) { MITK_INFO << "Could not find any segmentation in the storage."; return std::vector(); } return allSegmentationsVectorProperty->GetValue(); } mitk::SemanticTypes::ControlPoint mitk::RelationStorage::GetControlPointOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return SemanticTypes::ControlPoint(); } // retrieve a vector property that contains the information type and the referenced ID of a control point (0. information type 1. control point ID) mitk::VectorProperty* dataNodeVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); if (nullptr == dataNodeVectorProperty) { MITK_INFO << "Could not find the data node " << imageID << " in the storage."; return SemanticTypes::ControlPoint(); } std::vector dataNodeVectorValue = dataNodeVectorProperty->GetValue(); SemanticTypes::ControlPoint controlPoint; // an image node has to have exactly two values (the information type and the ID of the control point) if (dataNodeVectorValue.size() != 2) { MITK_INFO << "Incorrect data storage. Not two (2) values stored."; return SemanticTypes::ControlPoint(); } else { // the second value of the data node vector is the ID of the referenced control point std::string controlPointID = dataNodeVectorValue[1]; // retrieve a vector property that contains the integer values of the date of a control point (0. year 1. month 2. day) mitk::VectorProperty* controlPointVectorProperty = dynamic_cast*>(propertyList->GetProperty(controlPointID)); if (nullptr == controlPointVectorProperty) { MITK_INFO << "Could not find the control point " << controlPointID << " in the storage."; return SemanticTypes::ControlPoint(); } std::vector controlPointVectorValue = controlPointVectorProperty->GetValue(); // a control point has to have exactly three integer values (year, month and day) if (controlPointVectorValue.size() != 3) { MITK_INFO << "Incorrect control point storage. Not three (3) values of the date are stored."; return SemanticTypes::ControlPoint(); } else { // set the values of the control point controlPoint.UID = controlPointID; - controlPoint.year = controlPointVectorValue[0]; - controlPoint.month = controlPointVectorValue[1]; - controlPoint.day = controlPointVectorValue[2]; + controlPoint.date = boost::gregorian::date(controlPointVectorValue[0], + controlPointVectorValue[1], + controlPointVectorValue[2]); } } return controlPoint; } std::vector mitk::RelationStorage::GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return std::vector(); } // retrieve a vector property that contains the valid control point-IDs for the current case mitk::VectorProperty* vectorProperty = dynamic_cast*>(propertyList->GetProperty("controlpoints")); if (nullptr == vectorProperty) { MITK_INFO << "Could not find any control points in the storage."; return std::vector(); } std::vector vectorValue = vectorProperty->GetValue(); std::vector allControlPointsOfCase; for (const auto& controlPointUID : vectorValue) { SemanticTypes::ControlPoint generatedControlPoint = GenerateControlpoint(caseID, controlPointUID); if (!generatedControlPoint.UID.empty()) { allControlPointsOfCase.push_back(generatedControlPoint); } } return allControlPointsOfCase; } std::vector mitk::RelationStorage::GetAllExaminationPeriodsOfCase(const SemanticTypes::CaseID& caseID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return std::vector(); } // retrieve a vector property that contains the valid examination period UIDs for the current case mitk::VectorProperty::Pointer vectorProperty = dynamic_cast*>(propertyList->GetProperty("examinationperiods")); if (nullptr == vectorProperty) { MITK_INFO << "Could not find any examination periods in the storage."; return std::vector(); } std::vector vectorValue = vectorProperty->GetValue(); std::vector allExaminationPeriods; for (const auto& examinationPeriodID : vectorValue) { // retrieve a vector property that contains the represented control point-IDs mitk::VectorProperty::Pointer examinationPeriodVectorProperty = dynamic_cast*>(propertyList->GetProperty(examinationPeriodID)); if (nullptr == examinationPeriodVectorProperty) { MITK_INFO << "Could not find the examination period " << examinationPeriodID << " in the storage."; continue; } std::vector examinationPeriodVectorValue = examinationPeriodVectorProperty->GetValue(); // an examination period has an arbitrary number of vector values (name and control point UIDs) (at least one for the name) if (examinationPeriodVectorValue.empty()) { MITK_INFO << "Incorrect examination period storage. At least one (1) value for the examination period name has to be stored."; continue; } else { // set the values of the name and the control points SemanticTypes::ExaminationPeriod generatedExaminationPeriod; generatedExaminationPeriod.UID = examinationPeriodID; generatedExaminationPeriod.name = examinationPeriodVectorValue[0]; for (int i = 1; i < examinationPeriodVectorValue.size(); ++i) { generatedExaminationPeriod.controlPointUIDs.push_back(examinationPeriodVectorValue[i]); } allExaminationPeriods.push_back(generatedExaminationPeriod); } } return allExaminationPeriods; } mitk::SemanticTypes::InformationType mitk::RelationStorage::GetInformationTypeOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return SemanticTypes::InformationType(); } // retrieve a vector property that contains the information type and the referenced ID of an image data node (0. information type 1. control point ID) mitk::VectorProperty* dataNodeVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); if (nullptr == dataNodeVectorProperty) { MITK_INFO << "Could not find the image " << imageID << " in the storage."; return SemanticTypes::InformationType(); } std::vector dataNodeVectorValue = dataNodeVectorProperty->GetValue(); // an image node has to have exactly two values (the information type and the ID of the control point) if (dataNodeVectorValue.size() != 2) { MITK_INFO << "Incorrect data storage. Not two (2) values stored."; return SemanticTypes::InformationType(); } else { // the first value of the data node vector is the information type return dataNodeVectorValue[0]; } } std::vector mitk::RelationStorage::GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return std::vector(); } // retrieve a vector property that contains the valid information types of the current case mitk::VectorProperty* informationTypeVectorProperty = dynamic_cast*>(propertyList->GetProperty("informationtypes")); if (nullptr == informationTypeVectorProperty) { MITK_INFO << "Could not find any information types in the storage."; return std::vector(); } return informationTypeVectorProperty->GetValue(); } std::vector mitk::RelationStorage::GetAllImagesOfCase(const SemanticTypes::CaseID& caseID) { if (m_DataStorage.IsNull()) { MITK_INFO << "No valid data storage found in the mitkPersistenceService-class. Images of the current case can not be retrieved."; return std::vector(); } std::vector allImageIDsOfCase = 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->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; } std::vector mitk::RelationStorage::GetAllImageIDsOfCase(const SemanticTypes::CaseID& caseID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return std::vector(); } // retrieve a vector property that contains the valid image-IDs of the current case mitk::VectorProperty* allImagesVectorProperty = dynamic_cast*>(propertyList->GetProperty("images")); if (nullptr == allImagesVectorProperty) { MITK_INFO << "Could not find any image in the storage."; return std::vector(); } return allImagesVectorProperty->GetValue(); } std::vector mitk::RelationStorage::GetAllCaseIDs() { PERSISTENCE_GET_SERVICE_MACRO if (nullptr == persistenceService) { MITK_INFO << "Persistence service could not be loaded"; return std::vector(); } // the property list is valid for a certain scenario and contains all the case IDs of the radiological user's MITK session std::string listIdentifier = "caseIDs"; mitk::PropertyList::Pointer propertyList = persistenceService->GetPropertyList(listIdentifier); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << listIdentifier << " for the current MITK workbench / session."; return std::vector(); } // retrieve a vector property that contains all case IDs mitk::VectorProperty* caseIDsVectorProperty = dynamic_cast*>(propertyList->GetProperty(listIdentifier)); if (nullptr == caseIDsVectorProperty) { MITK_INFO << "Could not find the property " << listIdentifier << " for the " << listIdentifier << " property list."; return std::vector(); } return caseIDsVectorProperty->GetValue(); } void mitk::RelationStorage::AddCase(const SemanticTypes::CaseID& caseID) { PERSISTENCE_GET_SERVICE_MACRO if (nullptr == persistenceService) { MITK_INFO << "Persistence service could not be loaded"; return; } // the property list is valid for a certain scenario and contains all the case IDs of the radiological user's MITK session std::string listIdentifier = "caseIDs"; mitk::PropertyList::Pointer propertyList = persistenceService->GetPropertyList(listIdentifier); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << listIdentifier << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains all case IDs mitk::VectorProperty::Pointer caseIDsVectorProperty = dynamic_cast*>(propertyList->GetProperty(listIdentifier)); std::vector caseIDsVectorValue; if (nullptr == caseIDsVectorProperty) { caseIDsVectorProperty = mitk::VectorProperty::New(); } else { caseIDsVectorValue = caseIDsVectorProperty->GetValue(); } auto existingCase = std::find(caseIDsVectorValue.begin(), caseIDsVectorValue.end(), caseID); if (existingCase != caseIDsVectorValue.end()) { return; } else { // add case to the "caseIDs" property list caseIDsVectorValue.push_back(caseID); caseIDsVectorProperty->SetValue(caseIDsVectorValue); propertyList->SetProperty(listIdentifier, caseIDsVectorProperty); } } void mitk::RelationStorage::AddImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageNodeID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid image-IDs for the current case mitk::VectorProperty::Pointer allImagesVectorProperty = dynamic_cast*>(propertyList->GetProperty("images")); std::vector allImagesIDs; if (nullptr == allImagesVectorProperty) { allImagesVectorProperty = mitk::VectorProperty::New(); } else { allImagesIDs = allImagesVectorProperty->GetValue(); } auto existingImage = std::find(allImagesIDs.begin(), allImagesIDs.end(), imageNodeID); if (existingImage != allImagesIDs.end()) { return; } else { // add image to the "images" property list allImagesIDs.push_back(imageNodeID); allImagesVectorProperty->SetValue(allImagesIDs); propertyList->SetProperty("images", allImagesVectorProperty); // add the image itself mitk::VectorProperty::Pointer imageNodeVectorProperty = mitk::VectorProperty::New(); // an image node has to have exactly two values (the information type and the ID of the control point) std::vector imageNodeVectorValue(2); imageNodeVectorProperty->SetValue(imageNodeVectorValue); propertyList->SetProperty(imageNodeID, imageNodeVectorProperty); } } void mitk::RelationStorage::RemoveImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageNodeID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid image-IDs for the current case mitk::VectorProperty::Pointer allImagesVectorProperty = dynamic_cast*>(propertyList->GetProperty("images")); if (nullptr == allImagesVectorProperty) { MITK_INFO << "Could not find any images in the storage."; return; } // remove the image reference from the list of all images of the current case std::vector allImagesIDs = allImagesVectorProperty->GetValue(); allImagesIDs.erase(std::remove(allImagesIDs.begin(), allImagesIDs.end(), imageNodeID), allImagesIDs.end()); if (allImagesIDs.empty()) { // no more images stored -> remove the images property list propertyList->DeleteProperty("images"); } else { // or store the modified vector value allImagesVectorProperty->SetValue(allImagesIDs); } // remove the image instance itself propertyList->DeleteProperty(imageNodeID); } void mitk::RelationStorage::AddSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationNodeID, const SemanticTypes::ID& parentNodeID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid segmentation-IDs for the current case mitk::VectorProperty::Pointer allSegmentationsVectorProperty = dynamic_cast*>(propertyList->GetProperty("segmentations")); std::vector allSegmentationsIDs; if (nullptr == allSegmentationsVectorProperty) { allSegmentationsVectorProperty = mitk::VectorProperty::New(); } else { allSegmentationsIDs = allSegmentationsVectorProperty->GetValue(); } auto existingImage = std::find(allSegmentationsIDs.begin(), allSegmentationsIDs.end(), segmentationNodeID); if (existingImage != allSegmentationsIDs.end()) { return; } else { // add segmentation to the "segmentations" property list allSegmentationsIDs.push_back(segmentationNodeID); allSegmentationsVectorProperty->SetValue(allSegmentationsIDs); propertyList->SetProperty("segmentations", allSegmentationsVectorProperty); // add the segmentation itself mitk::VectorProperty::Pointer segmentationNodeVectorProperty = mitk::VectorProperty::New(); // a segmentation node has to have exactly two values (the ID of the referenced image and the ID of the referenced lesion) std::vector segmentationNodeVectorValue(2); segmentationNodeVectorValue[0] = parentNodeID; segmentationNodeVectorProperty->SetValue(segmentationNodeVectorValue); propertyList->SetProperty(segmentationNodeID, segmentationNodeVectorProperty); } } void mitk::RelationStorage::RemoveSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationNodeID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid segmentation-IDs for the current case mitk::VectorProperty::Pointer allSegmentationsVectorProperty = dynamic_cast*>(propertyList->GetProperty("segmentations")); if (nullptr == allSegmentationsVectorProperty) { MITK_INFO << "Could not find any segmentation in the storage."; return; } // remove the lesion reference from the list of all lesions of the current case std::vector allSegmentationsIDs = allSegmentationsVectorProperty->GetValue(); allSegmentationsIDs.erase(std::remove(allSegmentationsIDs.begin(), allSegmentationsIDs.end(), segmentationNodeID), allSegmentationsIDs.end()); if (allSegmentationsIDs.empty()) { // no more segmentations stored -> remove the segmentations property list propertyList->DeleteProperty("segmentations"); } else { // or store the modified vector value allSegmentationsVectorProperty->SetValue(allSegmentationsIDs); } // remove the lesion instance itself propertyList->DeleteProperty(segmentationNodeID); } void mitk::RelationStorage::AddLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid lesion-IDs for the current case mitk::VectorProperty::Pointer lesionsVectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); std::vector lesionsVectorValue; if (nullptr == lesionsVectorProperty) { lesionsVectorProperty = mitk::VectorProperty::New(); } else { lesionsVectorValue = lesionsVectorProperty->GetValue(); } const auto& existingIndex = std::find(lesionsVectorValue.begin(), lesionsVectorValue.end(), lesion.UID); if (existingIndex != lesionsVectorValue.end()) { return; } else { // add the new lesion id from the given lesion to the vector of all current lesion IDs lesionsVectorValue.push_back(lesion.UID); // overwrite the current vector property with the new, extended string vector lesionsVectorProperty->SetValue(lesionsVectorValue); propertyList->SetProperty("lesions", lesionsVectorProperty); // add the lesion with the lesion UID as the key and the lesion information as value std::vector lesionData; lesionData.push_back(lesion.name); lesionData.push_back(lesion.lesionClass.UID); mitk::VectorProperty::Pointer newLesionVectorProperty = mitk::VectorProperty::New(); newLesionVectorProperty->SetValue(lesionData); propertyList->SetProperty(lesion.UID, newLesionVectorProperty); // add the lesion class with the lesion class UID as key and the class type as value std::string lesionClassType = lesion.lesionClass.classType; propertyList->SetStringProperty(lesion.lesionClass.UID.c_str(), lesionClassType.c_str()); } } void mitk::RelationStorage::OverwriteLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid lesion-IDs for the current case mitk::VectorProperty* lesionVectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); if (nullptr == lesionVectorProperty) { MITK_INFO << "Could not find any lesion in the storage."; return; } std::vector lesionVectorValue = lesionVectorProperty->GetValue(); const auto existingLesion = std::find(lesionVectorValue.begin(), lesionVectorValue.end(), lesion.UID); if (existingLesion != lesionVectorValue.end()) { // overwrite the referenced lesion class UID with the new, given lesion class data std::vector lesionData; lesionData.push_back(lesion.name); lesionData.push_back(lesion.lesionClass.UID); mitk::VectorProperty::Pointer newLesionVectorProperty = mitk::VectorProperty::New(); newLesionVectorProperty->SetValue(lesionData); propertyList->SetProperty(lesion.UID, newLesionVectorProperty); // overwrite the lesion class with the lesion class UID as key and the new, given class type as value std::string lesionClassType = lesion.lesionClass.classType; propertyList->SetStringProperty(lesion.lesionClass.UID.c_str(), lesionClassType.c_str()); } else { MITK_INFO << "Could not find lesion " << lesion.UID << " in the storage. Cannot overwrite the lesion."; } } void mitk::RelationStorage::LinkSegmentationToLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID, const SemanticTypes::Lesion& lesion) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid lesion-IDs for the current case mitk::VectorProperty* lesionVectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); if (nullptr == lesionVectorProperty) { MITK_INFO << "Could not find any lesion property in the storage."; return; } std::vector lesionVectorValue = lesionVectorProperty->GetValue(); const auto existingLesion = std::find(lesionVectorValue.begin(), lesionVectorValue.end(), lesion.UID); if (existingLesion != lesionVectorValue.end()) { // set / overwrite the lesion reference of the given segmentation // retrieve a vector property that contains the referenced ID of a segmentation (0. image ID 1. lesion ID) mitk::VectorProperty* segmentationVectorProperty = dynamic_cast*>(propertyList->GetProperty(segmentationID)); if (nullptr == segmentationVectorProperty) { MITK_INFO << "Could not find the segmentation node " << segmentationID << " in the storage. Cannot link segmentation to lesion."; return; } std::vector segmentationVectorValue = segmentationVectorProperty->GetValue(); if (segmentationVectorValue.size() != 2) { MITK_INFO << "Incorrect segmentation storage. Not two (2) IDs stored."; return; } else { // the lesion ID of a segmentation is the second value in the vector segmentationVectorValue[1] = lesion.UID; segmentationVectorProperty->SetValue(segmentationVectorValue); } } else { MITK_INFO << "Could not find lesion " << lesion.UID << " in the storage. Cannot link segmentation to lesion."; } } void mitk::RelationStorage::UnlinkSegmentationFromLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the referenced ID of a segmentation (0. image ID 1. lesion ID) mitk::VectorProperty* segmentationVectorProperty = dynamic_cast*>(propertyList->GetProperty(segmentationID)); if (nullptr == segmentationVectorProperty) { MITK_INFO << "Could not find the segmentation node " << segmentationID << " in the storage. Cannot unlink lesion from segmentation."; return; } std::vector segmentationVectorValue = segmentationVectorProperty->GetValue(); // a segmentation has to have exactly two values (the ID of the linked image and the ID of the lesion) if (segmentationVectorValue.size() != 2) { MITK_INFO << "Incorrect data storage. Not two (2) values stored."; return; } else { // the second value of the data node vector is the ID of the referenced lesion // set the lesion reference to an empty string for removal segmentationVectorValue[1] = ""; segmentationVectorProperty->SetValue(segmentationVectorValue); } } void mitk::RelationStorage::RemoveLesion(const mitk::SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid lesions of the current case mitk::VectorProperty* lesionVectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); if (nullptr == lesionVectorProperty) { MITK_INFO << "Could not find any lesion property in the storage."; return; } // remove the lesion reference from the list of all lesions of the current case std::vector lesionVectorValue = lesionVectorProperty->GetValue(); lesionVectorValue.erase(std::remove(lesionVectorValue.begin(), lesionVectorValue.end(), lesion.UID), lesionVectorValue.end()); if (lesionVectorValue.empty()) { // no more lesions stored -> remove the lesions property list propertyList->DeleteProperty("lesions"); } else { // or store the modified vector value lesionVectorProperty->SetValue(lesionVectorValue); } // remove the lesion instance itself // the lesion data is stored under the lesion ID mitk::VectorProperty* lesionDataProperty = dynamic_cast*>(propertyList->GetProperty(lesion.UID)); if (nullptr == lesionDataProperty) { MITK_INFO << "Lesion " << lesion.UID << " not found (already removed?). Cannot remove the lesion."; return; } std::vector lesionData = lesionDataProperty->GetValue(); // a lesion date has to have exactly two values (the name of the lesion and the UID of the lesion class) if (lesionData.size() != 2) { MITK_INFO << "Incorrect lesion data storage. Not two (2) strings of the lesion UID and the lesion name are stored."; } else { std::string lesionClassID = lesionData[1]; RemoveLesionClass(caseID, lesionClassID); } propertyList->DeleteProperty(lesion.UID); } void mitk::RelationStorage::RemoveLesionClass(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& lesionClassID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the lesion class mitk::StringProperty* lesionClassProperty = dynamic_cast(propertyList->GetProperty(lesionClassID)); if (nullptr == lesionClassProperty) { MITK_INFO << "Lesion class " << lesionClassID << " not found (already removed?). Cannot remove the lesion class."; return; } // retrieve a vector property that contains the valid lesions of the current case mitk::VectorProperty* lesionVectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); if (nullptr == lesionVectorProperty) { return; } // check if the lesion class ID is referenced by any other lesion std::vector lesionVectorValue = lesionVectorProperty->GetValue(); const auto existingLesionClass = std::find_if(lesionVectorValue.begin(), lesionVectorValue.end(), [&propertyList, &lesionClassID](const std::string& lesionID) { mitk::VectorProperty* lesionDataProperty = dynamic_cast*>(propertyList->GetProperty(lesionID)); if (nullptr == lesionDataProperty) { return false; } else { std::vector lesionData = lesionDataProperty->GetValue(); // a lesion date has to have exactly two values (the name of the lesion and the UID of the lesion class) if (lesionData.size() != 2) { return false; } else { return lesionData[1] == lesionClassID; } } }); if (existingLesionClass == lesionVectorValue.end()) { // lesion class ID not referenced; remove lesion class propertyList->DeleteProperty(lesionClassID); } } void mitk::RelationStorage::AddControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid controlPoint UIDs for the current case mitk::VectorProperty::Pointer controlPointVectorProperty = dynamic_cast*>(propertyList->GetProperty("controlpoints")); std::vector controlPointVectorValue; if (nullptr == controlPointVectorProperty) { controlPointVectorProperty = mitk::VectorProperty::New(); } else { controlPointVectorValue = controlPointVectorProperty->GetValue(); } const auto existingControlPoint = std::find(controlPointVectorValue.begin(), controlPointVectorValue.end(), controlPoint.UID); if (existingControlPoint != controlPointVectorValue.end()) { return; } else { // add the new control point UID from the given control point to the vector of all current control point UIDs controlPointVectorValue.push_back(controlPoint.UID); // overwrite the current vector property with the new, extended string vector controlPointVectorProperty->SetValue(controlPointVectorValue); propertyList->SetProperty("controlpoints", controlPointVectorProperty); // store the control point values (the three integer values of a date) std::vector controlPointDate; - controlPointDate.push_back(controlPoint.year); - controlPointDate.push_back(controlPoint.month); - controlPointDate.push_back(controlPoint.day); + controlPointDate.push_back(controlPoint.date.year()); + controlPointDate.push_back(controlPoint.date.month()); + controlPointDate.push_back(controlPoint.date.day()); mitk::VectorProperty::Pointer newControlPointVectorProperty = mitk::VectorProperty::New(); newControlPointVectorProperty->SetValue(controlPointDate); propertyList->SetProperty(controlPoint.UID, newControlPointVectorProperty); } } void mitk::RelationStorage::LinkDataToControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& dataNodeID, const SemanticTypes::ControlPoint& controlPoint) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid controlPoint UIDs for the current case mitk::VectorProperty* controlPointVectorProperty = dynamic_cast*>(propertyList->GetProperty("controlpoints")); if (nullptr == controlPointVectorProperty) { MITK_INFO << "Could not find any control point property in the storage."; return; } std::vector controlPointVectorValue = controlPointVectorProperty->GetValue(); const auto existingControlPoint = std::find(controlPointVectorValue.begin(), controlPointVectorValue.end(), controlPoint.UID); if (existingControlPoint != controlPointVectorValue.end()) { // set / overwrite the control point reference of the given data // retrieve a vector property that contains the referenced ID of a data node (0. information type 1. control point ID) mitk::VectorProperty* dataNodeVectorProperty = dynamic_cast*>(propertyList->GetProperty(dataNodeID)); if (nullptr == dataNodeVectorProperty) { MITK_INFO << "Could not find the data node " << dataNodeID << " in the storage. Cannot link data to control point."; return; } std::vector dataNodeVectorValue = dataNodeVectorProperty->GetValue(); // an image node has to have exactly two values (the information type and the ID of the control point) if (dataNodeVectorValue.size() != 2) { MITK_INFO << "Incorrect data storage. Not two (2) values stored."; return; } else { // the second value of the data node vector is the ID of the referenced control point dataNodeVectorValue[1] = controlPoint.UID; dataNodeVectorProperty->SetValue(dataNodeVectorValue); } } else { MITK_INFO << "Could not find control point " << controlPoint.UID << " in the storage. Cannot link data to control point."; } } void mitk::RelationStorage::UnlinkDataFromControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& dataNodeID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the referenced ID of a date (0. information type 1. control point ID) mitk::VectorProperty* dataNodeVectorProperty = dynamic_cast*>(propertyList->GetProperty(dataNodeID)); if (nullptr == dataNodeVectorProperty) { MITK_INFO << "Could not find the date " << dataNodeID << " in the storage. Cannot unlink control point from date."; return; } std::vector dataNodeVectorValue = dataNodeVectorProperty->GetValue(); // a data node has to have exactly two values (the information type and the ID of the control point) if (dataNodeVectorValue.size() != 2) { MITK_INFO << "Incorrect data storage. Not two (2) values stored."; return; } else { // the second value of the data node vector is the ID of the referenced control point // set the control point reference to an empty string for removal dataNodeVectorValue[1] = ""; dataNodeVectorProperty->SetValue(dataNodeVectorValue); } } void mitk::RelationStorage::RemoveControlPointFromCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid controlPoint UIDs for the current case mitk::VectorProperty* allControlPointsVectorProperty = dynamic_cast*>(propertyList->GetProperty("controlpoints")); if (nullptr == allControlPointsVectorProperty) { MITK_INFO << "Could not find any control point property in the storage."; return; } // remove the control point reference from the list of all control points of the current case std::vector currentControlPointVectorValue = allControlPointsVectorProperty->GetValue(); currentControlPointVectorValue.erase(std::remove(currentControlPointVectorValue.begin(), currentControlPointVectorValue.end(), controlPoint.UID), currentControlPointVectorValue.end()); allControlPointsVectorProperty->SetValue(currentControlPointVectorValue); // remove the control point instance itself propertyList->DeleteProperty(controlPoint.UID); } void mitk::RelationStorage::AddExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid examination period UIDs for the current case mitk::VectorProperty::Pointer vectorProperty = dynamic_cast*>(propertyList->GetProperty("examinationperiods")); std::vector examinationPeriodsVectorValue; if (nullptr == vectorProperty) { vectorProperty = mitk::VectorProperty::New(); } else { examinationPeriodsVectorValue = vectorProperty->GetValue(); } const auto& existingIndex = std::find(examinationPeriodsVectorValue.begin(), examinationPeriodsVectorValue.end(), examinationPeriod.UID); if (existingIndex != examinationPeriodsVectorValue.end()) { return; } else { // add the new examination period id from the given examination period to the vector of all current examination period UIDs examinationPeriodsVectorValue.push_back(examinationPeriod.UID); // overwrite the current vector property with the new, extended string vector vectorProperty->SetValue(examinationPeriodsVectorValue); propertyList->SetProperty("examinationperiods", vectorProperty); // add the examination period with the UID as the key and the name as as the vector value std::vector examinationPeriodData; examinationPeriodData.push_back(examinationPeriod.name); mitk::VectorProperty::Pointer newExaminationPeriodVectorProperty = mitk::VectorProperty::New(); newExaminationPeriodVectorProperty->SetValue(examinationPeriodData); propertyList->SetProperty(examinationPeriod.UID, newExaminationPeriodVectorProperty); } } void mitk::RelationStorage::AddControlPointToExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod examinationPeriod) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the represented control point UIDs of the given examination period mitk::VectorProperty* controlPointUIDsVectorProperty = dynamic_cast*>(propertyList->GetProperty(examinationPeriod.UID)); if (nullptr == controlPointUIDsVectorProperty) { MITK_INFO << "Could not find the examination period " << examinationPeriod.UID << " in the storage. Cannot add the control point to the examination period."; return; } std::vector controlPointUIDsVectorValue = controlPointUIDsVectorProperty->GetValue(); // store the control point UID controlPointUIDsVectorValue.push_back(controlPoint.UID); // sort the vector according to the date of the control points referenced by the UIDs auto lambda = [&caseID, this](const SemanticTypes::ID& leftControlPointUID, const SemanticTypes::ID& rightControlPointUID) { const auto& leftControlPoint = GenerateControlpoint(caseID, leftControlPointUID); const auto& rightControlPoint = GenerateControlpoint(caseID, rightControlPointUID); - return leftControlPoint <= rightControlPoint; + return leftControlPoint.date <= rightControlPoint.date; }; std::sort(controlPointUIDsVectorValue.begin(), controlPointUIDsVectorValue.end(), lambda); // store the modified and sorted control point UID vector of this examination period controlPointUIDsVectorProperty->SetValue(controlPointUIDsVectorValue); } void mitk::RelationStorage::RemoveControlPointFromExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod examinationPeriod) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the represented control point UIDs of the given examination period mitk::VectorProperty* controlPointUIDsVectorProperty = dynamic_cast*>(propertyList->GetProperty(examinationPeriod.UID)); if (nullptr == controlPointUIDsVectorProperty) { MITK_INFO << "Could not find examination period " << examinationPeriod.UID << " in the storage. Cannot add the control point to the examination period."; return; } std::vector controlPointUIDsVectorValue = controlPointUIDsVectorProperty->GetValue(); // an examination period has an arbitrary number of vector values (name and control point UIDs) (at least one for the name) if (controlPointUIDsVectorValue.size() < 2) { MITK_INFO << "Incorrect examination period storage. At least one (1) control point ID has to be stored."; return; } else { controlPointUIDsVectorValue.erase(std::remove(controlPointUIDsVectorValue.begin(), controlPointUIDsVectorValue.end(), controlPoint.UID), controlPointUIDsVectorValue.end()); if (controlPointUIDsVectorValue.size() < 2) { RemoveExaminationPeriodFromCase(caseID, examinationPeriod); } else { // store the modified vector value controlPointUIDsVectorProperty->SetValue(controlPointUIDsVectorValue); } } } void mitk::RelationStorage::RemoveExaminationPeriodFromCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod examinationPeriod) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid examination period UIDs for the current case mitk::VectorProperty::Pointer vectorProperty = dynamic_cast*>(propertyList->GetProperty("examinationperiods")); if (nullptr == vectorProperty) { MITK_INFO << "Could not find any examination periods in the storage."; return; } std::vector examinationPeriodVectorValue = vectorProperty->GetValue(); examinationPeriodVectorValue.erase(std::remove(examinationPeriodVectorValue.begin(), examinationPeriodVectorValue.end(), examinationPeriod.UID), examinationPeriodVectorValue.end()); if (examinationPeriodVectorValue.empty()) { // no more examination periods stored -> remove the examination period property list propertyList->DeleteProperty("examinationperiods"); } else { // or store the modified vector value vectorProperty->SetValue(examinationPeriodVectorValue); } } void mitk::RelationStorage::AddInformationTypeToImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID, const SemanticTypes::InformationType informationType) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid information types of the current case mitk::VectorProperty::Pointer informationTypeVectorProperty = dynamic_cast*>(propertyList->GetProperty("informationtypes")); std::vector informationTypeVectorValue; if (nullptr == informationTypeVectorProperty) { informationTypeVectorProperty = mitk::VectorProperty::New(); } else { informationTypeVectorValue = informationTypeVectorProperty->GetValue(); } const auto existingInformationType = std::find(informationTypeVectorValue.begin(), informationTypeVectorValue.end(), informationType); if (existingInformationType == informationTypeVectorValue.end()) { // at first: add the information type to the storage informationTypeVectorValue.push_back(informationType); informationTypeVectorProperty->SetValue(informationTypeVectorValue); propertyList->SetProperty("informationtypes", informationTypeVectorProperty); } // set / overwrite the information type of the given data // retrieve a vector property that contains the referenced ID of an image (0. information type 1. control point ID) mitk::VectorProperty* imageNodeVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); if (nullptr == imageNodeVectorProperty) { MITK_INFO << "Could not find the image " << imageID << " in the storage. Cannot add information type to image."; return; } std::vector imageNodeVectorValue = imageNodeVectorProperty->GetValue(); // an image node has to have exactly two values (the information type and the ID of the control point) if (imageNodeVectorValue.size() != 2) { MITK_INFO << "Incorrect data storage. Not two (2) values stored."; return; } else { // the first value of the data node vector is the information type imageNodeVectorValue[0] = informationType; imageNodeVectorProperty->SetValue(imageNodeVectorValue); } } void mitk::RelationStorage::RemoveInformationTypeFromImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the referenced ID of an image (0. information type 1. control point ID) mitk::VectorProperty* imageNodeVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); if (nullptr == imageNodeVectorProperty) { MITK_INFO << "Could not find the image " << imageID << " in the storage. Cannot remove information type from image."; return; } std::vector imageNodeVectorValue = imageNodeVectorProperty->GetValue(); // an image node has to have exactly two values (the information type and the ID of the control point) if (imageNodeVectorValue.size() != 2) { MITK_INFO << "Incorrect data storage. Not two (2) values stored."; return; } else { // the first value of the data node vector is the information type // set the information type to an empty string for removal imageNodeVectorValue[0] = ""; imageNodeVectorProperty->SetValue(imageNodeVectorValue); } } void mitk::RelationStorage::RemoveInformationTypeFromCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType informationType) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid information types of the current case mitk::VectorProperty* informationTypeVectorProperty = dynamic_cast*>(propertyList->GetProperty("informationtypes")); if (nullptr == informationTypeVectorProperty) { MITK_INFO << "Could not find any information type property in the storage."; return; } std::vector informationTypeVectorValue = informationTypeVectorProperty->GetValue(); informationTypeVectorValue.erase(std::remove(informationTypeVectorValue.begin(), informationTypeVectorValue.end(), informationType), informationTypeVectorValue.end()); if (informationTypeVectorValue.empty()) { // no more information types stored -> remove the information types property list propertyList->DeleteProperty("informationtypes"); } else { // or store the modified vector value informationTypeVectorProperty->SetValue(informationTypeVectorValue); } } mitk::PropertyList::Pointer mitk::RelationStorage::GetStorageData(const SemanticTypes::CaseID& caseID) { // access the storage PERSISTENCE_GET_SERVICE_MACRO if (nullptr == persistenceService) { MITK_INFO << "Persistence service could not be loaded"; return nullptr; } // the property list is valid for a whole case and contains all the properties for the current case // the persistence service may create a new property list with the given ID, if no property list is found return persistenceService->GetPropertyList(const_cast(caseID)); } mitk::SemanticTypes::Lesion mitk::RelationStorage::GenerateLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& lesionID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return SemanticTypes::Lesion(); } mitk::VectorProperty* lesionDataProperty = dynamic_cast*>(propertyList->GetProperty(lesionID)); if (nullptr == lesionDataProperty) { MITK_INFO << "Lesion " << lesionID << " not found. Lesion can not be retrieved."; return SemanticTypes::Lesion(); } std::vector lesionData = lesionDataProperty->GetValue(); // a lesion date has to have exactly two values (the name of the lesion and the UID of the lesion class) if (lesionData.size() != 2) { MITK_INFO << "Incorrect lesion data storage. Not two (2) strings of the lesion name and the lesion UID are stored."; return SemanticTypes::Lesion(); } // the lesion class ID is stored as the second property std::string lesionClassID = lesionData[1]; mitk::StringProperty* lesionClassProperty = dynamic_cast(propertyList->GetProperty(lesionClassID)); if (nullptr != lesionClassProperty) { SemanticTypes::LesionClass generatedLesionClass; generatedLesionClass.UID = lesionClassID; generatedLesionClass.classType = lesionClassProperty->GetValue(); SemanticTypes::Lesion generatedLesion; generatedLesion.UID = lesionID; generatedLesion.name = lesionData[0]; generatedLesion.lesionClass = generatedLesionClass; return generatedLesion; } else { MITK_INFO << "Incorrect lesion class storage. Lesion " << lesionID << " can not be retrieved."; return SemanticTypes::Lesion(); } } mitk::SemanticTypes::ControlPoint mitk::RelationStorage::GenerateControlpoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& controlPointUID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return SemanticTypes::ControlPoint(); } // retrieve a vector property that contains the integer values of the date of a control point (0. year 1. month 2. day) mitk::VectorProperty* controlPointVectorProperty = dynamic_cast*>(propertyList->GetProperty(controlPointUID)); if (nullptr == controlPointVectorProperty) { MITK_INFO << "Could not find the control point " << controlPointUID << " in the storage."; return SemanticTypes::ControlPoint(); } std::vector controlPointVectorValue = controlPointVectorProperty->GetValue(); // a control point has to have exactly three integer values (year, month and day) if (controlPointVectorValue.size() != 3) { MITK_INFO << "Incorrect control point storage. Not three (3) values of the date are stored."; return SemanticTypes::ControlPoint(); } // set the values of the control point SemanticTypes::ControlPoint generatedControlPoint; generatedControlPoint.UID = controlPointUID; - generatedControlPoint.year = controlPointVectorValue[0]; - generatedControlPoint.month = controlPointVectorValue[1]; - generatedControlPoint.day = controlPointVectorValue[2]; + generatedControlPoint.date = boost::gregorian::date(controlPointVectorValue[0], + controlPointVectorValue[1], + controlPointVectorValue[2]); return generatedControlPoint; } diff --git a/Modules/SemanticRelations/src/mitkSemanticRelations.cpp b/Modules/SemanticRelations/src/mitkSemanticRelations.cpp index 4a6d57eda3..33cc8049f7 100644 --- a/Modules/SemanticRelations/src/mitkSemanticRelations.cpp +++ b/Modules/SemanticRelations/src/mitkSemanticRelations.cpp @@ -1,993 +1,993 @@ /*=================================================================== 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 "mitkNodePredicates.h" #include "mitkSemanticRelations.h" #include "mitkSemanticRelationException.h" #include "mitkUIDGeneratorBoost.h" // multi label module #include // c++ #include #include std::vector mitk::SemanticRelations::m_ObserverVector; mitk::SemanticRelations::SemanticRelations(DataStorage::Pointer dataStorage) : m_DataStorage(dataStorage) { m_RelationStorage = std::make_shared(); m_RelationStorage->SetDataStorage(m_DataStorage); } mitk::SemanticRelations::~SemanticRelations() { // nothing here } void mitk::SemanticRelations::AddObserver(ISemanticRelationsObserver* observer) { std::vector::iterator existingObserver = std::find(m_ObserverVector.begin(), m_ObserverVector.end(), observer); if (existingObserver != m_ObserverVector.end()) { // no need to add the already existing observer return; } m_ObserverVector.push_back(observer); } void mitk::SemanticRelations::RemoveObserver(ISemanticRelationsObserver* observer) { m_ObserverVector.erase(std::remove(m_ObserverVector.begin(), m_ObserverVector.end(), observer), m_ObserverVector.end()); } /************************************************************************/ /* functions to get instances / attributes */ /************************************************************************/ mitk::SemanticTypes::LesionVector mitk::SemanticRelations::GetAllLesionsOfCase(const SemanticTypes::CaseID& caseID) const { return m_RelationStorage->GetAllLesionsOfCase(caseID); } mitk::SemanticTypes::LesionVector mitk::SemanticRelations::GetAllLesionsOfCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) const { SemanticTypes::LesionVector allLesions = GetAllLesionsOfCase(caseID); // filter the lesions: use only those, where the associated data is connected to image data that refers to the given control point using a lambda function auto lambda = [&caseID, &controlPoint, this](const SemanticTypes::Lesion& lesion) { return !ControlPointContainsLesion(caseID, lesion, controlPoint); }; allLesions.erase(std::remove_if(allLesions.begin(), allLesions.end(), lambda), allLesions.end()); return allLesions; } mitk::SemanticTypes::LesionClassVector mitk::SemanticRelations::GetAllLesionClassesOfCase(const SemanticTypes::CaseID& caseID) const { SemanticTypes::LesionVector allLesionsOfCase = GetAllLesionsOfCase(caseID); SemanticTypes::LesionClassVector allLesionClassesOfCase; for (const auto& lesion : allLesionsOfCase) { allLesionClassesOfCase.push_back(lesion.lesionClass); } // remove duplicate entries - auto lessThan = [](const SemanticTypes::LesionClass& lesionClassL, const SemanticTypes::LesionClass& lesionClassR) + auto lessThan = [](const SemanticTypes::LesionClass& lesionClassLeft, const SemanticTypes::LesionClass& lesionClassRight) { - return lesionClassL.UID < lesionClassR.UID; + return lesionClassLeft.UID < lesionClassRight.UID; }; - auto equal = [](const SemanticTypes::LesionClass& lesionClassL, const SemanticTypes::LesionClass& lesionClassR) + auto equal = [](const SemanticTypes::LesionClass& lesionClassLeft, const SemanticTypes::LesionClass& lesionClassRight) { - return lesionClassL.UID == lesionClassR.UID; + return lesionClassLeft.UID == lesionClassRight.UID; }; std::sort(allLesionClassesOfCase.begin(), allLesionClassesOfCase.end(), lessThan); allLesionClassesOfCase.erase(std::unique(allLesionClassesOfCase.begin(), allLesionClassesOfCase.end(), equal), allLesionClassesOfCase.end()); return allLesionClassesOfCase; } mitk::SemanticTypes::LesionVector mitk::SemanticRelations::GetAllLesionsInImage(const DataNode* imageNode) const { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } if (m_DataStorage.IsNull()) { mitkThrowException(SemanticRelationException) << "Not a valid data storage."; } SemanticTypes::LesionVector allLesionsInImage; // get child nodes of the current node with the segmentation predicate DataStorage::SetOfObjects::ConstPointer segmentationNodes = m_DataStorage->GetDerivations(imageNode, NodePredicates::GetSegmentationPredicate(), false); for (auto it = segmentationNodes->Begin(); it != segmentationNodes->End(); ++it) { DataNode* segmentationNode = it->Value(); try { SemanticTypes::Lesion representedLesion = GetRepresentedLesion(segmentationNode); allLesionsInImage.push_back(representedLesion); } catch (const SemanticRelationException&) { continue; } } return allLesionsInImage; } mitk::SemanticTypes::Lesion mitk::SemanticRelations::GetRepresentedLesion(const DataNode* segmentationNode) const { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); SemanticTypes::ID segmentationID = GetIDFromDataNode(segmentationNode); SemanticTypes::Lesion representedLesion = m_RelationStorage->GetRepresentedLesion(caseID, segmentationID); if (representedLesion.UID.empty()) { mitkThrowException(SemanticRelationException) << "Could not find a represented lesion instance for the given segmentation node " << segmentationNode->GetName(); } else { return representedLesion; } } bool mitk::SemanticRelations::IsRepresentingALesion(const DataNode* segmentationNode) const { try { SemanticTypes::Lesion representedLesion = GetRepresentedLesion(segmentationNode); return true; } catch (const Exception&) { return false; } } bool mitk::SemanticRelations::InstanceExists(const DataNode* dataNode) const { try { SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); SemanticTypes::ID dataNodeID = GetIDFromDataNode(dataNode); if (mitk::NodePredicates::GetImagePredicate()->CheckNode(dataNode)) { std::vector allImageIDsOfCase = m_RelationStorage->GetAllImageIDsOfCase(caseID); return std::find(allImageIDsOfCase.begin(), allImageIDsOfCase.end(), dataNodeID) != allImageIDsOfCase.end(); } else if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { std::vector allSegmentationIDsOfCase = m_RelationStorage->GetAllSegmentationIDsOfCase(caseID); return std::find(allSegmentationIDsOfCase.begin(), allSegmentationIDsOfCase.end(), dataNodeID) != allSegmentationIDsOfCase.end(); } else { return false; } } catch (const SemanticRelationException&) { return false; } } mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllSegmentationsOfCase(const SemanticTypes::CaseID& caseID) const { if (m_DataStorage.IsNull()) { mitkThrowException(SemanticRelationException) << "Not a valid data storage."; } return m_RelationStorage->GetAllSegmentationsOfCase(caseID); } mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllSegmentationsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const { if (m_DataStorage.IsNull()) { mitkThrowException(SemanticRelationException) << "Not a valid data storage."; } if (InstanceExists(caseID, lesion)) { // lesion exists, retrieve all case segmentations from the storage DataNodeVector allSegmentationsOfLesion = GetAllSegmentationsOfCase(caseID); // filter all segmentations: check for semantic relation with the given lesion using a lambda function auto lambda = [&lesion, this](DataNode::Pointer segmentation) { try { SemanticTypes::Lesion representedLesion = GetRepresentedLesion(segmentation); return lesion.UID != representedLesion.UID; } catch (const SemanticRelationException&) { return true; } }; allSegmentationsOfLesion.erase(std::remove_if(allSegmentationsOfLesion.begin(), allSegmentationsOfLesion.end(), lambda), allSegmentationsOfLesion.end()); return allSegmentationsOfLesion; } else { mitkThrowException(SemanticRelationException) << "Could not find an existing lesion instance for the given caseID " << caseID << " and lesion " << lesion.UID << "."; } } mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllImagesOfCase(const SemanticTypes::CaseID& caseID) const { if (m_DataStorage.IsNull()) { mitkThrowException(SemanticRelationException) << "Not a valid data storage."; } return m_RelationStorage->GetAllImagesOfCase(caseID); } mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllImagesOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const { if (m_DataStorage.IsNull()) { 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->GetSources(segmentationNode, NodePredicates::GetImagePredicate(), false); for (auto it = parentNodes->Begin(); it != parentNodes->End(); ++it) { DataNode::Pointer dataNode = it->Value(); allImagesOfLesion.push_back(it->Value()); } } std::sort(allImagesOfLesion.begin(), allImagesOfLesion.end()); allImagesOfLesion.erase(std::unique(allImagesOfLesion.begin(), allImagesOfLesion.end()), allImagesOfLesion.end()); return allImagesOfLesion; } bool mitk::SemanticRelations::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const { mitk::SemanticTypes::LesionVector allLesions = GetAllLesionsOfCase(caseID); // filter all lesions: check for equality with the given lesion using a lambda function auto lambda = [&lesion](const SemanticTypes::Lesion& currentLesion) { return currentLesion.UID == lesion.UID; }; const auto existingLesion = std::find_if(allLesions.begin(), allLesions.end(), lambda); if (existingLesion != allLesions.end()) { return true; } else { return false; } } mitk::SemanticTypes::ControlPointVector mitk::SemanticRelations::GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID) const { return m_RelationStorage->GetAllControlPointsOfCase(caseID); } mitk::SemanticTypes::ControlPointVector mitk::SemanticRelations::GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const { SemanticTypes::ControlPointVector allControlPoints = GetAllControlPointsOfCase(caseID); // filter the control points: use only those, where the associated image data has a segmentation that refers to the given lesion using a lambda function auto lambda = [&caseID, &lesion, this](const SemanticTypes::ControlPoint& controlPoint) { return !ControlPointContainsLesion(caseID, lesion, controlPoint); }; allControlPoints.erase(std::remove_if(allControlPoints.begin(), allControlPoints.end(), lambda), allControlPoints.end()); return allControlPoints; } mitk::SemanticTypes::ControlPointVector mitk::SemanticRelations::GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) const { SemanticTypes::ControlPointVector allControlPoints = GetAllControlPointsOfCase(caseID); // filter the control points: use only those, where the associated image data refers to the given information type using a lambda function auto lambda = [&caseID, &informationType, this](const SemanticTypes::ControlPoint& controlPoint) { return !ControlPointContainsInformationType(caseID, informationType, controlPoint); }; allControlPoints.erase(std::remove_if(allControlPoints.begin(), allControlPoints.end(), lambda), allControlPoints.end()); return allControlPoints; } mitk::SemanticTypes::ControlPoint mitk::SemanticRelations::GetControlPointOfData(const DataNode* imageNode) const { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); SemanticTypes::ID dataNodeID = GetIDFromDataNode(imageNode); return m_RelationStorage->GetControlPointOfImage(caseID, dataNodeID); } mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllImagesOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) const { if (m_DataStorage.IsNull()) { mitkThrow() << "Not a valid data storage."; } if (InstanceExists(caseID, controlPoint)) { // control point exists, retrieve all images from the storage DataNodeVector allImagesOfControlPoint = GetAllImagesOfCase(caseID); // filter all images to remove the ones with a different control point using a lambda function auto lambda = [&controlPoint, this](DataNode::Pointer imageNode) { return controlPoint.UID != GetControlPointOfData(imageNode).UID; }; allImagesOfControlPoint.erase(std::remove_if(allImagesOfControlPoint.begin(), allImagesOfControlPoint.end(), lambda), allImagesOfControlPoint.end()); return allImagesOfControlPoint; } else { mitkThrowException(SemanticRelationException) << "Could not find an existing control point instance for the given caseID " << caseID << " and control point " << controlPoint.UID << "."; } } bool mitk::SemanticRelations::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) const { SemanticTypes::ControlPointVector allControlPoints = GetAllControlPointsOfCase(caseID); // filter all control points: check for equality with the given control point using a lambda function auto lambda = [&controlPoint](const SemanticTypes::ControlPoint& currentControlPoint) { return currentControlPoint.UID == controlPoint.UID; }; const auto existingControlPoint = std::find_if(allControlPoints.begin(), allControlPoints.end(), lambda); if (existingControlPoint != allControlPoints.end()) { return true; } else { return false; } } mitk::SemanticTypes::ExaminationPeriodVector mitk::SemanticRelations::GetAllExaminationPeriodsOfCase(const SemanticTypes::CaseID& caseID) const { return m_RelationStorage->GetAllExaminationPeriodsOfCase(caseID); } bool mitk::SemanticRelations::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) const { SemanticTypes::ExaminationPeriodVector allExaminationPeriods = GetAllExaminationPeriodsOfCase(caseID); // filter all examination periods: check for equality with the given examination period using a lambda function auto lambda = [&examinationPeriod](const SemanticTypes::ExaminationPeriod& currentExaminationPeriod) { return currentExaminationPeriod.UID == examinationPeriod.UID; }; const auto existingExaminationPeriod = std::find_if(allExaminationPeriods.begin(), allExaminationPeriods.end(), lambda); if (existingExaminationPeriod != allExaminationPeriods.end()) { return true; } else { return false; } } mitk::SemanticTypes::InformationTypeVector mitk::SemanticRelations::GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID) const { return m_RelationStorage->GetAllInformationTypesOfCase(caseID); } mitk::SemanticTypes::InformationTypeVector mitk::SemanticRelations::GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) const { SemanticTypes::InformationTypeVector allInformationTypes = GetAllInformationTypesOfCase(caseID); // filter the information types: use only those, where the associated data refers to the given control point using a lambda function auto lambda = [&caseID, &controlPoint, this](const SemanticTypes::InformationType& informationType) { return !ControlPointContainsInformationType(caseID, informationType, controlPoint); }; allInformationTypes.erase(std::remove_if(allInformationTypes.begin(), allInformationTypes.end(), lambda), allInformationTypes.end()); return allInformationTypes; } mitk::SemanticTypes::InformationType mitk::SemanticRelations::GetInformationTypeOfImage(const DataNode* imageNode) const { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); SemanticTypes::ID imageID = GetIDFromDataNode(imageNode); return m_RelationStorage->GetInformationTypeOfImage(caseID, imageID); } mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllImagesOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) const { if (m_DataStorage.IsNull()) { mitkThrow() << "Not a valid data storage."; } if (InstanceExists(caseID, informationType)) { // information type exists, retrieve all images from the storage DataNodeVector allImagesOfInformationType = GetAllImagesOfCase(caseID); // filter all images to remove the ones with a different information type using a lambda function auto lambda = [&informationType, this](DataNode::Pointer imageNode) { return informationType != GetInformationTypeOfImage(imageNode); }; allImagesOfInformationType.erase(std::remove_if(allImagesOfInformationType.begin(), allImagesOfInformationType.end(), lambda), allImagesOfInformationType.end()); return allImagesOfInformationType; } else { mitkThrowException(SemanticRelationException) << "Could not find an existing information type for the given caseID " << caseID << " and information type " << informationType; } } mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllSpecificImages(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const { if (m_DataStorage.IsNull()) { mitkThrow() << "Not a valid data storage."; } if (InstanceExists(caseID, controlPoint)) { if (InstanceExists(caseID, informationType)) { // control point exists, information type exists, retrieve all images from the storage DataNodeVector allImagesOfCase = GetAllImagesOfCase(caseID); // filter all images to remove the ones with a different control point and information type using a lambda function auto lambda = [&controlPoint, &informationType, this](DataNode::Pointer imageNode) { - return (informationType != GetInformationTypeOfImage(imageNode)) || (controlPoint != GetControlPointOfData(imageNode)); + return (informationType != GetInformationTypeOfImage(imageNode)) || (controlPoint.date != GetControlPointOfData(imageNode).date); }; allImagesOfCase.erase(std::remove_if(allImagesOfCase.begin(), allImagesOfCase.end(), lambda), allImagesOfCase.end()); return allImagesOfCase; } else { mitkThrowException(SemanticRelationException) << "Could not find an existing information type for the given caseID " << caseID << " and information type " << informationType; } } else { mitkThrowException(SemanticRelationException) << "Could not find an existing control point for the given caseID " << caseID << " and control point " << controlPoint.UID; } } mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllSpecificSegmentations(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const { if (m_DataStorage.IsNull()) { 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->GetDerivations(imageNode, NodePredicates::GetSegmentationPredicate(), false); for (auto it = segmentationNodes->Begin(); it != segmentationNodes->End(); ++it) { allSpecificSegmentations.push_back(it->Value()); } } return allSpecificSegmentations; } bool mitk::SemanticRelations::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) const { SemanticTypes::InformationTypeVector allInformationTypes = GetAllInformationTypesOfCase(caseID); // filter all information types: check for equality with the given information type using a lambda function auto lambda = [&informationType](const SemanticTypes::InformationType& currentInformationType) { return currentInformationType == informationType; }; const auto existingInformationType = std::find_if(allInformationTypes.begin(), allInformationTypes.end(), lambda); if (existingInformationType != allInformationTypes.end()) { return true; } else { return false; } } std::vector mitk::SemanticRelations::GetAllCaseIDs() const { return m_RelationStorage->GetAllCaseIDs(); } /************************************************************************/ /* functions to add / remove instances / attributes */ /************************************************************************/ void mitk::SemanticRelations::AddImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } // continue with a valid data node SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); SemanticTypes::ID nodeID = GetIDFromDataNode(imageNode); m_RelationStorage->AddCase(caseID); m_RelationStorage->AddImage(caseID, nodeID); SemanticTypes::InformationType informationType = GetDICOMModalityFromDataNode(imageNode); AddInformationTypeToImage(imageNode, informationType); // set the correct control point for this image SemanticTypes::ControlPoint controlPoint = GenerateControlPoint(imageNode); SetControlPointOfData(imageNode, controlPoint); } void mitk::SemanticRelations::RemoveImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } // continue with a valid data node SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); SemanticTypes::ID nodeID = GetIDFromDataNode(imageNode); RemoveInformationTypeFromImage(imageNode); UnlinkDataFromControlPoint(imageNode); m_RelationStorage->RemoveImage(caseID, nodeID); NotifyObserver(caseID); } void mitk::SemanticRelations::AddLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { if (InstanceExists(caseID, lesion)) { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to add already exists for the given case."; } else { m_RelationStorage->AddLesion(caseID, lesion); NotifyObserver(caseID); } } void mitk::SemanticRelations::OverwriteLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { if (InstanceExists(caseID, lesion)) { m_RelationStorage->OverwriteLesion(caseID, lesion); NotifyObserver(caseID); } else { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to overwrite does not exist for the given case."; } } void mitk::SemanticRelations::AddLesionAndLinkSegmentation(const DataNode* segmentationNode, const SemanticTypes::Lesion& lesion) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); AddLesion(caseID, lesion); LinkSegmentationToLesion(segmentationNode, lesion); NotifyObserver(caseID); } void mitk::SemanticRelations::RemoveLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { if (InstanceExists(caseID, lesion)) { DataNodeVector allSegmentationsOfLesion = GetAllSegmentationsOfLesion(caseID, lesion); if (allSegmentationsOfLesion.empty()) { // no more segmentations are linked to the specific lesion // the lesion can be removed from the storage m_RelationStorage->RemoveLesion(caseID, lesion); NotifyObserver(caseID); } else { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to remove is still referred to by a segmentation node. Lesion will not be removed."; } } else { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to remove does not exist for the given case."; } } void mitk::SemanticRelations::AddSegmentation(const DataNode* segmentationNode, const DataNode* parentNode) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } if (nullptr == parentNode) { mitkThrowException(SemanticRelationException) << "Not a valid parent data node."; } // continue with a valid data node SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); SemanticTypes::ID segmentationNodeID = GetIDFromDataNode(segmentationNode); SemanticTypes::ID parentNodeID = GetIDFromDataNode(parentNode); m_RelationStorage->AddSegmentation(caseID, segmentationNodeID, parentNodeID); NotifyObserver(caseID); } void mitk::SemanticRelations::LinkSegmentationToLesion(const DataNode* segmentationNode, const SemanticTypes::Lesion& lesion) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); if (InstanceExists(caseID, lesion)) { SemanticTypes::ID segmentationID = GetIDFromDataNode(segmentationNode); m_RelationStorage->LinkSegmentationToLesion(caseID, segmentationID, lesion); NotifyObserver(caseID); } else { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to link does not exist for the given case."; } } void mitk::SemanticRelations::UnlinkSegmentationFromLesion(const DataNode* segmentationNode) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); SemanticTypes::ID segmentationID = GetIDFromDataNode(segmentationNode); m_RelationStorage->UnlinkSegmentationFromLesion(caseID, segmentationID); NotifyObserver(caseID); } void mitk::SemanticRelations::RemoveSegmentation(const DataNode* segmentationNode) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } // continue with a valid data node SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); SemanticTypes::ID segmentationNodeID = GetIDFromDataNode(segmentationNode); m_RelationStorage->RemoveSegmentation(caseID, segmentationNodeID); NotifyObserver(caseID); } void mitk::SemanticRelations::SetControlPointOfData(const DataNode* dataNode, const SemanticTypes::ControlPoint& controlPoint) { if (nullptr == dataNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); SemanticTypes::ControlPointVector allControlPoints = GetAllControlPointsOfCase(caseID); // need to check if an already existing control point fits/contains the user control point SemanticTypes::ControlPoint existingControlPoint = FindExistingControlPoint(controlPoint, allControlPoints); if (!existingControlPoint.UID.empty()) { try { // found an already existing control point LinkDataToControlPoint(dataNode, existingControlPoint, false); } catch (const SemanticRelationException&) { mitkThrowException(SemanticRelationException) << "The data can not be linked. Inconsistency in the semantic relations storage assumed."; } } else { try { AddControlPointAndLinkData(dataNode, controlPoint, false); // added a new control point // find closest control point to add the new control point to the correct examination period SemanticTypes::ControlPoint closestControlPoint = FindClosestControlPoint(controlPoint, allControlPoints); SemanticTypes::ExaminationPeriodVector allExaminationPeriods = GetAllExaminationPeriodsOfCase(caseID); SemanticTypes::ExaminationPeriod examinationPeriod = FindExaminationPeriod(closestControlPoint, allExaminationPeriods); if (examinationPeriod.UID.empty()) { // no closest control point (exceed threshold) or no examination period found // create a new examination period for this control point and add it to the storage examinationPeriod.UID = UIDGeneratorBoost::GenerateUID(); examinationPeriod.name = "New examination period " + std::to_string(allExaminationPeriods.size()); AddExaminationPeriod(caseID, examinationPeriod); } // add the control point to the (newly created or found / close) examination period AddControlPointToExaminationPeriod(caseID, controlPoint, examinationPeriod); } catch (const SemanticRelationException&) { mitkThrowException(SemanticRelationException) << "The data can not be linked. Inconsistency in the semantic relations storage assumed."; } } ClearControlPoints(caseID); NotifyObserver(caseID); } void mitk::SemanticRelations::AddControlPointAndLinkData(const DataNode* dataNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence) { if (nullptr == dataNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); if (InstanceExists(caseID, controlPoint)) { mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to add already exists for the given case. \n Use 'LinkDataToControlPoint' instead."; } m_RelationStorage->AddControlPoint(caseID, controlPoint); LinkDataToControlPoint(dataNode, controlPoint, checkConsistence); } void mitk::SemanticRelations::LinkDataToControlPoint(const DataNode* dataNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence) { if (nullptr == dataNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); if (InstanceExists(caseID, controlPoint)) { SemanticTypes::ID dataID = GetIDFromDataNode(dataNode); m_RelationStorage->LinkDataToControlPoint(caseID, dataID, controlPoint); } else { mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to link does not exist for the given case."; } } void mitk::SemanticRelations::UnlinkDataFromControlPoint(const DataNode* dataNode) { if (nullptr == dataNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); SemanticTypes::ID dataID = GetIDFromDataNode(dataNode); SemanticTypes::ControlPoint controlPoint = m_RelationStorage->GetControlPointOfImage(caseID, dataID); m_RelationStorage->UnlinkDataFromControlPoint(caseID, dataID); ClearControlPoints(caseID); } void mitk::SemanticRelations::AddExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) { if (InstanceExists(caseID, examinationPeriod)) { mitkThrowException(SemanticRelationException) << "The examination period " << examinationPeriod.UID << " to add already exists for the given case."; } else { m_RelationStorage->AddExaminationPeriod(caseID, examinationPeriod); } } void mitk::SemanticRelations::AddControlPointToExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod& examinationPeriod) { if (!InstanceExists(caseID, controlPoint)) { mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to add does not exist for the given case."; } if (!InstanceExists(caseID, examinationPeriod)) { mitkThrowException(SemanticRelationException) << "The examination period " << examinationPeriod.UID << " does not exist for the given case. \n Use 'AddExaminationPeriod' before."; } m_RelationStorage->AddControlPointToExaminationPeriod(caseID, controlPoint, examinationPeriod); } void mitk::SemanticRelations::SetInformationType(const DataNode* imageNode, const SemanticTypes::InformationType& informationType) { RemoveInformationTypeFromImage(imageNode); AddInformationTypeToImage(imageNode, informationType); SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); NotifyObserver(caseID); } void mitk::SemanticRelations::AddInformationTypeToImage(const DataNode* imageNode, const SemanticTypes::InformationType& informationType) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); SemanticTypes::ID imageID = GetIDFromDataNode(imageNode); m_RelationStorage->AddInformationTypeToImage(caseID, imageID, informationType); } void mitk::SemanticRelations::RemoveInformationTypeFromImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); SemanticTypes::ID imageID = GetIDFromDataNode(imageNode); SemanticTypes::InformationType originalInformationType = m_RelationStorage->GetInformationTypeOfImage(caseID, imageID); m_RelationStorage->RemoveInformationTypeFromImage(caseID, imageID); // check for further references to the removed information type std::vector allImageIDsVectorValue = m_RelationStorage->GetAllImageIDsOfCase(caseID); for (const auto otherImageID : allImageIDsVectorValue) { SemanticTypes::InformationType otherInformationType = m_RelationStorage->GetInformationTypeOfImage(caseID, otherImageID); if (otherInformationType == originalInformationType) { // found the information type in another image -> cannot remove the information type from the case return; } } // given information type was not referred by any other image of the case -> the information type can be removed from the case m_RelationStorage->RemoveInformationTypeFromCase(caseID, originalInformationType); } /************************************************************************/ /* private functions */ /************************************************************************/ void mitk::SemanticRelations::NotifyObserver(const SemanticTypes::CaseID& caseID) const { for (auto& observer : m_ObserverVector) { observer->Update(caseID); } } bool mitk::SemanticRelations::ControlPointContainsLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ControlPoint& controlPoint) const { DataNodeVector allImagesOfLesion; try { allImagesOfLesion = GetAllImagesOfLesion(caseID, lesion); } catch (const SemanticRelationException&) { // error retrieving image data; lesion has to be outside the control point return false; } DataNodeVector allImagesOfControlPoint; try { allImagesOfControlPoint = GetAllImagesOfControlPoint(caseID, controlPoint); } catch (const SemanticRelationException&) { // error retrieving control point data; lesion has to be outside the control point return false; } std::sort(allImagesOfLesion.begin(), allImagesOfLesion.end()); std::sort(allImagesOfControlPoint.begin(), allImagesOfControlPoint.end()); DataNodeVector allImagesIntersection; // set intersection removes duplicated nodes, since 'GetAllImagesOfControlPoint' only contains at most one of each node std::set_intersection(allImagesOfLesion.begin(), allImagesOfLesion.end(), allImagesOfControlPoint.begin(), allImagesOfControlPoint.end(), std::back_inserter(allImagesIntersection)); // if the vector of intersecting data is empty, the control point does not contain the lesion return !allImagesIntersection.empty(); } bool mitk::SemanticRelations::ControlPointContainsInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType, const SemanticTypes::ControlPoint& controlPoint) const { DataNodeVector allImagesIntersection = GetAllSpecificImages(caseID, controlPoint, informationType); return !allImagesIntersection.empty(); } void mitk::SemanticRelations::ClearControlPoints(const SemanticTypes::CaseID& caseID) { SemanticTypes::ControlPointVector allControlPointsOfCase = GetAllControlPointsOfCase(caseID); DataNodeVector allImagesOfCase; try { allImagesOfCase = GetAllImagesOfCase(caseID); } catch (const SemanticRelationException&) { // error retrieving image data return; } SemanticTypes::ControlPointVector referencedControlPoints; for (const auto& image : allImagesOfCase) { try { referencedControlPoints.push_back(GetControlPointOfData(image)); } catch (const SemanticRelationException&) { // error retrieving control point of data continue; } } std::sort(allControlPointsOfCase.begin(), allControlPointsOfCase.end()); std::sort(referencedControlPoints.begin(), referencedControlPoints.end()); SemanticTypes::ControlPointVector nonReferencedControlPoints; std::set_difference(allControlPointsOfCase.begin(), allControlPointsOfCase.end(), referencedControlPoints.begin(), referencedControlPoints.end(), std::inserter(nonReferencedControlPoints, nonReferencedControlPoints.begin())); auto allExaminationPeriods = GetAllExaminationPeriodsOfCase(caseID); for (const auto& controlPoint : nonReferencedControlPoints) { const auto& examinationPeriod = FindExaminationPeriod(controlPoint, allExaminationPeriods); m_RelationStorage->RemoveControlPointFromExaminationPeriod(caseID, controlPoint, examinationPeriod); m_RelationStorage->RemoveControlPointFromCase(caseID, controlPoint); } } diff --git a/Modules/SemanticRelationsUI/src/QmitkControlPointDialog.cpp b/Modules/SemanticRelationsUI/src/QmitkControlPointDialog.cpp index b3f9af23ce..348aade318 100644 --- a/Modules/SemanticRelationsUI/src/QmitkControlPointDialog.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkControlPointDialog.cpp @@ -1,69 +1,69 @@ /*=================================================================== 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 "QmitkControlPointDialog.h" #include #include #include #include #include QmitkControlPointDialog::QmitkControlPointDialog(QWidget* parent) : QDialog(parent) { QBoxLayout* verticalLayout = new QVBoxLayout(this); verticalLayout->setMargin(5); verticalLayout->setSpacing(5); QLabel* dateLabel = new QLabel(tr("Set date"), this); verticalLayout->addWidget(dateLabel); m_DateEdit = new QDateEdit(this); m_DateEdit->setDisplayFormat("yyyy-MM-dd"); m_DateEdit->setFocus(); verticalLayout->addWidget(m_DateEdit); QPushButton* acceptButton = new QPushButton(tr("Ok"), this); QPushButton* cancelButton = new QPushButton(tr("Cancel"), this); acceptButton->setDefault(true); connect(acceptButton, &QPushButton::clicked, this, &QmitkControlPointDialog::accept); connect(cancelButton, &QPushButton::clicked, this, &QmitkControlPointDialog::reject); QBoxLayout* horizontalLayout = new QHBoxLayout(); horizontalLayout->setSpacing(5); horizontalLayout->addStretch(); horizontalLayout->addWidget(acceptButton); horizontalLayout->addWidget(cancelButton); verticalLayout->addLayout(horizontalLayout); setMinimumSize(250, 100); } QmitkControlPointDialog::~QmitkControlPointDialog() { } void QmitkControlPointDialog::SetCurrentDate(mitk::SemanticTypes::ControlPoint currentControlPoint) { - m_DateEdit->setDate(QDate(currentControlPoint.year, currentControlPoint.month, currentControlPoint.day)); + m_DateEdit->setDate(QDate(currentControlPoint.date.year(), currentControlPoint.date.month(), currentControlPoint.date.day())); } QDate QmitkControlPointDialog::GetCurrentDate() const { return m_DateEdit->date(); } diff --git a/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp b/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp index 9f0a204586..6dc1837a1c 100644 --- a/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp @@ -1,270 +1,268 @@ /*=================================================================== 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 "QmitkLesionTreeModel.h" // semantic relations module #include #include #include QmitkLesionTreeModel::QmitkLesionTreeModel(QObject* parent/* = nullptr*/) : QmitkAbstractSemanticRelationsStorageModel(parent) , m_RootItem(std::make_shared(mitk::LesionData())) { // nothing here } QmitkLesionTreeModel::~QmitkLesionTreeModel() { // nothing here } ////////////////////////////////////////////////////////////////////////// // overridden virtual functions from QAbstractItemModel ////////////////////////////////////////////////////////////////////////// QModelIndex QmitkLesionTreeModel::index(int row, int column, const QModelIndex& itemIndex) const { if (!hasIndex(row, column, itemIndex)) { return QModelIndex(); } auto childItem = GetItemByIndex(itemIndex)->GetChildInRow(row); if (nullptr == childItem) { return QModelIndex(); } return createIndex(row, column, childItem.get()); } QModelIndex QmitkLesionTreeModel::parent(const QModelIndex& itemIndex) const { if (!itemIndex.isValid()) { return QModelIndex(); } auto parentItem = GetItemByIndex(itemIndex)->GetParent(); if (parentItem.expired()) { return QModelIndex(); } auto sharedParent = parentItem.lock(); if (sharedParent == m_RootItem) { return QModelIndex(); } return createIndex(sharedParent->GetRow(), 0, sharedParent.get()); } int QmitkLesionTreeModel::rowCount(const QModelIndex& itemIndex/* = QModelIndex()*/) const { return GetItemByIndex(itemIndex)->ChildCount(); } int QmitkLesionTreeModel::columnCount(const QModelIndex&/* itemIndex = QModelIndex() */) const { if (0 == m_RootItem->ChildCount()) { // no lesion items stored, no need to display columns return 0; } return m_ControlPoints.size() + 1; } QVariant QmitkLesionTreeModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return QVariant(); } if (index.column() < 0 || index.column() > static_cast(m_ControlPoints.size())) { return QVariant(); } QmitkLesionTreeItem* currentItem = GetItemByIndex(index); if (Qt::DisplayRole == role) { if (currentItem->GetParent().expired()) { return QVariant(); } auto parentItem = currentItem->GetParent().lock(); // parent exists and is the root item -> 1. item of a lesion entry if (m_RootItem == parentItem) { // display role fills the first columns with the lesion UID / name if (0 == index.column()) { std::string itemString = currentItem->GetData().GetLesionName(); if (itemString.empty()) { itemString = currentItem->GetData().GetLesionUID(); } return QString::fromStdString(itemString); } else { // display role fills other columns with the lesion presence info const auto lesionPresence = currentItem->GetData().GetLesionPresence(); if (index.column() - 1 > static_cast(lesionPresence.size())) { return ""; } return QVariant(lesionPresence.at(index.column() - 1)); } } // parent is not the root item -> 2. item of a lesion entry else { // display role fills the first columns with the property name "Volume" if (0 == index.column()) { return "Volume"; } else { // display role fills other columns with the lesion volume info const auto lesionVolume= currentItem->GetData().GetLesionVolume(); if (index.column() - 1 > static_cast(lesionVolume.size())) { return ""; } return QVariant(lesionVolume.at(index.column() - 1)); } } } if (Qt::UserRole == role) { return QVariant::fromValue(currentItem); } return QVariant(); } QVariant QmitkLesionTreeModel::headerData(int section, Qt::Orientation orientation, int role) const { if (0 == m_RootItem->ChildCount()) { // no lesion items stored, no need to display the header return QVariant(); } if (Qt::Horizontal == orientation && Qt::DisplayRole == role) { if (0 == section) { return QVariant("Lesion"); } if (m_ControlPoints.size() >= section) { mitk::SemanticTypes::ControlPoint currentControlPoint = m_ControlPoints.at(section-1); - // generate a string from the control point - std::string currentControlPointAsString = mitk::GetControlPointAsString(currentControlPoint); - return QVariant(QString::fromStdString(currentControlPointAsString)); + return QVariant(QString::fromStdString(currentControlPoint.ToString())); } } return QVariant(); } void QmitkLesionTreeModel::NodePredicateChanged() { // does not react to node predicate changes } void QmitkLesionTreeModel::NodeAdded(const mitk::DataNode* node) { // does not react to data storage changes } void QmitkLesionTreeModel::NodeChanged(const mitk::DataNode* node) { // does not react to data storage changes } void QmitkLesionTreeModel::NodeRemoved(const mitk::DataNode* node) { // does not react to data storage changes } void QmitkLesionTreeModel::SetData() { m_RootItem = std::make_shared(mitk::LesionData()); // get all control points of current case m_ControlPoints = m_SemanticRelations->GetAllControlPointsOfCase(m_CaseID); // sort the vector of control points for the timeline std::sort(m_ControlPoints.begin(), m_ControlPoints.end()); SetLesionData(); } void QmitkLesionTreeModel::SetLesionData() { std::vector allLesions = m_SemanticRelations->GetAllLesionsOfCase(m_CaseID); for (auto& lesion : allLesions) { AddLesion(lesion); } } void QmitkLesionTreeModel::AddLesion(const mitk::SemanticTypes::Lesion& lesion) { if (nullptr == m_SemanticRelations) { return; } // create new lesion tree item data and modify it according to the control point data mitk::LesionData lesionData(lesion); mitk::GenerateAdditionalLesionData(lesionData, m_CaseID, m_SemanticRelations); // add the 1. level lesion item to the root item std::shared_ptr newLesionTreeItem = std::make_shared(lesionData); m_RootItem->AddChild(newLesionTreeItem); // add the 2. level lesion item to the 1. level lesion item std::shared_ptr newChildItem = std::make_shared(lesionData); newLesionTreeItem->AddChild(newChildItem); } QmitkLesionTreeItem* QmitkLesionTreeModel::GetItemByIndex(const QModelIndex& index) const { if (index.isValid()) { auto item = static_cast(index.internalPointer()); if (nullptr != item) { return item; } } return m_RootItem.get(); } diff --git a/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp b/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp index 9bbeab9eb5..a596a06c9c 100644 --- a/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp @@ -1,387 +1,386 @@ /*=================================================================== 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 // 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 { return m_ControlPoints.size(); } QVariant QmitkPatientTableModel::data(const QModelIndex& index, int role/* = Qt::DisplayRole*/) const { 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_ControlPoints.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::green)) : 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 (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::NodeAdded(const mitk::DataNode* node) { // does not react to data storage changes } void QmitkPatientTableModel::NodeChanged(const mitk::DataNode* node) { // nothing here, since the "'NodeChanged'-event is currently sent far too often //UpdateModelData(); } void QmitkPatientTableModel::NodeRemoved(const mitk::DataNode* node) { // does not react to data storage changes } 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::SetLesionPresence(const mitk::DataNode* dataNode, bool lesionPresence) { if (nullptr == dataNode) { return; } 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)); } } void QmitkPatientTableModel::SetData() { // get all control points of current case m_ControlPoints = m_SemanticRelations->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 = m_SemanticRelations->GetAllExaminationPeriodsOfCase(m_CaseID); // sort the vector of examination periods for the timeline mitk::SortExaminationPeriods(m_ExaminationPeriods, m_ControlPoints); // get all information types points of current case m_InformationTypes = m_SemanticRelations->GetAllInformationTypesOfCase(m_CaseID); m_PixmapMap.clear(); m_LesionPresence.clear(); SetDataNodes(); SetHeaderModel(); } void QmitkPatientTableModel::SetDataNodes() { std::vector allDataNodes; if("Image" == m_SelectedNodeType) { allDataNodes = m_SemanticRelations->GetAllImagesOfCase(m_CaseID); } else if("Segmentation" == m_SelectedNodeType) { allDataNodes = m_SemanticRelations->GetAllSegmentationsOfCase(m_CaseID); } for (const auto& dataNode : allDataNodes) { // set the pixmap for the current node QPixmap pixmapFromImage = QmitkSemanticRelationsUIHelper::GetPixmapFromImageNode(dataNode); SetPixmapOfNode(dataNode, &pixmapFromImage); // set the lesion presence for the current node if (m_SemanticRelations->InstanceExists(m_CaseID, m_Lesion)) { bool lesionPresence = lesionPresence = IsLesionPresentOnDataNode(dataNode); SetLesionPresence(dataNode, lesionPresence); } else { m_LesionPresence.clear(); } } } 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(); const auto& currentControlPoints = examinationPeriod.controlPointUIDs; for (const auto& controlPointUID : currentControlPoints) { const auto& controlPoint = mitk::GetControlPointByUID(controlPointUID, m_ControlPoints); - std::string controlPointAsString = mitk::GetControlPointAsString(controlPoint); - QStandardItem* controlPointItem = new QStandardItem(QString::fromStdString(controlPointAsString)); + QStandardItem* controlPointItem = new QStandardItem(QString::fromStdString(controlPoint.ToString())); standardItems.push_back(controlPointItem); examinationPeriodItem->appendColumn(standardItems); standardItems.clear(); } } m_HeaderModel->setItem(0, 0, rootItem); } bool QmitkPatientTableModel::IsLesionPresentOnDataNode(const mitk::DataNode* dataNode) const { if (m_DataStorage.IsExpired()) { return false; } auto dataStorage = m_DataStorage.Lock(); try { mitk::SemanticRelations::DataNodeVector allSegmentationsOfLesion = m_SemanticRelations->GetAllSegmentationsOfLesion(m_CaseID, m_Lesion); for (const auto& segmentation : allSegmentationsOfLesion) { if ("Segmentation" == m_SelectedNodeType) { if (dataNode == segmentation) { // found a segmentation of the node that is represented by the selected lesion return true; } } else if ("Image" == m_SelectedNodeType) { // get parent node of the current segmentation node with the node predicate mitk::DataStorage::SetOfObjects::ConstPointer parentNodes = dataStorage->GetSources(segmentation, mitk::NodePredicates::GetImagePredicate(), false); for (auto it = parentNodes->Begin(); it != parentNodes->End(); ++it) { if (dataNode == it.Value()) { // found a segmentation of the node that is represented by the selected lesion return true; } } } } } catch (const mitk::SemanticRelationException&) { return false; } return false; } mitk::DataNode* QmitkPatientTableModel::GetCurrentDataNode(const QModelIndex& index) const { mitk::SemanticTypes::ControlPoint currentControlPoint = m_ControlPoints.at(index.column()); mitk::SemanticTypes::InformationType currentInformationType = m_InformationTypes.at(index.row()); try { std::vector filteredDataNodes; if ("Image" == m_SelectedNodeType) { filteredDataNodes = m_SemanticRelations->GetAllSpecificImages(m_CaseID, currentControlPoint, currentInformationType); } else if ("Segmentation" == m_SelectedNodeType) { filteredDataNodes = m_SemanticRelations->GetAllSpecificSegmentations(m_CaseID, currentControlPoint, currentInformationType); } if (filteredDataNodes.empty()) { return nullptr; } return filteredDataNodes.front(); } catch (const mitk::SemanticRelationException&) { return nullptr; } } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetControlPointAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetControlPointAction.cpp index 18f713f319..a615cc6048 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetControlPointAction.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetControlPointAction.cpp @@ -1,152 +1,153 @@ /*=================================================================== 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 plugin #include "QmitkDataNodeSetControlPointAction.h" // semantic relations module #include #include #include // semantic relations UI module #include "QmitkControlPointDialog.h" // mitk gui common plugin #include // berry #include #include // qt #include QmitkDataNodeSetControlPointAction::QmitkDataNodeSetControlPointAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) : QAction(parent) , m_WorkbenchPartSite(workbenchPartSite) { setText(tr("Set control point")); m_Parent = parent; InitializeAction(); } QmitkDataNodeSetControlPointAction::QmitkDataNodeSetControlPointAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) : QAction(parent) , m_WorkbenchPartSite(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) { setText(tr("Set control point")); m_Parent = parent; InitializeAction(); } QmitkDataNodeSetControlPointAction::~QmitkDataNodeSetControlPointAction() { // nothing here } void QmitkDataNodeSetControlPointAction::SetDataStorage(mitk::DataStorage* dataStorage) { if (m_DataStorage != dataStorage) { // set the new data storage m_DataStorage = dataStorage; m_SemanticRelations = std::make_unique(m_DataStorage.Lock()); } } void QmitkDataNodeSetControlPointAction::InitializeAction() { connect(this, &QAction::triggered, this, &QmitkDataNodeSetControlPointAction::OnActionTriggered); } void QmitkDataNodeSetControlPointAction::OnActionTriggered(bool checked) { if (nullptr == m_SemanticRelations) { return; } auto dataNode = GetSelectedNode(); if (dataNode.IsNull()) { return; } QmitkControlPointDialog* inputDialog = new QmitkControlPointDialog(m_Parent); inputDialog->setWindowTitle("Set control point"); inputDialog->SetCurrentDate(m_SemanticRelations->GetControlPointOfData(dataNode)); int dialogReturnValue = inputDialog->exec(); if (QDialog::Rejected == dialogReturnValue) { return; } const QDate& userSelectedDate = inputDialog->GetCurrentDate(); mitk::SemanticTypes::ControlPoint controlPoint; controlPoint.UID = mitk::UIDGeneratorBoost::GenerateUID(); - controlPoint.year = userSelectedDate.year(); - controlPoint.month = userSelectedDate.month(); - controlPoint.day = userSelectedDate.day(); + controlPoint.date = boost::gregorian::date(userSelectedDate.year(), + userSelectedDate.month(), + userSelectedDate.day()); try { + m_SemanticRelations->UnlinkDataFromControlPoint(dataNode); m_SemanticRelations->SetControlPointOfData(dataNode, controlPoint); } catch (const mitk::SemanticRelationException&) { return; } } QList QmitkDataNodeSetControlPointAction::GetSelectedNodes() { QList selectedNodes; if (m_WorkbenchPartSite.Expired()) { return selectedNodes; } berry::ISelection::ConstPointer selection = m_WorkbenchPartSite.Lock()->GetWorkbenchWindow()->GetSelectionService()->GetSelection(); mitk::DataNodeSelection::ConstPointer currentSelection = selection.Cast(); if (currentSelection.IsNull() || currentSelection->IsEmpty()) { return selectedNodes; } selectedNodes = QList::fromStdList(currentSelection->GetSelectedDataNodes()); return selectedNodes; } mitk::DataNode::Pointer QmitkDataNodeSetControlPointAction::GetSelectedNode() { QList selectedNodes = GetSelectedNodes(); if (selectedNodes.empty()) { return nullptr; } // no batch action; should only be called with a single node mitk::DataNode::Pointer dataNode = selectedNodes.front(); if (nullptr == dataNode) { return nullptr; } return dataNode; }