diff --git a/Modules/SemanticRelations/Test/mitkSemanticRelationsTestHelper.cpp b/Modules/SemanticRelations/Test/mitkSemanticRelationsTestHelper.cpp index 8701928af5..ad47a94f29 100644 --- a/Modules/SemanticRelations/Test/mitkSemanticRelationsTestHelper.cpp +++ b/Modules/SemanticRelations/Test/mitkSemanticRelationsTestHelper.cpp @@ -1,398 +1,335 @@ /*=================================================================== 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 "mitkSemanticRelationsTestHelper.h" // mitk semantic relations +#include "mitkDICOMHelper.h" #include "mitkRelationStorage.h" #include "mitkUIDGeneratorBoost.h" // mitk core #include #include -#include +#include // mitk multilabel #include mitk::DataNode::Pointer mitk::SemanticRelationsTestHelper::GetPatientOneCTImage() { // create new empty image Image::Pointer image = Image::New(); // set properties of image (base data) - // 0x0010, 0x0010 (PatientName) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str(), + image->SetProperty(GetCaseIDDICOMProperty().c_str(), StringProperty::New("Patient1")); - // 0x0020, 0x000e (SeriesInstanceUID) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str(), + image->SetProperty(GetNodeIDDICOMProperty().c_str(), StringProperty::New(UIDGeneratorBoost::GenerateUID())); - - // 0x0008, 0x0022 (AcquisitionDate) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str(), + image->SetProperty(GetDateDICOMProperty().c_str(), StringProperty::New("20190101")); - // 0x0008, 0x0060 (Modality) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), + image->SetProperty(GetModalityDICOMProperty().c_str(), StringProperty::New("CT")); // create new data node and add image as base data DataNode::Pointer dataNode = DataNode::New(); dataNode->SetData(image); return dataNode; } mitk::DataNode::Pointer mitk::SemanticRelationsTestHelper::GetPatientOneMRImage() { // create new empty image Image::Pointer image = Image::New(); // set properties of image (base data) - // 0x0010, 0x0010 (PatientName) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str(), + image->SetProperty(GetCaseIDDICOMProperty().c_str(), StringProperty::New("Patient1")); - // 0x0020, 0x000e (SeriesInstanceUID) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str(), + image->SetProperty(GetNodeIDDICOMProperty().c_str(), StringProperty::New(UIDGeneratorBoost::GenerateUID())); - - // 0x0008, 0x0022 (AcquisitionDate) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str(), + image->SetProperty(GetDateDICOMProperty().c_str(), StringProperty::New("20190101")); - // 0x0008, 0x0060 (Modality) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), + image->SetProperty(GetModalityDICOMProperty().c_str(), StringProperty::New("MR")); // create new data node and add image as base data DataNode::Pointer dataNode = DataNode::New(); dataNode->SetData(image); return dataNode; } mitk::DataNode::Pointer mitk::SemanticRelationsTestHelper::GetPatientOneOtherCTImage() { // create new empty image Image::Pointer image = Image::New(); // set properties of image (base data) - // 0x0010, 0x0010 (PatientName) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str(), + image->SetProperty(GetCaseIDDICOMProperty().c_str(), StringProperty::New("Patient1")); - // 0x0020, 0x000e (SeriesInstanceUID) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str(), + image->SetProperty(GetNodeIDDICOMProperty().c_str(), StringProperty::New(UIDGeneratorBoost::GenerateUID())); - - // 0x0008, 0x0022 (AcquisitionDate) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str(), + image->SetProperty(GetDateDICOMProperty().c_str(), StringProperty::New("20190131")); - // 0x0008, 0x0060 (Modality) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), + image->SetProperty(GetModalityDICOMProperty().c_str(), StringProperty::New("CT")); // create new data node and add image as base data DataNode::Pointer dataNode = DataNode::New(); dataNode->SetData(image); return dataNode; } mitk::DataNode::Pointer mitk::SemanticRelationsTestHelper::GetPatientTwoPETImage() { // create new empty image Image::Pointer image = Image::New(); // set properties of image (base data) - // 0x0010, 0x0010 (PatientName) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str(), + image->SetProperty(GetCaseIDDICOMProperty().c_str(), StringProperty::New("Patient2")); - // 0x0020, 0x000e (SeriesInstanceUID) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str(), + image->SetProperty(GetNodeIDDICOMProperty().c_str(), StringProperty::New(UIDGeneratorBoost::GenerateUID())); - - // 0x0008, 0x0022 (AcquisitionDate) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str(), + image->SetProperty(GetDateDICOMProperty().c_str(), StringProperty::New("20180101")); - // 0x0008, 0x0060 (Modality) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), + image->SetProperty(GetModalityDICOMProperty().c_str(), StringProperty::New("PT")); // create new data node and add image as base data DataNode::Pointer dataNode = DataNode::New(); dataNode->SetData(image); return dataNode; } mitk::DataNode::Pointer mitk::SemanticRelationsTestHelper::GetPatientTwoSegmentation() { // create new empty segmentation LabelSetImage::Pointer segmentation = LabelSetImage::New(); - // set properties of image (base data) - // 0x0010, 0x0010 (PatientName) - segmentation->SetProperty(GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str(), - StringProperty::New("Patient2")); - // 0x0020, 0x000e (SeriesInstanceUID) - segmentation->SetProperty(GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str(), - StringProperty::New(UIDGeneratorBoost::GenerateUID())); - - // 0x0008, 0x0022 (AcquisitionDate) - segmentation->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str(), - StringProperty::New("20180101")); - // 0x0008, 0x0060 (Modality) - segmentation->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), - StringProperty::New("PT")); + // set properties of segmentation (base data) + segmentation->SetProperty(GetCaseIDDICOMProperty().c_str(), + TemporoSpatialStringProperty::New("Patient2")); + segmentation->SetProperty(GetNodeIDDICOMProperty().c_str(), + TemporoSpatialStringProperty::New(UIDGeneratorBoost::GenerateUID())); + segmentation->SetProperty(GetDateDICOMProperty().c_str(), + TemporoSpatialStringProperty::New("20180101")); + // segmentation modality is 'SEG' - // create new data node and add image as base data + // create new data node and add segmentation as base data DataNode::Pointer dataNode = DataNode::New(); dataNode->SetData(segmentation); return dataNode; } mitk::DataNode::Pointer mitk::SemanticRelationsTestHelper::GetPatientThreeCTImage() { // create new empty image Image::Pointer image = Image::New(); // set properties of image (base data) - // 0x0010, 0x0010 (PatientName) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str(), + image->SetProperty(GetCaseIDDICOMProperty().c_str(), StringProperty::New("Patient3")); - // 0x0020, 0x000e (SeriesInstanceUID) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str(), + image->SetProperty(GetNodeIDDICOMProperty().c_str(), StringProperty::New(UIDGeneratorBoost::GenerateUID())); - - // 0x0008, 0x0022 (AcquisitionDate) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str(), + image->SetProperty(GetDateDICOMProperty().c_str(), StringProperty::New("20190201")); - // 0x0008, 0x0060 (Modality) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), + image->SetProperty(GetModalityDICOMProperty().c_str(), StringProperty::New("CT")); // create new data node and add image as base data DataNode::Pointer dataNode = DataNode::New(); dataNode->SetData(image); return dataNode; } mitk::DataNode::Pointer mitk::SemanticRelationsTestHelper::GetPatientThreeCTSegmentation() { // create new empty segmentation LabelSetImage::Pointer segmentation = LabelSetImage::New(); // set properties of segmentation (base data) - // 0x0010, 0x0010 (PatientName) - segmentation->SetProperty(GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str(), - StringProperty::New("Patient3")); - // 0x0020, 0x000e (SeriesInstanceUID) - segmentation->SetProperty(GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str(), - StringProperty::New(UIDGeneratorBoost::GenerateUID())); - - // 0x0008, 0x0022 (AcquisitionDate) - segmentation->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str(), - StringProperty::New("20190201")); - // 0x0008, 0x0060 (Modality) - segmentation->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), - StringProperty::New("CT")); + segmentation->SetProperty(GetCaseIDDICOMProperty().c_str(), + TemporoSpatialStringProperty::New("Patient3")); + segmentation->SetProperty(GetNodeIDDICOMProperty().c_str(), + TemporoSpatialStringProperty::New(UIDGeneratorBoost::GenerateUID())); + segmentation->SetProperty(GetDateDICOMProperty().c_str(), + TemporoSpatialStringProperty::New("20190201")); + // segmentation modality is 'SEG' // create new data node and add segmentation as base data DataNode::Pointer dataNode = DataNode::New(); dataNode->SetData(segmentation); return dataNode; } mitk::DataNode::Pointer mitk::SemanticRelationsTestHelper::GetPatientThreeMRImage() { // create new empty image Image::Pointer image = Image::New(); // set properties of image (base data) - // 0x0010, 0x0010 (PatientName) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str(), + image->SetProperty(GetCaseIDDICOMProperty().c_str(), StringProperty::New("Patient3")); - // 0x0020, 0x000e (SeriesInstanceUID) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str(), + image->SetProperty(GetNodeIDDICOMProperty().c_str(), StringProperty::New(UIDGeneratorBoost::GenerateUID())); - - // 0x0008, 0x0022 (AcquisitionDate) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str(), + image->SetProperty(GetDateDICOMProperty().c_str(), StringProperty::New("20190215")); - // 0x0008, 0x0060 (Modality) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), + image->SetProperty(GetModalityDICOMProperty().c_str(), StringProperty::New("MR")); // create new data node and add image as base data DataNode::Pointer dataNode = DataNode::New(); dataNode->SetData(image); return dataNode; } mitk::DataNode::Pointer mitk::SemanticRelationsTestHelper::GetPatientThreeMRSegmentation() { // create new empty segmentation LabelSetImage::Pointer segmentation = LabelSetImage::New(); // set properties of segmentation (base data) - // 0x0010, 0x0010 (PatientName) - segmentation->SetProperty(GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str(), - StringProperty::New("Patient3")); - // 0x0020, 0x000e (SeriesInstanceUID) - segmentation->SetProperty(GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str(), - StringProperty::New(UIDGeneratorBoost::GenerateUID())); - - // 0x0008, 0x0022 (AcquisitionDate) - segmentation->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str(), - StringProperty::New("20190215")); - // 0x0008, 0x0060 (Modality) - segmentation->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), - StringProperty::New("MR")); + segmentation->SetProperty(GetCaseIDDICOMProperty().c_str(), + TemporoSpatialStringProperty::New("Patient3")); + segmentation->SetProperty(GetNodeIDDICOMProperty().c_str(), + TemporoSpatialStringProperty::New(UIDGeneratorBoost::GenerateUID())); + segmentation->SetProperty(GetDateDICOMProperty().c_str(), + TemporoSpatialStringProperty::New("20190215")); + // segmentation modality is 'SEG' // create new data node and add segmentation as base data DataNode::Pointer dataNode = DataNode::New(); dataNode->SetData(segmentation); return dataNode; } mitk::DataNode::Pointer mitk::SemanticRelationsTestHelper::GetInvalidDate() { // create new empty image Image::Pointer image = Image::New(); // set properties of image (base data) - // 0x0010, 0x0010 (PatientName) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str(), + image->SetProperty(GetCaseIDDICOMProperty().c_str(), StringProperty::New("Patient4")); - // 0x0020, 0x000e (SeriesInstanceUID) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str(), + image->SetProperty(GetNodeIDDICOMProperty().c_str(), StringProperty::New(UIDGeneratorBoost::GenerateUID())); - // no DICOM information for 0x0008, 0x0022 (AcquisitionDate) + // no DICOM information for Date - // 0x0008, 0x0060 (Modality) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), + image->SetProperty(GetModalityDICOMProperty().c_str(), StringProperty::New("CT")); // create new data node and add image as base data DataNode::Pointer dataNode = DataNode::New(); dataNode->SetData(image); return dataNode; } mitk::DataNode::Pointer mitk::SemanticRelationsTestHelper::GetInvalidModality() { // create new empty image Image::Pointer image = Image::New(); // set properties of image (base data) - // 0x0010, 0x0010 (PatientName) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str(), + image->SetProperty(GetCaseIDDICOMProperty().c_str(), StringProperty::New("Patient5")); - // 0x0020, 0x000e (SeriesInstanceUID) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str(), + image->SetProperty(GetNodeIDDICOMProperty().c_str(), StringProperty::New(UIDGeneratorBoost::GenerateUID())); - - // 0x0008, 0x0022 (AcquisitionDate) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str(), + image->SetProperty(GetDateDICOMProperty().c_str(), StringProperty::New("20180101")); - // no DICOM information for 0x0008, 0x0060 (Modality) + // no DICOM information for Modality // create new data node and add image as base data DataNode::Pointer dataNode = DataNode::New(); dataNode->SetData(image); return dataNode; } mitk::DataNode::Pointer mitk::SemanticRelationsTestHelper::GetInvalidID() { // create new empty image Image::Pointer image = Image::New(); // set properties of image (base data) - // 0x0010, 0x0010 (PatientName) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str(), + image->SetProperty(GetCaseIDDICOMProperty().c_str(), StringProperty::New("Patient6")); - // no DICOM information for 0x0020, 0x000e (SeriesInstanceUID) + // no DICOM information for NodeID - // 0x0008, 0x0022 (AcquisitionDate) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str(), + image->SetProperty(GetDateDICOMProperty().c_str(), StringProperty::New("20180101")); - - // 0x0008, 0x0060 (Modality) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), + image->SetProperty(GetModalityDICOMProperty().c_str(), StringProperty::New("CT")); // create new data node and add image as base data DataNode::Pointer dataNode = DataNode::New(); dataNode->SetData(image); return dataNode; } mitk::DataNode::Pointer mitk::SemanticRelationsTestHelper::GetInvalidCaseID() { // create new empty image Image::Pointer image = Image::New(); // set properties of image (base data) - // // no DICOM information for 0x0010, 0x0010 (PatientName) + // no DICOM information for CaseID - // 0x0020, 0x000e (SeriesInstanceUID) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str(), + image->SetProperty(GetNodeIDDICOMProperty().c_str(), StringProperty::New(UIDGeneratorBoost::GenerateUID())); - - // 0x0008, 0x0022 (AcquisitionDate) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str(), + image->SetProperty(GetDateDICOMProperty().c_str(), StringProperty::New("20180101")); - - // 0x0008, 0x0060 (Modality) - image->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), + image->SetProperty(GetModalityDICOMProperty().c_str(), StringProperty::New("CT")); // create new data node and add image as base data DataNode::Pointer dataNode = DataNode::New(); dataNode->SetData(image); return dataNode; } void mitk::SemanticRelationsTestHelper::ClearRelationStorage() { // access the storage PERSISTENCE_GET_SERVICE_MACRO if (nullptr == persistenceService) { MITK_DEBUG << "Persistence service could not be loaded"; return; } auto allCaseIDs = mitk::RelationStorage::GetAllCaseIDs(); for (auto& caseID : allCaseIDs) { persistenceService->RemovePropertyList(caseID); } std::string listIdentifier("caseIDs"); persistenceService->RemovePropertyList(listIdentifier); } diff --git a/Modules/SemanticRelations/include/mitkDICOMHelper.h b/Modules/SemanticRelations/include/mitkDICOMHelper.h index 082ad69e0f..9f99908c0c 100644 --- a/Modules/SemanticRelations/include/mitkDICOMHelper.h +++ b/Modules/SemanticRelations/include/mitkDICOMHelper.h @@ -1,110 +1,130 @@ /*=================================================================== 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 MITKDICOMHELPER_H #define MITKDICOMHELPER_H #include // semantic relations module #include "mitkSemanticTypes.h" // mitk core #include #include // c++ #include /* * @brief Provides helper functions to convert DICOM Tag information. * * In order to identify the patient of an image or segmentation or to set the control point of DICOM data, * these functions are used to retrieve the DICOM tags from the given data nodes and convert them into semantic types * that can be used by the SemanticRelations class. */ namespace mitk { + /** + * @brief Creates a property name for a DICOM tag. + * The tag is "0x0010, 0x0010" (PatientName) + */ + MITKSEMANTICRELATIONS_EXPORT std::string GetCaseIDDICOMProperty(); + /** + * @brief Creates a property name for a DICOM tag. + * The tag is "0x0020, 0x000e" (SeriesInstanceUID) + */ + MITKSEMANTICRELATIONS_EXPORT std::string GetNodeIDDICOMProperty(); + /** + * @brief Creates a property name for a DICOM tag. + * The tag is "0x0008, 0x0022" (AcquisitionDate) + */ + MITKSEMANTICRELATIONS_EXPORT std::string GetDateDICOMProperty(); + /** + * @brief Creates a property name for a DICOM tag. + * The tag is "0x0008, 0x0060" (Modality) + */ + MITKSEMANTICRELATIONS_EXPORT std::string GetModalityDICOMProperty(); /* * @brief Extracts a specific DICOM tag from the node's base data * and returns the tag as a string. This tag string is used as an identifier for the patient (case). * * @pre The given data node or the node's base data has to be valid (!nullptr). * @pre The node's base data has to have the specific DICOM Tag property set. * @throw mitk::Exception if the given data node, the node's base data or the extracted DICOM tag are invalid (==nullptr). * * @par dataNode The data node, of which the DICOM tag should be extracted. * * @return The extracted DICOM tag as string. * An empty string, if the DICOM tag can not be extracted (i.e. the data node or * the underlying base data is invalid or the DICOM tag does not exist for the given data node). */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::CaseID GetCaseIDFromDataNode(const mitk::DataNode* dataNode); /* * @brief Extracts a specific DICOM tag (currently "0x0020, 0x000e": SeriesInstanceUID) from the node's base data * and returns the tag as a string. This tag string is used as an identifier for the image instance. * * @pre The given data node or the node's base data has to be valid (!nullptr). * @pre The node's base data has to have the "0x0020, 0x000e" DICOM Tag property set. * @throw mitk::Exception if the given data node, the node's base data or the extracted DICOM tag are invalid (==nullptr). * * @par dataNode The data node, of which the DICOM tag should be extracted. * * @return The extracted DICOM tag as string. * An empty string, if the DICOM tag can not be extracted (i.e. the data node or * the underlying base data is invalid or the DICOM tag does not exist for the given data node). */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ID GetIDFromDataNode(const mitk::DataNode* dataNode); /* * @brief Extracts a specific DICOM tag (currently "0x0008, 0x0022": AcquisitionDate) from the node's base data * and returns the tag as a control point. * * @pre The given data node or the node's base data has to be valid (!nullptr). * @pre The node's base data has to have the "0x0008, 0x0022" DICOM Tag property set. * @throw mitk::Exception if the given data node, the node's base data or the extracted DICOM tag are invalid (==nullptr). * * @par dataNode The data node, of which the DICOM tag should be extracted. * * @return The extracted DICOM tag as control point. * An empty control point, if the DICOM tag can not be extracted (i.e. the data node or * the underlying base data is invalid or the DICOM tag does not exist for the given data node). */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint GetDICOMDateFromDataNode(const mitk::DataNode* dataNode); /** * @brief Extracts a specific DICOM tag from the node's base data and returns the tag as a information type (a string). * * @pre The given data node or the node's base data has to be valid (!nullptr). * @pre The node's base data has to have the "0x0008, 0x0060" DICOM Tag property set. * @throw mitk::Exception if the given data node, the node's base data or the extracted DICOM tag are invalid (==nullptr). * * @par dataNode The data node, of which the DICOM tag should be extracted. * * @return The extracted DICOM tag as information type (a string). * An empty information type, if the DICOM tag can not be extracted (i.e. the data node or * the underlying base data is invalid or the DICOM tag does not exist for the given data node). */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::InformationType GetDICOMModalityFromDataNode(const mitk::DataNode* dataNode); /* * @brief Removes leading and trailing whitespace from the given string. * * @par identifier The value of a DICOM tag. * * @return The trimmed DICOM tag */ MITKSEMANTICRELATIONS_EXPORT std::string TrimDICOM(const std::string& identifier); } // namespace mitk #endif // MITKDICOMHELPER_H diff --git a/Modules/SemanticRelations/src/mitkControlPointManager.cpp b/Modules/SemanticRelations/src/mitkControlPointManager.cpp index 16dc2a314b..98f1486379 100644 --- a/Modules/SemanticRelations/src/mitkControlPointManager.cpp +++ b/Modules/SemanticRelations/src/mitkControlPointManager.cpp @@ -1,170 +1,179 @@ /*=================================================================== 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 "mitkSemanticRelationException.h" #include "mitkUIDGeneratorBoost.h" // mitk core #include mitk::SemanticTypes::ControlPoint mitk::GenerateControlPoint(const DataNode* datanode) { - SemanticTypes::ControlPoint controlPoint = GetDICOMDateFromDataNode(datanode); - controlPoint.UID = UIDGeneratorBoost::GenerateUID(); + SemanticTypes::ControlPoint controlPoint; + try + { + controlPoint = GetDICOMDateFromDataNode(datanode); + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot generate a control point from the DICOM tag of the given data node"; + } + controlPoint.UID = UIDGeneratorBoost::GenerateUID(); return controlPoint; } mitk::SemanticTypes::ControlPoint mitk::GetControlPointByUID(const SemanticTypes::ID& controlPointUID, const std::vector& allControlPoints) { auto lambda = [&controlPointUID](const SemanticTypes::ControlPoint& currentControlPoint) { return currentControlPoint.UID == controlPointUID; }; const auto existingControlPoint = std::find_if(allControlPoints.begin(), allControlPoints.end(), lambda); mitk::SemanticTypes::ControlPoint controlPoint; if (existingControlPoint != allControlPoints.end()) { controlPoint = *existingControlPoint; } return controlPoint; } mitk::SemanticTypes::ControlPoint mitk::FindExistingControlPoint(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ControlPointVector& allControlPoints) { for (const auto& currentControlPoint : allControlPoints) { if (controlPoint.date == currentControlPoint.date) { return currentControlPoint; } } return SemanticTypes::ControlPoint(); } mitk::SemanticTypes::ControlPoint mitk::FindClosestControlPoint(const SemanticTypes::ControlPoint& controlPoint, SemanticTypes::ControlPointVector& allControlPoints) { if (allControlPoints.empty()) { return SemanticTypes::ControlPoint(); } // sort the vector of control points for easier lookup std::sort(allControlPoints.begin(), allControlPoints.end()); // new control point does not match an existing control point // check if the control point is close to an already existing control point std::vector::const_iterator it; for (it = allControlPoints.begin(); it != allControlPoints.end(); ++it) { if (controlPoint.date < it->date) { break; } } SemanticTypes::ControlPoint nextControlPoint; SemanticTypes::ControlPoint previousControlPoint; if (it == allControlPoints.begin()) { // new date is smaller ("older") than the smallest already existing control point nextControlPoint = *it; } else if (it != allControlPoints.end()) { // new date is greater ("newer") than an already existing control point, // but smaller ("older") than another already existing control point nextControlPoint = *it; previousControlPoint = *(--it); } else { // new date is greater ("newer") than the greatest already existing control point previousControlPoint = *(--it); } // test distance to next and previous time period double distanceToNextExaminationPeriod = nextControlPoint.DistanceInDays(controlPoint); double distanceToPreviousExaminationPeriod = previousControlPoint.DistanceInDays(controlPoint); SemanticTypes::ControlPoint closestControlPoint; int closestDistanceInDays = 0; if (distanceToNextExaminationPeriod < distanceToPreviousExaminationPeriod) { // control point is closer to the next control point closestControlPoint = nextControlPoint; closestDistanceInDays = distanceToNextExaminationPeriod; } else { // control point is closer to the previous control point closestControlPoint = previousControlPoint; closestDistanceInDays = distanceToPreviousExaminationPeriod; } int THRESHOLD_DISTANCE_IN_DAYS = 30; if (closestDistanceInDays <= THRESHOLD_DISTANCE_IN_DAYS) { return closestControlPoint; } return SemanticTypes::ControlPoint(); } mitk::SemanticTypes::ExaminationPeriod mitk::FindExaminationPeriod(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriodVector& allExaminationPeriods) { 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.date < rightControlPoint.date; }; std::sort(allExaminationPeriods.begin(), allExaminationPeriods.end(), lambda); } diff --git a/Modules/SemanticRelations/src/mitkDICOMHelper.cpp b/Modules/SemanticRelations/src/mitkDICOMHelper.cpp index aeb41fdc8b..7be974ef13 100644 --- a/Modules/SemanticRelations/src/mitkDICOMHelper.cpp +++ b/Modules/SemanticRelations/src/mitkDICOMHelper.cpp @@ -1,170 +1,190 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // semantic relations module #include "mitkDICOMHelper.h" #include "mitkSemanticRelationException.h" #include "mitkUIDGeneratorBoost.h" // mitk core #include // c++ #include mitk::SemanticTypes::ControlPoint GetControlPointFromString(const std::string& dateAsString) { // date expected to be YYYYMMDD (8 characters) if (dateAsString.size() != 8) { // string does not represent a DICOM date mitkThrowException(mitk::SemanticRelationException) << "Not a valid DICOM date format."; } mitk::SemanticTypes::ControlPoint controlPoint; controlPoint.SetDateFromString(dateAsString); return controlPoint; } +std::string mitk::GetCaseIDDICOMProperty() +{ + // extract suitable DICOM tag to use as the case id + // two alternatives can be used: + // - DICOM tag "0x0010, 0x0010" is PatientName + // - DICOM tag "0x0010, 0x0020" is PatientID + // in the current implementation the PatientName (0x0010, 0x0010) is used + return GeneratePropertyNameForDICOMTag(0x0010, 0x0010); +} + +std::string mitk::GetNodeIDDICOMProperty() +{ + // extract suitable DICOM tag to use as the data node id + // DICOM tag "0x0020, 0x000e" is SeriesInstanceUID + return GeneratePropertyNameForDICOMTag(0x0020, 0x000e); +} + +std::string mitk::GetDateDICOMProperty() +{ + // extract suitable DICOM tag to use as the data node id + // DICOM tag "0x0008, 0x0022" is AcquisitionDate + return GeneratePropertyNameForDICOMTag(0x0008, 0x0022); +} + +std::string mitk::GetModalityDICOMProperty() +{ + // extract suitable DICOM tag to use as the information type + // DICOM tag "0x0008, 0x0060" is Modality + return GeneratePropertyNameForDICOMTag(0x0008, 0x0060); +} + mitk::SemanticTypes::CaseID mitk::GetCaseIDFromDataNode(const mitk::DataNode* dataNode) { 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()); + mitk::BaseProperty* dicomTag = baseData->GetProperty(GetCaseIDDICOMProperty().c_str()); if (nullptr == dicomTag) { mitkThrowException(SemanticRelationException) << "Not a valid DICOM property."; } std::string dicomTagAsString = dicomTag->GetValueAsString(); return dicomTagAsString; } mitk::SemanticTypes::ID mitk::GetIDFromDataNode(const mitk::DataNode* dataNode) { 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()); + mitk::BaseProperty* dicomTag = baseData->GetProperty(GetNodeIDDICOMProperty().c_str()); if (nullptr == dicomTag) { mitkThrowException(SemanticRelationException) << "Not a valid DICOM property."; } std::string dicomTagAsString = dicomTag->GetValueAsString(); return dicomTagAsString; } mitk::SemanticTypes::ControlPoint mitk::GetDICOMDateFromDataNode(const mitk::DataNode* dataNode) { 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()); + mitk::BaseProperty* acquisitionDateProperty = baseData->GetProperty(GetDateDICOMProperty().c_str()); if (nullptr == acquisitionDateProperty) { mitkThrowException(SemanticRelationException) << "Not a valid DICOM property."; } std::string acquisitionDateAsString = acquisitionDateProperty->GetValueAsString(); SemanticTypes::ControlPoint controlPoint; try { controlPoint = GetControlPointFromString(acquisitionDateAsString); } catch (SemanticRelationException &e) { mitkReThrow(e) << "Cannot retrieve a valid DICOM date."; } return controlPoint; } mitk::SemanticTypes::InformationType mitk::GetDICOMModalityFromDataNode(const mitk::DataNode* dataNode) { 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()); + mitk::BaseProperty* dicomTag = baseData->GetProperty(GetModalityDICOMProperty().c_str()); if (nullptr == dicomTag) { mitkThrowException(SemanticRelationException) << "Not a valid DICOM property."; } std::string dicomTagAsString = dicomTag->GetValueAsString(); return dicomTagAsString; } std::string mitk::TrimDICOM(const std::string& identifier) { if (identifier.empty()) { return identifier; } // leading whitespace std::size_t first = identifier.find_first_not_of(' '); if (std::string::npos == first) { return ""; } // trailing whitespace std::size_t last = identifier.find_last_not_of(' '); return identifier.substr(first, last - first + 1); } diff --git a/Modules/SemanticRelations/src/mitkLesionManager.cpp b/Modules/SemanticRelations/src/mitkLesionManager.cpp index 12af5b9253..a9e83d88e2 100644 --- a/Modules/SemanticRelations/src/mitkLesionManager.cpp +++ b/Modules/SemanticRelations/src/mitkLesionManager.cpp @@ -1,120 +1,123 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // semantic relations module #include "mitkLesionManager.h" #include "mitkSemanticRelationException.h" #include "mitkSemanticRelationsInference.h" #include "mitkRelationStorage.h" #include "mitkUIDGeneratorBoost.h" double GetLesionVolume(const mitk::SemanticTypes::CaseID& caseID, const mitk::SemanticTypes::Lesion& lesion, const mitk::SemanticTypes::ControlPoint& controlPoint); mitk::SemanticTypes::Lesion mitk::GenerateNewLesion(const std::string& lesionClassType/* = ""*/) { SemanticTypes::Lesion lesion; lesion.UID = mitk::UIDGeneratorBoost::GenerateUID(); lesion.name = "New lesion"; lesion.lesionClass = GenerateNewLesionClass(lesionClassType); return lesion; } mitk::SemanticTypes::LesionClass mitk::GenerateNewLesionClass(const std::string& lesionClassType/* = ""*/) { SemanticTypes::LesionClass lesionClass; lesionClass.UID = mitk::UIDGeneratorBoost::GenerateUID(); lesionClass.classType = lesionClassType; return lesionClass; } mitk::SemanticTypes::Lesion mitk::GetLesionByUID(const SemanticTypes::ID& lesionUID, const std::vector& allLesions) { auto lambda = [&lesionUID](const SemanticTypes::Lesion& currentLesion) { return currentLesion.UID == lesionUID; }; const auto existingLesion = std::find_if(allLesions.begin(), allLesions.end(), lambda); SemanticTypes::Lesion lesion; if (existingLesion != allLesions.end()) { lesion = *existingLesion; } return lesion; } mitk::SemanticTypes::LesionClass mitk::FindExistingLesionClass(const std::string& lesionClassType, const std::vector& allLesionClasses) { auto lambda = [&lesionClassType](const SemanticTypes::LesionClass& currentLesionClass) { return currentLesionClass.classType == lesionClassType; }; const auto existingLesionClass = std::find_if(allLesionClasses.begin(), allLesionClasses.end(), lambda); SemanticTypes::LesionClass lesionClass; if (existingLesionClass != allLesionClasses.end()) { lesionClass = *existingLesionClass; } return lesionClass; } void mitk::GenerateAdditionalLesionData(LesionData& lesionData, const SemanticTypes::CaseID& caseID) { std::vector lesionPresence; std::vector lesionVolume; SemanticTypes::Lesion lesion = lesionData.GetLesion(); bool presence = false; double volume = 0.0; - try + + SemanticTypes::ControlPointVector controlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); + // sort the vector of control points for the timeline + std::sort(controlPoints.begin(), controlPoints.end()); + for (const auto& controlPoint : controlPoints) { - SemanticTypes::ControlPointVector controlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); - for (const auto& controlPoint : controlPoints) + try { presence = SemanticRelationsInference::IsLesionPresentAtControlPoint(caseID, lesion, controlPoint); - lesionPresence.push_back(presence); - - volume = GetLesionVolume(caseID, lesion, controlPoint); - lesionVolume.push_back(volume); } - } - catch (const SemanticRelationException&) - { - return; + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot determine the lesion presence for generating additional lesion data."; + } + + lesionPresence.push_back(presence); + volume = GetLesionVolume(caseID, lesion, controlPoint); + lesionVolume.push_back(volume); } lesionData.SetLesionPresence(lesionPresence); lesionData.SetLesionVolume(lesionVolume); } double GetLesionVolume(const mitk::SemanticTypes::CaseID& caseID, const mitk::SemanticTypes::Lesion& lesion, const mitk::SemanticTypes::ControlPoint& controlPoint) { bool presence = mitk::SemanticRelationsInference::IsLesionPresentAtControlPoint(caseID, lesion, controlPoint); if (presence) { return 1.0; } else { return 0.0; } } diff --git a/Modules/SemanticRelations/src/mitkSemanticRelationsDataStorageAccess.cpp b/Modules/SemanticRelations/src/mitkSemanticRelationsDataStorageAccess.cpp index b02e89d69e..e020f5706b 100644 --- a/Modules/SemanticRelations/src/mitkSemanticRelationsDataStorageAccess.cpp +++ b/Modules/SemanticRelations/src/mitkSemanticRelationsDataStorageAccess.cpp @@ -1,218 +1,236 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSemanticRelationsDataStorageAccess.h" // semantic relations module #include "mitkControlPointManager.h" #include "mitkDICOMHelper.h" #include "mitkNodePredicates.h" #include "mitkRelationStorage.h" #include "mitkSemanticRelationException.h" #include "mitkSemanticRelationsInference.h" // c++ #include #include mitk::SemanticRelationsDataStorageAccess::SemanticRelationsDataStorageAccess(DataStorage* dataStorage) : m_DataStorage(dataStorage) { // nothing here } mitk::SemanticRelationsDataStorageAccess::~SemanticRelationsDataStorageAccess() { // nothing here } /************************************************************************/ /* functions to get instances / attributes */ /************************************************************************/ mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllSegmentationsOfCase(const SemanticTypes::CaseID& caseID) const { if (m_DataStorage.IsExpired()) { mitkThrowException(SemanticRelationException) << "Not a valid data storage."; } std::vector allSegmentationIDsOfCase = RelationStorage::GetAllSegmentationIDsOfCase(caseID); std::vector allSegmentationsOfCase; // get all segmentation nodes of the current data storage // only those nodes are respected, that are currently held in the data storage DataStorage::SetOfObjects::ConstPointer segmentationNodes = m_DataStorage.Lock()->GetSubset(NodePredicates::GetSegmentationPredicate()); for (auto it = segmentationNodes->Begin(); it != segmentationNodes->End(); ++it) { DataNode* segmentationNode = it->Value(); + + std::string caseID; + std::string segmentationID; 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); - } + caseID = GetCaseIDFromDataNode(segmentationNode); + segmentationID = GetIDFromDataNode(segmentationNode); } - catch (const std::exception&) + catch (SemanticRelationException&) { // found a segmentation node that is not stored in the semantic relations // this segmentation node does not have any DICOM information --> exception thrown // continue with the next segmentation to compare IDs continue; } + + if (caseID == caseID && (std::find(allSegmentationIDsOfCase.begin(), allSegmentationIDsOfCase.end(), segmentationID) != allSegmentationIDsOfCase.end())) + { + // found current image node in the storage, add it to the return vector + allSegmentationsOfCase.push_back(segmentationNode); + } } return allSegmentationsOfCase; } mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllSegmentationsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const { if (SemanticRelationsInference::InstanceExists(caseID, lesion)) { // lesion exists, retrieve all case segmentations from the storage DataNodeVector allSegmentationsOfLesion = GetAllSegmentationsOfCase(caseID); // filter all segmentations: check for semantic relation with the given lesion using a lambda function auto lambda = [&lesion, this](DataNode::Pointer segmentation) { try { SemanticTypes::Lesion representedLesion = SemanticRelationsInference::GetLesionOfSegmentation(segmentation); return lesion.UID != representedLesion.UID; } catch (const SemanticRelationException&) { return true; } }; allSegmentationsOfLesion.erase(std::remove_if(allSegmentationsOfLesion.begin(), allSegmentationsOfLesion.end(), lambda), allSegmentationsOfLesion.end()); return allSegmentationsOfLesion; } else { mitkThrowException(SemanticRelationException) << "Could not find an existing lesion instance for the given caseID " << caseID << " and lesion " << lesion.UID << "."; } } mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllImagesOfCase(const SemanticTypes::CaseID& caseID) const { if (m_DataStorage.IsExpired()) { mitkThrowException(SemanticRelationException) << "Not a valid data storage."; } std::vector allImageIDsOfCase = RelationStorage::GetAllImageIDsOfCase(caseID); std::vector allImagesOfCase; // get all image nodes of the current data storage // only those nodes are respected, that are currently held in the data storage DataStorage::SetOfObjects::ConstPointer imageNodes = m_DataStorage.Lock()->GetSubset(NodePredicates::GetImagePredicate()); for (auto it = imageNodes->Begin(); it != imageNodes->End(); ++it) { DataNode* imageNode = it->Value(); - // find the corresponding image node for the given segmentation ID - std::string nodeCaseID = GetCaseIDFromDataNode(imageNode); - std::string nodeImageID = GetIDFromDataNode(imageNode); - if (nodeCaseID == caseID && (std::find(allImageIDsOfCase.begin(), allImageIDsOfCase.end(), nodeImageID) != allImageIDsOfCase.end())) + + std::string caseID; + std::string imageID; + try + { + // find the corresponding image node for the given segmentation ID + caseID = GetCaseIDFromDataNode(imageNode); + imageID = GetIDFromDataNode(imageNode); + } + catch (SemanticRelationException&) + { + // found an image node that is not stored in the semantic relations + // this image node does not have any DICOM information --> exception thrown + // continue with the next image to compare IDs + continue; + } + + if (caseID == caseID && (std::find(allImageIDsOfCase.begin(), allImageIDsOfCase.end(), imageID) != allImageIDsOfCase.end())) { // found current image node in the storage, add it to the return vector allImagesOfCase.push_back(imageNode); } } return allImagesOfCase; } mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllImagesOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const { if (m_DataStorage.IsExpired()) { mitkThrowException(SemanticRelationException) << "Not a valid data storage."; } DataNodeVector allImagesOfLesion; // 1. get all segmentations that define the lesion // 2. retrieve the parent node (source) of the found segmentation node DataNodeVector allSegmentationsOfLesion = GetAllSegmentationsOfLesion(caseID, lesion); for (const auto& segmentationNode : allSegmentationsOfLesion) { // get parent node of the current segmentation node with the node predicate DataStorage::SetOfObjects::ConstPointer parentNodes = m_DataStorage.Lock()->GetSources(segmentationNode, NodePredicates::GetImagePredicate(), false); for (auto it = parentNodes->Begin(); it != parentNodes->End(); ++it) { DataNode::Pointer dataNode = it->Value(); allImagesOfLesion.push_back(it->Value()); } } std::sort(allImagesOfLesion.begin(), allImagesOfLesion.end()); allImagesOfLesion.erase(std::unique(allImagesOfLesion.begin(), allImagesOfLesion.end()), allImagesOfLesion.end()); return allImagesOfLesion; } mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllSpecificImages(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const { if (SemanticRelationsInference::InstanceExists(caseID, controlPoint)) { if (SemanticRelationsInference::InstanceExists(caseID, informationType)) { // control point exists, information type exists, retrieve all images from the storage DataNodeVector allImagesOfCase = GetAllImagesOfCase(caseID); // filter all images to remove the ones with a different control point and information type using a lambda function auto lambda = [&controlPoint, &informationType, this](DataNode::Pointer imageNode) { return (informationType != SemanticRelationsInference::GetInformationTypeOfImage(imageNode)) || (controlPoint.date != SemanticRelationsInference::GetControlPointOfImage(imageNode).date); }; allImagesOfCase.erase(std::remove_if(allImagesOfCase.begin(), allImagesOfCase.end(), lambda), allImagesOfCase.end()); return allImagesOfCase; } else { mitkThrowException(SemanticRelationException) << "Could not find an existing information type for the given caseID " << caseID << " and information type " << informationType; } } else { mitkThrowException(SemanticRelationException) << "Could not find an existing control point for the given caseID " << caseID << " and control point " << controlPoint.UID; } } mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllSpecificSegmentations(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const { if (m_DataStorage.IsExpired()) { mitkThrow() << "Not a valid data storage."; } DataNodeVector allSpecificImages = GetAllSpecificImages(caseID, controlPoint, informationType); DataNodeVector allSpecificSegmentations; for (const auto& imageNode : allSpecificImages) { DataStorage::SetOfObjects::ConstPointer segmentationNodes = m_DataStorage.Lock()->GetDerivations(imageNode, NodePredicates::GetSegmentationPredicate(), false); for (auto it = segmentationNodes->Begin(); it != segmentationNodes->End(); ++it) { allSpecificSegmentations.push_back(it->Value()); } } return allSpecificSegmentations; } diff --git a/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp b/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp index f8919d2c8d..21dfdb3301 100644 --- a/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp +++ b/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp @@ -1,479 +1,528 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSemanticRelationsInference.h" // semantic relations module #include "mitkControlPointManager.h" #include "mitkDICOMHelper.h" #include "mitkNodePredicates.h" #include "mitkRelationStorage.h" #include "mitkSemanticRelationException.h" /************************************************************************/ /* functions to get instances / attributes */ /************************************************************************/ mitk::SemanticTypes::LesionClassVector mitk::SemanticRelationsInference::GetAllLesionClassesOfCase(const SemanticTypes::CaseID& caseID) { SemanticTypes::LesionVector allLesionsOfCase = RelationStorage::GetAllLesionsOfCase(caseID); SemanticTypes::LesionClassVector allLesionClassesOfCase; for (const auto& lesion : allLesionsOfCase) { allLesionClassesOfCase.push_back(lesion.lesionClass); } // remove duplicate entries auto lessThan = [](const SemanticTypes::LesionClass& lesionClassLeft, const SemanticTypes::LesionClass& lesionClassRight) { return lesionClassLeft.UID < lesionClassRight.UID; }; auto equal = [](const SemanticTypes::LesionClass& lesionClassLeft, const SemanticTypes::LesionClass& lesionClassRight) { return lesionClassLeft.UID == lesionClassRight.UID; }; std::sort(allLesionClassesOfCase.begin(), allLesionClassesOfCase.end(), lessThan); allLesionClassesOfCase.erase(std::unique(allLesionClassesOfCase.begin(), allLesionClassesOfCase.end(), equal), allLesionClassesOfCase.end()); return allLesionClassesOfCase; } mitk::SemanticTypes::Lesion mitk::SemanticRelationsInference::GetLesionOfSegmentation(const DataNode* segmentationNode) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); - SemanticTypes::ID segmentationID = GetIDFromDataNode(segmentationNode); + SemanticTypes::CaseID caseID = ""; + SemanticTypes::ID segmentationID = ""; + try + { + caseID = GetCaseIDFromDataNode(segmentationNode); + segmentationID = GetIDFromDataNode(segmentationNode); + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot get the lesion of the given segmentation data node."; + } + return RelationStorage::GetLesionOfSegmentation(caseID, segmentationID); } mitk::SemanticTypes::LesionVector mitk::SemanticRelationsInference::GetAllLesionsOfImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); - SemanticTypes::ID imageID = GetIDFromDataNode(imageNode); + SemanticTypes::CaseID caseID = ""; + SemanticTypes::ID imageID = ""; + try + { + caseID = GetCaseIDFromDataNode(imageNode); + imageID = GetIDFromDataNode(imageNode); + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot get all lesions of the given image data node."; + } SemanticTypes::LesionVector allLesionsOfImage; // 1. get all segmentations that are connected to the given image // 2. get the lesion of each segmentation // 3. guarantee uniqueness of lesions SemanticTypes::IDVector allSegmentationIDsOfImage = RelationStorage::GetAllSegmentationIDsOfImage(caseID, imageID); for (const auto& segmentationID : allSegmentationIDsOfImage) { // get represented lesion of the current segmentation SemanticTypes::Lesion representedLesion = RelationStorage::GetLesionOfSegmentation(caseID, segmentationID); if (!representedLesion.UID.empty()) { allLesionsOfImage.push_back(representedLesion); } } // remove duplicate entries auto lessThan = [](const SemanticTypes::Lesion& lesionLeft, const SemanticTypes::Lesion& lesionRight) { return lesionLeft.UID < lesionRight.UID; }; auto equal = [](const SemanticTypes::Lesion& lesionLeft, const SemanticTypes::Lesion& lesionRight) { return lesionLeft.UID == lesionRight.UID; }; std::sort(allLesionsOfImage.begin(), allLesionsOfImage.end(), lessThan); allLesionsOfImage.erase(std::unique(allLesionsOfImage.begin(), allLesionsOfImage.end(), equal), allLesionsOfImage.end()); return allLesionsOfImage; } mitk::SemanticTypes::LesionVector mitk::SemanticRelationsInference::GetAllLesionsOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { SemanticTypes::LesionVector allLesions = RelationStorage::GetAllLesionsOfCase(caseID); // filter the lesions: use only those, where the associated data is connected to image data that refers to the given control point using a lambda function auto lambda = [&caseID, &controlPoint](const SemanticTypes::Lesion& lesion) { return !SpecificImageExists(caseID, lesion, controlPoint); }; allLesions.erase(std::remove_if(allLesions.begin(), allLesions.end(), lambda), allLesions.end()); return allLesions; } bool mitk::SemanticRelationsInference::IsRepresentingALesion(const DataNode* segmentationNode) { + SemanticTypes::Lesion representedLesion; try { - SemanticTypes::Lesion representedLesion = GetLesionOfSegmentation(segmentationNode); - if (representedLesion.UID.empty()) - { - return false; - } + representedLesion = GetLesionOfSegmentation(segmentationNode); } - catch (const Exception&) + catch (const SemanticRelationException&) { return false; } - return true; + return !representedLesion.UID.empty(); } bool mitk::SemanticRelationsInference::IsRepresentingALesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID) { SemanticTypes::Lesion representedLesion = RelationStorage::GetLesionOfSegmentation(caseID, segmentationID); - if (representedLesion.UID.empty()) - { - return false; - } - - return true; + return !representedLesion.UID.empty(); } bool mitk::SemanticRelationsInference::IsLesionPresent(const SemanticTypes::Lesion& lesion, const DataNode* dataNode) { + SemanticTypes::CaseID caseID = ""; + SemanticTypes::ID dataNodeID = ""; try { - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); - SemanticTypes::ID dataNodeID = GetIDFromDataNode(dataNode); - - if (NodePredicates::GetImagePredicate()->CheckNode(dataNode)) - { - return IsLesionPresentOnImage(caseID, lesion, dataNodeID); - } - - if (NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) - { - return IsLesionPresentOnSegmentation(caseID, lesion, dataNodeID); - } - - return false; + caseID = GetCaseIDFromDataNode(dataNode); + dataNodeID = GetIDFromDataNode(dataNode); } catch (const SemanticRelationException&) { return false; } + + if (NodePredicates::GetImagePredicate()->CheckNode(dataNode)) + { + return IsLesionPresentOnImage(caseID, lesion, dataNodeID); + } + + if (NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) + { + return IsLesionPresentOnSegmentation(caseID, lesion, dataNodeID); + } + + return false; } bool mitk::SemanticRelationsInference::IsLesionPresentOnImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ID& imageID) { - SemanticTypes::IDVector allImageIDsOfLesion = GetAllImageIDsOfLesion(caseID, lesion); + SemanticTypes::IDVector allImageIDsOfLesion; + try + { + allImageIDsOfLesion = GetAllImageIDsOfLesion(caseID, lesion); + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot get all image IDs of the given lesion to determine the lesion presence."; + } + for (const auto& imageIDOfLesion : allImageIDsOfLesion) { if (imageIDOfLesion == imageID) { return true; } } return false; } bool mitk::SemanticRelationsInference::IsLesionPresentOnSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ID& segmentationID) { const auto representedLesion = RelationStorage::GetLesionOfSegmentation(caseID, segmentationID); return lesion.UID == representedLesion.UID; } bool mitk::SemanticRelationsInference::IsLesionPresentAtControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ControlPoint& controlPoint) { - SemanticTypes::IDVector allImageIDsOfLesion = GetAllImageIDsOfLesion(caseID, lesion); - for (const auto& imageID : allImageIDsOfLesion) + SemanticTypes::IDVector allImageIDsOfLesion; + try { - auto imageControlPoint = mitk::RelationStorage::GetControlPointOfImage(caseID, imageID); + allImageIDsOfLesion = GetAllImageIDsOfLesion(caseID, lesion); + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot get all image IDs of the given lesion to determine the lesion presence."; + } + + for (const auto& imageIDOfLesion : allImageIDsOfLesion) + { + auto imageControlPoint = mitk::RelationStorage::GetControlPointOfImage(caseID, imageIDOfLesion); if (imageControlPoint.date == controlPoint.date) { return true; } } return false; } bool mitk::SemanticRelationsInference::InstanceExists(const DataNode* dataNode) { + SemanticTypes::CaseID caseID = ""; + SemanticTypes::ID dataNodeID = ""; try { - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); - SemanticTypes::ID dataNodeID = GetIDFromDataNode(dataNode); - - if (NodePredicates::GetImagePredicate()->CheckNode(dataNode)) - { - std::vector allImageIDsOfCase = RelationStorage::GetAllImageIDsOfCase(caseID); - return std::find(allImageIDsOfCase.begin(), allImageIDsOfCase.end(), dataNodeID) != allImageIDsOfCase.end(); - } - else if (NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) - { - std::vector allSegmentationIDsOfCase = RelationStorage::GetAllSegmentationIDsOfCase(caseID); - return std::find(allSegmentationIDsOfCase.begin(), allSegmentationIDsOfCase.end(), dataNodeID) != allSegmentationIDsOfCase.end(); - } - else - { - return false; - } + caseID = GetCaseIDFromDataNode(dataNode); + dataNodeID = GetIDFromDataNode(dataNode); } catch (const SemanticRelationException&) { return false; } + + if (NodePredicates::GetImagePredicate()->CheckNode(dataNode)) + { + std::vector allImageIDsOfCase = RelationStorage::GetAllImageIDsOfCase(caseID); + return std::find(allImageIDsOfCase.begin(), allImageIDsOfCase.end(), dataNodeID) != allImageIDsOfCase.end(); + } + + if (NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) + { + std::vector allSegmentationIDsOfCase = RelationStorage::GetAllSegmentationIDsOfCase(caseID); + return std::find(allSegmentationIDsOfCase.begin(), allSegmentationIDsOfCase.end(), dataNodeID) != allSegmentationIDsOfCase.end(); + } + + return false; } bool mitk::SemanticRelationsInference::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { SemanticTypes::LesionVector allLesions = RelationStorage::GetAllLesionsOfCase(caseID); // filter all lesions: check for equality with the given lesion using a lambda function - auto lambda = [&lesion](const SemanticTypes::Lesion& currentLesion) { return currentLesion.UID == lesion.UID; }; + 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; - } + return existingLesion != allLesions.end(); } mitk::SemanticTypes::IDVector mitk::SemanticRelationsInference::GetAllImageIDsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { if (!InstanceExists(caseID, lesion)) { mitkThrowException(SemanticRelationException) << "Could not find an existing lesion instance for the given caseID " << caseID << " and lesion " << lesion.UID << "."; } SemanticTypes::IDVector allImageIDsOfLesion; // 1. get all segmentations that define the lesion // 2. get the parentID (imageID) of each segmentation // 3. guarantee uniqueness of image IDs SemanticTypes::IDVector allSegmentationIDsOfLesion = RelationStorage::GetAllSegmentationIDsOfLesion(caseID, lesion); for (const auto& segmentationID : allSegmentationIDsOfLesion) { // get parent ID of the current segmentation ID SemanticTypes::ID imageID = RelationStorage::GetImageIDOfSegmentation(caseID, segmentationID); if(!imageID.empty()) { allImageIDsOfLesion.push_back(imageID); } } std::sort(allImageIDsOfLesion.begin(), allImageIDsOfLesion.end()); allImageIDsOfLesion.erase(std::unique(allImageIDsOfLesion.begin(), allImageIDsOfLesion.end()), allImageIDsOfLesion.end()); return allImageIDsOfLesion; } mitk::SemanticTypes::ControlPoint mitk::SemanticRelationsInference::GetControlPointOfImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); - SemanticTypes::ID dataNodeID = GetIDFromDataNode(imageNode); - return RelationStorage::GetControlPointOfImage(caseID, dataNodeID); + SemanticTypes::CaseID caseID = ""; + SemanticTypes::ID imageID = ""; + try + { + caseID = GetCaseIDFromDataNode(imageNode); + imageID = GetIDFromDataNode(imageNode); + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot get the control point of the given image data node."; + } + + return RelationStorage::GetControlPointOfImage(caseID, imageID); } mitk::SemanticTypes::ControlPointVector mitk::SemanticRelationsInference::GetAllControlPointsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); // filter the control points: use only those, where the associated image data has a segmentation that refers to the given lesion using a lambda function auto lambda = [&caseID, &lesion](const SemanticTypes::ControlPoint& controlPoint) { return !SpecificImageExists(caseID, lesion, controlPoint); }; allControlPoints.erase(std::remove_if(allControlPoints.begin(), allControlPoints.end(), lambda), allControlPoints.end()); return allControlPoints; } mitk::SemanticTypes::ControlPointVector mitk::SemanticRelationsInference::GetAllControlPointsOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) { SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); // filter the control points: use only those, where the associated image data refers to the given information type using a lambda function auto lambda = [&caseID, &informationType](const SemanticTypes::ControlPoint& controlPoint) { return !SpecificImageExists(caseID, informationType, controlPoint); }; allControlPoints.erase(std::remove_if(allControlPoints.begin(), allControlPoints.end(), lambda), allControlPoints.end()); return allControlPoints; } bool mitk::SemanticRelationsInference::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); // filter all control points: check for equality with the given control point using a lambda function auto lambda = [&controlPoint](const SemanticTypes::ControlPoint& currentControlPoint) { return currentControlPoint.UID == controlPoint.UID; }; const auto existingControlPoint = std::find_if(allControlPoints.begin(), allControlPoints.end(), lambda); if (existingControlPoint != allControlPoints.end()) { return true; } else { return false; } } bool mitk::SemanticRelationsInference::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) { SemanticTypes::ExaminationPeriodVector allExaminationPeriods = RelationStorage::GetAllExaminationPeriodsOfCase(caseID); // filter all examination periods: check for equality with the given examination period using a lambda function auto lambda = [&examinationPeriod](const SemanticTypes::ExaminationPeriod& currentExaminationPeriod) { return currentExaminationPeriod.UID == examinationPeriod.UID; }; const auto existingExaminationPeriod = std::find_if(allExaminationPeriods.begin(), allExaminationPeriods.end(), lambda); if (existingExaminationPeriod != allExaminationPeriods.end()) { return true; } else { return false; } } mitk::SemanticTypes::InformationType mitk::SemanticRelationsInference::GetInformationTypeOfImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); - SemanticTypes::ID imageID = GetIDFromDataNode(imageNode); + SemanticTypes::CaseID caseID = ""; + SemanticTypes::ID imageID = ""; + try + { + caseID = GetCaseIDFromDataNode(imageNode); + imageID = GetIDFromDataNode(imageNode); + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot get the information type of the given image data node."; + } + return RelationStorage::GetInformationTypeOfImage(caseID, imageID); } mitk::SemanticTypes::InformationTypeVector mitk::SemanticRelationsInference::GetAllInformationTypesOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { SemanticTypes::InformationTypeVector allInformationTypes = RelationStorage::GetAllInformationTypesOfCase(caseID); // filter the information types: use only those, where the associated data refers to the given control point using a lambda function auto lambda = [&caseID, &controlPoint](const SemanticTypes::InformationType& informationType) { return !SpecificImageExists(caseID, informationType, controlPoint); }; allInformationTypes.erase(std::remove_if(allInformationTypes.begin(), allInformationTypes.end(), lambda), allInformationTypes.end()); return allInformationTypes; } bool mitk::SemanticRelationsInference::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) { SemanticTypes::InformationTypeVector allInformationTypes = RelationStorage::GetAllInformationTypesOfCase(caseID); // filter all information types: check for equality with the given information type using a lambda function auto lambda = [&informationType](const SemanticTypes::InformationType& currentInformationType) { return currentInformationType == informationType; }; const auto existingInformationType = std::find_if(allInformationTypes.begin(), allInformationTypes.end(), lambda); if (existingInformationType != allInformationTypes.end()) { return true; } else { return false; } } bool mitk::SemanticRelationsInference::SpecificImageExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::InformationType& informationType) { SemanticTypes::IDVector allImageIDsOfLesion; try { allImageIDsOfLesion = GetAllImageIDsOfLesion(caseID, lesion); } catch (const SemanticRelationException&) { return false; } SemanticTypes::IDVector allImageIDsOfInformationType = RelationStorage::GetAllImageIDsOfInformationType(caseID, informationType); std::sort(allImageIDsOfLesion.begin(), allImageIDsOfLesion.end()); std::sort(allImageIDsOfInformationType.begin(), allImageIDsOfInformationType.end()); SemanticTypes::IDVector allImageIDsIntersection; // set_intersection removes duplicated nodes, since 'GetAllImageIDsOfInformationType' only contains at most one of each node std::set_intersection(allImageIDsOfLesion.begin(), allImageIDsOfLesion.end(), allImageIDsOfInformationType.begin(), allImageIDsOfInformationType.end(), std::back_inserter(allImageIDsIntersection)); // if the vector of intersecting image IDs is empty, the information type does not contain the lesion return !allImageIDsIntersection.empty(); } bool mitk::SemanticRelationsInference::SpecificImageExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ControlPoint& controlPoint) { SemanticTypes::IDVector allImageIDsOfLesion; try { allImageIDsOfLesion = GetAllImageIDsOfLesion(caseID, lesion); } catch (const SemanticRelationException&) { return false; } SemanticTypes::IDVector allImageIDsOfControlPoint = RelationStorage::GetAllImageIDsOfControlPoint(caseID, controlPoint); std::sort(allImageIDsOfLesion.begin(), allImageIDsOfLesion.end()); std::sort(allImageIDsOfControlPoint.begin(), allImageIDsOfControlPoint.end()); SemanticTypes::IDVector allImageIDsIntersection; // set_intersection removes duplicated nodes, since 'GetAllImageIDsOfControlPoint' only contains at most one of each node std::set_intersection(allImageIDsOfLesion.begin(), allImageIDsOfLesion.end(), allImageIDsOfControlPoint.begin(), allImageIDsOfControlPoint.end(), std::back_inserter(allImageIDsIntersection)); // if the vector of intersecting image IDs is empty, the control point does not contain the lesion return !allImageIDsIntersection.empty(); } bool mitk::SemanticRelationsInference::SpecificImageExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType, const SemanticTypes::ControlPoint& controlPoint) { SemanticTypes::IDVector allImageIDsOfInformationType = RelationStorage::GetAllImageIDsOfInformationType(caseID, informationType); SemanticTypes::IDVector allImageIDsOfControlPoint = RelationStorage::GetAllImageIDsOfControlPoint(caseID, controlPoint); std::sort(allImageIDsOfInformationType.begin(), allImageIDsOfInformationType.end()); std::sort(allImageIDsOfControlPoint.begin(), allImageIDsOfControlPoint.end()); SemanticTypes::IDVector allImageIDsIntersection; // set_intersection removes duplicated nodes std::set_intersection(allImageIDsOfInformationType.begin(), allImageIDsOfInformationType.end(), allImageIDsOfControlPoint.begin(), allImageIDsOfControlPoint.end(), std::back_inserter(allImageIDsIntersection)); // if the vector of intersecting image IDs is empty no image exists for the given information type and control point return !allImageIDsIntersection.empty(); } diff --git a/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp b/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp index 4aad423f13..6e65122cb3 100644 --- a/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp +++ b/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp @@ -1,442 +1,592 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSemanticRelationsIntegration.h" // semantic relations module #include "mitkControlPointManager.h" #include "mitkDICOMHelper.h" #include "mitkNodePredicates.h" #include "mitkRelationStorage.h" #include "mitkSemanticRelationException.h" #include "mitkSemanticRelationsInference.h" #include "mitkUIDGeneratorBoost.h" // multi label module #include // c++ #include #include std::vector mitk::SemanticRelationsIntegration::m_ObserverVector; void mitk::SemanticRelationsIntegration::AddObserver(ISemanticRelationsObserver* observer) { std::vector::iterator existingObserver = std::find(m_ObserverVector.begin(), m_ObserverVector.end(), observer); if (existingObserver != m_ObserverVector.end()) { // no need to add the already existing observer return; } m_ObserverVector.push_back(observer); } void mitk::SemanticRelationsIntegration::RemoveObserver(ISemanticRelationsObserver* observer) { m_ObserverVector.erase(std::remove(m_ObserverVector.begin(), m_ObserverVector.end(), observer), m_ObserverVector.end()); } /************************************************************************/ /* functions to add / remove instances / attributes */ /************************************************************************/ void mitk::SemanticRelationsIntegration::AddImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); - SemanticTypes::ID nodeID = GetIDFromDataNode(imageNode); + SemanticTypes::CaseID caseID; + SemanticTypes::ID imageID; + SemanticTypes::InformationType informationType; + SemanticTypes::ControlPoint controlPoint; + try // retrieve information + { + caseID = GetCaseIDFromDataNode(imageNode); + imageID = GetIDFromDataNode(imageNode); - RelationStorage::AddCase(caseID); - RelationStorage::AddImage(caseID, nodeID); + informationType = GetDICOMModalityFromDataNode(imageNode); + controlPoint = GenerateControlPoint(imageNode); + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot add the given image data node."; + } - SemanticTypes::InformationType informationType = GetDICOMModalityFromDataNode(imageNode); - AddInformationTypeToImage(imageNode, informationType); + try // add and set information + { + RelationStorage::AddCase(caseID); + RelationStorage::AddImage(caseID, imageID); - // set the correct control point for this image - SemanticTypes::ControlPoint controlPoint = GenerateControlPoint(imageNode); - SetControlPointOfImage(imageNode, controlPoint); + AddInformationTypeToImage(imageNode, informationType); + SetControlPointOfImage(imageNode, controlPoint); + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot add the given image data node."; + } } void mitk::SemanticRelationsIntegration::RemoveImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); - SemanticTypes::ID nodeID = GetIDFromDataNode(imageNode); + SemanticTypes::CaseID caseID; + SemanticTypes::ID imageID; + try // retrieve information + { + caseID = GetCaseIDFromDataNode(imageNode); + imageID = GetIDFromDataNode(imageNode); + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot remove the given image data node."; + } - RemoveInformationTypeFromImage(imageNode); - UnlinkImageFromControlPoint(imageNode); - RelationStorage::RemoveImage(caseID, nodeID); + try // add and set information + { + RemoveInformationTypeFromImage(imageNode); + UnlinkImageFromControlPoint(imageNode); + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot remove the given image data node."; + } + + RelationStorage::RemoveImage(caseID, imageID); NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::AddLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { if (SemanticRelationsInference::InstanceExists(caseID, lesion)) { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to add already exists for the given case."; } - else - { - RelationStorage::AddLesion(caseID, lesion); - NotifyObserver(caseID); - } + + RelationStorage::AddLesion(caseID, lesion); + NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::OverwriteLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { if (SemanticRelationsInference::InstanceExists(caseID, lesion)) { RelationStorage::OverwriteLesion(caseID, lesion); NotifyObserver(caseID); } else { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to overwrite does not exist for the given case."; } } void mitk::SemanticRelationsIntegration::AddLesionAndLinkSegmentation(const DataNode* segmentationNode, const SemanticTypes::Lesion& lesion) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); - AddLesion(caseID, lesion); - LinkSegmentationToLesion(segmentationNode, lesion); + SemanticTypes::CaseID caseID; + try + { + caseID = GetCaseIDFromDataNode(segmentationNode); + AddLesion(caseID, lesion); + LinkSegmentationToLesion(segmentationNode, lesion); + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot add given lesion and link the given segmentation data node."; + } + NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::RemoveLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { if (SemanticRelationsInference::InstanceExists(caseID, lesion)) { SemanticTypes::IDVector allSegmentationIDsOfLesion = RelationStorage::GetAllSegmentationIDsOfLesion(caseID, lesion); if (allSegmentationIDsOfLesion.empty()) { // no more segmentations are linked to the specific lesion // the lesion can be removed from the storage RelationStorage::RemoveLesion(caseID, lesion); NotifyObserver(caseID); } else { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to remove is still referred to by a segmentation node. Lesion will not be removed."; } } else { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to remove does not exist for the given case."; } } void mitk::SemanticRelationsIntegration::AddSegmentation(const DataNode* segmentationNode, const DataNode* parentNode) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } if (nullptr == parentNode) { mitkThrowException(SemanticRelationException) << "Not a valid parent data node."; } - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); - SemanticTypes::ID segmentationNodeID = GetIDFromDataNode(segmentationNode); - SemanticTypes::ID parentNodeID = GetIDFromDataNode(parentNode); + SemanticTypes::CaseID caseID; + SemanticTypes::ID segmentationNodeID; + SemanticTypes::ID parentNodeID; + try + { + caseID = GetCaseIDFromDataNode(segmentationNode); + segmentationNodeID = GetIDFromDataNode(segmentationNode); + parentNodeID = GetIDFromDataNode(parentNode); + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot add the given segmentation data node."; + } RelationStorage::AddSegmentation(caseID, segmentationNodeID, parentNodeID); NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::LinkSegmentationToLesion(const DataNode* segmentationNode, const SemanticTypes::Lesion& lesion) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); + SemanticTypes::CaseID caseID; + SemanticTypes::ID segmentationID; + try + { + caseID = GetCaseIDFromDataNode(segmentationNode); + segmentationID = GetIDFromDataNode(segmentationNode); + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot link the given segmentation data node to the given lesion."; + } + if (SemanticRelationsInference::InstanceExists(caseID, lesion)) { - SemanticTypes::ID segmentationID = GetIDFromDataNode(segmentationNode); RelationStorage::LinkSegmentationToLesion(caseID, segmentationID, lesion); NotifyObserver(caseID); } else { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to link does not exist for the given case."; } } void mitk::SemanticRelationsIntegration::UnlinkSegmentationFromLesion(const DataNode* segmentationNode) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); - SemanticTypes::ID segmentationID = GetIDFromDataNode(segmentationNode); + SemanticTypes::CaseID caseID; + SemanticTypes::ID segmentationID; + try + { + caseID = GetCaseIDFromDataNode(segmentationNode); + segmentationID = GetIDFromDataNode(segmentationNode); + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot unlink the given segmentation data node from its lesion."; + } + RelationStorage::UnlinkSegmentationFromLesion(caseID, segmentationID); NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::RemoveSegmentation(const DataNode* segmentationNode) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); - SemanticTypes::ID segmentationNodeID = GetIDFromDataNode(segmentationNode); + SemanticTypes::CaseID caseID; + SemanticTypes::ID segmentationNodeID; + try + { + caseID = GetCaseIDFromDataNode(segmentationNode); + segmentationNodeID = GetIDFromDataNode(segmentationNode); + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot remove the given segmentation data node."; + } + RelationStorage::RemoveSegmentation(caseID, segmentationNodeID); NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::SetControlPointOfImage(const DataNode* imageNode, const SemanticTypes::ControlPoint& controlPoint) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); + SemanticTypes::CaseID caseID; + try + { + caseID = GetCaseIDFromDataNode(imageNode); + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot set the given control point for the given image data node."; + } + SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); // need to check if an already existing control point fits/contains the user control point SemanticTypes::ControlPoint existingControlPoint = FindExistingControlPoint(controlPoint, allControlPoints); - if (!existingControlPoint.UID.empty()) + try { - try + if (!existingControlPoint.UID.empty()) { // found an already existing control point LinkImageToControlPoint(imageNode, existingControlPoint, false); } - catch (const SemanticRelationException&) - { - mitkThrowException(SemanticRelationException) << "The image data node can not be linked. Inconsistency in the semantic relations storage assumed."; - } - } - else - { - try + else { AddControlPointAndLinkImage(imageNode, controlPoint, false); // added a new control point // find closest control point to add the new control point to the correct examination period SemanticTypes::ControlPoint closestControlPoint = FindClosestControlPoint(controlPoint, allControlPoints); SemanticTypes::ExaminationPeriodVector allExaminationPeriods = RelationStorage::GetAllExaminationPeriodsOfCase(caseID); SemanticTypes::ExaminationPeriod examinationPeriod = 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 image data node can not be linked. Inconsistency in the semantic relations storage assumed."; - } + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot set the given control point for the given image data node."; } ClearControlPoints(caseID); NotifyObserver(caseID); + } void mitk::SemanticRelationsIntegration::AddControlPointAndLinkImage(const DataNode* imageNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); + SemanticTypes::CaseID caseID; + try + { + caseID = GetCaseIDFromDataNode(imageNode); + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot add the given control point and link the given image data node."; + } + if (SemanticRelationsInference::InstanceExists(caseID, controlPoint)) { mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to add already exists for the given case. \n Use 'LinkImageToControlPoint' instead."; } RelationStorage::AddControlPoint(caseID, controlPoint); - LinkImageToControlPoint(imageNode, controlPoint, checkConsistence); + + try + { + LinkImageToControlPoint(imageNode, controlPoint, checkConsistence); + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot add the given control point and link the given image data node."; + } } void mitk::SemanticRelationsIntegration::LinkImageToControlPoint(const DataNode* imageNode, const SemanticTypes::ControlPoint& controlPoint, bool /*checkConsistence*/) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); + SemanticTypes::CaseID caseID; + SemanticTypes::ID imageID; + try + { + caseID = GetCaseIDFromDataNode(imageNode); + imageID = GetIDFromDataNode(imageNode); + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot link the image data node to the given control point."; + } + if (SemanticRelationsInference::InstanceExists(caseID, controlPoint)) { - SemanticTypes::ID dataID = GetIDFromDataNode(imageNode); - RelationStorage::LinkImageToControlPoint(caseID, dataID, controlPoint); + RelationStorage::LinkImageToControlPoint(caseID, imageID, controlPoint); } else { mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to link does not exist for the given case."; } } void mitk::SemanticRelationsIntegration::UnlinkImageFromControlPoint(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); - SemanticTypes::ID dataID = GetIDFromDataNode(imageNode); - SemanticTypes::ControlPoint controlPoint = RelationStorage::GetControlPointOfImage(caseID, dataID); - RelationStorage::UnlinkImageFromControlPoint(caseID, dataID); + SemanticTypes::CaseID caseID = ""; + SemanticTypes::ID imageID = ""; + try + { + caseID = GetCaseIDFromDataNode(imageNode); + imageID = GetIDFromDataNode(imageNode); + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot unlink the given image data node from its control point."; + } + + SemanticTypes::ControlPoint controlPoint = RelationStorage::GetControlPointOfImage(caseID, imageID); + RelationStorage::UnlinkImageFromControlPoint(caseID, imageID); ClearControlPoints(caseID); } void mitk::SemanticRelationsIntegration::AddExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) { if (SemanticRelationsInference::InstanceExists(caseID, examinationPeriod)) { mitkThrowException(SemanticRelationException) << "The examination period " << examinationPeriod.UID << " to add already exists for the given case."; } else { RelationStorage::AddExaminationPeriod(caseID, examinationPeriod); } } void mitk::SemanticRelationsIntegration::AddControlPointToExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod& examinationPeriod) { if (!SemanticRelationsInference::InstanceExists(caseID, controlPoint)) { mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to add does not exist for the given case."; } if (!SemanticRelationsInference::InstanceExists(caseID, examinationPeriod)) { mitkThrowException(SemanticRelationException) << "The examination period " << examinationPeriod.UID << " does not exist for the given case. \n Use 'AddExaminationPeriod' before."; } RelationStorage::AddControlPointToExaminationPeriod(caseID, controlPoint, examinationPeriod); } void mitk::SemanticRelationsIntegration::SetInformationType(const DataNode* imageNode, const SemanticTypes::InformationType& informationType) { - RemoveInformationTypeFromImage(imageNode); - AddInformationTypeToImage(imageNode, informationType); + SemanticTypes::CaseID caseID; + try + { + caseID = GetCaseIDFromDataNode(imageNode); + + RemoveInformationTypeFromImage(imageNode); + AddInformationTypeToImage(imageNode, informationType); + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot set the given information type for the given image data node."; + } - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::AddInformationTypeToImage(const DataNode* imageNode, const SemanticTypes::InformationType& informationType) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); - SemanticTypes::ID imageID = GetIDFromDataNode(imageNode); + SemanticTypes::CaseID caseID = ""; + SemanticTypes::ID imageID = ""; + try + { + caseID = GetCaseIDFromDataNode(imageNode); + imageID = GetIDFromDataNode(imageNode); + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot add the given information type to the given image data node."; + } + RelationStorage::AddInformationTypeToImage(caseID, imageID, informationType); } void mitk::SemanticRelationsIntegration::RemoveInformationTypeFromImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); - SemanticTypes::ID imageID = GetIDFromDataNode(imageNode); + SemanticTypes::CaseID caseID = ""; + SemanticTypes::ID imageID = ""; + try + { + caseID = GetCaseIDFromDataNode(imageNode); + imageID = GetIDFromDataNode(imageNode); + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot remove the information type from the given image data node."; + } + SemanticTypes::InformationType originalInformationType = RelationStorage::GetInformationTypeOfImage(caseID, imageID); RelationStorage::RemoveInformationTypeFromImage(caseID, imageID); // check for further references to the removed information type std::vector allImageIDsVectorValue = RelationStorage::GetAllImageIDsOfCase(caseID); for (const auto& otherImageID : allImageIDsVectorValue) { SemanticTypes::InformationType otherInformationType = RelationStorage::GetInformationTypeOfImage(caseID, otherImageID); if (otherInformationType == originalInformationType) { // found the information type in another image -> cannot remove the information type from the case return; } } // given information type was not referred by any other image of the case -> the information type can be removed from the case RelationStorage::RemoveInformationType(caseID, originalInformationType); } /************************************************************************/ /* private functions */ /************************************************************************/ void mitk::SemanticRelationsIntegration::NotifyObserver(const SemanticTypes::CaseID& caseID) const { for (auto& observer : m_ObserverVector) { observer->Update(caseID); } } void mitk::SemanticRelationsIntegration::ClearControlPoints(const SemanticTypes::CaseID& caseID) { SemanticTypes::ControlPointVector allControlPointsOfCase = RelationStorage::GetAllControlPointsOfCase(caseID); std::vector allImageIDsVectorValue = RelationStorage::GetAllImageIDsOfCase(caseID); SemanticTypes::ControlPointVector referencedControlPoints; for (const auto& imageID : allImageIDsVectorValue) { auto controlPointOfImage = RelationStorage::GetControlPointOfImage(caseID, imageID); referencedControlPoints.push_back(controlPointOfImage); } std::sort(allControlPointsOfCase.begin(), allControlPointsOfCase.end()); std::sort(referencedControlPoints.begin(), referencedControlPoints.end()); SemanticTypes::ControlPointVector nonReferencedControlPoints; std::set_difference(allControlPointsOfCase.begin(), allControlPointsOfCase.end(), referencedControlPoints.begin(), referencedControlPoints.end(), std::inserter(nonReferencedControlPoints, nonReferencedControlPoints.begin())); auto allExaminationPeriods = RelationStorage::GetAllExaminationPeriodsOfCase(caseID); for (const auto& controlPoint : nonReferencedControlPoints) { const auto& examinationPeriod = FindExaminationPeriod(controlPoint, allExaminationPeriods); RelationStorage::RemoveControlPointFromExaminationPeriod(caseID, controlPoint, examinationPeriod); RelationStorage::RemoveControlPoint(caseID, controlPoint); } } diff --git a/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp b/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp index 17069bfe7f..80b857e1f7 100644 --- a/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp @@ -1,349 +1,342 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // semantic relations UI module #include "QmitkPatientTableModel.h" #include "QmitkPatientTableHeaderView.h" #include "QmitkSemanticRelationsUIHelper.h" // semantic relations module #include #include #include #include #include #include #include // qt #include // c++ #include #include QmitkPatientTableModel::QmitkPatientTableModel(QObject* parent /*= nullptr*/) : QmitkAbstractSemanticRelationsStorageModel(parent) , m_SelectedNodeType("Image") { m_HeaderModel = new QStandardItemModel(this); } QmitkPatientTableModel::~QmitkPatientTableModel() { // nothing here } QModelIndex QmitkPatientTableModel::index(int row, int column, const QModelIndex& parent/* = QModelIndex()*/) const { if (hasIndex(row, column, parent)) { return createIndex(row, column); } return QModelIndex(); } QModelIndex QmitkPatientTableModel::parent(const QModelIndex& /*child*/) const { return QModelIndex(); } int QmitkPatientTableModel::rowCount(const QModelIndex& parent/* = QModelIndex()*/) const { if (parent.isValid()) { return 0; } return m_InformationTypes.size(); } int QmitkPatientTableModel::columnCount(const QModelIndex& parent/* = QModelIndex()*/) const { if (parent.isValid()) { return 0; } return m_ControlPoints.size(); } QVariant QmitkPatientTableModel::data(const QModelIndex& index, int role/* = Qt::DisplayRole*/) const { // special role for returning the horizontal header if (QmitkPatientTableHeaderView::HorizontalHeaderDataRole == role) { return QVariant::fromValue(m_HeaderModel); } if (!index.isValid()) { return QVariant(); } if (index.row() < 0 || index.row() >= static_cast(m_InformationTypes.size()) || index.column() < 0 || index.column() >= static_cast(m_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::darkGreen)) : QVariant(QColor(Qt::transparent)); } return QVariant(QColor(Qt::transparent)); } return QVariant(); } QVariant QmitkPatientTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (Qt::Vertical == orientation && Qt::DisplayRole == role) { if (static_cast(m_InformationTypes.size()) > section) { mitk::SemanticTypes::InformationType currentInformationType = m_InformationTypes.at(section); return QVariant(QString::fromStdString(currentInformationType)); } } return QVariant(); } Qt::ItemFlags QmitkPatientTableModel::flags(const QModelIndex& index) const { Qt::ItemFlags flags; mitk::DataNode* dataNode = GetCurrentDataNode(index); if (nullptr != dataNode) { flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; } return flags; } void QmitkPatientTableModel::SetNodeType(const std::string& nodeType) { m_SelectedNodeType = nodeType; UpdateModelData(); } void QmitkPatientTableModel::NodePredicateChanged() { UpdateModelData(); } void QmitkPatientTableModel::SetData() { // get all control points of current case m_ControlPoints = mitk::RelationStorage::GetAllControlPointsOfCase(m_CaseID); // sort the vector of control points for the timeline std::sort(m_ControlPoints.begin(), m_ControlPoints.end()); // get all examination periods of current case m_ExaminationPeriods = mitk::RelationStorage::GetAllExaminationPeriodsOfCase(m_CaseID); // sort the vector of examination periods for the timeline mitk::SortExaminationPeriods(m_ExaminationPeriods, m_ControlPoints); // get all information types points of current case m_InformationTypes = mitk::RelationStorage::GetAllInformationTypesOfCase(m_CaseID); if ("Image" == m_SelectedNodeType) { m_CurrentDataNodes = m_SemanticRelationsDataStorageAccess->GetAllImagesOfCase(m_CaseID); } else if ("Segmentation" == m_SelectedNodeType) { m_CurrentDataNodes = m_SemanticRelationsDataStorageAccess->GetAllSegmentationsOfCase(m_CaseID); } SetHeaderModel(); SetPixmaps(); SetLesionPresences(); } void QmitkPatientTableModel::SetHeaderModel() { m_HeaderModel->clear(); QStandardItem* rootItem = new QStandardItem("Timeline"); QList standardItems; for (const auto& examinationPeriod : m_ExaminationPeriods) { QStandardItem* examinationPeriodItem = new QStandardItem(QString::fromStdString(examinationPeriod.name)); standardItems.push_back(examinationPeriodItem); rootItem->appendColumn(standardItems); standardItems.clear(); const auto& currentControlPoints = examinationPeriod.controlPointUIDs; for (const auto& controlPointUID : currentControlPoints) { const auto& controlPoint = mitk::GetControlPointByUID(controlPointUID, m_ControlPoints); QStandardItem* controlPointItem = new QStandardItem(QString::fromStdString(controlPoint.ToString())); standardItems.push_back(controlPointItem); examinationPeriodItem->appendColumn(standardItems); standardItems.clear(); } } m_HeaderModel->setItem(0, 0, rootItem); } void QmitkPatientTableModel::SetPixmaps() { m_PixmapMap.clear(); for (const auto& dataNode : m_CurrentDataNodes) { // set the pixmap for the current node QPixmap pixmapFromImage = QmitkSemanticRelationsUIHelper::GetPixmapFromImageNode(dataNode); SetPixmapOfNode(dataNode, &pixmapFromImage); } } void QmitkPatientTableModel::SetPixmapOfNode(const mitk::DataNode* dataNode, QPixmap* pixmapFromImage) { if (nullptr == dataNode) { return; } std::map::iterator iter = m_PixmapMap.find(dataNode); if (iter != m_PixmapMap.end()) { // key already existing if (nullptr != pixmapFromImage) { // overwrite already stored pixmap iter->second = pixmapFromImage->scaled(120, 120, Qt::IgnoreAspectRatio); } else { // remove key if no pixmap is given m_PixmapMap.erase(iter); } } else { m_PixmapMap.insert(std::make_pair(dataNode, pixmapFromImage->scaled(120, 120, Qt::IgnoreAspectRatio))); } } void QmitkPatientTableModel::SetLesionPresences() { m_LesionPresence.clear(); if (!mitk::SemanticRelationsInference::InstanceExists(m_CaseID, m_Lesion)) { return; } for (const auto& dataNode : m_CurrentDataNodes) { if (!mitk::SemanticRelationsInference::InstanceExists(dataNode)) { continue; } - try - { - // set the lesion presence for the current node - bool lesionPresence = mitk::SemanticRelationsInference::IsLesionPresent(m_Lesion, dataNode); - SetLesionPresenceOfNode(dataNode, lesionPresence); - } - catch (const mitk::SemanticRelationException&) - { - continue; - } + // set the lesion presence for the current node + bool lesionPresence = mitk::SemanticRelationsInference::IsLesionPresent(m_Lesion, dataNode); + SetLesionPresenceOfNode(dataNode, lesionPresence); } } void QmitkPatientTableModel::SetLesionPresenceOfNode(const mitk::DataNode* dataNode, bool lesionPresence) { std::map::iterator iter = m_LesionPresence.find(dataNode); if (iter != m_LesionPresence.end()) { // key already existing, overwrite already stored bool value iter->second = lesionPresence; } else { m_LesionPresence.insert(std::make_pair(dataNode, lesionPresence)); } } mitk::DataNode* QmitkPatientTableModel::GetCurrentDataNode(const QModelIndex& index) const { if (!index.isValid()) { return nullptr; } 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_SemanticRelationsDataStorageAccess->GetAllSpecificImages(m_CaseID, currentControlPoint, currentInformationType); } else if ("Segmentation" == m_SelectedNodeType) { filteredDataNodes = m_SemanticRelationsDataStorageAccess->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/QmitkDataNodeAddToSemanticRelationsAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeAddToSemanticRelationsAction.cpp index c92128429c..4d9f80a60c 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeAddToSemanticRelationsAction.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeAddToSemanticRelationsAction.cpp @@ -1,171 +1,189 @@ /*=================================================================== 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 "QmitkDataNodeAddToSemanticRelationsAction.h" // semantic relations module #include #include #include #include // mitk gui common plugin #include +// mitk core +#include + // qt #include // namespace that contains the concrete action namespace AddToSemanticRelationsAction { void Run(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataStorage* dataStorage, const mitk::DataNode* dataNode) { if (nullptr == dataNode) { return; } if (mitk::NodePredicates::GetImagePredicate()->CheckNode(dataNode)) { AddImage(semanticRelationsIntegration, dataNode); } else if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { AddSegmentation(semanticRelationsIntegration, dataStorage, dataNode); } } void AddImage(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataNode* image) { if (nullptr == image) { return; } try { // add the image to the semantic relations storage semanticRelationsIntegration->AddImage(image); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; - QMessageBox msgBox; - msgBox.setWindowTitle("Could not add the selected image."); - msgBox.setText("The program wasn't able to correctly add the selected images.\n" + QMessageBox msgBox(QMessageBox::Warning, + "Could not add the selected image.", + "The program wasn't able to correctly add the selected images.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); - msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } } void AddSegmentation(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataStorage* dataStorage, const mitk::DataNode* segmentation) { if (nullptr == segmentation) { return; } mitk::BaseData* baseData = segmentation->GetData(); if (nullptr == baseData) { return; } // continue with valid segmentation data // get parent node of the current segmentation node with the node predicate mitk::DataStorage::SetOfObjects::ConstPointer parentNodes = dataStorage->GetSources(segmentation, mitk::NodePredicates::GetImagePredicate(), false); // check for already existing, identifying base properties - mitk::BaseProperty* caseIDProperty = baseData->GetProperty("DICOM.0010.0010"); - mitk::BaseProperty* nodeIDProperty = baseData->GetProperty("DICOM.0020.000E"); + auto caseIDPropertyName = mitk::GetCaseIDDICOMProperty(); + auto nodeIDPropertyName = mitk::GetNodeIDDICOMProperty(); + mitk::BaseProperty* caseIDProperty = baseData->GetProperty(caseIDPropertyName.c_str()); + mitk::BaseProperty* nodeIDProperty = baseData->GetProperty(nodeIDPropertyName.c_str()); if (nullptr == caseIDProperty || nullptr == nodeIDProperty) { MITK_INFO << "No DICOM tags for case and node identification found. Transferring DICOM tags from the parent node to the selected segmentation node."; - mitk::SemanticTypes::CaseID caseID = mitk::GetCaseIDFromDataNode(parentNodes->front()); - mitk::SemanticTypes::ID nodeID = mitk::GetIDFromDataNode(parentNodes->front()); - // transfer DICOM tags to the segmentation node - mitk::StringProperty::Pointer caseIDTag = mitk::StringProperty::New(caseID); - baseData->SetProperty("DICOM.0010.0010", caseIDTag); // DICOM tag is "PatientName" + mitk::SemanticTypes::CaseID caseID; + mitk::SemanticTypes::ID nodeID; + try + { + caseID = mitk::GetCaseIDFromDataNode(parentNodes->front()); + nodeID = mitk::GetIDFromDataNode(parentNodes->front()); + } + catch (const mitk::SemanticRelationException& e) + { + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox(QMessageBox::Warning, + "Could not add the selected segmentation.", + "The program wasn't able to correctly add the selected segmentation.\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str())); + msgBox.exec(); + return; + } - // add UID to distinguish between different segmentations of the same parent node - mitk::StringProperty::Pointer nodeIDTag = mitk::StringProperty::New(nodeID + mitk::UIDGeneratorBoost::GenerateUID()); - baseData->SetProperty("DICOM.0020.000E", nodeIDTag); // DICOM tag is "SeriesInstanceUID" + // transfer DICOM tags to the segmentation node + baseData->SetProperty(caseIDPropertyName, + mitk::TemporoSpatialStringProperty::New(caseID)); + // add UID to distinguish between different segmentations of the same parent node + baseData->SetProperty(nodeIDPropertyName, + mitk::TemporoSpatialStringProperty::New(nodeID + mitk::UIDGeneratorBoost::GenerateUID())); } try { // add the segmentation with its parent image to the semantic relations storage semanticRelationsIntegration->AddSegmentation(segmentation, parentNodes->front()); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; - QMessageBox msgBox; - msgBox.setWindowTitle("Could not add the selected segmentation."); - msgBox.setText("The program wasn't able to correctly add the selected segmentation.\n" + QMessageBox msgBox(QMessageBox::Warning, + "Could not add the selected segmentation.", + "The program wasn't able to correctly add the selected segmentation.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); - msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } } } QmitkDataNodeAddToSemanticRelationsAction::QmitkDataNodeAddToSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) : QAction(parent) , QmitkAbstractSemanticRelationsAction(workbenchPartSite) { setText(tr("Add to semantic relations")); InitializeAction(); } QmitkDataNodeAddToSemanticRelationsAction::QmitkDataNodeAddToSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) : QAction(parent) , QmitkAbstractSemanticRelationsAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) { setText(tr("Add to semantic relations")); InitializeAction(); } QmitkDataNodeAddToSemanticRelationsAction::~QmitkDataNodeAddToSemanticRelationsAction() { // nothing here } void QmitkDataNodeAddToSemanticRelationsAction::InitializeAction() { connect(this, &QAction::triggered, this, &QmitkDataNodeAddToSemanticRelationsAction::OnActionTriggered); } void QmitkDataNodeAddToSemanticRelationsAction::OnActionTriggered(bool /*checked*/) { if (nullptr == m_SemanticRelationsIntegration) { return; } if (m_DataStorage.IsExpired()) { return; } auto dataNode = GetSelectedNode(); AddToSemanticRelationsAction::Run(m_SemanticRelationsIntegration.get(), m_DataStorage.Lock(), dataNode); } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp index 201d1232d7..3340b471fc 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp @@ -1,433 +1,432 @@ /*=================================================================== 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 "QmitkLesionInfoWidget.h" #include "QmitkSemanticRelationsNodeSelectionDialog.h" // semantic relations UI module #include // semantic relations module #include #include #include #include #include // registration ontology module //#include // qt #include #include #include #include #include QmitkLesionInfoWidget::QmitkLesionInfoWidget(mitk::DataStorage* dataStorage, QWidget* parent /*= nullptr*/) : QWidget(parent) , m_DataStorage(dataStorage) , m_SemanticRelationsDataStorageAccess(std::make_unique(dataStorage)) , m_SemanticRelationsIntegration(std::make_unique()) { Initialize(); } QmitkLesionInfoWidget::~QmitkLesionInfoWidget() { // nothing here } void QmitkLesionInfoWidget::Initialize() { m_Controls.setupUi(this); m_Controls.lesionTreeView->setAlternatingRowColors(true); m_Controls.lesionTreeView->setSelectionMode(QAbstractItemView::SingleSelection); m_Controls.lesionTreeView->setSelectionBehavior(QAbstractItemView::SelectRows); m_Controls.lesionTreeView->setContextMenuPolicy(Qt::CustomContextMenu); m_StorageModel = new QmitkLesionTreeModel(m_Controls.lesionTreeView); if (m_DataStorage.IsExpired()) { return; } auto dataStorage = m_DataStorage.Lock(); m_StorageModel->SetDataStorage(dataStorage); m_Controls.lesionTreeView->setModel(m_StorageModel); SetUpConnections(); } void QmitkLesionInfoWidget::SetUpConnections() { connect(m_StorageModel, &QmitkLesionTreeModel::ModelUpdated, this, &QmitkLesionInfoWidget::OnModelUpdated); // connect buttons to modify semantic relations connect(m_Controls.addLesionPushButton, &QPushButton::clicked, this, &QmitkLesionInfoWidget::OnAddLesionButtonClicked); // connect each list widget with a custom slots connect(m_Controls.lesionTreeView->selectionModel(), &QItemSelectionModel::currentChanged, this, &QmitkLesionInfoWidget::OnSelectionChanged); // connect context menu entries connect(m_Controls.lesionTreeView, &QTreeView::customContextMenuRequested, this, &QmitkLesionInfoWidget::OnLesionListContextMenuRequested); } void QmitkLesionInfoWidget::SetCaseID(const mitk::SemanticTypes::CaseID& caseID) { m_CaseID = caseID; m_StorageModel->SetCaseID(caseID); } void QmitkLesionInfoWidget::SetDataNodeSelection(const QList& dataNodeSelection) { m_StorageModel->SetDataNodeSelection(dataNodeSelection); } ////////////////////////////////////////////////////////////////////////// // Implementation of the QT_SLOTS ////////////////////////////////////////////////////////////////////////// void QmitkLesionInfoWidget::OnModelUpdated() { m_Controls.lesionTreeView->expandAll(); int columns = m_Controls.lesionTreeView->model()->columnCount(); for (int i = 0; i < columns; ++i) { m_Controls.lesionTreeView->resizeColumnToContents(i); } } void QmitkLesionInfoWidget::OnAddLesionButtonClicked() { if (m_CaseID.empty()) { - QMessageBox msgBox; - msgBox.setWindowTitle("No case ID set."); - msgBox.setText("In order to add a lesion, please specify the current case / patient."); - msgBox.setIcon(QMessageBox::Warning); + QMessageBox msgBox(QMessageBox::Warning, + "No case ID set.", + "In order to add a lesion, please specify the current case / patient."); msgBox.exec(); return; } mitk::SemanticTypes::Lesion newLesion = mitk::GenerateNewLesion(); try { m_SemanticRelationsIntegration->AddLesion(m_CaseID, newLesion); } catch (mitk::SemanticRelationException& e) { MITK_INFO << "Could not add a new lesion. " << e; } } void QmitkLesionInfoWidget::OnSelectionChanged(const QModelIndex& current, const QModelIndex& /*previous*/) { // only the UID is needed to identify a representing lesion QVariant data = m_StorageModel->data(current, Qt::UserRole); if (!data.canConvert()) { return; } auto lesion = data.value()->GetData().GetLesion(); if (false == mitk::SemanticRelationsInference::InstanceExists(m_CaseID, lesion)) { // no UID of a existing lesion found; cannot create a lesion return; } // if selected data nodes are set, reset to empty list to // hide "selected data nodes presence background highlighting" in the model if (!m_StorageModel->GetSelectedDataNodes().isEmpty()) { m_StorageModel->SetDataNodeSelection(QList()); } emit LesionSelectionChanged(lesion); } void QmitkLesionInfoWidget::OnLesionListContextMenuRequested(const QPoint& pos) { if (nullptr == m_SemanticRelationsIntegration) { return; } if (m_CaseID.empty()) { QMessageBox msgBox(QMessageBox::Warning, "No case ID set.", "In order to access the context menu entries a case ID has to be set."); msgBox.exec(); return; } QModelIndex index = m_Controls.lesionTreeView->indexAt(pos); if (!index.isValid()) { // no item clicked; cannot retrieve the current lesion return; } QVariant data = m_StorageModel->data(index, Qt::UserRole); mitk::SemanticTypes::Lesion selectedLesion; if (data.canConvert()) { selectedLesion = data.value()->GetData().GetLesion(); } else { return; } QMenu* menu = new QMenu(m_Controls.lesionTreeView); QAction* linkToSegmentation = new QAction("Link to segmentation", this); linkToSegmentation->setEnabled(true); connect(linkToSegmentation, &QAction::triggered, [this, selectedLesion] { OnLinkToSegmentation(selectedLesion); }); menu->addAction(linkToSegmentation); QAction* setLesionName = new QAction("Set lesion name", this); setLesionName->setEnabled(true); connect(setLesionName, &QAction::triggered, [this, selectedLesion] { OnSetLesionName(selectedLesion); }); menu->addAction(setLesionName); QAction* setLesionClass = new QAction("Set lesion class", this); setLesionClass->setEnabled(true); connect(setLesionClass, &QAction::triggered, [this, selectedLesion] { OnSetLesionClass(selectedLesion); }); menu->addAction(setLesionClass); QAction* propageLesionToImage = new QAction("Propagate lesion to image", this); propageLesionToImage->setEnabled(true); connect(propageLesionToImage, &QAction::triggered, [this, selectedLesion] { OnPropagateLesion(selectedLesion); }); menu->addAction(propageLesionToImage); QAction* removeLesion = new QAction("Remove lesion", this); removeLesion->setEnabled(true); connect(removeLesion, &QAction::triggered, [this, selectedLesion] { OnRemoveLesion(selectedLesion); }); menu->addAction(removeLesion); menu->popup(QCursor::pos()); } void QmitkLesionInfoWidget::OnLinkToSegmentation(mitk::SemanticTypes::Lesion selectedLesion) { if (m_DataStorage.IsExpired()) { return; } auto dataStorage = m_DataStorage.Lock(); QmitkSemanticRelationsNodeSelectionDialog* dialog = new QmitkSemanticRelationsNodeSelectionDialog(this, "Select segmentation to link to the selected lesion.", ""); dialog->setWindowTitle("Select segmentation node"); dialog->SetDataStorage(dataStorage); dialog->SetNodePredicate(mitk::NodePredicates::GetSegmentationPredicate()); dialog->SetSelectOnlyVisibleNodes(true); dialog->SetCaseID(m_CaseID); int dialogReturnValue = dialog->exec(); if (QDialog::Rejected == dialogReturnValue) { return; } auto nodes = dialog->GetSelectedNodes(); mitk::DataNode::Pointer selectedDataNode = nullptr; if (!nodes.isEmpty()) { // only single selection allowed selectedDataNode = nodes.front(); } if (nullptr == selectedDataNode || false == mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(selectedDataNode)) { QMessageBox msgBox(QMessageBox::Warning, "No valid segmentation node selected.", "In order to link the selected lesion to a segmentation, please specify a valid segmentation node."); msgBox.exec(); return; } mitk::BaseData* baseData = selectedDataNode->GetData(); if (nullptr == baseData) { QMessageBox msgBox(QMessageBox::Warning, "No valid base data.", "In order to link the selected lesion to a segmentation, please specify a valid segmentation node."); msgBox.exec(); return; } try { m_SemanticRelationsIntegration->LinkSegmentationToLesion(selectedDataNode, selectedLesion); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox(QMessageBox::Warning, "Could not link the selected lesion.", "The program wasn't able to correctly link the selected lesion with the selected segmentation.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.exec(); } } void QmitkLesionInfoWidget::OnSetLesionName(mitk::SemanticTypes::Lesion selectedLesion) { // use the lesion information to set the input text for the dialog QmitkLesionTextDialog* inputDialog = new QmitkLesionTextDialog(this); inputDialog->setWindowTitle("Set lesion name"); inputDialog->SetLineEditText(selectedLesion.name); int dialogReturnValue = inputDialog->exec(); if (QDialog::Rejected == dialogReturnValue) { return; } selectedLesion.name = inputDialog->GetLineEditText().toStdString(); m_SemanticRelationsIntegration->OverwriteLesion(m_CaseID, selectedLesion); } void QmitkLesionInfoWidget::OnSetLesionClass(mitk::SemanticTypes::Lesion selectedLesion) { // use the lesion information to set the input text for the dialog QmitkLesionTextDialog* inputDialog = new QmitkLesionTextDialog(this); inputDialog->setWindowTitle("Set lesion class"); inputDialog->SetLineEditText(selectedLesion.lesionClass.classType); // prepare the completer for the dialogs input text field mitk::LesionClassVector allLesionClasses = mitk::SemanticRelationsInference::GetAllLesionClassesOfCase(m_CaseID); QStringList wordList; for (const auto& lesionClass : allLesionClasses) { wordList << QString::fromStdString(lesionClass.classType); } QCompleter* completer = new QCompleter(wordList, this); completer->setCaseSensitivity(Qt::CaseInsensitive); inputDialog->GetLineEdit()->setCompleter(completer); int dialogReturnValue = inputDialog->exec(); if (QDialog::Rejected == dialogReturnValue) { return; } // retrieve the new input lesion class type and check for an already existing lesion class types std::string newLesionClassType = inputDialog->GetLineEditText().toStdString(); mitk::SemanticTypes::LesionClass existingLesionClass = mitk::FindExistingLesionClass(newLesionClassType, allLesionClasses); if (existingLesionClass.UID.empty()) { // could not find lesion class information for the new lesion class type // create a new lesion class for the selected lesion existingLesionClass = mitk::GenerateNewLesionClass(newLesionClassType); } selectedLesion.lesionClass = existingLesionClass; m_SemanticRelationsIntegration->OverwriteLesion(m_CaseID, selectedLesion); } void QmitkLesionInfoWidget::OnPropagateLesion(mitk::SemanticTypes::Lesion selectedLesion) { if (m_DataStorage.IsExpired()) { return; } auto dataStorage = m_DataStorage.Lock(); QmitkSemanticRelationsNodeSelectionDialog* dialog = new QmitkSemanticRelationsNodeSelectionDialog(this, "Select data node to propagate the selected lesion.", ""); dialog->setWindowTitle("Select image node"); dialog->SetDataStorage(dataStorage); dialog->SetNodePredicate(mitk::NodePredicates::GetImagePredicate()); dialog->SetSelectOnlyVisibleNodes(true); dialog->SetCaseID(m_CaseID); dialog->SetLesion(selectedLesion); int dialogReturnValue = dialog->exec(); if (QDialog::Rejected == dialogReturnValue) { return; } auto nodes = dialog->GetSelectedNodes(); mitk::DataNode::Pointer selectedDataNode = nullptr; if (!nodes.isEmpty()) { // only single selection allowed selectedDataNode = nodes.front(); } if (nullptr == selectedDataNode || false == mitk::NodePredicates::GetImagePredicate()->CheckNode(selectedDataNode)) { QMessageBox msgBox(QMessageBox::Warning, "No valid image node selected.", "In order to propagate the selected lesion to an image, please specify a valid image node."); msgBox.exec(); return; } mitk::BaseData* baseData = selectedDataNode->GetData(); if (nullptr == baseData) { QMessageBox msgBox(QMessageBox::Warning, "No valid base data.", "In order to propagate the selected lesion to an image, please specify a valid image node."); msgBox.exec(); return; } try { /* auto allSegmentationsOfLesion = m_SemanticRelationsDataStorageAccess->GetAllSegmentationsOfLesion(m_CaseID, selectedLesion); mitk::FindClosestSegmentationMask(); */ } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox(QMessageBox::Warning, "Could not propagate the selected lesion.", "The program wasn't able to correctly propagate the selected lesion to the selected image.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.exec(); } } void QmitkLesionInfoWidget::OnRemoveLesion(mitk::SemanticTypes::Lesion selectedLesion) { try { m_SemanticRelationsIntegration->RemoveLesion(m_CaseID, selectedLesion); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox(QMessageBox::Warning, "Could not remove the selected lesion.", "The program wasn't able to correctly remove the selected lesion from the semantic relations model.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.exec(); } }