diff --git a/Modules/SemanticRelations/files.cmake b/Modules/SemanticRelations/files.cmake index 1f88658ea9..84f58f3c85 100644 --- a/Modules/SemanticRelations/files.cmake +++ b/Modules/SemanticRelations/files.cmake @@ -1,10 +1,11 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkControlPointManager.cpp mitkDICOMHelper.cpp + mitkLesionManager.cpp mitkNodePredicates.cpp mitkRelationStorage.cpp mitkSemanticRelations.cpp mitkUIDGeneratorBoost.cpp ) diff --git a/Modules/SemanticRelations/include/mitkLesionManager.h b/Modules/SemanticRelations/include/mitkLesionManager.h new file mode 100644 index 0000000000..6dd2ac6fbf --- /dev/null +++ b/Modules/SemanticRelations/include/mitkLesionManager.h @@ -0,0 +1,71 @@ +/*=================================================================== + +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 MITKLESIONMANAGER_H +#define MITKLESIONMANAGER_H + +// semantic relations module +#include "mitkSemanticTypes.h" +#include + +// mitk core +#include + +/* +* @brief Provides helper functions that are needed to work with lesions. +* +* These functions help to generate new lesions, check for existing lesions or provide functionality +* to find existing lesion class types. +*/ +namespace mitk +{ + typedef std::vector LesionClassVector; + + /** + * @brief Generate a new lesion and lesion class with UIDs and the given string as lesion class type. + * + * @param lesionClassType The lesion class type as string. Default parameter is "". + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::Lesion GenerateNewLesion(const std::string& lesionClassType = ""); + /** + * @brief Generate a new lesion class with UID and the given string as lesion class type. + * + * @param lesionClassType The lesion class type as string. Default parameter is "". + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionClass GenerateNewLesionClass(const std::string& lesionClassType = ""); + + /** + * @brief Find and return a whole lesion including its lesion class given a specific lesion UID. + * + * @param lesionUID The lesion UID as string. + * @param allLesions All currently known lesions of a specific case. + * + * @return The lesion with its UID and the lesion class. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::Lesion GetLesionByUID(const SemanticTypes::ID& lesionUID, const std::vector& allLesions); + /** + * @brief Find and return the whole lesion class including its UID given a specific lesion class type. + * + * @param lesionClassType The lesion class type as string. + * @param allLesionClasses All currently known lesion classes of a specific case. + * + * @return The lesion class with its UID and the class type. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionClass FindExistingLesionClass(const std::string& lesionClassType, const std::vector& allLesionClasses); + +} // namespace mitk + +#endif // MITKLESIONMANAGER_H diff --git a/Modules/SemanticRelations/include/mitkRelationStorage.h b/Modules/SemanticRelations/include/mitkRelationStorage.h index fbd2fd46f3..6c65788c0a 100644 --- a/Modules/SemanticRelations/include/mitkRelationStorage.h +++ b/Modules/SemanticRelations/include/mitkRelationStorage.h @@ -1,83 +1,86 @@ /*=================================================================== 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 MITKRELATIONSTORAGE_H #define MITKRELATIONSTORAGE_H // semantic relations module #include "mitkDICOMHelper.h" #include "mitkSemanticTypes.h" // mitk core #include #include namespace mitk { class RelationStorage { public: void SetDataStorage(DataStorage::Pointer dataStorage); std::vector GetAllLesionsOfCase(const SemanticTypes::CaseID& caseID); SemanticTypes::Lesion GetRepresentedLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID); std::vector GetAllSegmentationsOfCase(const SemanticTypes::CaseID& caseID); SemanticTypes::ControlPoint GetControlPointOfData(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& dataNodeID); std::vector GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID); SemanticTypes::InformationType GetInformationTypeOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID); std::vector GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID); std::vector GetAllImagesOfCase(const SemanticTypes::CaseID& caseID); std::vector GetAllImageIDsOfCase(const SemanticTypes::CaseID& caseID); std::vector GetAllCaseIDs(); void AddCase(const SemanticTypes::CaseID& caseID); void AddImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageNodeID); void RemoveImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageNodeID); void AddSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationNodeID, const SemanticTypes::ID& parentDataNodeID); void RemoveSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationNodeID); void AddLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); void OverwriteLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); void LinkSegmentationToLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID, const SemanticTypes::Lesion& lesion); void UnlinkSegmentationFromLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID); void RemoveLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); + void RemoveLesionClass(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& lesionClassID); void AddControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); void OverwriteControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); void LinkDataToControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& dataNodeID, const SemanticTypes::ControlPoint& controlPoint); void UnlinkDataFromControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& dataNodeID); void RemoveControlPointFromCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); void AddInformationTypeToImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID, const SemanticTypes::InformationType informationType); void RemoveInformationTypeFromImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID); void RemoveInformationTypeFromCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType informationType); private: // access the storage and retrieve the case data, stored under the given case ID mitk::PropertyList::Pointer GetStorageData(const SemanticTypes::CaseID& caseID); + mitk::SemanticTypes::Lesion GenerateLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& lesionID); + DataStorage::Pointer m_DataStorage; }; } // namespace mitk #endif // MITKRELATIONSTORAGE_H diff --git a/Modules/SemanticRelations/include/mitkSemanticRelations.h b/Modules/SemanticRelations/include/mitkSemanticRelations.h index c27d17df6b..d10f03bbef 100644 --- a/Modules/SemanticRelations/include/mitkSemanticRelations.h +++ b/Modules/SemanticRelations/include/mitkSemanticRelations.h @@ -1,597 +1,604 @@ /*=================================================================== 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 MITKSEMANTICRELATIONS_H #define MITKSEMANTICRELATIONS_H #include // semantic relations module #include "mitkControlPointManager.h" #include "mitkDICOMHelper.h" #include "mitkISemanticRelationsObservable.h" #include "mitkISemanticRelationsObserver.h" #include "mitkRelationStorage.h" #include "mitkSemanticTypes.h" // mitk core #include #include namespace mitk { /* * @brief The API provides functions to query and manipulate image relations and instances, * that are helpful during follow-up examination, like control-points (time period), * types of the images or lesions that may be visible on multiple images. * * The class is able to generate IDs from given data nodes using DICOM information. * These IDs are used to identify the corresponding instances of a specific case. * The case can also be directly identified by the given case ID. * * In the BlackSwan context the case is identified with the DICOM PatientID. * * In order for most functions to work the case ID has to be defined in the model. If not, * the functions do nothing. */ class MITKSEMANTICRELATIONS_EXPORT SemanticRelations : public ISemanticRelationsObservable { public: SemanticRelations(mitk::DataStorage::Pointer dataStorage); ~SemanticRelations(); typedef std::vector LesionVector; + typedef std::vector LesionClassVector; typedef std::vector ControlpointVector; typedef std::vector InformationTypeVector; typedef std::vector DataNodeVector; /************************************************************************/ /* functions to implement the observer pattern */ /************************************************************************/ /* * @brief Adds the given concrete observer to the vector that holds all currently registered observer. * If the observer is already registered, it will not be added to the observer vector. * * @param observer The concrete observer to register. */ virtual void AddObserver(ISemanticRelationsObserver* observer) override; /* * @brief Removes the given concrete observer from the vector that holds all currently registered observer. * * @param observer The concrete observer to unregister. */ virtual void RemoveObserver(ISemanticRelationsObserver* observer) override; /************************************************************************/ /* functions to get instances / attributes */ /************************************************************************/ /* * @brief Returns a vector of all lesions that are currently available for the given case. * The lesions may be marked by a segmentation or may be empty - with no connection to a specific image / segmentation of the case data. * If no lesions are stored for the current case, an empty vector is returned. * * @param caseID The current case identifier is defined by the given string. * @return A vector of lesions. */ LesionVector GetAllLesionsOfCase(const SemanticTypes::CaseID& caseID) const; + /** + * @brief + * + * + */ + LesionClassVector GetAllLesionClassesOfCase(const SemanticTypes::CaseID& caseID) const; /* - * @brief Returns a vector of all lesions that are valid for the given case, given a specific lesion + * @brief Returns a vector of all lesions that are valid for the given case, given a specific control point. * * @param caseID The current case identifier is defined by the given string. * @param controlPoint A specific control point which has to be available at a returned (found) lesion: * Only those lesions are returned for which the image of the associated segmentation is linked to the given control point. * If the control point instance does not exist, an empty vector is returned. * @return A vector of control points. */ LesionVector GetAllLesionsOfCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) const; /* * @brief Returns a vector of all lesions that are currently available for the current case and are connected to the given image (via a segmentation). * If no lesions are stored for the current case, an empty vector is returned. If no segmentation nodes are * connected with the image node, no lesions for the specific image will be found and an empty vector is returned. * * @pre The given image data node has to be valid (!nullptr). - * @throw mitk::Exception if the given image data node is invalid (==nullptr). + * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). * @pre The image node has to have associated segmentation nodes (child nodes) in order to reference a lesion. * * @param imageNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. * @return A vector of lesions. */ LesionVector GetAllLesionsInImage(const DataNode* imageNode) const; /* * @brief Returns the lesion that is defined by the given segmentation data. * * @pre The given segmentation data node has to be valid (!nullptr). - * @throw mitk::Exception if the given segmentation data node is invalid (==nullptr). + * @throw SemanticRelationException, if the given segmentation data node is invalid (==nullptr). * @pre The segmentation data node has to represent a lesion. If not, the retrieved lesion will be empty, which leads to an exception. * @throw SemanticRelationException, if the segmentation does not represent an existing lesion (this can be checked via 'IsRepresentingALesion'). * * @param segmentationNode The segmentation identifier is extracted from the given data node. * @return The represented lesion. */ SemanticTypes::Lesion GetRepresentedLesion(const DataNode* segmentationNode) const; /* * @brief Check if the given segmentation refers to an existing lesion instance. * This function can be used before calling 'GetRepresentedLesion' in order to avoid a possible exception. * * @param segmentationNode The segmentation identifier is extracted from the given data node. * @return True, if the segmentation refers to an existing lesion; false otherwise. */ bool IsRepresentingALesion(const DataNode* segmentationNode) const; /* * @brief Return a vector of all segmentations that are currently available for the given case. * The segmentations may be connected / not connected to a lesion of the case. * If no segmentations are stored for the current case, an empty vector is returned. * * @pre The data storage member has to be valid (!nullptr). - * @throw mitk::Exception if the data storage member is invalid (==nullptr). + * @throw SemanticRelationException, if the data storage member is invalid (==nullptr). * * @param caseID The current case identifier is defined by the given string. * @return A vector of data nodes representing segmentations. */ mitk::SemanticRelations::DataNodeVector GetAllSegmentationsOfCase(const SemanticTypes::CaseID& caseID) const; /* * @brief Return a vector of all segmentations that define the given lesion. These segmentations don't have to be linked to the same image. * If the lesion is not referred to by any segmentation, an empty vector is returned. * * @pre The data storage member has to be valid (!nullptr). - * @throw mitk::Exception if the data storage member is invalid (==nullptr). + * @throw SemanticRelationException, if the data storage member is invalid (==nullptr). * @pre The UID of the lesion has to exist for a lesion instance. * @throw SemanticRelationException, if UID of the lesion does not exist for a lesion instance (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param lesion A Lesion with a UID that identifies the corresponding lesion instance. * @return A vector of data nodes representing segmentations that define the given lesion. */ DataNodeVector GetAllSegmentationsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const; /* * @brief Return a vector of all images that are currently available for the given case. * * @pre The data storage member has to be valid (!nullptr). - * @throw mitk::Exception if the data storage member is invalid (==nullptr). + * @throw SemanticRelationException, if the data storage member is invalid (==nullptr). * * @param caseID The current case identifier is defined by the given string. * @return A vector of data nodes representing images. */ DataNodeVector GetAllImagesOfCase(const SemanticTypes::CaseID& caseID) const; /* * @brief Return a vector of all images that are connected to those segmentations that are linked to the given lesion. * If the lesion is not referred to by any segmentation, an empty vector is returned. * * @pre The UID of the lesion has to exist for a lesion instance. * @throw SemanticRelationException, if UID of the lesion does not exist for a lesion instance (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param lesion A Lesion with a UID that identifies the corresponding lesion instance. * @return A vector of data nodes representing images on which the lesions are visible. */ DataNodeVector GetAllImagesOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const; /* * @brief Check if the given lesion instance exists. * This function can be used before calling 'GetAllSegmentationsOfLesion' in order to avoid a possible exception. * This function can be used before calling 'AddLesionInstance' in order to avoid a possible exception. * * @param caseID The current case identifier is defined by the given string. * @param lesion A Lesion with a UID that identifies the corresponding lesion instance. * @return True, if the lesion instance exists; false otherwise. */ bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const; /* * @brief Return a vector of all control points that are valid for the given case. * * @param caseID The current case identifier is defined by the given string. * @return A vector of control points. */ ControlpointVector GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID) const; /* * @brief Return a vector of all control points that are valid for the given case, given a specific lesion * * @param caseID The current case identifier is defined by the given string. * @param lesion A specific lesion which has to be available at a returned (found) control point: * Only those control points are returned for which an associated data has a segmentation that references the given lesion. * If the lesion does not exists, an empty vector is returned. * @return A vector of control points. */ ControlpointVector GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const; /* * @brief Return a vector of all control points that are valid for the given case, given a specific information type. * * @param caseID The current case identifier is defined by the given string. * @param informationType A specific information type which has to be available at a returned (found) control point: * Only those control points are returned for which an associated data has the given information type. * If the information type instance does not exists, an empty vector is returned. * @return A vector of control points. */ ControlpointVector GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) const; /* * @brief Return the control point of a data node. * If the data node is not linked to a control point or the data node refers to a non-existing control point, * a control point with an empty UID is returned. * * @pre The given data node has to be valid (!nullptr). - * @throw mitk::Exception if the given data node is invalid (==nullptr). + * @throw SemanticRelationException, if the given data node is invalid (==nullptr). * * @param dataNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. * @return The control point of the given data node. */ SemanticTypes::ControlPoint GetControlPointOfData(const DataNode* dataNode) const; /* * @brief Return a vector of all image nodes that link to the given control point. * If the control point is not referred to by any data node, an empty vector is returned. * * @pre The UID of the control point has to exist for a control point instance. * @throw SemanticRelationException, if the UID of the control point does not exist for a control point instance (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param controlPoint A control point with a UID that identifies the corresponding control point instance. * @return A vector of image nodes that link to the given control point. */ DataNodeVector GetAllImagesOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) const; /* * @brief Check if the given control point instance exists. * This function can be used before calling 'GetAllDataOfControlPoint' in order to avoid a possible exception. * This function can be used before adding, linking and unlinking control points to avoid a possible exception. * * @param caseID The current case identifier is defined by the given string. * @param controlPoint A control point with a UID that identifies the corresponding control point instance. * @return True, if the control point instance exists; false otherwise. */ bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) const; /* * @brief Return a vector of all information types that are valid for the given case. * * @param caseID The current case identifier is defined by the given string. * @return A vector of information types. */ InformationTypeVector GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID) const; /* * @brief Return a vector of all information types that are valid for the given case, given a specific control point. * * @param caseID The current case identifier is defined by the given string. * @param controlPoint A specific control point which has to be available at a returned (found) information type: * Only those information types are returned for which an associated data is linked to the given control point. * If the control point instance does not exist, an empty vector is returned. * @return A vector of information types. */ InformationTypeVector GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) const; /* * @brief Return the information type of the given image. * If the image does not contain any information type, an empty information type is returned. * * @pre The given image data node has to be valid (!nullptr). - * @throw mitk::Exception if the given image data node is invalid (==nullptr). + * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). * * @param imageNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. * @return The information type of the given data node. */ SemanticTypes::InformationType GetInformationTypeOfImage(const DataNode* imageNode) const; /* * @brief Return a vector of all image nodes that are defined with the given information type. * * @pre The information type has to exist for the given case (and is therefore used by at least one data node). * @throw SemanticRelationException, if the information type is not used by any data node (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param informationType An information type that identifies the corresponding information type instance. * @return A vector of image nodes that are defined with the given information type. */ DataNodeVector GetAllImagesOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) const; /* * @brief Return a vector of all image nodes that are defined with the given information type and with the given control point. * * @pre The UID of the control point has to exist for a control point instance. * The information type has to exist for the given case (and is therefore used by at least one data node). * @throw SemanticRelationException, if the UID of the control point does not exist for a control point instance (this can be checked via 'InstanceExists') or * if the information type is not used by any data node (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param controlPoint A control point with a UID that identifies the corresponding control point instance. * @param informationType An information type that identifies the corresponding information type instance. * @return A vector of image nodes that are defined with the given information type with the given control point. */ DataNodeVector GetAllSpecificImages(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const; /** * @brief Return a vector of all segmentation nodes that are defined with the given information type and with the given control point. * The function uses the 'GetAllSpecificImages'-function to retrieve the specific images and then searches for the derived nodes (segmentation child nodes). * * @pre The UID of the control point has to exist for a control point instance. * The information type has to exist for the given case (and is therefore used by at least one data node). * @throw SemanticRelationException, if the UID of the control point does not exist for a control point instance (this can be checked via 'InstanceExists') or * if the information type is not used by any data node (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param controlPoint A control point with a UID that identifies the corresponding control point instance. * @param informationType An information type that identifies the corresponding information type instance. * @return A vector of segmentation nodes that are defined with the given information type with the given control point. */ DataNodeVector GetAllSpecificSegmentations(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const; /* * @brief Check if the given information type exists. * This function can be used before calling 'GetAllDataOfInformationType' in order to avoid a possible exception. * * @param caseID The current case identifier is defined by the given string. * @param informationType An information type * @return True, if the information type exists; false otherwise. */ bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) const; /* * @brief Return a vector of all CaseIDs that are currently available. * * @return A vector of CaseIDs as strings. */ std::vector GetAllCaseIDs() const; /************************************************************************/ /* functions to add / remove instances / attributes */ /************************************************************************/ /* * @brief Add the given image to the set of already existing images. * The date is extracted from the DICOM data of the image node and is compared to already existing control points in the semantic relations model. * The function tries to find a fitting control point or to extend an already existing control point, if the extracted control point is close to * any other, already existing control point. * Finally, the image is linked to the correct control point. * * @pre The given image data node has to be valid (!nullptr). - * @throw mitk::Exception if the given image data node is invalid (==nullptr). + * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). * * @param imageNode The current case identifier and node identifier is extracted from the given image data node, which contains DICOM information about the case and the node. */ void AddImage(const DataNode* imageNode); /* * @brief Remove the given image from the set of already existing images. * * @pre The given image data node has to be valid (!nullptr). - * @throw mitk::Exception if the given image data node is invalid (==nullptr). + * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). * * @param imageNode The current case identifier and node identifier is extracted from the given image data node, which contains DICOM information about the case and the node. */ void RemoveImage(const DataNode* imageNode); /* * @brief Add a newly created lesion to the set of already existing lesions - with no connection to a specific image / segmentation of the case data. * * @pre The UID of the lesion must not already exist for a lesion instance. * @throw SemanticRelationException, it the UID of the lesion already exists for a lesion instance (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param lesion The lesion instance to add. */ void AddLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); /* * @brief Overwrite an already existing lesion instance (this may be useful to overwrite the lesion with a different lesion class). * * @pre The UID of the lesion has to exist for a lesion instance. * @throw SemanticRelationException, if the UID of the lesion does not exist for a lesion instance (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param lesion The lesion instance that overwrites an existing lesion. */ void OverwriteLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); /* * @brief Add a newly created lesion to the set of already existing lesions. The lesion is added and a reference to * the lesion is added to the segmentation data. If the segmentation is already linked to a lesion, the * old linkage is overwritten (this can be checked via 'IsRepresentingALesion'). * * @pre The given segmentation data node has to be valid (!nullptr). - * @throw mitk::Exception if the given segmentation data node is invalid (==nullptr). + * @throw SemanticRelationException, if the given segmentation data node is invalid (==nullptr). * @pre The UID of the lesion must not already exist for a lesion instance. * @throw SemanticRelationException, if the UID of the lesion already exists for a lesion instance (this can be checked via 'InstanceExists'). * * @param segmentationNode The segmentation identifier is extracted from the given data node. The segmentation node has DICOM information from its parent node. * @param lesion The lesion instance to add and link. */ void AddLesionAndLinkSegmentation(const DataNode* segmentationNode, const SemanticTypes::Lesion& lesion); /* * @brief Remove the given lesion from the set of already existing lesions. * * @pre The UID of the lesion has to exist for a lesion instance. * @throw SemanticRelationException, if the UID of the lesion does not exist for a lesion instance (this can be checked via 'InstanceExists'). * @pre The function needs to assure that no segmentation is still representing (linked to) this lesion. * @throw SemanticRelationException, if the lesion instance to remove is still linked to by any segmentation (this can be checked via 'GetAllSegmentationsOfLesion'). * * @param caseID The current case identifier is defined by the given string. * @param lesion The lesion instance to remove. */ void RemoveLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); /* * @brief Add a segmentation instance to the set of already existing segmentations - with no connection to a specific lesion. * * @param segmentationNode The segmentation identifier is extracted from the given data node. The segmentation node has DICOM information from its parent node. * @param parentNode The node identifier of the parent node is extracted from the given parent data node. */ void AddSegmentation(const DataNode* segmentationNode, const DataNode* parentNode); /* * @brief Link the given segmentation instance to an an already existing lesion instance. If the segmentation is already linked to a lesion instance, the * old linkage is overwritten (this can be checked via 'IsRepresentingALesion'). * * @pre The given segmentation data node has to be valid (!nullptr). - * @throw mitk::Exception if the given segmentation data node is invalid (==nullptr). + * @throw SemanticRelationException, if the given segmentation data node is invalid (==nullptr). * @pre The UID of the lesion has to exist for a lesion instance. * @throw SemanticRelationException, if the UID of the lesion does not exist for a lesion instance (this can be checked via 'InstanceExists'). * * @param segmentationNode The segmentation identifier is extracted from the given data node. The segmentation node has DICOM information from its parent node. * @param lesion The lesion instance to link. */ void LinkSegmentationToLesion(const DataNode* segmentationNode, const SemanticTypes::Lesion& lesion); /* * @brief Unlink the given segmentation instance from the linked lesion instance. * The lesion may stay unlinked to any segmentation. * * @pre The given segmentation data node has to be valid (!nullptr). - * @throw mitk::Exception if the given segmentation data node is invalid (==nullptr). + * @throw SemanticRelationException, if the given segmentation data node is invalid (==nullptr). * * @param segmentationNode The segmentation identifier is extracted from the given data node. The segmentation node has DICOM information from its parent node. */ void UnlinkSegmentationFromLesion(const DataNode* segmentationNode); /* * @brief Remove the given segmentation from the set of already existing segmentations. * * @pre The given segmentation data node has to be valid (!nullptr). - * @throw mitk::Exception if the given segmentation data node is invalid (==nullptr). + * @throw SemanticRelationException, if the given segmentation data node is invalid (==nullptr). * * @param segmentationNode The segmentation identifier is extracted from the given data node. The segmentation node has DICOM information from its parent node. */ void RemoveSegmentation(const DataNode* segmentationNode); /* * @brief Add a newly created control point to the set of already existing control points. A reference to the control point is added to the given data. * This function combines adding a control point and linking it, since a control point with no associated data is not allowed. * * @pre The given data node has to be valid (!nullptr). - * @throw mitk::Exception if the given data node is invalid (==nullptr). + * @throw SemanticRelationException, if the given data node is invalid (==nullptr). * @pre The UID of the control point must not already exist for a control point instance. * @throw SemanticRelationException, if the UID of the control point already exists for a control point instance (this can be checked via 'InstanceExists'). * @pre The given control point must not already be contained in an existing control point interval. * @throw SemanticRelationException, if the given control point is already contained in an existing control point interval (this can be checked via 'CheckContainingControlPoint'). * @pre The given control point must contain the date of the given data node (if parameter 'checkConsistence = true'). * @throw SemanticRelationException, if the given control point does not contain the date of the given data node and 'checkConsistence = true' (this can be checked via 'ControlPointManager::InsideControlPoint'). * * @param dataNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. * @param controlPoint The control point instance to add. For a newly added control point always has "startDate = endDate". * @param checkConsistence If true, the function checks, whether the date of the data node actually lies inside the control point to link. */ void AddControlPointAndLinkData(const DataNode* dataNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence = true); /* * @brief Link the given data to an already existing control point and overwrite the start or end point of the control point. * * @pre The given data node has to be valid (!nullptr). - * @throw mitk::Exception if the given data node is invalid (==nullptr). + * @throw SemanticRelationException, if the given data node is invalid (==nullptr). * @pre The UID of the control point has to exist for a control point instance. * @throw SemanticRelationException, if the UID of the control point does not exists for a control point instance (this can be checked via 'InstanceExists'). * @pre The given control point must contain the date of the given data node (if parameter 'checkConsistence = true'). * @throw SemanticRelationException, if the given control point does not contain the date of the given data node and 'checkConsistence = true' (this can be checked via 'ControlPointManager::InsideControlPoint'). * @pre The given control point must differ from the overwritten control point, but only either in the start point or in the end point. * It is not possible to overwrite a single control point from a single date and simultaneously changing both ends of the time period. * @throw SemanticRelationException, if the given control point does not differ from the overwritten control point or if the given control point differs in the start date and the end date from the overwritten control point. * @pre The given control point must not overlap with an already existing control point. * @throw SemanticRelationException, if the given control point overlaps with an already existing control point interval (this can be checked via 'CheckOverlappingControlPoint'). * * @param dataNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. * @param controlPoint The control point instance that overwrites an existing control point. * @param checkConsistence If true, the function checks, whether the date of the data node actually lies inside the control point to link. */ void OverwriteControlPointAndLinkData(const DataNode* dataNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence = true); /* * @brief Link the given data to an already existing control point. * * @pre The given data node has to be valid (!nullptr). - * @throw mitk::Exception if the given data node is invalid (==nullptr). + * @throw SemanticRelationException, if the given data node is invalid (==nullptr). * @pre The UID of the control point has to exist for a control point instance. * @throw SemanticRelationException, if the UID of the control point does not exists for a control point instance (this can be checked via 'InstanceExists'). * @pre The given control point must contain the date of the given data node (if parameter 'checkConsistence = true'). * @throw SemanticRelationException, if the given control point does not contain the date of the given data node and 'checkConsistence = true' (this can be checked via 'ControlPointManager::InsideControlPoint'). * * @param dataNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. * @param controlPoint The control point instance to add link. * @param checkConsistence If true, the function checks, whether the date of the data node actually lies inside the control point to link. */ void LinkDataToControlPoint(const DataNode* dataNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence = true); /* * @brief Unlink the given image from the linked control point. * If data is unlinked from a control point, the function needs to check whether the control point is still linked to any other data: * - if not, the control point instance will be removed (has to be removed since a control point with no associated data is not allowed). * - if so, the function has to make sure that the control point instance is shortened to its minimum time period (e.g. moving the end point to an earlier date). * * @param dataNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. */ void UnlinkDataFromControlPoint(const DataNode* dataNode); /* * @brief Set the information type of the given image. * * @pre The given image data node has to be valid (!nullptr). - * @throw mitk::Exception if the given image data node is invalid (==nullptr). + * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). * @post If the information type instance did not exist before, it is now added. * * @param imageNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. * @param informationType An information type that identifies the corresponding information type instance. */ void AddInformationTypeToImage(const DataNode* imageNode, const SemanticTypes::InformationType& informationType); /* * @brief Remove the information type of the given image. * If the information type is removed, the function needs to check whether the information type is referenced by any other image: * - if not, the information type instance can be removed (has to be removed since an information type with no associated image is not allowed). * - if so, the information type is just removed from the given image. * * @pre The given image data node has to be valid (!nullptr). - * @throw mitk::Exception if the given image data node is invalid (==nullptr). + * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). * * @param imageNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. */ void RemoveInformationTypeFromImage(const DataNode* imageNode); private: // the relation storage serves as a storage accessor and can be sub-classed for custom demands std::shared_ptr m_RelationStorage; DataStorage::Pointer m_DataStorage; /* * @brief A vector that stores the currently registered observer of this observable subject. */ static std::vector m_ObserverVector; /* * @brief The SemanticRelations, as an example of an observable subject, notifies (updates) the observer with a given case ID. * The view's caseID was set before in the GUI. The parts of the view that observe changes in the semantic relations are only updated, * if the given case ID is equal to the observer's current caseID and thus the observer currently shows the semantic information of the given case. * * @param caseID The caseID that identifies the currently active patient / case. */ virtual void NotifyObserver(const mitk::SemanticTypes::CaseID& caseID) const override; /* * @brief Determine if the given control point contains images, which are connected to segmentations that represent the given lesion. * If the lesion or the control point are not correctly stored, the function returns false. * * @param caseID The current case identifier is defined by the given string. * @param lesion A Lesion with a UID that identifies the corresponding lesion instance. * @param controlPoint A control point with a UID that identifies the corresponding control point instance. * * @return True, if the given control point contains data that is related to the given lesion; false otherwise. */ bool ControlPointContainsLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ControlPoint& controlPoint) const; /* * @brief Determine if the given control point contains images, which refer to the given information type. * If the information type or the control point are not correctly stored, the function returns false. * * @param caseID The current case identifier is defined by the given string. * @param informationType An information type that identifies the corresponding information type instance. * @param controlPoint A control point with a UID that identifies the corresponding control point instance. * * @return True, if the given control point contains data that is related to the given information type; false otherwise. */ bool ControlPointContainsInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType, const SemanticTypes::ControlPoint& controlPoint) const; /* * @brief Check if the given control point overlaps with an already existing control point. * If the UID already exists, then the given control point may overlap with the previous or next control point, * because the start point or the end point of the given control point might be modified (different to the same control point * that is stored at the moment). * If the UID does not already exists, the previous and next control point are found by comparing the dates of the already * existing control points and the given control point. * * @param caseID The current case identifier is defined by the given string. * @param controlPoint A control point with a UID that identifies the corresponding control point instance. */ bool CheckOverlappingControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); /* * @brief Check if the given control point is contained within an already existing control point. * If the UID already exists, then the given control point is contained in this same control point. * However, the function does not check if the given control point is really contained (just compares UID) in this case. * If the UID does not already exist, the already existing control points are tested to see if they contain the * given control point. * * @param caseID The current case identifier is defined by the given string. * @param controlPoint A control point with a UID that identifies the corresponding control point instance. */ bool CheckContainingControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); }; } // namespace mitk #endif // MITKSEMANTICRELATIONS_H diff --git a/Modules/SemanticRelations/include/mitkSemanticTypes.h b/Modules/SemanticRelations/include/mitkSemanticTypes.h index e7255a23ef..fce79141d7 100644 --- a/Modules/SemanticRelations/include/mitkSemanticTypes.h +++ b/Modules/SemanticRelations/include/mitkSemanticTypes.h @@ -1,115 +1,116 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKSEMANTICTYPES_H #define MITKSEMANTICTYPES_H // c++ #include #include #include #include #include namespace mitk { namespace SemanticTypes { typedef std::string ID; typedef std::string CaseID; // an ID of the current case (e.g. the DICOM PatientID) typedef std::string InformationType; /* * @brief The date type to be used for control points. */ struct Date { ID UID; int year = 0; int month = 0; int day = 0; bool operator<(const Date& other) const { return std::tie(year, month, day) < std::tie(other.year, other.month, other.day); } bool operator>(const Date& other) const { return std::tie(year, month, day) > std::tie(other.year, other.month, other.day); } bool operator==(const Date& other) const { return (!operator<(other) && !operator>(other)); } bool operator!=(const Date& other) const { return (operator<(other) || operator>(other)); } bool operator<=(const Date& other) const { return (operator<(other) || operator==(other)); } bool operator>=(const Date& other) const { return (operator>(other) || operator==(other)); } }; /* * @brief The concept of a control point. */ struct ControlPoint // alternatively: ExaminationPoint { ID UID; Date startPoint; Date endPoint; bool operator<(const ControlPoint& other) const { return startPoint < other.startPoint; } bool operator!=(const ControlPoint& other) const { return (startPoint != other.startPoint) || (endPoint != other.endPoint); } bool operator==(const ControlPoint& other) const { return (startPoint == other.startPoint) && (endPoint == other.endPoint); } }; /* - * @brief The concept of a lesion. + * @brief The concept of a lesion class. */ struct LesionClass { ID UID; std::string classType = ""; // could be a "focal lesion" or "diffuse lesion" in the BlackSwan context }; /* * @brief The concept of a lesion. */ struct Lesion { ID UID; + std::string name = ""; LesionClass lesionClass; }; } // namespace SemanticTypes } // namespace mitk #endif // MITKSEMANTICTYPES_H diff --git a/Modules/SemanticRelations/src/mitkDICOMHelper.cpp b/Modules/SemanticRelations/src/mitkDICOMHelper.cpp index 1820278a67..cc05dc88cd 100644 --- a/Modules/SemanticRelations/src/mitkDICOMHelper.cpp +++ b/Modules/SemanticRelations/src/mitkDICOMHelper.cpp @@ -1,162 +1,163 @@ /*=================================================================== 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::Date GetDateFromString(const std::string& dateAsString); mitk::SemanticTypes::CaseID mitk::GetCaseIDFromDataNode(const mitk::DataNode* dataNode) { if (nullptr == dataNode) { - mitkThrow() << "Not a valid data node."; + mitkThrowException(SemanticRelationException) << "Not a valid data node."; } mitk::BaseData* baseData = dataNode->GetData(); if (nullptr == baseData) { - mitkThrow() << "No valid base data."; + mitkThrowException(SemanticRelationException) << "No valid base data."; } // extract suitable DICOM tag to use as the case id // two alternatives can be used: // - DICOM tag "0x0010, 0x0010" is PatientName // - DICOM tag "0x0010, 0x0020" is PatientID // in the current implementation the PatientID (0x0010, 0x0010) is used mitk::BaseProperty* dicomTag = baseData->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str()); if (nullptr == dicomTag) { - mitkThrow() << "Not a valid DICOM property."; + 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) { - mitkThrow() << "Not a valid data node."; + mitkThrowException(SemanticRelationException) << "Not a valid data node."; } mitk::BaseData* baseData = dataNode->GetData(); if (nullptr == baseData) { - mitkThrow() << "No valid base data."; + mitkThrowException(SemanticRelationException) << "No valid base data."; } // extract suitable DICOM tag to use as the data node id // DICOM tag "0x0020, 0x000e" is SeriesInstanceUID mitk::BaseProperty* dicomTag = baseData->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str()); if (nullptr == dicomTag) { - mitkThrow() << "Not a valid DICOM property."; + mitkThrowException(SemanticRelationException) << "Not a valid DICOM property."; } std::string dicomTagAsString = dicomTag->GetValueAsString(); return dicomTagAsString; } mitk::SemanticTypes::Date mitk::GetDICOMDateFromDataNode(const mitk::DataNode* dataNode) { if (nullptr == dataNode) { - mitkThrow() << "Not a valid data node."; + mitkThrowException(SemanticRelationException) << "Not a valid data node."; } mitk::BaseData* baseData = dataNode->GetData(); if (nullptr == baseData) { - mitkThrow() << "No valid base data."; + mitkThrowException(SemanticRelationException) << "No valid base data."; } // extract suitable DICOM tag to use as the data node id // DICOM tag "0x0008, 0x0022" is AcquisitionDate mitk::BaseProperty* acquisitionDateProperty = baseData->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str()); if (nullptr == acquisitionDateProperty) { - mitkThrow() << "Not a valid DICOM property."; + mitkThrowException(SemanticRelationException) << "Not a valid DICOM property."; } std::string acquisitionDateAsString = acquisitionDateProperty->GetValueAsString(); return GetDateFromString(acquisitionDateAsString); } mitk::SemanticTypes::InformationType mitk::GetDICOMModalityFromDataNode(const mitk::DataNode* dataNode) { if (nullptr == dataNode) { - mitkThrow() << "Not a valid data node."; + mitkThrowException(SemanticRelationException) << "Not a valid data node."; } mitk::BaseData* baseData = dataNode->GetData(); if (nullptr == baseData) { - mitkThrow() << "No valid base data."; + mitkThrowException(SemanticRelationException) << "No valid base data."; } // extract suitable DICOM tag to use as the information type // DICOM tag "0x0008, 0x0060" is Modality mitk::BaseProperty* dicomTag = baseData->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str()); if (nullptr == dicomTag) { - mitkThrow() << "Not a valid DICOM property."; + mitkThrowException(SemanticRelationException) << "Not a valid DICOM property."; } std::string dicomTagAsString = dicomTag->GetValueAsString(); return dicomTagAsString; } std::string mitk::TrimDICOM(const std::string& identifier) { if (identifier.empty()) { return identifier; } // leading whitespace std::size_t first = identifier.find_first_not_of(' '); if (std::string::npos == first) { return ""; } // trailing whitespace std::size_t last = identifier.find_last_not_of(' '); return identifier.substr(first, last - first + 1); } mitk::SemanticTypes::Date GetDateFromString(const std::string& dateAsString) { if (dateAsString.size() != 8) // string does not represent a DICOM date { return mitk::SemanticTypes::Date(); } mitk::SemanticTypes::Date date; date.UID = mitk::UIDGeneratorBoost::GenerateUID(); // date expected to be YYYYMMDD (8 characters) date.year = std::strtoul(dateAsString.substr(0, 4).c_str(), nullptr, 10); date.month = std::strtoul(dateAsString.substr(4, 2).c_str(), nullptr, 10); date.day = std::strtoul(dateAsString.substr(6, 2).c_str(), nullptr, 10); return date; } diff --git a/Modules/SemanticRelations/src/mitkLesionManager.cpp b/Modules/SemanticRelations/src/mitkLesionManager.cpp new file mode 100644 index 0000000000..a98c5797f5 --- /dev/null +++ b/Modules/SemanticRelations/src/mitkLesionManager.cpp @@ -0,0 +1,83 @@ +/*=================================================================== + +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 + +// mitk core +#include +#include + +// c++ +#include + +mitk::SemanticTypes::Lesion mitk::GenerateNewLesion(const std::string& lesionClassType/* = ""*/) +{ + mitk::SemanticTypes::Lesion lesion; + lesion.UID = mitk::UIDGeneratorBoost::GenerateUID(); + lesion.name = lesion.UID; + lesion.lesionClass = mitk::SemanticTypes::LesionClass(); + lesion.lesionClass.UID = mitk::UIDGeneratorBoost::GenerateUID(); + lesion.lesionClass.classType = lesionClassType; + + return lesion; +} + +mitk::SemanticTypes::LesionClass mitk::GenerateNewLesionClass(const std::string& lesionClassType/* = ""*/) +{ + mitk::SemanticTypes::LesionClass lesionClass; + lesionClass.UID = mitk::UIDGeneratorBoost::GenerateUID(); + lesionClass.classType = lesionClassType; + + return lesionClass; +} + +mitk::SemanticTypes::Lesion mitk::GetLesionByUID(const SemanticTypes::ID& lesionUID, const std::vector& allLesions) +{ + auto lambda = [&lesionUID](const SemanticTypes::Lesion& currentLesion) + { + return currentLesion.UID == lesionUID; + }; + + const auto existingLesion = std::find_if(allLesions.begin(), allLesions.end(), lambda); + + mitk::SemanticTypes::Lesion lesion; + if (existingLesion != allLesions.end()) + { + lesion = *existingLesion; + } + + return lesion; +} + +mitk::SemanticTypes::LesionClass mitk::FindExistingLesionClass(const std::string& lesionClassType, const std::vector& allLesionClasses) +{ + auto lambda = [&lesionClassType](const SemanticTypes::LesionClass& currentLesionClass) + { + return currentLesionClass.classType == lesionClassType; + }; + + const auto existingLesionClass = std::find_if(allLesionClasses.begin(), allLesionClasses.end(), lambda); + + mitk::SemanticTypes::LesionClass lesionClass; + if (existingLesionClass != allLesionClasses.end()) + { + lesionClass = *existingLesionClass; + } + + return lesionClass; +} diff --git a/Modules/SemanticRelations/src/mitkRelationStorage.cpp b/Modules/SemanticRelations/src/mitkRelationStorage.cpp index 1ab66219ff..f341d4b0a6 100644 --- a/Modules/SemanticRelations/src/mitkRelationStorage.cpp +++ b/Modules/SemanticRelations/src/mitkRelationStorage.cpp @@ -1,1351 +1,1401 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // semantic relations module #include "mitkRelationStorage.h" #include "mitkNodePredicates.h" // multi label module #include // mitk core #include #include // c++ #include #include void mitk::RelationStorage::SetDataStorage(DataStorage::Pointer dataStorage) { if (m_DataStorage != dataStorage) { // set the new data storage m_DataStorage = dataStorage; } } std::vector mitk::RelationStorage::GetAllLesionsOfCase(const SemanticTypes::CaseID& caseID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return std::vector(); } // retrieve a vector property that contains the valid lesion-IDs for the current case mitk::VectorProperty* vectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); if (nullptr == vectorProperty) { MITK_INFO << "Could not find any lesion in the storage."; return std::vector(); } std::vector vectorValue = vectorProperty->GetValue(); std::vector allLesionsOfCase; for (const auto& lesionID : vectorValue) { - // the lesion class ID is stored under the lesion ID - mitk::StringProperty* lesionClassIDProperty = dynamic_cast(propertyList->GetProperty(lesionID)); - if (nullptr == lesionClassIDProperty) + SemanticTypes::Lesion generatedLesion = GenerateLesion(caseID, lesionID); + if (!generatedLesion.UID.empty()) { - MITK_INFO << "Incorrect lesion storage. Lesion " << lesionID << " can not be created and retrieved"; - continue; - } - - // the lesion class type is stored under the lesion class ID - std::string lesionClassID = lesionClassIDProperty->GetValue(); - mitk::StringProperty* lesionClassProperty = dynamic_cast(propertyList->GetProperty(lesionClassID)); - - if (nullptr != lesionClassProperty) - { - SemanticTypes::LesionClass generatedLesionClass; - generatedLesionClass.UID = lesionClassID; - generatedLesionClass.classType = lesionClassProperty->GetValue(); - - SemanticTypes::Lesion generatedLesion; - generatedLesion.UID = lesionID; - generatedLesion.lesionClass = generatedLesionClass; - allLesionsOfCase.push_back(generatedLesion); } - else - { - MITK_INFO << "Incorrect lesion class storage. Lesion " << lesionID << " can not be created and retrieved"; - } } return allLesionsOfCase; } mitk::SemanticTypes::Lesion mitk::RelationStorage::GetRepresentedLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return SemanticTypes::Lesion(); } // retrieve a vector property that contains the referenced ID of a segmentation (0. image ID 1. lesion ID) mitk::VectorProperty* segmentationVectorProperty = dynamic_cast*>(propertyList->GetProperty(segmentationID)); if (nullptr == segmentationVectorProperty) { MITK_INFO << "Could not find the segmentation node " << segmentationID << " in the storage."; return SemanticTypes::Lesion(); } std::vector segmentationVectorValue = segmentationVectorProperty->GetValue(); // the lesion ID of a segmentation is the second value in the vector if (segmentationVectorValue.size() != 2) { MITK_INFO << "Incorrect segmentation storage. Not two (2) IDs stored."; return SemanticTypes::Lesion(); } else { - SemanticTypes::Lesion representedLesion; - std::string lesionID = segmentationVectorValue[1]; - // the lesion class ID is stored under the lesion ID - std::string lesionClassID; - propertyList->GetStringProperty(lesionID.c_str(), lesionClassID); - representedLesion.UID = lesionID; - - // the lesion class type is stored under the lesion class ID - std::string lesionClass; - propertyList->GetStringProperty(lesionClassID.c_str(), lesionClass); - - SemanticTypes::LesionClass representedLesionClass; - representedLesionClass.UID = lesionClassID; - representedLesionClass.classType = lesionClass; - - representedLesion.lesionClass = representedLesionClass; - - return representedLesion; + return GenerateLesion(caseID, lesionID); } } std::vector mitk::RelationStorage::GetAllSegmentationsOfCase(const SemanticTypes::CaseID& caseID) { if (m_DataStorage.IsNull()) { MITK_INFO << "No valid data storage found in the mitkPersistenceService-class. Segmentations of the current case can not be retrieved."; return std::vector(); } mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return std::vector(); } // retrieve a vector property that contains the valid segmentation-IDs for the current case mitk::VectorProperty* segmentationsProperty = dynamic_cast*>(propertyList->GetProperty("segmentations")); if (nullptr == segmentationsProperty) { MITK_INFO << "Could not find any segmentation in the storage."; return std::vector(); } std::vector allSegmentationIDsOfCase = segmentationsProperty->GetValue(); std::vector allSegmentationsOfCase; // get all segmentation nodes of the current data storage // only those nodes are respected, that are currently held in the data storage DataStorage::SetOfObjects::ConstPointer segmentationNodes = m_DataStorage->GetSubset(NodePredicates::GetSegmentationPredicate()); for (auto it = segmentationNodes->Begin(); it != segmentationNodes->End(); ++it) { DataNode* segmentationNode = it->Value(); try { // find the corresponding segmentation node for the given segmentation ID std::string nodeCaseID = GetCaseIDFromDataNode(segmentationNode); std::string nodeSegmentationID = GetIDFromDataNode(segmentationNode); if (nodeCaseID == caseID && (std::find(allSegmentationIDsOfCase.begin(), allSegmentationIDsOfCase.end(), nodeSegmentationID) != allSegmentationIDsOfCase.end())) { // found current image node in the storage, add it to the return vector allSegmentationsOfCase.push_back(segmentationNode); } } catch (const std::exception&) { // found a segmentation node that is not stored in the semantic relations // this segmentation node does not have any DICOM information --> exception thrown // continue with the next segmentation to compare IDs continue; } } return allSegmentationsOfCase; } mitk::SemanticTypes::ControlPoint mitk::RelationStorage::GetControlPointOfData(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& dataNodeID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return SemanticTypes::ControlPoint(); } // retrieve a vector property that contains the information type and the referenced ID of a data node (0. information type 1. control point ID) mitk::VectorProperty* dataNodeVectorProperty = dynamic_cast*>(propertyList->GetProperty(dataNodeID)); if (nullptr == dataNodeVectorProperty) { MITK_INFO << "Could not find the data node " << dataNodeID << " in the storage."; return SemanticTypes::ControlPoint(); } std::vector dataNodeVectorValue = dataNodeVectorProperty->GetValue(); SemanticTypes::ControlPoint controlPoint; // an image node has to have exactly two values (the information type and the ID of the control point) if (dataNodeVectorValue.size() != 2) { MITK_INFO << "Incorrect data storage. Not two (2) values stored."; return SemanticTypes::ControlPoint(); } else { // the second value of the data node vector is the ID of the referenced control point std::string controlPointID = dataNodeVectorValue[1]; // retrieve a vector property that contains the referenced ID of the dates of a control point (0. startPoint ID 1. endPoint ID) mitk::VectorProperty* controlPointVectorProperty = dynamic_cast*>(propertyList->GetProperty(controlPointID)); if (nullptr == controlPointVectorProperty) { MITK_INFO << "Could not find the control point " << controlPointID << " in the storage."; return SemanticTypes::ControlPoint(); } std::vector controlPointVectorValue = controlPointVectorProperty->GetValue(); // a control point has to have exactly two values (the ID of two dates) if (controlPointVectorValue.size() != 2) { MITK_INFO << "Incorrect control point storage. Not two (2) IDs of the start point and the end point are stored."; return SemanticTypes::ControlPoint(); } else { // retrieve the start date std::string startPointID = controlPointVectorValue[0]; mitk::VectorProperty* startPointVectorProperty = dynamic_cast*>(propertyList->GetProperty(startPointID)); if (nullptr == startPointVectorProperty) { MITK_INFO << "Could not find the start date " << startPointID << " of the control point " << controlPointID << " in the storage."; return SemanticTypes::ControlPoint(); } std::vector startPointValue = startPointVectorProperty->GetValue(); SemanticTypes::Date startPoint; // a date has to have exactly three integer values if (startPointValue.size() != 3) { MITK_INFO << "Incorrect start point storage. Not three (3) values of the date are stored."; return SemanticTypes::ControlPoint(); } else { startPoint.UID = startPointID; startPoint.year = startPointValue[0]; startPoint.month = startPointValue[1]; startPoint.day = startPointValue[2]; } // retrieve the end date std::string endPointID = controlPointVectorValue[1]; mitk::VectorProperty* endPointVectorProperty = dynamic_cast*>(propertyList->GetProperty(endPointID)); if (nullptr == endPointVectorProperty) { MITK_INFO << "Could not find the end date " << endPointID << " of the control point " << controlPointID << " in the storage."; return SemanticTypes::ControlPoint(); } std::vector endPointValue = endPointVectorProperty->GetValue(); SemanticTypes::Date endPoint; // a date has to have exactly three integer values if (endPointValue.size() != 3) { MITK_INFO << "Incorrect end point storage. Not three (3) values of the date are stored."; return SemanticTypes::ControlPoint(); } else { endPoint.UID = endPointID; endPoint.year = endPointValue[0]; endPoint.month = endPointValue[1]; endPoint.day = endPointValue[2]; } // set the values of the control point controlPoint.UID = controlPointID; controlPoint.startPoint = startPoint; controlPoint.endPoint = endPoint; } } return controlPoint; } std::vector mitk::RelationStorage::GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return std::vector(); } // retrieve a vector property that contains the valid control point-IDs for the current case mitk::VectorProperty* vectorProperty = dynamic_cast*>(propertyList->GetProperty("controlpoints")); if (nullptr == vectorProperty) { MITK_INFO << "Could not find any control points in the storage."; return std::vector(); } std::vector vectorValue = vectorProperty->GetValue(); std::vector allControlPoints; for (const auto& controlPointID : vectorValue) { // retrieve a vector property that contains the referenced ID of the dates of a control point (0. startPoint ID 1. endPoint ID) mitk::VectorProperty* controlPointVectorProperty = dynamic_cast*>(propertyList->GetProperty(controlPointID)); if (nullptr == controlPointVectorProperty) { MITK_INFO << "Could not find the control point " << controlPointID << " in the storage."; continue; } std::vector controlPointVectorValue = controlPointVectorProperty->GetValue(); // a control point has to have exactly two values (the ID of two dates) if (controlPointVectorValue.size() != 2) { MITK_INFO << "Incorrect control point storage. Not two (2) IDs of the start point and the end point are stored."; continue; } else { // retrieve the start date std::string startPointID = controlPointVectorValue[0]; mitk::VectorProperty* startPointVectorProperty = dynamic_cast*>(propertyList->GetProperty(startPointID)); if (nullptr == startPointVectorProperty) { MITK_INFO << "Could not find the start date " << startPointID << " of the control point " << controlPointID << " in the storage."; continue; } std::vector startPointValue = startPointVectorProperty->GetValue(); SemanticTypes::Date startPoint; // a date has to have exactly three integer values if (startPointValue.size() != 3) { MITK_INFO << "Incorrect start point storage. Not three (3) values of the date are stored."; continue; } else { startPoint.UID = startPointID; startPoint.year = startPointValue[0]; startPoint.month = startPointValue[1]; startPoint.day = startPointValue[2]; } // retrieve the end date std::string endPointID = controlPointVectorValue[1]; mitk::VectorProperty* endPointVectorProperty = dynamic_cast*>(propertyList->GetProperty(endPointID)); if (nullptr == endPointVectorProperty) { MITK_INFO << "Could not find the end date " << endPointID << " of the control point " << controlPointID << " in the storage."; continue; } std::vector endPointValue = endPointVectorProperty->GetValue(); SemanticTypes::Date endPoint; // a date has to have exactly three integer values if (endPointValue.size() != 3) { MITK_INFO << "Incorrect end point storage. Not three (3) values of the date are stored."; continue; } else { endPoint.UID = endPointID; endPoint.year = endPointValue[0]; endPoint.month = endPointValue[1]; endPoint.day = endPointValue[2]; } SemanticTypes::ControlPoint generatedControlPoint; generatedControlPoint.UID = controlPointID; generatedControlPoint.startPoint = startPoint; generatedControlPoint.endPoint = endPoint; allControlPoints.push_back(generatedControlPoint); } } return allControlPoints; } mitk::SemanticTypes::InformationType mitk::RelationStorage::GetInformationTypeOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return SemanticTypes::InformationType(); } // retrieve a vector property that contains the information type and the referenced ID of an image data node (0. information type 1. control point ID) mitk::VectorProperty* dataNodeVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); if (nullptr == dataNodeVectorProperty) { MITK_INFO << "Could not find the image " << imageID << " in the storage."; return SemanticTypes::InformationType(); } std::vector dataNodeVectorValue = dataNodeVectorProperty->GetValue(); // an image node has to have exactly two values (the information type and the ID of the control point) if (dataNodeVectorValue.size() != 2) { MITK_INFO << "Incorrect data storage. Not two (2) values stored."; return SemanticTypes::InformationType(); } else { // the first value of the data node vector is the information type return dataNodeVectorValue[0]; } } std::vector mitk::RelationStorage::GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return std::vector(); } // retrieve a vector property that contains the valid information types of the current case mitk::VectorProperty* informationTypeVectorProperty = dynamic_cast*>(propertyList->GetProperty("informationtypes")); if (nullptr == informationTypeVectorProperty) { MITK_INFO << "Could not find any information types in the storage."; return std::vector(); } return informationTypeVectorProperty->GetValue(); } std::vector mitk::RelationStorage::GetAllImagesOfCase(const SemanticTypes::CaseID& caseID) { if (m_DataStorage.IsNull()) { MITK_INFO << "No valid data storage found in the mitkPersistenceService-class. Images of the current case can not be retrieved."; return std::vector(); } std::vector allImageIDsOfCase = GetAllImageIDsOfCase(caseID); std::vector allImagesOfCase; // get all image nodes of the current data storage // only those nodes are respected, that are currently held in the data storage DataStorage::SetOfObjects::ConstPointer imageNodes = m_DataStorage->GetSubset(NodePredicates::GetImagePredicate()); for (auto it = imageNodes->Begin(); it != imageNodes->End(); ++it) { DataNode* imageNode = it->Value(); // find the corresponding image node for the given segmentation ID std::string nodeCaseID = GetCaseIDFromDataNode(imageNode); std::string nodeImageID = GetIDFromDataNode(imageNode); if (nodeCaseID == caseID && (std::find(allImageIDsOfCase.begin(), allImageIDsOfCase.end(), nodeImageID) != allImageIDsOfCase.end())) { // found current image node in the storage, add it to the return vector allImagesOfCase.push_back(imageNode); } } return allImagesOfCase; } std::vector mitk::RelationStorage::GetAllImageIDsOfCase(const SemanticTypes::CaseID& caseID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return std::vector(); } // retrieve a vector property that contains the valid image-IDs of the current case mitk::VectorProperty* allImagesVectorProperty = dynamic_cast*>(propertyList->GetProperty("images")); if (nullptr == allImagesVectorProperty) { MITK_INFO << "Could not find any image in the storage."; return std::vector(); } return allImagesVectorProperty->GetValue(); } std::vector mitk::RelationStorage::GetAllCaseIDs() { PERSISTENCE_GET_SERVICE_MACRO if (nullptr == persistenceService) { MITK_INFO << "Persistence service could not be loaded"; return std::vector(); } // the property list is valid for a certain scenario and contains all the case IDs of the radiological user's MITK session std::string listIdentifier = "caseIDs"; mitk::PropertyList::Pointer propertyList = persistenceService->GetPropertyList(listIdentifier); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << listIdentifier << " for the current MITK workbench / session."; return std::vector(); } // retrieve a vector property that contains all case IDs mitk::VectorProperty* caseIDsVectorProperty = dynamic_cast*>(propertyList->GetProperty(listIdentifier)); if (nullptr == caseIDsVectorProperty) { MITK_INFO << "Could not find the property " << listIdentifier << " for the " << listIdentifier << " property list."; return std::vector(); } return caseIDsVectorProperty->GetValue(); } void mitk::RelationStorage::AddCase(const SemanticTypes::CaseID& caseID) { PERSISTENCE_GET_SERVICE_MACRO if (nullptr == persistenceService) { MITK_INFO << "Persistence service could not be loaded"; return; } // the property list is valid for a certain scenario and contains all the case IDs of the radiological user's MITK session std::string listIdentifier = "caseIDs"; mitk::PropertyList::Pointer propertyList = persistenceService->GetPropertyList(listIdentifier); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << listIdentifier << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains all case IDs mitk::VectorProperty::Pointer caseIDsVectorProperty = dynamic_cast*>(propertyList->GetProperty(listIdentifier)); std::vector caseIDsVectorValue; if (nullptr == caseIDsVectorProperty) { caseIDsVectorProperty = mitk::VectorProperty::New(); } else { caseIDsVectorValue = caseIDsVectorProperty->GetValue(); } auto existingCase = std::find(caseIDsVectorValue.begin(), caseIDsVectorValue.end(), caseID); if (existingCase != caseIDsVectorValue.end()) { return; } else { // add case to the "caseIDs" property list caseIDsVectorValue.push_back(caseID); caseIDsVectorProperty->SetValue(caseIDsVectorValue); propertyList->SetProperty(listIdentifier, caseIDsVectorProperty); } } void mitk::RelationStorage::AddImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageNodeID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid image-IDs for the current case mitk::VectorProperty::Pointer allImagesVectorProperty = dynamic_cast*>(propertyList->GetProperty("images")); std::vector allImagesIDs; if (nullptr == allImagesVectorProperty) { allImagesVectorProperty = mitk::VectorProperty::New(); } else { allImagesIDs = allImagesVectorProperty->GetValue(); } auto existingImage = std::find(allImagesIDs.begin(), allImagesIDs.end(), imageNodeID); if (existingImage != allImagesIDs.end()) { return; } else { // add image to the "images" property list allImagesIDs.push_back(imageNodeID); allImagesVectorProperty->SetValue(allImagesIDs); propertyList->SetProperty("images", allImagesVectorProperty); // add the image itself mitk::VectorProperty::Pointer imageNodeVectorProperty = mitk::VectorProperty::New(); // an image node has to have exactly two values (the information type and the ID of the control point) std::vector imageNodeVectorValue(2); imageNodeVectorProperty->SetValue(imageNodeVectorValue); propertyList->SetProperty(imageNodeID, imageNodeVectorProperty); } } void mitk::RelationStorage::RemoveImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageNodeID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid image-IDs for the current case mitk::VectorProperty::Pointer allImagesVectorProperty = dynamic_cast*>(propertyList->GetProperty("images")); if (nullptr == allImagesVectorProperty) { MITK_INFO << "Could not find any images in the storage."; return; } // remove the image reference from the list of all images of the current case std::vector allImagesIDs = allImagesVectorProperty->GetValue(); allImagesIDs.erase(std::remove(allImagesIDs.begin(), allImagesIDs.end(), imageNodeID), allImagesIDs.end()); if (allImagesIDs.size() == 0) { // no more images stored -> remove the images property list propertyList->DeleteProperty("images"); } else { // or store the modified vector value allImagesVectorProperty->SetValue(allImagesIDs); } // remove the image instance itself propertyList->DeleteProperty(imageNodeID); } void mitk::RelationStorage::AddSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationNodeID, const SemanticTypes::ID& parentNodeID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid segmentation-IDs for the current case mitk::VectorProperty::Pointer allSegmentationsVectorProperty = dynamic_cast*>(propertyList->GetProperty("segmentations")); std::vector allSegmentationsIDs; if (nullptr == allSegmentationsVectorProperty) { allSegmentationsVectorProperty = mitk::VectorProperty::New(); } else { allSegmentationsIDs = allSegmentationsVectorProperty->GetValue(); } auto existingImage = std::find(allSegmentationsIDs.begin(), allSegmentationsIDs.end(), segmentationNodeID); if (existingImage != allSegmentationsIDs.end()) { return; } else { // add segmentation to the "segmentations" property list allSegmentationsIDs.push_back(segmentationNodeID); allSegmentationsVectorProperty->SetValue(allSegmentationsIDs); propertyList->SetProperty("segmentations", allSegmentationsVectorProperty); // add the segmentation itself mitk::VectorProperty::Pointer segmentationNodeVectorProperty = mitk::VectorProperty::New(); // a segmentation node has to have exactly two values (the ID of the referenced image and the ID of the referenced lesion) std::vector segmentationNodeVectorValue(2); segmentationNodeVectorValue[0] = parentNodeID; segmentationNodeVectorProperty->SetValue(segmentationNodeVectorValue); propertyList->SetProperty(segmentationNodeID, segmentationNodeVectorProperty); } } void mitk::RelationStorage::RemoveSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationNodeID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid segmentation-IDs for the current case mitk::VectorProperty::Pointer allSegmentationsVectorProperty = dynamic_cast*>(propertyList->GetProperty("segmentations")); if (nullptr == allSegmentationsVectorProperty) { MITK_INFO << "Could not find any segmentation in the storage."; return; } // remove the lesion reference from the list of all lesions of the current case std::vector allSegmentationsIDs = allSegmentationsVectorProperty->GetValue(); allSegmentationsIDs.erase(std::remove(allSegmentationsIDs.begin(), allSegmentationsIDs.end(), segmentationNodeID), allSegmentationsIDs.end()); if (allSegmentationsIDs.size() == 0) { // no more segmentations stored -> remove the segmentations property list propertyList->DeleteProperty("segmentations"); } else { // or store the modified vector value allSegmentationsVectorProperty->SetValue(allSegmentationsIDs); } // remove the lesion instance itself propertyList->DeleteProperty(segmentationNodeID); } void mitk::RelationStorage::AddLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid lesion-IDs for the current case mitk::VectorProperty::Pointer lesionsVectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); std::vector lesionsVectorValue; if (nullptr == lesionsVectorProperty) { lesionsVectorProperty = mitk::VectorProperty::New(); } else { lesionsVectorValue = lesionsVectorProperty->GetValue(); } const auto& existingIndex = std::find(lesionsVectorValue.begin(), lesionsVectorValue.end(), lesion.UID); if (existingIndex != lesionsVectorValue.end()) { return; } else { // add the new lesion id from the given lesion to the vector of all current lesion IDs lesionsVectorValue.push_back(lesion.UID); // overwrite the current vector property with the new, extended string vector lesionsVectorProperty->SetValue(lesionsVectorValue); propertyList->SetProperty("lesions", lesionsVectorProperty); - // add the lesion with the lesion UID as the key and the lesion class UID as value - std::string lesionID = lesion.UID; - std::string lesionClassID = lesion.lesionClass.UID; - propertyList->SetStringProperty(lesionID.c_str(), lesionClassID.c_str()); + // add the lesion with the lesion UID as the key and the lesion information as value + std::vector lesionData; + lesionData.push_back(lesion.name); + lesionData.push_back(lesion.lesionClass.UID); + mitk::VectorProperty::Pointer newLesionVectorProperty = mitk::VectorProperty::New(); + newLesionVectorProperty->SetValue(lesionData); + propertyList->SetProperty(lesion.UID, newLesionVectorProperty); // add the lesion class with the lesion class UID as key and the class type as value std::string lesionClassType = lesion.lesionClass.classType; - propertyList->SetStringProperty(lesionClassID.c_str(), lesionClassType.c_str()); + propertyList->SetStringProperty(lesion.lesionClass.UID.c_str(), lesionClassType.c_str()); } } void mitk::RelationStorage::OverwriteLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid lesion-IDs for the current case mitk::VectorProperty* lesionVectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); if (nullptr == lesionVectorProperty) { MITK_INFO << "Could not find any lesion in the storage."; return; } std::vector lesionVectorValue = lesionVectorProperty->GetValue(); const auto existingLesion = std::find(lesionVectorValue.begin(), lesionVectorValue.end(), lesion.UID); if (existingLesion != lesionVectorValue.end()) { - // overwrite the lesion with the new, given lesion class UID - std::string lesionID = *existingLesion; - std::string lesionClassID = lesion.lesionClass.UID; - propertyList->SetStringProperty(lesionID.c_str(), lesionClassID.c_str()); + // overwrite the referenced lesion class UID with the new, given lesion class data + std::vector lesionData; + lesionData.push_back(lesion.name); + lesionData.push_back(lesion.lesionClass.UID); + mitk::VectorProperty::Pointer newLesionVectorProperty = mitk::VectorProperty::New(); + newLesionVectorProperty->SetValue(lesionData); + propertyList->SetProperty(lesion.UID, newLesionVectorProperty); - // overwrite the lesion class with the lesion class UID as key and the class type as value + // overwrite the lesion class with the lesion class UID as key and the new, given class type as value std::string lesionClassType = lesion.lesionClass.classType; - propertyList->SetStringProperty(lesionClassID.c_str(), lesionClassType.c_str()); + propertyList->SetStringProperty(lesion.lesionClass.UID.c_str(), lesionClassType.c_str()); } else { MITK_INFO << "Could not find lesion " << lesion.UID << " in the storage. Cannot overwrite the lesion."; } } void mitk::RelationStorage::LinkSegmentationToLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID, const SemanticTypes::Lesion& lesion) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid lesion-IDs for the current case mitk::VectorProperty* lesionVectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); if (nullptr == lesionVectorProperty) { MITK_INFO << "Could not find any lesion property in the storage."; return; } std::vector lesionVectorValue = lesionVectorProperty->GetValue(); const auto existingLesion = std::find(lesionVectorValue.begin(), lesionVectorValue.end(), lesion.UID); if (existingLesion != lesionVectorValue.end()) { // set / overwrite the lesion reference of the given segmentation // retrieve a vector property that contains the referenced ID of a segmentation (0. image ID 1. lesion ID) mitk::VectorProperty* segmentationVectorProperty = dynamic_cast*>(propertyList->GetProperty(segmentationID)); if (nullptr == segmentationVectorProperty) { MITK_INFO << "Could not find the segmentation node " << segmentationID << " in the storage. Cannot link segmentation to lesion."; return; } std::vector segmentationVectorValue = segmentationVectorProperty->GetValue(); if (segmentationVectorValue.size() != 2) { MITK_INFO << "Incorrect segmentation storage. Not two (2) IDs stored."; return; } else { // the lesion ID of a segmentation is the second value in the vector segmentationVectorValue[1] = lesion.UID; segmentationVectorProperty->SetValue(segmentationVectorValue); } } else { MITK_INFO << "Could not find lesion " << lesion.UID << " in the storage. Cannot link segmentation to lesion."; } } void mitk::RelationStorage::UnlinkSegmentationFromLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the referenced ID of a segmentation (0. image ID 1. lesion ID) mitk::VectorProperty* segmentationVectorProperty = dynamic_cast*>(propertyList->GetProperty(segmentationID)); if (nullptr == segmentationVectorProperty) { MITK_INFO << "Could not find the segmentation node " << segmentationID << " in the storage. Cannot unlink lesion from segmentation."; return; } std::vector segmentationVectorValue = segmentationVectorProperty->GetValue(); // a segmentation has to have exactly two values (the ID of the linked image and the ID of the lesion) if (segmentationVectorValue.size() != 2) { MITK_INFO << "Incorrect data storage. Not two (2) values stored."; return; } else { // the second value of the data node vector is the ID of the referenced lesion // set the lesion reference to an empty string for removal segmentationVectorValue[1] = ""; segmentationVectorProperty->SetValue(segmentationVectorValue); } } void mitk::RelationStorage::RemoveLesion(const mitk::SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid lesions of the current case mitk::VectorProperty* lesionVectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); if (nullptr == lesionVectorProperty) { MITK_INFO << "Could not find any lesion property in the storage."; return; } // remove the lesion reference from the list of all lesions of the current case std::vector lesionVectorValue = lesionVectorProperty->GetValue(); lesionVectorValue.erase(std::remove(lesionVectorValue.begin(), lesionVectorValue.end(), lesion.UID), lesionVectorValue.end()); if (lesionVectorValue.size() == 0) { // no more lesions stored -> remove the lesions property list propertyList->DeleteProperty("lesions"); } else { // or store the modified vector value lesionVectorProperty->SetValue(lesionVectorValue); } // remove the lesion instance itself - // the lesion class ID is stored under the lesion ID - mitk::StringProperty* lesionClassIDProperty = dynamic_cast(propertyList->GetProperty(lesion.UID)); - if (nullptr == lesionClassIDProperty) + // the lesion data is stored under the lesion ID + mitk::VectorProperty* lesionDataProperty = dynamic_cast*>(propertyList->GetProperty(lesion.UID)); + if (nullptr == lesionDataProperty) { MITK_INFO << "Lesion " << lesion.UID << " not found (already removed?). Cannot remove the lesion."; return; } - // the lesion class type is stored under the lesion class ID - std::string lesionClassID = lesionClassIDProperty->GetValue(); + std::vector lesionData = lesionDataProperty->GetValue(); + // a lesion date has to have exactly two values (the name of the lesion and the UID of the lesion class) + if (lesionData.size() != 2) + { + MITK_INFO << "Incorrect lesion data storage. Not two (2) strings of the lesion UID and the lesion name are stored."; + } + else + { + std::string lesionClassID = lesionData[1]; + RemoveLesionClass(caseID, lesionClassID); + } + propertyList->DeleteProperty(lesion.UID); +} + +void mitk::RelationStorage::RemoveLesionClass(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& lesionClassID) +{ + mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return; + } + + // retrieve a vector property that contains the lesion class mitk::StringProperty* lesionClassProperty = dynamic_cast(propertyList->GetProperty(lesionClassID)); if (nullptr == lesionClassProperty) { - MITK_INFO << "No lesion class found for " << lesion.UID << ". Just removing the lesion class ID."; + MITK_INFO << "Lesion class " << lesionClassID << " not found (already removed?). Cannot remove the lesion class."; + return; } - else + + // retrieve a vector property that contains the valid lesions of the current case + mitk::VectorProperty* lesionVectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); + if (nullptr == lesionVectorProperty) { - // check if the lesion class ID is referenced by any other lesion - const auto existingLesionClass = std::find_if(lesionVectorValue.begin(), lesionVectorValue.end(), - [&propertyList, &lesionClassID](const std::string& lesionID) + return; + } + + // check if the lesion class ID is referenced by any other lesion + std::vector lesionVectorValue = lesionVectorProperty->GetValue(); + const auto existingLesionClass = std::find_if(lesionVectorValue.begin(), lesionVectorValue.end(), + [&propertyList, &lesionClassID](const std::string& lesionID) + { + mitk::VectorProperty* lesionDataProperty = dynamic_cast*>(propertyList->GetProperty(lesionID)); + if (nullptr == lesionDataProperty) + { + return false; + } + else { - mitk::StringProperty* lesionClassIDProperty = dynamic_cast(propertyList->GetProperty(lesionID)); - if (nullptr == lesionClassIDProperty) + std::vector lesionData = lesionDataProperty->GetValue(); + // a lesion date has to have exactly two values (the name of the lesion and the UID of the lesion class) + if (lesionData.size() != 2) { return false; } else { - return lesionClassIDProperty->GetValue() == lesionClassID; + return lesionData[1] == lesionClassID; } - }); - - if (existingLesionClass == lesionVectorValue.end()) - { - // lesion class ID not referenced; remove lesion class - propertyList->DeleteProperty(lesionClassID); } + }); + + if (existingLesionClass == lesionVectorValue.end()) + { + // lesion class ID not referenced; remove lesion class + propertyList->DeleteProperty(lesionClassID); } - propertyList->DeleteProperty(lesion.UID); } void mitk::RelationStorage::AddControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid controlPoint UIDs for the current case mitk::VectorProperty::Pointer controlPointVectorProperty = dynamic_cast*>(propertyList->GetProperty("controlpoints")); std::vector controlPointVectorValue; if (nullptr == controlPointVectorProperty) { controlPointVectorProperty = mitk::VectorProperty::New(); } else { controlPointVectorValue = controlPointVectorProperty->GetValue(); } const auto existingControlPoint = std::find(controlPointVectorValue.begin(), controlPointVectorValue.end(), controlPoint.UID); if (existingControlPoint != controlPointVectorValue.end()) { return; } else { // add the new control point UID from the given control point to the vector of all current control point UIDs controlPointVectorValue.push_back(controlPoint.UID); // overwrite the current vector property with the new, extended string vector controlPointVectorProperty->SetValue(controlPointVectorValue); propertyList->SetProperty("controlpoints", controlPointVectorProperty); // set the year, month and day of the start point std::vector startPointValue; startPointValue.push_back(controlPoint.startPoint.year); startPointValue.push_back(controlPoint.startPoint.month); startPointValue.push_back(controlPoint.startPoint.day); // store the start point mitk::VectorProperty::Pointer startPointVectorProperty = mitk::VectorProperty::New(); startPointVectorProperty->SetValue(startPointValue); propertyList->SetProperty(controlPoint.startPoint.UID, startPointVectorProperty); // set the year, month and day of the end point std::vector endPointValue; endPointValue.push_back(controlPoint.endPoint.year); endPointValue.push_back(controlPoint.endPoint.month); endPointValue.push_back(controlPoint.endPoint.day); // store the end point mitk::VectorProperty::Pointer endPointVectorProperty = mitk::VectorProperty::New(); endPointVectorProperty->SetValue(endPointValue); propertyList->SetProperty(controlPoint.endPoint.UID, endPointVectorProperty); // store the start point UID and the end point UID std::vector controlPointDateReferences; controlPointDateReferences.push_back(controlPoint.startPoint.UID); controlPointDateReferences.push_back(controlPoint.endPoint.UID); // store the control point references (the start point UID and the end point UID) mitk::VectorProperty::Pointer newControlPointVectorProperty = mitk::VectorProperty::New(); newControlPointVectorProperty->SetValue(controlPointDateReferences); propertyList->SetProperty(controlPoint.UID, newControlPointVectorProperty); } } void mitk::RelationStorage::OverwriteControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid controlPoint UIDs for the current case mitk::VectorProperty* controlPointVectorProperty = dynamic_cast*>(propertyList->GetProperty("controlpoints")); if (nullptr == controlPointVectorProperty) { MITK_INFO << "Could not find any control point property in the storage."; return; } std::vector controlPointVectorValue = controlPointVectorProperty->GetValue(); const auto existingControlPoint = std::find(controlPointVectorValue.begin(), controlPointVectorValue.end(), controlPoint.UID); if (existingControlPoint != controlPointVectorValue.end()) { // retrieve the start date mitk::VectorProperty* startPointVectorProperty = dynamic_cast*>(propertyList->GetProperty(controlPoint.startPoint.UID)); if (nullptr == startPointVectorProperty) { MITK_INFO << "Could not find the start date " << controlPoint.startPoint.UID << " of the control point " << controlPoint.UID << " in the storage."; return; } std::vector startPointValue = startPointVectorProperty->GetValue(); // a date has to have exactly three integer values if (startPointValue.size() != 3) { MITK_INFO << "Incorrect start point storage. Not three (3) values of the date are stored."; return; } else { // set the year, month and day of the start point startPointValue[0] = controlPoint.startPoint.year; startPointValue[1] = controlPoint.startPoint.month; startPointValue[2] = controlPoint.startPoint.day; // overwrite the start point startPointVectorProperty->SetValue(startPointValue); } // retrieve the end date mitk::VectorProperty* endPointVectorProperty = dynamic_cast*>(propertyList->GetProperty(controlPoint.endPoint.UID)); if (nullptr == endPointVectorProperty) { MITK_INFO << "Could not find the end date " << controlPoint.endPoint.UID << " of the control point " << controlPoint.UID << " in the storage."; return; } std::vector endPointValue = endPointVectorProperty->GetValue(); // a date has to have exactly three integer values if (endPointValue.size() != 3) { MITK_INFO << "Incorrect end point storage. Not three (3) values of the date are stored."; return; } else { // set the year, month and day of the end point endPointValue[0] = controlPoint.endPoint.year; endPointValue[1] = controlPoint.endPoint.month; endPointValue[2] = controlPoint.endPoint.day; // overwrite the end point endPointVectorProperty->SetValue(endPointValue); } } else { MITK_INFO << "Could not find control point " << controlPoint.UID << " in the storage. Cannot overwrite the control point."; } } void mitk::RelationStorage::LinkDataToControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& dataNodeID, const SemanticTypes::ControlPoint& controlPoint) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid controlPoint UIDs for the current case mitk::VectorProperty* controlPointVectorProperty = dynamic_cast*>(propertyList->GetProperty("controlpoints")); if (nullptr == controlPointVectorProperty) { MITK_INFO << "Could not find any control point property in the storage."; return; } std::vector controlPointVectorValue = controlPointVectorProperty->GetValue(); const auto existingControlPoint = std::find(controlPointVectorValue.begin(), controlPointVectorValue.end(), controlPoint.UID); if (existingControlPoint != controlPointVectorValue.end()) { // set / overwrite the control point reference of the given data // retrieve a vector property that contains the referenced ID of a data node (0. information type 1. control point ID) mitk::VectorProperty* dataNodeVectorProperty = dynamic_cast*>(propertyList->GetProperty(dataNodeID)); if (nullptr == dataNodeVectorProperty) { MITK_INFO << "Could not find the data node " << dataNodeID << " in the storage. Cannot link data to control point."; return; } std::vector dataNodeVectorValue = dataNodeVectorProperty->GetValue(); // an image node has to have exactly two values (the information type and the ID of the control point) if (dataNodeVectorValue.size() != 2) { MITK_INFO << "Incorrect data storage. Not two (2) values stored."; return; } else { // the second value of the data node vector is the ID of the referenced control point dataNodeVectorValue[1] = controlPoint.UID; dataNodeVectorProperty->SetValue(dataNodeVectorValue); } } else { MITK_INFO << "Could not find control point " << controlPoint.UID << " in the storage. Cannot link data to control point."; } } void mitk::RelationStorage::UnlinkDataFromControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& dataNodeID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the referenced ID of a date (0. information type 1. control point ID) mitk::VectorProperty* dataNodeVectorProperty = dynamic_cast*>(propertyList->GetProperty(dataNodeID)); if (nullptr == dataNodeVectorProperty) { MITK_INFO << "Could not find the date " << dataNodeID << " in the storage. Cannot unlink control point from date."; return; } std::vector dataNodeVectorValue = dataNodeVectorProperty->GetValue(); // a data node has to have exactly two values (the information type and the ID of the control point) if (dataNodeVectorValue.size() != 2) { MITK_INFO << "Incorrect data storage. Not two (2) values stored."; return; } else { // the second value of the data node vector is the ID of the referenced control point // set the control point reference to an empty string for removal dataNodeVectorValue[1] = ""; dataNodeVectorProperty->SetValue(dataNodeVectorValue); } } void mitk::RelationStorage::RemoveControlPointFromCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid controlPoint UIDs for the current case mitk::VectorProperty* allControlPointsVectorProperty = dynamic_cast*>(propertyList->GetProperty("controlpoints")); if (nullptr == allControlPointsVectorProperty) { MITK_INFO << "Could not find any control point property in the storage."; return; } // remove the control point reference from the list of all control points of the current case std::vector currentControlPointVectorValue = allControlPointsVectorProperty->GetValue(); currentControlPointVectorValue.erase(std::remove(currentControlPointVectorValue.begin(), currentControlPointVectorValue.end(), controlPoint.UID), currentControlPointVectorValue.end()); allControlPointsVectorProperty->SetValue(currentControlPointVectorValue); // remove the control point instance itself // the start and end point is stored under the control point ID // retrieve a vector property that contains the referenced ID of the dates of a control point (0. startPoint ID 1. endPoint ID) mitk::VectorProperty* controlPointVectorProperty = dynamic_cast*>(propertyList->GetProperty(controlPoint.UID)); if (nullptr == controlPointVectorProperty) { MITK_INFO << "Control point " << controlPoint.UID << " not found (already removed?). Cannot remove the control point."; return; } std::vector controlPointVectorValue = controlPointVectorProperty->GetValue(); // a control point has to have exactly two values (the ID of two dates) if (controlPointVectorValue.size() != 2) { MITK_INFO << "Incorrect control point storage. Not two (2) IDs of the start point and the end point are stored."; } else { // retrieve the start date std::string startPointID = controlPointVectorValue[0]; // delete the start date property propertyList->DeleteProperty(startPointID); // retrieve the end date std::string endPointID = controlPointVectorValue[1]; // delete the end date property propertyList->DeleteProperty(endPointID); } propertyList->DeleteProperty(controlPoint.UID); } void mitk::RelationStorage::AddInformationTypeToImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID, const SemanticTypes::InformationType informationType) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid information types of the current case mitk::VectorProperty::Pointer informationTypeVectorProperty = dynamic_cast*>(propertyList->GetProperty("informationtypes")); std::vector informationTypeVectorValue; if (nullptr == informationTypeVectorProperty) { informationTypeVectorProperty = mitk::VectorProperty::New(); } else { informationTypeVectorValue = informationTypeVectorProperty->GetValue(); } const auto existingInformationType = std::find(informationTypeVectorValue.begin(), informationTypeVectorValue.end(), informationType); if (existingInformationType == informationTypeVectorValue.end()) { // at first: add the information type to the storage informationTypeVectorValue.push_back(informationType); informationTypeVectorProperty->SetValue(informationTypeVectorValue); propertyList->SetProperty("informationtypes", informationTypeVectorProperty); } // set / overwrite the information type of the given data // retrieve a vector property that contains the referenced ID of an image (0. information type 1. control point ID) mitk::VectorProperty* imageNodeVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); if (nullptr == imageNodeVectorProperty) { MITK_INFO << "Could not find the image " << imageID << " in the storage. Cannot add information type to image."; return; } std::vector imageNodeVectorValue = imageNodeVectorProperty->GetValue(); // an image node has to have exactly two values (the information type and the ID of the control point) if (imageNodeVectorValue.size() != 2) { MITK_INFO << "Incorrect data storage. Not two (2) values stored."; return; } else { // the first value of the data node vector is the information type imageNodeVectorValue[0] = informationType; imageNodeVectorProperty->SetValue(imageNodeVectorValue); } } void mitk::RelationStorage::RemoveInformationTypeFromImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the referenced ID of an image (0. information type 1. control point ID) mitk::VectorProperty* imageNodeVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); if (nullptr == imageNodeVectorProperty) { MITK_INFO << "Could not find the image " << imageID << " in the storage. Cannot remove information type from image."; return; } std::vector imageNodeVectorValue = imageNodeVectorProperty->GetValue(); // an image node has to have exactly two values (the information type and the ID of the control point) if (imageNodeVectorValue.size() != 2) { MITK_INFO << "Incorrect data storage. Not two (2) values stored."; return; } else { // the first value of the data node vector is the information type // set the information type to an empty string for removal imageNodeVectorValue[0] = ""; imageNodeVectorProperty->SetValue(imageNodeVectorValue); } } void mitk::RelationStorage::RemoveInformationTypeFromCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType informationType) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid information types of the current case mitk::VectorProperty* informationTypeVectorProperty = dynamic_cast*>(propertyList->GetProperty("informationtypes")); if (nullptr == informationTypeVectorProperty) { MITK_INFO << "Could not find any information type property in the storage."; return; } std::vector informationTypeVectorValue = informationTypeVectorProperty->GetValue(); informationTypeVectorValue.erase(std::remove(informationTypeVectorValue.begin(), informationTypeVectorValue.end(), informationType), informationTypeVectorValue.end()); if (informationTypeVectorValue.size() == 0) { // no more information types stored -> remove the information types property list propertyList->DeleteProperty("informationtypes"); } else { // or store the modified vector value informationTypeVectorProperty->SetValue(informationTypeVectorValue); } } mitk::PropertyList::Pointer mitk::RelationStorage::GetStorageData(const SemanticTypes::CaseID& caseID) { // access the storage PERSISTENCE_GET_SERVICE_MACRO if (nullptr == persistenceService) { MITK_INFO << "Persistence service could not be loaded"; return nullptr; } // the property list is valid for a whole case and contains all the properties for the current case // the persistence service may create a new property list with the given ID, if no property list is found return persistenceService->GetPropertyList(const_cast(caseID)); } + +mitk::SemanticTypes::Lesion mitk::RelationStorage::GenerateLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& lesionID) +{ + mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return SemanticTypes::Lesion(); + } + + mitk::VectorProperty* lesionDataProperty = dynamic_cast*>(propertyList->GetProperty(lesionID)); + if (nullptr == lesionDataProperty) + { + MITK_INFO << "Lesion " << lesionID << " not found. Lesion can not be retrieved."; + return SemanticTypes::Lesion(); + } + + std::vector lesionData = lesionDataProperty->GetValue(); + // a lesion date has to have exactly two values (the name of the lesion and the UID of the lesion class) + if (lesionData.size() != 2) + { + MITK_INFO << "Incorrect lesion data storage. Not two (2) strings of the lesion name and the lesion UID are stored."; + return SemanticTypes::Lesion(); + } + + // the lesion class ID is stored as the second property + std::string lesionClassID = lesionData[1]; + mitk::StringProperty* lesionClassProperty = dynamic_cast(propertyList->GetProperty(lesionClassID)); + if (nullptr != lesionClassProperty) + { + SemanticTypes::LesionClass generatedLesionClass; + generatedLesionClass.UID = lesionClassID; + generatedLesionClass.classType = lesionClassProperty->GetValue(); + + SemanticTypes::Lesion generatedLesion; + generatedLesion.UID = lesionID; + generatedLesion.name = lesionData[0]; + generatedLesion.lesionClass = generatedLesionClass; + + return generatedLesion; + } + else + { + MITK_INFO << "Incorrect lesion class storage. Lesion " << lesionID << " can not be retrieved."; + return SemanticTypes::Lesion(); + } +} diff --git a/Modules/SemanticRelations/src/mitkSemanticRelations.cpp b/Modules/SemanticRelations/src/mitkSemanticRelations.cpp index 9b33e10cd4..39e19e9de9 100644 --- a/Modules/SemanticRelations/src/mitkSemanticRelations.cpp +++ b/Modules/SemanticRelations/src/mitkSemanticRelations.cpp @@ -1,992 +1,1019 @@ /*=================================================================== 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 "mitkSemanticRelations.h" #include "mitkSemanticRelationException.h" #include "mitkNodePredicates.h" #include "mitkUIDGeneratorBoost.h" // multi label module #include // c++ #include std::vector mitk::SemanticRelations::m_ObserverVector; mitk::SemanticRelations::SemanticRelations(mitk::DataStorage::Pointer dataStorage) : m_DataStorage(dataStorage) { m_RelationStorage = std::make_shared(); m_RelationStorage->SetDataStorage(m_DataStorage); } mitk::SemanticRelations::~SemanticRelations() { // nothing here } void mitk::SemanticRelations::AddObserver(ISemanticRelationsObserver* observer) { std::vector::iterator existingObserver = std::find(m_ObserverVector.begin(), m_ObserverVector.end(), observer); if (existingObserver != m_ObserverVector.end()) { // no need to add the already existing observer return; } m_ObserverVector.push_back(observer); } void mitk::SemanticRelations::RemoveObserver(ISemanticRelationsObserver* observer) { m_ObserverVector.erase(std::remove(m_ObserverVector.begin(), m_ObserverVector.end(), observer), m_ObserverVector.end()); } /************************************************************************/ /* functions to get instances / attributes */ /************************************************************************/ mitk::SemanticRelations::LesionVector mitk::SemanticRelations::GetAllLesionsOfCase(const SemanticTypes::CaseID& caseID) const { return m_RelationStorage->GetAllLesionsOfCase(caseID); } mitk::SemanticRelations::LesionVector mitk::SemanticRelations::GetAllLesionsOfCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) const { LesionVector allLesions = GetAllLesionsOfCase(caseID); // filter the lesions: use only those, where the associated data is connected to image data that refers to the given control point using a lambda function auto lambda = [&caseID, &controlPoint, this](const SemanticTypes::Lesion& lesion) { return !ControlPointContainsLesion(caseID, lesion, controlPoint); }; allLesions.erase(std::remove_if(allLesions.begin(), allLesions.end(), lambda), allLesions.end()); return allLesions; } +mitk::SemanticRelations::LesionClassVector mitk::SemanticRelations::GetAllLesionClassesOfCase(const SemanticTypes::CaseID& caseID) const +{ + LesionVector allLesionsOfCase = GetAllLesionsOfCase(caseID); + LesionClassVector allLesionClassesOfCase; + + for (const auto& lesion : allLesionsOfCase) + { + allLesionClassesOfCase.push_back(lesion.lesionClass); + } + + // remove duplicate entries + auto lessThan = [](const SemanticTypes::LesionClass& lesionClassL, const SemanticTypes::LesionClass& lesionClassR) + { + return lesionClassL.UID < lesionClassR.UID; + }; + + auto equal = [](const SemanticTypes::LesionClass& lesionClassL, const SemanticTypes::LesionClass& lesionClassR) + { + return lesionClassL.UID == lesionClassR.UID; + }; + + std::sort(allLesionClassesOfCase.begin(), allLesionClassesOfCase.end(), lessThan); + allLesionClassesOfCase.erase(std::unique(allLesionClassesOfCase.begin(), allLesionClassesOfCase.end(), equal), allLesionClassesOfCase.end()); + + return allLesionClassesOfCase; +} + mitk::SemanticRelations::LesionVector mitk::SemanticRelations::GetAllLesionsInImage(const DataNode* imageNode) const { if (nullptr == imageNode) { - mitkThrow() << "Not a valid image data node."; + mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } if (m_DataStorage.IsNull()) { - mitkThrow() << "Not a valid data storage."; + mitkThrowException(SemanticRelationException) << "Not a valid data storage."; } LesionVector allLesionsInImage; // get child nodes of the current node with the segmentation predicate DataStorage::SetOfObjects::ConstPointer segmentationNodes = m_DataStorage->GetDerivations(imageNode, NodePredicates::GetSegmentationPredicate(), false); for (auto it = segmentationNodes->Begin(); it != segmentationNodes->End(); ++it) { DataNode* segmentationNode = it->Value(); try { SemanticTypes::Lesion representedLesion = GetRepresentedLesion(segmentationNode); allLesionsInImage.push_back(representedLesion); } catch (const SemanticRelationException&) { continue; } } return allLesionsInImage; } mitk::SemanticTypes::Lesion mitk::SemanticRelations::GetRepresentedLesion(const DataNode* segmentationNode) const { if (nullptr == segmentationNode) { - mitkThrow() << "Not a valid segmentation data node."; + mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); SemanticTypes::ID segmentationID = GetIDFromDataNode(segmentationNode); SemanticTypes::Lesion representedLesion = m_RelationStorage->GetRepresentedLesion(caseID, segmentationID); if (representedLesion.UID.empty()) { mitkThrowException(SemanticRelationException) << "Could not find a represented lesion instance for the given segmentation node " << segmentationNode->GetName(); } else { return representedLesion; } } bool mitk::SemanticRelations::IsRepresentingALesion(const DataNode* segmentationNode) const { try { SemanticTypes::Lesion representedLesion = GetRepresentedLesion(segmentationNode); return true; } - catch (const SemanticRelationException&) + catch (const Exception&) { return false; } } mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllSegmentationsOfCase(const SemanticTypes::CaseID& caseID) const { if (m_DataStorage.IsNull()) { - mitkThrow() << "Not a valid data storage."; + mitkThrowException(SemanticRelationException) << "Not a valid data storage."; } return m_RelationStorage->GetAllSegmentationsOfCase(caseID); } mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllSegmentationsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const { if (m_DataStorage.IsNull()) { - mitkThrow() << "Not a valid data storage."; + mitkThrowException(SemanticRelationException) << "Not a valid data storage."; } if (InstanceExists(caseID, lesion)) { // lesion exists, retrieve all case segmentations from the storage DataNodeVector allSegmentationsOfLesion = GetAllSegmentationsOfCase(caseID); // filter all segmentations: check for semantic relation with the given lesion using a lambda function auto lambda = [&lesion, this](DataNode::Pointer segmentation) { try { SemanticTypes::Lesion representedLesion = GetRepresentedLesion(segmentation); return lesion.UID != representedLesion.UID; } catch (const SemanticRelationException&) { return true; } }; allSegmentationsOfLesion.erase(std::remove_if(allSegmentationsOfLesion.begin(), allSegmentationsOfLesion.end(), lambda), allSegmentationsOfLesion.end()); return allSegmentationsOfLesion; } else { mitkThrowException(SemanticRelationException) << "Could not find an existing lesion instance for the given caseID " << caseID << " and lesion " << lesion.UID << "."; } } mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllImagesOfCase(const SemanticTypes::CaseID& caseID) const { if (m_DataStorage.IsNull()) { - mitkThrow() << "Not a valid data storage."; + mitkThrowException(SemanticRelationException) << "Not a valid data storage."; } return m_RelationStorage->GetAllImagesOfCase(caseID); } mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllImagesOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const { if (m_DataStorage.IsNull()) { - mitkThrow() << "Not a valid data storage."; + mitkThrowException(SemanticRelationException) << "Not a valid data storage."; } DataNodeVector allImagesOfLesion; // 1. get all segmentations that define the lesion // 2. retrieve the parent node (source) of the found segmentation node DataNodeVector allSegmentationsOfLesion = GetAllSegmentationsOfLesion(caseID, lesion); for (const auto& segmentationNode : allSegmentationsOfLesion) { // get parent node of the current segmentation node with the node predicate DataStorage::SetOfObjects::ConstPointer parentNodes = m_DataStorage->GetSources(segmentationNode, NodePredicates::GetImagePredicate(), false); for (auto it = parentNodes->Begin(); it != parentNodes->End(); ++it) { DataNode::Pointer dataNode = it->Value(); allImagesOfLesion.push_back(it->Value()); } } std::sort(allImagesOfLesion.begin(), allImagesOfLesion.end()); allImagesOfLesion.erase(std::unique(allImagesOfLesion.begin(), allImagesOfLesion.end()), allImagesOfLesion.end()); return allImagesOfLesion; } bool mitk::SemanticRelations::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const { LesionVector allLesions = GetAllLesionsOfCase(caseID); // filter all lesions: check for equality with the given lesion using a lambda function auto lambda = [&lesion](const SemanticTypes::Lesion& currentLesion) { return currentLesion.UID == lesion.UID; }; const auto existingLesion = std::find_if(allLesions.begin(), allLesions.end(), lambda); if (existingLesion != allLesions.end()) { return true; } else { return false; } } mitk::SemanticRelations::ControlpointVector mitk::SemanticRelations::GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID) const { return m_RelationStorage->GetAllControlPointsOfCase(caseID); } mitk::SemanticRelations::ControlpointVector mitk::SemanticRelations::GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const { ControlpointVector allControlPoints = GetAllControlPointsOfCase(caseID); // filter the control points: use only those, where the associated image data has a segmentation that refers to the given lesion using a lambda function auto lambda = [&caseID, &lesion, this](const SemanticTypes::ControlPoint& controlPoint) { return !ControlPointContainsLesion(caseID, lesion, controlPoint); }; allControlPoints.erase(std::remove_if(allControlPoints.begin(), allControlPoints.end(), lambda), allControlPoints.end()); return allControlPoints; } mitk::SemanticRelations::ControlpointVector mitk::SemanticRelations::GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) const { ControlpointVector allControlPoints = GetAllControlPointsOfCase(caseID); // filter the control points: use only those, where the associated image data refers to the given information type using a lambda function auto lambda = [&caseID, &informationType, this](const SemanticTypes::ControlPoint& controlPoint) { return !ControlPointContainsInformationType(caseID, informationType, controlPoint); }; allControlPoints.erase(std::remove_if(allControlPoints.begin(), allControlPoints.end(), lambda), allControlPoints.end()); return allControlPoints; } mitk::SemanticTypes::ControlPoint mitk::SemanticRelations::GetControlPointOfData(const DataNode* dataNode) const { if (nullptr == dataNode) { - mitkThrow() << "Not a valid data node."; + mitkThrowException(SemanticRelationException) << "Not a valid data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); SemanticTypes::ID dataNodeID = GetIDFromDataNode(dataNode); return m_RelationStorage->GetControlPointOfData(caseID, dataNodeID); } mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllImagesOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) const { if (m_DataStorage.IsNull()) { mitkThrow() << "Not a valid data storage."; } if (InstanceExists(caseID, controlPoint)) { // control point exists, retrieve all images from the storage DataNodeVector allImagesOfControlPoint = GetAllImagesOfCase(caseID); // filter all images to remove the ones with a different control point using a lambda function auto lambda = [&controlPoint, this](DataNode::Pointer imageNode) { return controlPoint.UID != GetControlPointOfData(imageNode).UID; }; allImagesOfControlPoint.erase(std::remove_if(allImagesOfControlPoint.begin(), allImagesOfControlPoint.end(), lambda), allImagesOfControlPoint.end()); return allImagesOfControlPoint; } else { mitkThrowException(SemanticRelationException) << "Could not find an existing control point instance for the given caseID " << caseID << " and control point " << controlPoint.UID << "."; } } bool mitk::SemanticRelations::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) const { ControlpointVector allControlPoints = GetAllControlPointsOfCase(caseID); // filter all control points: check for equality with the given control point using a lambda function auto lambda = [&controlPoint](const SemanticTypes::ControlPoint& currentControlPoint) { return currentControlPoint.UID == controlPoint.UID; }; const auto existingControlPoint = std::find_if(allControlPoints.begin(), allControlPoints.end(), lambda); if (existingControlPoint != allControlPoints.end()) { return true; } else { return false; } } mitk::SemanticRelations::InformationTypeVector mitk::SemanticRelations::GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID) const { return m_RelationStorage->GetAllInformationTypesOfCase(caseID); } mitk::SemanticRelations::InformationTypeVector mitk::SemanticRelations::GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) const { InformationTypeVector allInformationTypes = GetAllInformationTypesOfCase(caseID); // filter the information types: use only those, where the associated data refers to the given control point using a lambda function auto lambda = [&caseID, &controlPoint, this](const SemanticTypes::InformationType& informationType) { return !ControlPointContainsInformationType(caseID, informationType, controlPoint); }; allInformationTypes.erase(std::remove_if(allInformationTypes.begin(), allInformationTypes.end(), lambda), allInformationTypes.end()); return allInformationTypes; } mitk::SemanticTypes::InformationType mitk::SemanticRelations::GetInformationTypeOfImage(const DataNode* imageNode) const { if (nullptr == imageNode) { - mitkThrow() << "Not a valid image data node."; + mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); SemanticTypes::ID imageID = GetIDFromDataNode(imageNode); return m_RelationStorage->GetInformationTypeOfImage(caseID, imageID); } mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllImagesOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) const { if (m_DataStorage.IsNull()) { mitkThrow() << "Not a valid data storage."; } if (InstanceExists(caseID, informationType)) { // information type exists, retrieve all images from the storage DataNodeVector allImagesOfInformationType = GetAllImagesOfCase(caseID); // filter all images to remove the ones with a different information type using a lambda function auto lambda = [&informationType, this](DataNode::Pointer imageNode) { return informationType != GetInformationTypeOfImage(imageNode); }; allImagesOfInformationType.erase(std::remove_if(allImagesOfInformationType.begin(), allImagesOfInformationType.end(), lambda), allImagesOfInformationType.end()); return allImagesOfInformationType; } else { mitkThrowException(SemanticRelationException) << "Could not find an existing information type for the given caseID " << caseID << " and information type " << informationType; } } mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllSpecificImages(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const { if (m_DataStorage.IsNull()) { mitkThrow() << "Not a valid data storage."; } if (InstanceExists(caseID, controlPoint)) { if (InstanceExists(caseID, informationType)) { // control point exists, information type exists, retrieve all images from the storage DataNodeVector allImagesOfCase = GetAllImagesOfCase(caseID); // filter all images to remove the ones with a different control point and information type using a lambda function auto lambda = [&controlPoint, &informationType, this](DataNode::Pointer imageNode) { return (informationType != GetInformationTypeOfImage(imageNode)) || (controlPoint != GetControlPointOfData(imageNode)); }; allImagesOfCase.erase(std::remove_if(allImagesOfCase.begin(), allImagesOfCase.end(), lambda), allImagesOfCase.end()); return allImagesOfCase; } else { mitkThrowException(SemanticRelationException) << "Could not find an existing information type for the given caseID " << caseID << " and information type " << informationType; } } else { mitkThrowException(SemanticRelationException) << "Could not find an existing control point for the given caseID " << caseID << " and control point " << controlPoint.UID; } } mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllSpecificSegmentations(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const { if (m_DataStorage.IsNull()) { mitkThrow() << "Not a valid data storage."; } DataNodeVector allSpecificImages = GetAllSpecificImages(caseID, controlPoint, informationType); DataNodeVector allSpecificSegmentations; for (const auto& imageNode : allSpecificImages) { DataStorage::SetOfObjects::ConstPointer segmentationNodes = m_DataStorage->GetDerivations(imageNode, NodePredicates::GetSegmentationPredicate(), false); for (auto it = segmentationNodes->Begin(); it != segmentationNodes->End(); ++it) { allSpecificSegmentations.push_back(it->Value()); } } return allSpecificSegmentations; } bool mitk::SemanticRelations::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) const { InformationTypeVector allInformationTypes = GetAllInformationTypesOfCase(caseID); // filter all information types: check for equality with the given information type using a lambda function auto lambda = [&informationType](const SemanticTypes::InformationType& currentInformationType) { return currentInformationType == informationType; }; const auto existingInformationType = std::find_if(allInformationTypes.begin(), allInformationTypes.end(), lambda); if (existingInformationType != allInformationTypes.end()) { return true; } else { return false; } } std::vector mitk::SemanticRelations::GetAllCaseIDs() const { return m_RelationStorage->GetAllCaseIDs(); } /************************************************************************/ /* functions to add / remove instances / attributes */ /************************************************************************/ void mitk::SemanticRelations::AddImage(const mitk::DataNode* imageNode) { if (nullptr == imageNode) { - mitkThrow() << "Not a valid data node."; + mitkThrowException(SemanticRelationException) << "Not a valid data node."; } // continue with a valid data node SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); SemanticTypes::ID nodeID = GetIDFromDataNode(imageNode); m_RelationStorage->AddCase(caseID); m_RelationStorage->AddImage(caseID, nodeID); SemanticTypes::InformationType informationType = GetDICOMModalityFromDataNode(imageNode); AddInformationTypeToImage(imageNode, informationType); // find the correct control point for this image std::vector allControlPoints = GetAllControlPointsOfCase(caseID); if (allControlPoints.empty()) { // no stored control point // create a new control point for the image data SemanticTypes::ControlPoint newControlPoint = GenerateControlPoint(imageNode); AddControlPointAndLinkData(imageNode, newControlPoint); NotifyObserver(caseID); return; } // some control points already exist - find a control point where the date of the data node fits in SemanticTypes::ControlPoint fittingControlPoint = FindFittingControlPoint(imageNode, allControlPoints); if (fittingControlPoint.UID.empty()) { // did not find a fitting control point, although some control points already exist // need to see if a close control point can be extended SemanticTypes::ControlPoint extendedControlPoint = ExtendClosestControlPoint(imageNode, allControlPoints); if (extendedControlPoint.UID.empty()) { // closest control point can not be extended // create a new control point for the image data SemanticTypes::ControlPoint newControlPoint = GenerateControlPoint(imageNode); AddControlPointAndLinkData(imageNode, newControlPoint); } else { // found a control point that was extended OverwriteControlPointAndLinkData(imageNode, extendedControlPoint); } } else { // found a fitting control point LinkDataToControlPoint(imageNode, fittingControlPoint); } NotifyObserver(caseID); } void mitk::SemanticRelations::RemoveImage(const mitk::DataNode* imageNode) { if (nullptr == imageNode) { - mitkThrow() << "Not a valid data node."; + mitkThrowException(SemanticRelationException) << "Not a valid data node."; } // continue with a valid data node SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); SemanticTypes::ID nodeID = GetIDFromDataNode(imageNode); RemoveInformationTypeFromImage(imageNode); UnlinkDataFromControlPoint(imageNode); m_RelationStorage->RemoveImage(caseID, nodeID); NotifyObserver(caseID); } void mitk::SemanticRelations::AddLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { if (InstanceExists(caseID, lesion)) { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to add already exists for the given case."; } else { m_RelationStorage->AddLesion(caseID, lesion); NotifyObserver(caseID); } } void mitk::SemanticRelations::OverwriteLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { if (InstanceExists(caseID, lesion)) { m_RelationStorage->OverwriteLesion(caseID, lesion); NotifyObserver(caseID); } else { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to overwrite does not exist for the given case."; } } void mitk::SemanticRelations::AddLesionAndLinkSegmentation(const DataNode* segmentationNode, const SemanticTypes::Lesion& lesion) { if (nullptr == segmentationNode) { - mitkThrow() << "Not a valid segmentation data node."; + mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); AddLesion(caseID, lesion); LinkSegmentationToLesion(segmentationNode, lesion); NotifyObserver(caseID); } void mitk::SemanticRelations::RemoveLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { if (InstanceExists(caseID, lesion)) { DataNodeVector allSegmentationsOfLesion = GetAllSegmentationsOfLesion(caseID, lesion); if (allSegmentationsOfLesion.empty()) { // no more segmentations are linked to the specific lesion // the lesion can be removed from the storage m_RelationStorage->RemoveLesion(caseID, lesion); NotifyObserver(caseID); } else { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to remove is still referred to by a segmentation node. Lesion will not be removed."; } } else { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to remove does not exist for the given case."; } } void mitk::SemanticRelations::AddSegmentation(const mitk::DataNode* segmentationNode, const mitk::DataNode* parentNode) { if (nullptr == segmentationNode) { - mitkThrow() << "Not a valid segmentation data node."; + mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } if (nullptr == parentNode) { - mitkThrow() << "Not a valid parent data node."; + mitkThrowException(SemanticRelationException) << "Not a valid parent data node."; } // continue with a valid data node SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); SemanticTypes::ID segmentationNodeID = GetIDFromDataNode(segmentationNode); SemanticTypes::ID parentNodeID = GetIDFromDataNode(parentNode); m_RelationStorage->AddSegmentation(caseID, segmentationNodeID, parentNodeID); NotifyObserver(caseID); } void mitk::SemanticRelations::LinkSegmentationToLesion(const DataNode* segmentationNode, const SemanticTypes::Lesion& lesion) { if (nullptr == segmentationNode) { - mitkThrow() << "Not a valid segmentation data node."; + mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); if (InstanceExists(caseID, lesion)) { SemanticTypes::ID segmentationID = GetIDFromDataNode(segmentationNode); m_RelationStorage->LinkSegmentationToLesion(caseID, segmentationID, lesion); NotifyObserver(caseID); } else { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to link does not exist for the given case."; } } void mitk::SemanticRelations::UnlinkSegmentationFromLesion(const DataNode* segmentationNode) { if (nullptr == segmentationNode) { - mitkThrow() << "Not a valid segmentation data node."; + mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); SemanticTypes::ID segmentationID = GetIDFromDataNode(segmentationNode); m_RelationStorage->UnlinkSegmentationFromLesion(caseID, segmentationID); NotifyObserver(caseID); } void mitk::SemanticRelations::RemoveSegmentation(const mitk::DataNode* segmentationNode) { if (nullptr == segmentationNode) { - mitkThrow() << "Not a valid segmentation data node."; + mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } // continue with a valid data node SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); SemanticTypes::ID segmentationNodeID = GetIDFromDataNode(segmentationNode); m_RelationStorage->RemoveSegmentation(caseID, segmentationNodeID); NotifyObserver(caseID); } void mitk::SemanticRelations::AddControlPointAndLinkData(const DataNode* dataNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence) { if (nullptr == dataNode) { - mitkThrow() << "Not a valid data node."; + mitkThrowException(SemanticRelationException) << "Not a valid data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); if (InstanceExists(caseID, controlPoint)) { mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to add already exists for the given case. \n Use 'LinkDataToControlPoint' instead."; } // control point does not already exist bool contained = CheckContainingControlPoint(caseID, controlPoint); if (contained) { mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to add is already contained in an existing control point."; } // control point is not already contained in an existing control point if (checkConsistence) { // to check the consistency, the control point manager checks, if the date extracted from the data node is inside the given control point bool insideControlPoint = InsideControlPoint(dataNode, controlPoint); if (!insideControlPoint) { mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to add does not contain the date of the given data node."; } } m_RelationStorage->AddControlPoint(caseID, controlPoint); LinkDataToControlPoint(dataNode, controlPoint, checkConsistence); } void mitk::SemanticRelations::OverwriteControlPointAndLinkData(const DataNode* dataNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence) { if (nullptr == dataNode) { - mitkThrow() << "Not a valid data node."; + mitkThrowException(SemanticRelationException) << "Not a valid data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); ControlpointVector allControlPoints = GetAllControlPointsOfCase(caseID); const auto existingControlPoint = std::find_if(allControlPoints.begin(), allControlPoints.end(), [&controlPoint](const SemanticTypes::ControlPoint& currentControlPoint) { return currentControlPoint.UID == controlPoint.UID; }); if (existingControlPoint != allControlPoints.end()) { // control point does already exist if (checkConsistence) { // to check the consistency, the control point manager checks, if the date extracted from the data node is inside the given control point bool insideControlPoint = InsideControlPoint(dataNode, controlPoint); if (!insideControlPoint) { mitkThrowException(SemanticRelationException) << "The overwriting control point " << controlPoint.UID << " does not contain the date of the given data node."; } } bool sameStartPoint = controlPoint.startPoint == (*existingControlPoint).startPoint; bool sameEndPoint = controlPoint.endPoint == (*existingControlPoint).endPoint; if (!sameStartPoint && !sameEndPoint) { mitkThrowException(SemanticRelationException) << "The overwriting control point " << controlPoint.UID << " differs in the start date and in the end date from the original control point."; } if (sameStartPoint && sameEndPoint) { mitkThrowException(SemanticRelationException) << "The overwriting control point " << controlPoint.UID << " does not differ from the original control point."; } bool overlapping = CheckOverlappingControlPoint(caseID, controlPoint); if (overlapping) { mitkThrowException(SemanticRelationException) << "The overwriting control point " << controlPoint.UID << " overlaps with an already existing control point"; } else { m_RelationStorage->OverwriteControlPoint(caseID, controlPoint); LinkDataToControlPoint(dataNode, controlPoint, checkConsistence); } } else { mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to overwrite does not exist for the given case. \n Use 'AddControlPointAndLinkData' instead."; } } void mitk::SemanticRelations::LinkDataToControlPoint(const DataNode* dataNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence) { if (nullptr == dataNode) { - mitkThrow() << "Not a valid data node."; + mitkThrowException(SemanticRelationException) << "Not a valid data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); if (InstanceExists(caseID, controlPoint)) { SemanticTypes::ID dataID = GetIDFromDataNode(dataNode); // control point does already exist if (checkConsistence) { // to check the consistency, the control point manager checks, if the date extracted from the data node is inside the given control point bool insideControlPoint = InsideControlPoint(dataNode, controlPoint); if (!insideControlPoint) { mitkThrowException(SemanticRelationException) << "The data to link does not lie inside the given control point " << controlPoint.UID << " ."; } } m_RelationStorage->LinkDataToControlPoint(caseID, dataID, controlPoint); } else { mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to link does not exist for the given case."; } } void mitk::SemanticRelations::UnlinkDataFromControlPoint(const DataNode* dataNode) { if (nullptr == dataNode) { - mitkThrow() << "Not a valid data node."; + mitkThrowException(SemanticRelationException) << "Not a valid data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); SemanticTypes::ID dataID = GetIDFromDataNode(dataNode); mitk::SemanticTypes::ControlPoint controlPoint = m_RelationStorage->GetControlPointOfData(caseID, dataID); m_RelationStorage->UnlinkDataFromControlPoint(caseID, dataID); DataNodeVector allImagesOfControlPoint = GetAllImagesOfControlPoint(caseID, controlPoint); if (allImagesOfControlPoint.empty()) { // no more data is linked to the specific control point // the control point can be removed from the storage m_RelationStorage->RemoveControlPointFromCase(caseID, controlPoint); } else { // some data is still linked to this control point // the control point can not be removed, but has to be adjusted to fit the remaining data SemanticTypes::ControlPoint adjustedControlPoint = GenerateControlPoint(allImagesOfControlPoint); // set the UIDs to be the same, so that all references still work adjustedControlPoint.UID = controlPoint.UID; adjustedControlPoint.startPoint.UID = controlPoint.startPoint.UID; adjustedControlPoint.endPoint.UID = controlPoint.endPoint.UID; m_RelationStorage->OverwriteControlPoint(caseID, adjustedControlPoint); } } void mitk::SemanticRelations::AddInformationTypeToImage(const DataNode* imageNode, const SemanticTypes::InformationType& informationType) { if (nullptr == imageNode) { - mitkThrow() << "Not a valid image data node."; + mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); SemanticTypes::ID imageID = GetIDFromDataNode(imageNode); m_RelationStorage->AddInformationTypeToImage(caseID, imageID, informationType); } void mitk::SemanticRelations::RemoveInformationTypeFromImage(const DataNode* imageNode) { if (nullptr == imageNode) { - mitkThrow() << "Not a valid image data node."; + mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); SemanticTypes::ID imageID = GetIDFromDataNode(imageNode); SemanticTypes::InformationType originalInformationType = m_RelationStorage->GetInformationTypeOfImage(caseID, imageID); m_RelationStorage->RemoveInformationTypeFromImage(caseID, imageID); // check for further references to the removed information type std::vector allImageIDsVectorValue = m_RelationStorage->GetAllImageIDsOfCase(caseID); for (const auto otherImageID : allImageIDsVectorValue) { SemanticTypes::InformationType otherInformationType = m_RelationStorage->GetInformationTypeOfImage(caseID, otherImageID); if (otherInformationType == originalInformationType) { // found the information type in another image -> cannot remove the information type from the case return; } } // given information type was not referred by any other image of the case -> the information type can be removed from the case m_RelationStorage->RemoveInformationTypeFromCase(caseID, originalInformationType); } /************************************************************************/ /* private functions */ /************************************************************************/ void mitk::SemanticRelations::NotifyObserver(const mitk::SemanticTypes::CaseID& caseID) const { for (auto& observer : m_ObserverVector) { observer->Update(caseID); } } bool mitk::SemanticRelations::ControlPointContainsLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ControlPoint& controlPoint) const { DataNodeVector allImagesOfLesion; try { allImagesOfLesion = GetAllImagesOfLesion(caseID, lesion); } catch (const SemanticRelationException&) { // error retrieving image data; lesion has to be outside the control point return false; } DataNodeVector allImagesOfControlPoint; try { allImagesOfControlPoint = GetAllImagesOfControlPoint(caseID, controlPoint); } catch (const SemanticRelationException&) { // error retrieving control point data; lesion has to be outside the control point return false; } std::sort(allImagesOfLesion.begin(), allImagesOfLesion.end()); std::sort(allImagesOfControlPoint.begin(), allImagesOfControlPoint.end()); DataNodeVector allImagesIntersection; // set intersection removes duplicated nodes, since 'GetAllImagesOfControlPoint' only contains at most one of each node std::set_intersection(allImagesOfLesion.begin(), allImagesOfLesion.end(), allImagesOfControlPoint.begin(), allImagesOfControlPoint.end(), std::back_inserter(allImagesIntersection)); // if the vector of intersecting data is empty, the control point does not contain the lesion return !allImagesIntersection.empty(); } bool mitk::SemanticRelations::ControlPointContainsInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType, const SemanticTypes::ControlPoint& controlPoint) const { DataNodeVector allImagesIntersection = GetAllSpecificImages(caseID, controlPoint, informationType); return !allImagesIntersection.empty(); } bool mitk::SemanticRelations::CheckOverlappingControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { ControlpointVector allControlPoints = GetAllControlPointsOfCase(caseID); if (allControlPoints.empty()) { return false; } std::sort(allControlPoints.begin(), allControlPoints.end()); const auto existingControlPoint = std::find_if(allControlPoints.begin(), allControlPoints.end(), [&controlPoint](const SemanticTypes::ControlPoint& currentControlPoint) { return currentControlPoint.UID == controlPoint.UID; }); auto nextControlPoint = allControlPoints.end(); auto previousControlPoint = allControlPoints.begin(); if (existingControlPoint != allControlPoints.end()) { // case overwriting: control point already contained in the list of all existing control points nextControlPoint = std::next(existingControlPoint, 1); if (existingControlPoint != allControlPoints.begin()) { previousControlPoint = std::prev(existingControlPoint, 1); } } else { // case adding: control point not contained in the list of all existing control points nextControlPoint = std::find_if(allControlPoints.begin(), allControlPoints.end(), [&controlPoint](const SemanticTypes::ControlPoint& currentControlPoint) { return currentControlPoint.startPoint >= controlPoint.endPoint; }); if (nextControlPoint != allControlPoints.begin()) { previousControlPoint = std::prev(existingControlPoint, 1); } } // check the neighboring control points for overlap bool overlapWithNext = false; bool overlapWithPrevious = CheckForOverlap(controlPoint, *previousControlPoint); if (nextControlPoint != allControlPoints.end()) { overlapWithNext = CheckForOverlap(controlPoint, *nextControlPoint); } return overlapWithNext || overlapWithPrevious; // return true if at least one overlap is detected } bool mitk::SemanticRelations::CheckContainingControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { ControlpointVector allControlPoints = GetAllControlPointsOfCase(caseID); if (allControlPoints.empty()) { return false; } const auto existingControlPoint = std::find_if(allControlPoints.begin(), allControlPoints.end(), [&controlPoint](const SemanticTypes::ControlPoint& currentControlPoint) { return currentControlPoint.UID == controlPoint.UID; }); if (existingControlPoint != allControlPoints.end()) { // case overwriting: control point already contained in the list of all existing control points // -> duplicated control point found (regardless of the actual start point and end point) return true; } else { // case adding: control point not contained in the list of all existing control points for (const auto& existingControlPoint : allControlPoints) { bool contained = InsideControlPoint(controlPoint, existingControlPoint); if (contained) { return true; } } } return false; } diff --git a/Modules/SemanticRelationsUI/files.cmake b/Modules/SemanticRelationsUI/files.cmake index 710e2381d3..ceda26fe10 100644 --- a/Modules/SemanticRelationsUI/files.cmake +++ b/Modules/SemanticRelationsUI/files.cmake @@ -1,32 +1,26 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkModuleActivator.cpp QmitkAbstractSemanticRelationsStorageModel.cpp QmitkControlPointDialog.cpp - QmitkLesionInfoWidget.cpp + QmitkLesionTextDialog.cpp QmitkPatientTableInspector.cpp QmitkPatientTableModel.cpp - QmitkSelectionWidget.cpp - QmitkSelectNodeDialog.cpp + QmitkSemanticRelationsModel.cpp QmitkSemanticRelationsUIHelper.cpp - QmitkSimpleDatamanagerWidget.cpp ) set(MOC_H_FILES include/QmitkAbstractSemanticRelationsStorageInspector.h include/QmitkAbstractSemanticRelationsStorageModel.h include/QmitkControlPointDialog.h - include/QmitkLesionInfoWidget.h + include/QmitkLesionTextDialog.h include/QmitkPatientTableInspector.h include/QmitkPatientTableModel.h - include/QmitkSelectionWidget.h - include/QmitkSelectNodeDialog.h - include/QmitkSimpleDatamanagerWidget.h + include/QmitkSemanticRelationsModel.h ) set(UI_FILES - src/QmitkLesionInfoWidgetControls.ui src/QmitkPatientTableInspector.ui - src/QmitkSimpleDatamanagerWidgetControls.ui ) diff --git a/Modules/SemanticRelationsUI/include/QmitkLesionTextDialog.h b/Modules/SemanticRelationsUI/include/QmitkLesionTextDialog.h new file mode 100644 index 0000000000..b50806adf9 --- /dev/null +++ b/Modules/SemanticRelationsUI/include/QmitkLesionTextDialog.h @@ -0,0 +1,49 @@ +/*=================================================================== + +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 QMITKLESIONTEXTDIALOG_H +#define QMITKLESIONTEXTDIALOG_H + +#include + +// semantic relations module +#include "mitkSemanticTypes.h" + +#include +#include + +class MITKSEMANTICRELATIONSUI_EXPORT QmitkLesionTextDialog : public QDialog +{ + Q_OBJECT + +public: + + QmitkLesionTextDialog(QWidget *parent = nullptr); + + virtual ~QmitkLesionTextDialog(); + + void SetLineEditText(const std::string& lineEditText); + QString GetLineEditText() const; + + QLineEdit* GetLineEdit() const; + +private: + + QLineEdit* m_LineEdit; + +}; + +#endif // QMITKLESIONTEXTDIALOG_H diff --git a/Modules/SemanticRelationsUI/include/QmitkSelectNodeDialog.h b/Modules/SemanticRelationsUI/include/QmitkSelectNodeDialog.h deleted file mode 100644 index 3848e7bab7..0000000000 --- a/Modules/SemanticRelationsUI/include/QmitkSelectNodeDialog.h +++ /dev/null @@ -1,71 +0,0 @@ -/*=================================================================== - -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 QMITKSELECTNODEDIALOG_H -#define QMITKSELECTNODEDIALOG_H - -#include - -// semantic relations UI module -#include "QmitkSelectionWidget.h" - -// mitk -#include - -// qt -#include -#include - -/* -* @brief The QmitkSelectNodeDialog is a QDialog to select nodes in a user defined way. The Widget to use for data node selection can be injected. -* -* The QmitkSelectNodeDialog provides a 'SetSelectionWidget'-function, that uses the 'QmitkSelectionWidget'-parameter to receive a specific widget. -* This specific widget can be implemented in any way, as a subclass of 'QmitkSelectionWidget'. -* -* The QmitkSelectNodeDialog holds the currently selected data node, that can be received by using the 'GetSelectedDataNode'-function. However, in order to -* have the dialog return the currently selected data node, the specific subclass of the 'QmitkSelectionWidget' has to emit the 'SelectionChanged(mitk::DataNode*)'-signal -* with the currently selected data node. This data node will be stored and returned on request. -*/ -class MITKSEMANTICRELATIONSUI_EXPORT QmitkSelectNodeDialog : public QDialog -{ - Q_OBJECT - -public: - - QmitkSelectNodeDialog(QWidget* parent = nullptr); - virtual ~QmitkSelectNodeDialog(); - - void SetSelectionWidget(QmitkSelectionWidget* selectionWidget); - mitk::DataNode* GetSelectedDataNode() const; - -private Q_SLOTS: - - /* - * @brief A slot that is used on the 'SelectionChanged(mitk::DataNode*)'-signal of the injected 'QmitkSelectionWidget'. - * - * @par A pointer to the data node that is newly selected - */ - void OnSelectionWidgetSelectionChanged(mitk::DataNode*); - -private: - - QBoxLayout* m_VerticalLayout; - QmitkSelectionWidget* m_SelectionWidget; - mitk::DataNode* m_SelectedDataNode; - -}; - -#endif // QMITKSELECTNODEDIALOG_H diff --git a/Modules/SemanticRelationsUI/include/QmitkSelectionWidget.h b/Modules/SemanticRelationsUI/include/QmitkSelectionWidget.h deleted file mode 100644 index 331297130d..0000000000 --- a/Modules/SemanticRelationsUI/include/QmitkSelectionWidget.h +++ /dev/null @@ -1,78 +0,0 @@ -/*=================================================================== - -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 QMITKSELECTIONWIDGET_H -#define QMITKSELECTIONWIDGET_H - -// semantic relations UI module -#include "MitkSemanticRelationsUIExports.h" - -// semantic relations module -#include - -// mitk core -#include -#include - -// qt -#include - -/* -* @brief The QmitkSelectionWidget is an abstract widget that provides subclasses with some base-functions and offers a Q_SIGNAL. -* -* The QmitkSelectionWidget is typically used in a way where a specific derived widget should be used / injected. Several subclasses can be created -* and used, wherever the QmitkSelectionWidget is needed. -* -* This abstract widget provides a DataStorage instance, a SemanticRelations instance and the possibility to update the data storage for all its derived widgets. -* -* The "SelectionChanged"-signal can be emitted from each subclass. This is needed in order to inform other widgets about a changed selection in the derived widget. -* The 'QmitkPatientTableWidget' and the 'QmitkSimpleDatamanagerWidget' are currently derived from this abstract widget. -*/ -class MITKSEMANTICRELATIONSUI_EXPORT QmitkSelectionWidget : public QWidget -{ - Q_OBJECT - -public: - - virtual ~QmitkSelectionWidget() = 0; - - /* - * @brief Sets the data storage and updates the semantic relations pointer at once - * - * @par dataStorage A pointer to the data storage to set - */ - virtual void SetDataStorage(mitk::DataStorage* dataStorage); - -Q_SIGNALS: - - /* - * @brief A base signal that can be emitted in any subclass of this widget - * - * Has to be emitted in order for a QmitkSelectNodeDialog to receive the currently selected data node. - * - * @par A pointer to the data node that is newly selected - */ - void SelectionChanged(mitk::DataNode*); - -protected: - - QmitkSelectionWidget(mitk::DataStorage* dataStorage, QWidget* parent = nullptr); - mitk::DataStorage* m_DataStorage; - std::shared_ptr m_SemanticRelations; - -}; - -#endif // QMITKSELECTIONWIDGET_H diff --git a/Modules/SemanticRelationsUI/include/QmitkSimpleDatamanagerWidget.h b/Modules/SemanticRelationsUI/include/QmitkSimpleDatamanagerWidget.h deleted file mode 100644 index 24cf7b5a58..0000000000 --- a/Modules/SemanticRelationsUI/include/QmitkSimpleDatamanagerWidget.h +++ /dev/null @@ -1,76 +0,0 @@ -/*=================================================================== - -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 QMITKSIMPLEDATAMANAGERWIDGET_H -#define QMITKSIMPLEDATAMANAGERWIDGET_H - -// semantic relations UI module -#include "MitkSemanticRelationsUIExports.h" -#include -#include "QmitkSelectionWidget.h" - -// mitk core -#include -#include -#include - -// mitk qt widgets -#include -#include - -/* -* @brief The QmitkSimpleDatamanagerWidget is a widget that shows the currently available data of the data storage in a hierarchy tree view. -* -* The QmitkSimpleDatamanagerWidget is derived from 'QmitkSelectionWidget', so that the "SelectionChanged"-signal can be emitted. -* This is done in order to inform other widgets about a changed selection in this widget (e.g. the 'QmitkSelectNodeDialog'). -* The QmitkSimpleDatamanagerWidget is currently injected in the 'QmitkSelectNodeDialog' in the 'QmitkSemanticRelationsView' class. -*/ -class MITKSEMANTICRELATIONSUI_EXPORT QmitkSimpleDatamanagerWidget : public QmitkSelectionWidget -{ - Q_OBJECT - -public: - - QmitkSimpleDatamanagerWidget(mitk::DataStorage* dataStorage, QWidget* parent = nullptr); - virtual ~QmitkSimpleDatamanagerWidget(); - -private Q_SLOTS: - - /* - * @brief A slot that is connected with the 'selectionChanged'-signal of the underlying model. - * - * This function gets the currently selected data node and emits the 'SelectionChanged(mitk::DataNode*)'-signal of the injected 'QmitkSelectionWidget'. - */ - void OnNodeTreeViewSelectionChanged(const QItemSelection&, const QItemSelection&); - -private: - - void Init(); - void SetUpConnections(); - - Ui::QmitkSimpleDatamanagerWidgetControls m_Controls; - - std::unique_ptr m_NodeTreeModel; - std::unique_ptr m_FilterModel; - - mitk::NodePredicateBase::Pointer m_HelperObjectFilterPredicate; - mitk::NodePredicateBase::Pointer m_NodeWithNoDataFilterPredicate; - - mitk::DataNode* m_SelectedDataNode; - -}; - -#endif // QMITKSIMPLEDATAMANAGERWIDGET_H diff --git a/Modules/SemanticRelationsUI/src/QmitkControlPointDialog.cpp b/Modules/SemanticRelationsUI/src/QmitkControlPointDialog.cpp index 968272578a..678ba857c8 100644 --- a/Modules/SemanticRelationsUI/src/QmitkControlPointDialog.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkControlPointDialog.cpp @@ -1,69 +1,69 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkControlPointDialog.h" #include #include #include #include #include QmitkControlPointDialog::QmitkControlPointDialog(QWidget* parent) : QDialog(parent) { //QDialog::setFixedSize(250, 105); QBoxLayout* verticalLayout = new QVBoxLayout(this); verticalLayout->setMargin(5); verticalLayout->setSpacing(5); QLabel* dateLabel = new QLabel(tr("Set date"), this); verticalLayout->addWidget(dateLabel); m_DateEdit = new QDateEdit(this); m_DateEdit->setDisplayFormat("yyyy-MM-dd"); m_DateEdit->setFocus(); verticalLayout->addWidget(m_DateEdit); QPushButton* acceptButton = new QPushButton(tr("Ok"), this); QPushButton* cancelButton = new QPushButton(tr("Cancel"), this); acceptButton->setDefault(true); - connect(acceptButton, SIGNAL(clicked()), SLOT(accept())); - connect(cancelButton, SIGNAL(clicked()), SLOT(reject())); + connect(acceptButton, &QPushButton::clicked, this, &QmitkControlPointDialog::accept); + connect(cancelButton, &QPushButton::clicked, this, &QmitkControlPointDialog::reject); QBoxLayout* horizontalLayout = new QHBoxLayout(); horizontalLayout->setSpacing(5); horizontalLayout->addStretch(); horizontalLayout->addWidget(acceptButton); horizontalLayout->addWidget(cancelButton); verticalLayout->addLayout(horizontalLayout); } QmitkControlPointDialog::~QmitkControlPointDialog() { } void QmitkControlPointDialog::SetCurrentDate(mitk::SemanticTypes::Date currentDate) { m_DateEdit->setDate(QDate(currentDate.year, currentDate.month, currentDate.day)); } QDate QmitkControlPointDialog::GetCurrentDate() const { return m_DateEdit->date(); } diff --git a/Modules/SemanticRelationsUI/src/QmitkControlPointDialog.cpp b/Modules/SemanticRelationsUI/src/QmitkLesionTextDialog.cpp similarity index 57% copy from Modules/SemanticRelationsUI/src/QmitkControlPointDialog.cpp copy to Modules/SemanticRelationsUI/src/QmitkLesionTextDialog.cpp index 968272578a..37de577465 100644 --- a/Modules/SemanticRelationsUI/src/QmitkControlPointDialog.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkLesionTextDialog.cpp @@ -1,69 +1,70 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ -#include "QmitkControlPointDialog.h" +#include "QmitkLesionTextDialog.h" -#include -#include #include #include #include -QmitkControlPointDialog::QmitkControlPointDialog(QWidget* parent) +QmitkLesionTextDialog::QmitkLesionTextDialog(QWidget* parent) : QDialog(parent) { - //QDialog::setFixedSize(250, 105); - QBoxLayout* verticalLayout = new QVBoxLayout(this); verticalLayout->setMargin(5); verticalLayout->setSpacing(5); - QLabel* dateLabel = new QLabel(tr("Set date"), this); - verticalLayout->addWidget(dateLabel); + QLabel* dialogLabel = new QLabel(tr("Set lesion information"), this); + verticalLayout->addWidget(dialogLabel); - m_DateEdit = new QDateEdit(this); - m_DateEdit->setDisplayFormat("yyyy-MM-dd"); - m_DateEdit->setFocus(); - verticalLayout->addWidget(m_DateEdit); + m_LineEdit = new QLineEdit(this); + m_LineEdit->setFocus(); + verticalLayout->addWidget(m_LineEdit); QPushButton* acceptButton = new QPushButton(tr("Ok"), this); QPushButton* cancelButton = new QPushButton(tr("Cancel"), this); acceptButton->setDefault(true); - connect(acceptButton, SIGNAL(clicked()), SLOT(accept())); - connect(cancelButton, SIGNAL(clicked()), SLOT(reject())); + connect(acceptButton, &QPushButton::clicked, this, &QmitkLesionTextDialog::accept); + connect(cancelButton, &QPushButton::clicked, this, &QmitkLesionTextDialog::reject); QBoxLayout* horizontalLayout = new QHBoxLayout(); horizontalLayout->setSpacing(5); horizontalLayout->addStretch(); horizontalLayout->addWidget(acceptButton); horizontalLayout->addWidget(cancelButton); verticalLayout->addLayout(horizontalLayout); } -QmitkControlPointDialog::~QmitkControlPointDialog() +QmitkLesionTextDialog::~QmitkLesionTextDialog() +{ + // nothing here +} + +void QmitkLesionTextDialog::SetLineEditText(const std::string& lineEditText) { + m_LineEdit->setText(QString::fromStdString(lineEditText)); } -void QmitkControlPointDialog::SetCurrentDate(mitk::SemanticTypes::Date currentDate) +QString QmitkLesionTextDialog::GetLineEditText() const { - m_DateEdit->setDate(QDate(currentDate.year, currentDate.month, currentDate.day)); + return m_LineEdit->text(); } -QDate QmitkControlPointDialog::GetCurrentDate() const +QLineEdit* QmitkLesionTextDialog::GetLineEdit() const { - return m_DateEdit->date(); + return m_LineEdit; } diff --git a/Modules/SemanticRelationsUI/src/QmitkPatientTableInspector.cpp b/Modules/SemanticRelationsUI/src/QmitkPatientTableInspector.cpp index 8b19305250..184ad698b9 100644 --- a/Modules/SemanticRelationsUI/src/QmitkPatientTableInspector.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkPatientTableInspector.cpp @@ -1,255 +1,255 @@ /*=================================================================== 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 "QmitkPatientTableInspector.h" #include "QmitkControlPointDialog.h" #include "QmitkCustomVariants.h" // semantic relations module #include #include #include // qt #include #include QmitkPatientTableInspector::QmitkPatientTableInspector(QWidget* parent/* =nullptr*/) { m_Controls.setupUi(this); m_Controls.tableView->horizontalHeader()->setHighlightSections(false); m_Controls.tableView->verticalHeader()->setHighlightSections(false); m_Controls.tableView->setSelectionMode(QAbstractItemView::SingleSelection); m_Controls.tableView->setSelectionBehavior(QAbstractItemView::SelectItems); m_Controls.tableView->setContextMenuPolicy(Qt::CustomContextMenu); m_StorageModel = new QmitkPatientTableModel(this); m_Controls.tableView->setModel(m_StorageModel); m_ContextMenu = new QMenu(m_Controls.tableView); SetUpConnections(); } QAbstractItemView* QmitkPatientTableInspector::GetView() { return m_Controls.tableView; } const QAbstractItemView* QmitkPatientTableInspector::GetView() const { return m_Controls.tableView; } void QmitkPatientTableInspector::SetSelectionMode(SelectionMode mode) { m_Controls.tableView->setSelectionMode(mode); } QmitkPatientTableInspector::SelectionMode QmitkPatientTableInspector::GetSelectionMode() const { return m_Controls.tableView->selectionMode(); } void QmitkPatientTableInspector::SetCaseID(const mitk::SemanticTypes::CaseID& caseID) { m_StorageModel->SetCaseID(caseID); } void QmitkPatientTableInspector::SetLesion(const mitk::SemanticTypes::Lesion& lesion) { m_StorageModel->SetLesion(lesion); } void QmitkPatientTableInspector::Initialize() { m_StorageModel->SetDataStorage(m_DataStorage.Lock()); m_StorageModel->SetNodePredicate(m_NodePredicate); m_Connector->SetView(m_Controls.tableView); } void QmitkPatientTableInspector::OnModelUpdated() { m_Controls.tableView->resizeRowsToContents(); m_Controls.tableView->resizeColumnsToContents(); } void QmitkPatientTableInspector::OnNodeButtonClicked(const QString& nodeType) { m_StorageModel->SetNodeType(nodeType.toStdString()); } void QmitkPatientTableInspector::OnTableViewContextMenuRequested(const QPoint& pos) { QModelIndex selectedIndex = m_Controls.tableView->indexAt(pos); if (!selectedIndex.isValid()) { return; } QVariant qvariantDataNode = m_StorageModel->data(selectedIndex, QmitkDataNodeRawPointerRole); if (qvariantDataNode.canConvert()) { m_SelectedDataNode = qvariantDataNode.value(); m_ContextMenu->clear(); QAction* setInformationTypeAction = new QAction("Set information type", m_ContextMenu); m_ContextMenu->addAction(setInformationTypeAction); - connect(setInformationTypeAction, SIGNAL(triggered()), this, SLOT(OnContextMenuSetInformationType())); + connect(setInformationTypeAction, &QAction::triggered, this, &QmitkPatientTableInspector::OnContextMenuSetInformationType); QAction* setControlPointAction = new QAction("Set control point", m_ContextMenu); m_ContextMenu->addAction(setControlPointAction); - connect(setControlPointAction, SIGNAL(triggered()), this, SLOT(OnContextMenuSetControlPoint())); + connect(setControlPointAction, &QAction::triggered, this, &QmitkPatientTableInspector::OnContextMenuSetControlPoint); m_ContextMenu->popup(QCursor::pos()); } } void QmitkPatientTableInspector::OnContextMenuSetInformationType() { bool ok = false; QString text = QInputDialog::getText(m_Controls.tableView, tr("Set information type of selected node"), tr("Information type:"), QLineEdit::Normal, "", &ok); if (ok && !text.isEmpty()) { m_StorageModel->GetSemanticRelations()->RemoveInformationTypeFromImage(m_SelectedDataNode); m_StorageModel->GetSemanticRelations()->AddInformationTypeToImage(m_SelectedDataNode, text.toStdString()); m_StorageModel->UpdateModelData(); } } void QmitkPatientTableInspector::OnContextMenuSetControlPoint() { QmitkControlPointDialog* inputDialog = new QmitkControlPointDialog(m_Controls.tableView); inputDialog->setWindowTitle("Set control point"); inputDialog->SetCurrentDate(mitk::GetDICOMDateFromDataNode(m_SelectedDataNode)); int dialogReturnValue = inputDialog->exec(); if (QDialog::Rejected == dialogReturnValue) { return; } // store the current control point to relink it, if anything goes wrong mitk::SemanticTypes::ControlPoint originalControlPoint = m_StorageModel->GetSemanticRelations()->GetControlPointOfData(m_SelectedDataNode); // unlink the data, that is about to receive a new date // this is needed in order to not extend a single control point, to which the selected node is currently linked m_StorageModel->GetSemanticRelations()->UnlinkDataFromControlPoint(m_SelectedDataNode); const QDate& userSelectedDate = inputDialog->GetCurrentDate(); mitk::SemanticTypes::Date date; date.UID = mitk::UIDGeneratorBoost::GenerateUID(); date.year = userSelectedDate.year(); date.month = userSelectedDate.month(); date.day = userSelectedDate.day(); std::vector allControlPoints = m_StorageModel->GetSemanticRelations()->GetAllControlPointsOfCase(m_StorageModel->GetCaseID()); if (!allControlPoints.empty()) { // need to check if an already existing control point fits/contains the user control point mitk::SemanticTypes::ControlPoint fittingControlPoint = mitk::FindFittingControlPoint(date, allControlPoints); if (!fittingControlPoint.UID.empty()) { try { // found a fitting control point m_StorageModel->GetSemanticRelations()->LinkDataToControlPoint(m_SelectedDataNode, fittingControlPoint, false); m_StorageModel->UpdateModelData(); } catch (const mitk::SemanticRelationException&) { MITK_INFO << "The data can not be linked to the fitting control point."; try { // link to the original control point m_StorageModel->GetSemanticRelations()->LinkDataToControlPoint(m_SelectedDataNode, originalControlPoint, false); } catch (const mitk::SemanticRelationException&) { MITK_INFO << "The data can not be linked to its original control point. Inconsistency in the semantic relations storage assumed."; } } return; } // did not find a fitting control point, although some control points already exist // need to check if a close control point can be found and extended mitk::SemanticTypes::ControlPoint extendedControlPoint = mitk::ExtendClosestControlPoint(date, allControlPoints); if (!extendedControlPoint.UID.empty()) { try { // found and extended a close control point m_StorageModel->GetSemanticRelations()->OverwriteControlPointAndLinkData(m_SelectedDataNode, extendedControlPoint, false); m_StorageModel->UpdateModelData(); } catch (const mitk::SemanticRelationException&) { MITK_INFO << "The extended control point can not be overwritten and the data can not be linked to this control point."; try { // link to the original control point m_StorageModel->GetSemanticRelations()->LinkDataToControlPoint(m_SelectedDataNode, originalControlPoint, false); } catch (const mitk::SemanticRelationException&) { MITK_INFO << "The data can not be linked to its original control point. Inconsistency in the semantic relations storage assumed."; } } return; } } // generate a control point from the user-given date mitk::SemanticTypes::ControlPoint controlPointFromUserDate = mitk::GenerateControlPoint(date); try { m_StorageModel->GetSemanticRelations()->AddControlPointAndLinkData(m_SelectedDataNode, controlPointFromUserDate, false); m_StorageModel->UpdateModelData(); } catch (const mitk::SemanticRelationException&) { MITK_INFO << "The control point can not be added and the data can not be linked to this control point."; try { // link to the original control point m_StorageModel->GetSemanticRelations()->LinkDataToControlPoint(m_SelectedDataNode, originalControlPoint, false); } catch (const mitk::SemanticRelationException&) { MITK_INFO << "The data can not be linked to its original control point. Inconsistency in the semantic relations storage assumed."; } } } void QmitkPatientTableInspector::SetUpConnections() { connect(m_StorageModel, SIGNAL(ModelUpdated()), SLOT(OnModelUpdated())); connect(m_Controls.tableView, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(OnTableViewContextMenuRequested(const QPoint&))); QSignalMapper* nodeButtonSignalMapper = new QSignalMapper(this); nodeButtonSignalMapper->setMapping(m_Controls.imageNodeButton, QString("Image")); nodeButtonSignalMapper->setMapping(m_Controls.segmentationNodeButton, QString("Segmentation")); connect(nodeButtonSignalMapper, SIGNAL(mapped(const QString&)), this, SLOT(OnNodeButtonClicked(const QString&))); connect(m_Controls.imageNodeButton, SIGNAL(clicked()), nodeButtonSignalMapper, SLOT(map())); connect(m_Controls.segmentationNodeButton, SIGNAL(clicked()), nodeButtonSignalMapper, SLOT(map())); m_Controls.imageNodeButton->setChecked(true); } diff --git a/Modules/SemanticRelationsUI/src/QmitkSelectNodeDialog.cpp b/Modules/SemanticRelationsUI/src/QmitkSelectNodeDialog.cpp deleted file mode 100644 index 393456e262..0000000000 --- a/Modules/SemanticRelationsUI/src/QmitkSelectNodeDialog.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/*=================================================================== - -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 "QmitkSelectNodeDialog.h" - -// qt -#include - -QmitkSelectNodeDialog::QmitkSelectNodeDialog(QWidget* parent/* = nullptr*/) - : QDialog(parent) - , m_SelectionWidget(nullptr) - , m_SelectedDataNode(nullptr) -{ - m_VerticalLayout = new QVBoxLayout(this); - - QPushButton* acceptButton = new QPushButton(tr("Ok"), this); - QPushButton* cancelButton = new QPushButton(tr("Cancel"), this); - acceptButton->setDefault(true); - - m_VerticalLayout->addWidget(acceptButton); - m_VerticalLayout->addWidget(cancelButton); - connect(acceptButton, SIGNAL(clicked()), SLOT(accept())); - connect(cancelButton, SIGNAL(clicked()), SLOT(reject())); -} - -QmitkSelectNodeDialog::~QmitkSelectNodeDialog() -{ - // nothing here -} - -void QmitkSelectNodeDialog::SetSelectionWidget(QmitkSelectionWidget* selectionWidget) -{ - m_SelectionWidget = selectionWidget; - m_VerticalLayout->insertWidget(0, m_SelectionWidget); - connect(m_SelectionWidget, SIGNAL(SelectionChanged(mitk::DataNode*)), SLOT(OnSelectionWidgetSelectionChanged(mitk::DataNode*))); -} - -void QmitkSelectNodeDialog::OnSelectionWidgetSelectionChanged(mitk::DataNode* dataNode) -{ - m_SelectedDataNode = dataNode; -} - -mitk::DataNode* QmitkSelectNodeDialog::GetSelectedDataNode() const -{ - return m_SelectedDataNode; -} diff --git a/Modules/SemanticRelationsUI/src/QmitkSelectionWidget.cpp b/Modules/SemanticRelationsUI/src/QmitkSelectionWidget.cpp deleted file mode 100644 index 78584ed8a1..0000000000 --- a/Modules/SemanticRelationsUI/src/QmitkSelectionWidget.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/*=================================================================== - -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 "QmitkSelectionWidget.h" - -QmitkSelectionWidget::QmitkSelectionWidget(mitk::DataStorage* dataStorage, QWidget* parent /*=nullptr*/) - : QWidget(parent) - , m_DataStorage(dataStorage) - , m_SemanticRelations(std::make_shared(dataStorage)) -{ - // nothing here -} - -QmitkSelectionWidget::~QmitkSelectionWidget() -{ - // nothing here -} - -void QmitkSelectionWidget::SetDataStorage(mitk::DataStorage* dataStorage) -{ - if (m_DataStorage != dataStorage) - { - // set the new data storage - m_DataStorage = dataStorage; - m_SemanticRelations = std::make_shared(m_DataStorage); - } -} diff --git a/Modules/SemanticRelationsUI/src/QmitkSimpleDatamanagerWidget.cpp b/Modules/SemanticRelationsUI/src/QmitkSimpleDatamanagerWidget.cpp deleted file mode 100644 index ca0910195b..0000000000 --- a/Modules/SemanticRelationsUI/src/QmitkSimpleDatamanagerWidget.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/*=================================================================== - -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 "QmitkSimpleDatamanagerWidget.h" - -#include "QmitkCustomVariants.h" - -// semantic relations module -#include -#include -#include - -// mitk core -#include "mitkNodePredicateData.h" -#include "mitkNodePredicateOr.h" -#include "mitkNodePredicateProperty.h" - -QmitkSimpleDatamanagerWidget::QmitkSimpleDatamanagerWidget(mitk::DataStorage* dataStorage, QWidget* parent /*=nullptr*/) - : QmitkSelectionWidget(dataStorage, parent) -{ - Init(); -} - -QmitkSimpleDatamanagerWidget::~QmitkSimpleDatamanagerWidget() -{ - // nothing here -} - -void QmitkSimpleDatamanagerWidget::Init() -{ - // create GUI from the Qt Designer's .ui file - m_Controls.setupUi(this); - - // create a new model - m_NodeTreeModel = std::make_unique(m_DataStorage, false, this); - m_NodeTreeModel->setParent(this); - m_NodeTreeModel->SetPlaceNewNodesOnTop(true); - m_NodeTreeModel->SetAllowHierarchyChange(false); - // prepare filters - m_HelperObjectFilterPredicate = mitk::NodePredicateOr::New(mitk::NodePredicateProperty::New("helper object", mitk::BoolProperty::New(true)), - mitk::NodePredicateProperty::New("hidden object", mitk::BoolProperty::New(true))); - m_NodeWithNoDataFilterPredicate = mitk::NodePredicateData::New(0); - - m_FilterModel = std::make_unique(this); - m_FilterModel->setSourceModel(m_NodeTreeModel.get()); - m_FilterModel->AddFilterPredicate(m_HelperObjectFilterPredicate); - m_FilterModel->AddFilterPredicate(m_NodeWithNoDataFilterPredicate); - - m_Controls.dataNodeTreeView->setModel(m_FilterModel.get()); - m_Controls.dataNodeTreeView->setHeaderHidden(true); - m_Controls.dataNodeTreeView->setSelectionBehavior(QAbstractItemView::SelectRows); - m_Controls.dataNodeTreeView->setSelectionMode(QAbstractItemView::SingleSelection); - m_Controls.dataNodeTreeView->setAlternatingRowColors(true); - m_Controls.dataNodeTreeView->setTextElideMode(Qt::ElideMiddle); - //m_Controls.dataNodeTreeView->installEventFilter(new QmitkNodeTableViewKeyFilter(this)); - - //m_ItemDelegate = new QmitkDataManagerItemDelegate(m_Controls.dataNodeTreeView); - //m_Controls.dataNodeTreeView->setItemDelegate(m_ItemDelegate); - - SetUpConnections(); -} - -void QmitkSimpleDatamanagerWidget::SetUpConnections() -{ - connect(m_Controls.dataNodeTreeView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), SLOT(OnNodeTreeViewSelectionChanged(const QItemSelection&, const QItemSelection&))); -} - -void QmitkSimpleDatamanagerWidget::OnNodeTreeViewSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) -{ - QModelIndex selectedIndex = m_Controls.dataNodeTreeView->currentIndex(); - if (!selectedIndex.isValid()) - { - return; - } - - QVariant qvariantDataNode = m_FilterModel->data(selectedIndex, QmitkDataNodeRawPointerRole); - if (qvariantDataNode.canConvert()) - { - m_SelectedDataNode = qvariantDataNode.value(); - emit SelectionChanged(m_SelectedDataNode); - } -} diff --git a/Modules/SemanticRelationsUI/src/QmitkSimpleDatamanagerWidgetControls.ui b/Modules/SemanticRelationsUI/src/QmitkSimpleDatamanagerWidgetControls.ui deleted file mode 100644 index 1812f56f0e..0000000000 --- a/Modules/SemanticRelationsUI/src/QmitkSimpleDatamanagerWidgetControls.ui +++ /dev/null @@ -1,31 +0,0 @@ - - - QmitkSimpleDatamanagerWidgetControls - - - true - - - - 0 - 0 - 350 - 300 - - - - - - - Available data nodes - - - - - - - - - - - diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/files.cmake b/Plugins/org.mitk.gui.qt.semanticrelations/files.cmake index d1fe5933d4..c749680cc0 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/files.cmake +++ b/Plugins/org.mitk.gui.qt.semanticrelations/files.cmake @@ -1,27 +1,30 @@ set(INTERNAL_CPP_FILES mitkPluginActivator.cpp + QmitkLesionInfoWidget.cpp QmitkSemanticRelationsNodeSelectionDialog.cpp QmitkSemanticRelationsView.cpp ) set(UI_FILES + src/internal/QmitkLesionInfoWidgetControls.ui src/internal/QmitkSemanticRelationsControls.ui ) set(MOC_H_FILES src/internal/mitkPluginActivator.h + src/internal/QmitkLesionInfoWidget.h src/internal/QmitkSemanticRelationsNodeSelectionDialog.h src/internal/QmitkSemanticRelationsView.h ) set(CACHED_RESOURCE_FILES resources/SemanticRelations_48.png plugin.xml ) set(QRC_FILES ) foreach(file ${INTERNAL_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/internal/${file}) endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/Modules/SemanticRelationsUI/src/QmitkLesionInfoWidget.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp similarity index 58% rename from Modules/SemanticRelationsUI/src/QmitkLesionInfoWidget.cpp rename to Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp index 3627b94bb5..4a4c4c59b9 100644 --- a/Modules/SemanticRelationsUI/src/QmitkLesionInfoWidget.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp @@ -1,748 +1,1032 @@ /*=================================================================== 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 +// semantic relations plugin #include "QmitkLesionInfoWidget.h" +#include "QmitkSemanticRelationsNodeSelectionDialog.h" -#include "QmitkCustomVariants.h" +// semantic relations UI module +#include // semantic relations module +#include #include +#include #include +#include "QmitkCustomVariants.h" + // multi label module #include // mitk core #include // qt +#include #include #include #include #include -const QBrush QmitkLesionInfoWidget::DEFAULT_BACKGROUND_COLOR = QBrush(Qt::white); +const QBrush QmitkLesionInfoWidget::DEFAULT_BACKGROUND_COLOR = QBrush(Qt::transparent); const QBrush QmitkLesionInfoWidget::SELECTED_BACKGROUND_COLOR = QBrush(Qt::green); const QBrush QmitkLesionInfoWidget::CONNECTED_BACKGROUND_COLOR = QBrush(Qt::darkGreen); QmitkLesionInfoWidget::QmitkLesionInfoWidget(mitk::DataStorage* dataStorage, QWidget* parent /*= nullptr*/) : QWidget(parent) , m_DataStorage(dataStorage) , m_SemanticRelations(std::make_unique(dataStorage)) - , m_SimpleDatamanagerWidget(new QmitkSimpleDatamanagerWidget(dataStorage)) - , m_SimpleDatamanagerNodeDialog(new QmitkSelectNodeDialog(this)) { Init(); } QmitkLesionInfoWidget::~QmitkLesionInfoWidget() { m_SemanticRelations->RemoveObserver(this); } void QmitkLesionInfoWidget::Init() { // create GUI from the Qt Designer's .ui file m_Controls.setupUi(this); m_Controls.lesionListWidget->setContextMenuPolicy(Qt::CustomContextMenu); m_Controls.segmentationListWidget->setContextMenuPolicy(Qt::CustomContextMenu); m_Controls.imageListWidget->setContextMenuPolicy(Qt::CustomContextMenu); - m_SimpleDatamanagerNodeDialog->setWindowTitle("Select node"); - m_SimpleDatamanagerNodeDialog->SetSelectionWidget(m_SimpleDatamanagerWidget); - SetUpConnections(); m_SemanticRelations->AddObserver(this); } void QmitkLesionInfoWidget::SetUpConnections() { + // connect buttons to modify semantic relations + connect(m_Controls.addLesionPushButton, &QPushButton::clicked, this, &QmitkLesionInfoWidget::OnAddLesionButtonClicked); + connect(m_Controls.addSegmentationPushButton, &QPushButton::clicked, this, &QmitkLesionInfoWidget::OnAddSegmentationButtonClicked); + connect(m_Controls.addImagePushButton, &QPushButton::clicked, this, &QmitkLesionInfoWidget::OnAddImageButtonClicked); + // connect each list widget with a custom slots - connect(m_Controls.lesionListWidget, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), SLOT(OnCurrentLesionItemChanged(QListWidgetItem*, QListWidgetItem*))); - connect(m_Controls.lesionListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), SLOT(OnLesionItemDoubleClicked(QListWidgetItem*))); - connect(m_Controls.segmentationListWidget, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), SLOT(OnCurrentSegmentationItemChanged(QListWidgetItem*, QListWidgetItem*))); - connect(m_Controls.segmentationListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), SLOT(OnSegmentationItemDoubleClicked(QListWidgetItem*))); - connect(m_Controls.imageListWidget, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), SLOT(OnCurrentImageItemChanged(QListWidgetItem*, QListWidgetItem*))); - connect(m_Controls.imageListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), SLOT(OnImageItemDoubleClicked(QListWidgetItem*))); + connect(m_Controls.lesionListWidget, &QListWidget::currentItemChanged, this, &QmitkLesionInfoWidget::OnCurrentLesionItemChanged); + connect(m_Controls.lesionListWidget, &QListWidget::itemDoubleClicked, this, &QmitkLesionInfoWidget::OnLesionItemDoubleClicked); + connect(m_Controls.segmentationListWidget, &QListWidget::currentItemChanged, this, &QmitkLesionInfoWidget::OnCurrentSegmentationItemChanged); + connect(m_Controls.segmentationListWidget, &QListWidget::itemDoubleClicked, this, &QmitkLesionInfoWidget::OnSegmentationItemDoubleClicked); + connect(m_Controls.imageListWidget, &QListWidget::currentItemChanged, this, &QmitkLesionInfoWidget::OnCurrentImageItemChanged); + connect(m_Controls.imageListWidget, &QListWidget::itemDoubleClicked, this, &QmitkLesionInfoWidget::OnImageItemDoubleClicked); // connect context menu entries - connect(m_Controls.lesionListWidget, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(OnLesionListContextMenuRequested(const QPoint&))); - connect(m_Controls.segmentationListWidget, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(OnSegmentationListContextMenuRequested(const QPoint&))); - connect(m_Controls.imageListWidget, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(OnImageListContextMenuRequested(const QPoint&))); + connect(m_Controls.lesionListWidget, &QListWidget::customContextMenuRequested, this, &QmitkLesionInfoWidget::OnLesionListContextMenuRequested); + connect(m_Controls.segmentationListWidget, &QListWidget::customContextMenuRequested, this, &QmitkLesionInfoWidget::OnSegmentationListContextMenuRequested); + connect(m_Controls.imageListWidget, &QListWidget::customContextMenuRequested, this, &QmitkLesionInfoWidget::OnImageListContextMenuRequested); } void QmitkLesionInfoWidget::SetCurrentCaseID(const mitk::SemanticTypes::CaseID& caseID) { m_CaseID = caseID; Update(m_CaseID); } void QmitkLesionInfoWidget::Update(const mitk::SemanticTypes::CaseID& caseID) { if (nullptr == m_SemanticRelations) { return; } // if the case ID of updated instance is equal to the currently active caseID if (caseID == m_CaseID) { ResetLesionListWidget(); ResetSegmentationListWidget(); ResetImageListWidget(); } } +////////////////////////////////////////////////////////////////////////// +// Implementation of the QT_SLOTS +////////////////////////////////////////////////////////////////////////// +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); + msgBox.exec(); + return; + } + + mitk::SemanticTypes::Lesion newLesion = mitk::GenerateNewLesion(); + try + { + m_SemanticRelations->AddLesion(m_CaseID, newLesion); + } + catch (mitk::SemanticRelationException& e) + { + MITK_INFO << "Could not add a new lesion. " << e; + } +} + +void QmitkLesionInfoWidget::OnAddSegmentationButtonClicked() +{ + if (m_CaseID.empty()) + { + QMessageBox msgBox; + msgBox.setWindowTitle("No case ID set."); + msgBox.setText("In order to add a segmentation, please specify the current case / patient."); + msgBox.setIcon(QMessageBox::Warning); + msgBox.exec(); + return; + } + + QmitkSemanticRelationsNodeSelectionDialog* dialog = new QmitkSemanticRelationsNodeSelectionDialog(this, "Select segmentations to add to the semantic relations storage", ""); + dialog->SetDataStorage(m_DataStorage); + dialog->setWindowTitle("Select segmentation node"); + dialog->SetNodePredicate(mitk::NodePredicates::GetSegmentationPredicate()); + dialog->SetSelectOnlyVisibleNodes(true); + dialog->SetSelectionMode(QAbstractItemView::MultiSelection); + dialog->SetCaseID(m_CaseID); + + int dialogReturnValue = dialog->exec(); + if (QDialog::Rejected == dialogReturnValue) + { + return; + } + + auto nodes = dialog->GetSelectedNodes(); + for (mitk::DataNode* dataNode : nodes) + { + if (nullptr == dataNode) + { + continue; + } + + mitk::BaseData* baseData = dataNode->GetData(); + if (nullptr == baseData) + { + continue; + } + + // continue with valid segmentation data + // get parent node of the current segmentation node with the node predicate + mitk::DataStorage::SetOfObjects::ConstPointer parentNodes = m_DataStorage->GetSources(dataNode, 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"); + 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" + + // 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" + } + + try + { + m_SemanticRelations->AddSegmentation(dataNode, 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" + "Reason:\n" + QString::fromStdString(exceptionMessage.str())); + msgBox.setIcon(QMessageBox::Warning); + msgBox.exec(); + return; + } + } +} + +void QmitkLesionInfoWidget::OnAddImageButtonClicked() +{ + QmitkSemanticRelationsNodeSelectionDialog* dialog = new QmitkSemanticRelationsNodeSelectionDialog(this, "Select images to add to the semantic relations storage", ""); + dialog->SetDataStorage(m_DataStorage); + dialog->setWindowTitle("Select image node"); + dialog->SetNodePredicate(mitk::NodePredicates::GetImagePredicate()); + dialog->SetSelectOnlyVisibleNodes(true); + dialog->SetSelectionMode(QAbstractItemView::MultiSelection); + dialog->SetCaseID(m_CaseID); + + int dialogReturnValue = dialog->exec(); + if (QDialog::Rejected == dialogReturnValue) + { + return; + } + + auto nodes = dialog->GetSelectedNodes(); + for (mitk::DataNode* dataNode : nodes) + { + if (nullptr != dataNode) + { + try + { + // add the image to the semantic relations storage + m_SemanticRelations->AddImage(dataNode); + mitk::SemanticTypes::CaseID caseID = mitk::GetCaseIDFromDataNode(dataNode); + emit ImageAdded(caseID); + } + 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" + "Reason:\n" + QString::fromStdString(exceptionMessage.str())); + msgBox.setIcon(QMessageBox::Warning); + msgBox.exec(); + return; + } + } + } +} + void QmitkLesionInfoWidget::OnCurrentLesionItemChanged(QListWidgetItem* current, QListWidgetItem* /*previous*/) { if (nullptr == m_SemanticRelations || nullptr == current || nullptr == m_DataStorage) { return; } // only the UID is needed to identify a representing lesion - m_CurrentLesion.UID = current->text().toStdString(); + m_CurrentLesion.UID = current->data(Qt::UserRole).toString().toStdString(); if (false == m_SemanticRelations->InstanceExists(m_CaseID, m_CurrentLesion)) { // no UID found; cannot create a lesion return; } if (SELECTED_BACKGROUND_COLOR == current->background() || CONNECTED_BACKGROUND_COLOR == current->background()) { DarkenBackgroundColors(); } else { ResetBackgroundColors(); } current->setBackground(SELECTED_BACKGROUND_COLOR); try { - // set background color for all corresponding segmentations of the currently selected lesion + // get all corresponding segmentations of the currently selected lesion and set the background color mitk::SemanticRelations::DataNodeVector allSegmentationsOfLesion = m_SemanticRelations->GetAllSegmentationsOfLesion(m_CaseID, m_CurrentLesion); for (const auto& segmentation : allSegmentationsOfLesion) { QList items = m_Controls.segmentationListWidget->findItems(QString::fromStdString(segmentation->GetName()), Qt::MatchExactly); for (auto& item : items) { item->setBackground(SELECTED_BACKGROUND_COLOR); } - // set background color for all corresponding images of the current segmentation - // get parent node of the current segmentation node with the node predicate + // get parent nodes of the current segmentation node and set the background color mitk::DataStorage::SetOfObjects::ConstPointer parentNodes = m_DataStorage->GetSources(segmentation, mitk::NodePredicates::GetImagePredicate(), false); for (auto it = parentNodes->Begin(); it != parentNodes->End(); ++it) { QList items = m_Controls.imageListWidget->findItems(QString::fromStdString(it->Value()->GetName()), Qt::MatchExactly); for (auto& item : items) { item->setBackground(SELECTED_BACKGROUND_COLOR); } } } } - catch (mitk::Exception& e) + catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox; msgBox.setWindowTitle("No segmentations retrieved"); msgBox.setText("Could not automatically retrieve all segmentations of the selected lesion.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } } void QmitkLesionInfoWidget::OnLesionItemDoubleClicked(QListWidgetItem* clickedItem) { - if (nullptr == m_SemanticRelations) + if (nullptr == clickedItem + || nullptr == m_SemanticRelations) { return; } - if (nullptr == clickedItem) - { - // no item clicked; cannot retrieve the current lesion - return; - } + // only the UID is needed to identify a representing lesion + m_CurrentLesion.UID = clickedItem->data(Qt::UserRole).toString().toStdString(); + m_CurrentLesion.name = clickedItem->text().toStdString(); } void QmitkLesionInfoWidget::OnCurrentSegmentationItemChanged(QListWidgetItem* current, QListWidgetItem* /*previous*/) { if (nullptr == m_SemanticRelations || nullptr == current || nullptr == m_DataStorage) { return; } QString segmentationName = current->text(); m_CurrentSegmentation = m_DataStorage->GetNamedNode(segmentationName.toStdString()); if (nullptr == m_CurrentSegmentation) { return; } if (SELECTED_BACKGROUND_COLOR == current->background() || CONNECTED_BACKGROUND_COLOR == current->background()) { DarkenBackgroundColors(); } else { ResetBackgroundColors(); } current->setBackground(SELECTED_BACKGROUND_COLOR); - // set background color for all corresponding images of the current segmentation - // get parent node of the current segmentation node with the node predicate + + // get parent nodes of the current segmentation node and set the background color mitk::DataStorage::SetOfObjects::ConstPointer parentNodes = m_DataStorage->GetSources(m_CurrentSegmentation, mitk::NodePredicates::GetImagePredicate(), false); for (auto it = parentNodes->Begin(); it != parentNodes->End(); ++it) { QList items = m_Controls.imageListWidget->findItems(QString::fromStdString(it->Value()->GetName()), Qt::MatchExactly); for (auto& item : items) { item->setBackground(SELECTED_BACKGROUND_COLOR); } } + // get represented lesion of the current segmentation node and set the background color if (m_SemanticRelations->IsRepresentingALesion(m_CurrentSegmentation)) { try { - // set background color for the represented lesion of the currently selected segmentation mitk::SemanticTypes::Lesion representedLesion = m_SemanticRelations->GetRepresentedLesion(m_CurrentSegmentation); - QList items = m_Controls.lesionListWidget->findItems(QString::fromStdString(representedLesion.UID), Qt::MatchExactly); - for (auto& item : items) + for (int i = 0; i < m_Controls.lesionListWidget->count(); ++i) { - item->setBackground(SELECTED_BACKGROUND_COLOR); + QListWidgetItem* item = m_Controls.lesionListWidget->item(i); + std::string currentLesionUID = item->data(Qt::UserRole).toString().toStdString(); + if (currentLesionUID == representedLesion.UID) + { + item->setBackground(SELECTED_BACKGROUND_COLOR); + } } } - catch (mitk::Exception& e) + catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox; msgBox.setWindowTitle("No lesion retrieved"); msgBox.setText("Could not automatically retrieve the represented lesion of the selected segmentation.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } } } void QmitkLesionInfoWidget::OnSegmentationItemDoubleClicked(QListWidgetItem* clickedItem) { if (nullptr == clickedItem || nullptr == m_DataStorage) { - // no item clicked; cannot retrieve the current segmentation return; } QString segmentationName = clickedItem->text(); m_CurrentSegmentation = m_DataStorage->GetNamedNode(segmentationName.toStdString()); if (nullptr == m_CurrentSegmentation) { return; } mitk::LabelSetImage* labelSetImage = dynamic_cast(m_CurrentSegmentation->GetData()); if (nullptr == labelSetImage) { return; } int activeLayer = labelSetImage->GetActiveLayer(); mitk::Label* activeLabel = labelSetImage->GetActiveLabel(activeLayer); labelSetImage->UpdateCenterOfMass(activeLabel->GetValue(), activeLayer); const mitk::Point3D& centerPosition = activeLabel->GetCenterOfMassCoordinates(); if (centerPosition.GetVnlVector().max_value() > 0.0) { emit JumpToPosition(centerPosition); } } void QmitkLesionInfoWidget::OnCurrentImageItemChanged(QListWidgetItem* current, QListWidgetItem* /*previous*/) { if (nullptr == m_SemanticRelations || nullptr == current || nullptr == m_DataStorage) { return; } QString imageName = current->text(); m_CurrentImage = m_DataStorage->GetNamedNode(imageName.toStdString()); if (nullptr == m_CurrentImage) { return; } if (SELECTED_BACKGROUND_COLOR == current->background() || CONNECTED_BACKGROUND_COLOR == current->background()) { DarkenBackgroundColors(); } else { ResetBackgroundColors(); } current->setBackground(SELECTED_BACKGROUND_COLOR); - // set background color for all corresponding segmentations of the current image - // get child nodes of the current image node with the segmentation predicate + // get child nodes of the current image node and set the background color mitk::DataStorage::SetOfObjects::ConstPointer segmentationNodes = m_DataStorage->GetDerivations(m_CurrentImage, mitk::NodePredicates::GetSegmentationPredicate(), false); for (auto it = segmentationNodes->Begin(); it != segmentationNodes->End(); ++it) { QList items = m_Controls.segmentationListWidget->findItems(QString::fromStdString(it->Value()->GetName()), Qt::MatchExactly); for (auto& item : items) { item->setBackground(SELECTED_BACKGROUND_COLOR); } try { + // get represented lesion of the current segmentation node and set the background color if (m_SemanticRelations->IsRepresentingALesion(it->Value())) { - // set background color for the represented lesion of the current segmentation mitk::SemanticTypes::Lesion representedLesion = m_SemanticRelations->GetRepresentedLesion(it->Value()); - QList items = m_Controls.lesionListWidget->findItems(QString::fromStdString(representedLesion.UID), Qt::MatchExactly); - for (auto& item : items) + for (int i = 0; i < m_Controls.lesionListWidget->count(); ++i) { - item->setBackground(SELECTED_BACKGROUND_COLOR); + QListWidgetItem* item = m_Controls.lesionListWidget->item(i); + std::string currentLesionUID = item->data(Qt::UserRole).toString().toStdString(); + if (currentLesionUID == representedLesion.UID) + { + item->setBackground(SELECTED_BACKGROUND_COLOR); + } } } } - catch (mitk::Exception& e) + catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox; msgBox.setWindowTitle("No lesion retrieved"); msgBox.setText("Could not automatically retrieve the represented lesion of the selected segmentation.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } } } void QmitkLesionInfoWidget::OnImageItemDoubleClicked(QListWidgetItem* clickedItem) { - if (nullptr == m_SemanticRelations) + if (nullptr == clickedItem + || nullptr == m_SemanticRelations) { return; } - - if (nullptr == clickedItem) - { - // no item clicked; cannot retrieve the current image - return; - } } void QmitkLesionInfoWidget::OnLesionListContextMenuRequested(const QPoint& pos) { QListWidgetItem* currentItem = m_Controls.lesionListWidget->itemAt(pos); if (nullptr == currentItem) { // no item clicked; cannot retrieve the current lesion return; } - mitk::SemanticTypes::Lesion selectedLesion; - selectedLesion.UID = currentItem->text().toStdString(); - if (selectedLesion.UID.empty()) + mitk::SemanticTypes::ID selectedLesionUID; + selectedLesionUID = currentItem->data(Qt::UserRole).toString().toStdString(); + if (selectedLesionUID.empty()) { // no UID found; cannot create a lesion return; } QMenu* menu = new QMenu(m_Controls.lesionListWidget); QAction* linkToSegmentation = new QAction("Link to segmentation", this); linkToSegmentation->setEnabled(true); - connect(linkToSegmentation, &QAction::triggered, [this, selectedLesion] { OnLinkToSegmentation(selectedLesion); }); + connect(linkToSegmentation, &QAction::triggered, [this, selectedLesionUID] { OnLinkToSegmentation(selectedLesionUID); }); menu->addAction(linkToSegmentation); + QAction* setLesionName = new QAction("Set lesion name", this); + setLesionName->setEnabled(true); + connect(setLesionName, &QAction::triggered, [this, selectedLesionUID] { OnSetLesionName(selectedLesionUID); }); + menu->addAction(setLesionName); + QAction* setLesionClass = new QAction("Set lesion class", this); setLesionClass->setEnabled(true); - connect(setLesionClass, &QAction::triggered, [this, selectedLesion] { OnSetLesionClass(selectedLesion); }); + connect(setLesionClass, &QAction::triggered, [this, selectedLesionUID] { OnSetLesionClass(selectedLesionUID); }); menu->addAction(setLesionClass); QAction* removeLesion = new QAction("Remove lesion", this); removeLesion->setEnabled(true); - connect(removeLesion, &QAction::triggered, [this, selectedLesion] { OnRemoveLesion(selectedLesion); }); + connect(removeLesion, &QAction::triggered, [this, selectedLesionUID] { OnRemoveLesion(selectedLesionUID); }); menu->addAction(removeLesion); menu->popup(QCursor::pos()); } void QmitkLesionInfoWidget::OnSegmentationListContextMenuRequested(const QPoint& pos) { QListWidgetItem* currentItem = m_Controls.segmentationListWidget->itemAt(pos); if (nullptr == currentItem) { // no item clicked; cannot retrieve the current segmentation return; } const mitk::DataNode* selectedSegmentation = m_DataStorage->GetNamedNode(currentItem->text().toStdString()); if (nullptr == selectedSegmentation) { return; } if (false == mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(selectedSegmentation)) { return; } QMenu* menu = new QMenu(m_Controls.segmentationListWidget); QAction* unlinkFromLesion = new QAction("Unlink from lesion", this); unlinkFromLesion->setEnabled(true); connect(unlinkFromLesion, &QAction::triggered, [this, selectedSegmentation] { OnUnlinkFromLesion(selectedSegmentation); }); menu->addAction(unlinkFromLesion); QAction* removeSegmentation = new QAction("Remove segmentation", this); removeSegmentation->setEnabled(true); connect(removeSegmentation, &QAction::triggered, [this, selectedSegmentation] { OnRemoveSegmentation(selectedSegmentation); }); menu->addAction(removeSegmentation); menu->popup(QCursor::pos()); } void QmitkLesionInfoWidget::OnImageListContextMenuRequested(const QPoint& pos) { QListWidgetItem* currentItem = m_Controls.imageListWidget->itemAt(pos); if (nullptr == currentItem) { // no item clicked; cannot retrieve the current image return; } const mitk::DataNode* selectedImage = m_DataStorage->GetNamedNode(currentItem->text().toStdString()); if (nullptr == selectedImage) { return; } if (false == mitk::NodePredicates::GetImagePredicate()->CheckNode(selectedImage)) { return; } QMenu* menu = new QMenu(m_Controls.imageListWidget); QAction* removeImage = new QAction("Remove image", this); removeImage->setEnabled(true); connect(removeImage, &QAction::triggered, [this, selectedImage] { OnRemoveImage(selectedImage); }); menu->addAction(removeImage); menu->popup(QCursor::pos()); } -void QmitkLesionInfoWidget::OnLinkToSegmentation(const mitk::SemanticTypes::Lesion& selectedLesion) +void QmitkLesionInfoWidget::OnLinkToSegmentation(const mitk::SemanticTypes::ID& selectedLesionUID) { - int dialogReturnValue = m_SimpleDatamanagerNodeDialog->exec(); + if (m_CaseID.empty()) + { + QMessageBox msgBox; + msgBox.setWindowTitle("No case ID set."); + msgBox.setText("In order to link a lesion to a segmentation, please specify the current case / patient."); + msgBox.setIcon(QMessageBox::Warning); + msgBox.exec(); + return; + } + + // retrieve the full lesion with its lesion class using the given lesion UID + mitk::SemanticTypes::Lesion selectedLesion = mitk::GetLesionByUID(selectedLesionUID, m_SemanticRelations->GetAllLesionsOfCase(m_CaseID)); + if (selectedLesion.UID.empty()) + { + // could not find lesion information for the selected lesion + return; + } + + QmitkSemanticRelationsNodeSelectionDialog* dialog = new QmitkSemanticRelationsNodeSelectionDialog(this, "Select segmentation to link to the selected lesion.", ""); + dialog->SetDataStorage(m_DataStorage); + dialog->setWindowTitle("Select segmentation node"); + dialog->SetNodePredicate(mitk::NodePredicates::GetSegmentationPredicate()); + dialog->SetSelectOnlyVisibleNodes(true); + dialog->SetSelectionMode(QAbstractItemView::SingleSelection); + dialog->SetCaseID(m_CaseID); + + int dialogReturnValue = dialog->exec(); if (QDialog::Rejected == dialogReturnValue) { return; } - mitk::DataNode* selectedDataNode = m_SimpleDatamanagerNodeDialog->GetSelectedDataNode(); + auto nodes = dialog->GetSelectedNodes(); + mitk::DataNode::Pointer selectedDataNode = nullptr; + if (!nodes.isEmpty()) + { + // only single selection allowed + selectedDataNode = nodes.front(); + } + if (nullptr == selectedDataNode) { QMessageBox msgBox; msgBox.setWindowTitle("No valid segmentation node selected."); msgBox.setText("In order to link the selected lesion to a segmentation, please specify a valid segmentation node."); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } if (false == mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(selectedDataNode)) { QMessageBox msgBox; msgBox.setWindowTitle("No segmentation selected"); msgBox.setText("In order to link the selected lesion to a segmentation, please specify a valid segmentation node."); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } mitk::BaseData* baseData = selectedDataNode->GetData(); if (nullptr == baseData) { QMessageBox msgBox; msgBox.setWindowTitle("No valid base data."); msgBox.setText("In order to link the selected lesion to a segmentation, please specify a valid segmentation node."); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } try { m_SemanticRelations->LinkSegmentationToLesion(selectedDataNode, selectedLesion); } - catch (mitk::Exception& e) + catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox; msgBox.setWindowTitle("Could not link the selected lesion."); msgBox.setText("The program wasn't able to correctly link the selected lesion with the selected segmentation.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); } } -void QmitkLesionInfoWidget::OnRemoveLesion(const mitk::SemanticTypes::Lesion& selectedLesion) +void QmitkLesionInfoWidget::OnSetLesionName(const mitk::SemanticTypes::ID& selectedLesionUID) +{ + // retrieve the full lesion with its lesion class using the given lesion UID + mitk::SemanticTypes::Lesion selectedLesion = mitk::GetLesionByUID(selectedLesionUID, m_SemanticRelations->GetAllLesionsOfCase(m_CaseID)); + if (selectedLesion.UID.empty()) + { + // could not find lesion information for the selected lesion + return; + } + + // 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; + } + + std::string newLesionName = inputDialog->GetLineEditText().toStdString(); + + selectedLesion.name = newLesionName; + m_SemanticRelations->OverwriteLesion(m_CaseID, selectedLesion); +} + +void QmitkLesionInfoWidget::OnSetLesionClass(const mitk::SemanticTypes::ID& selectedLesionUID) +{ + // retrieve the full lesion with its lesion class using the given lesion UID + mitk::SemanticTypes::Lesion selectedLesion = mitk::GetLesionByUID(selectedLesionUID, m_SemanticRelations->GetAllLesionsOfCase(m_CaseID)); + if (selectedLesion.UID.empty()) + { + // could not find lesion information for the selected lesion + return; + } + + // 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 = m_SemanticRelations->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_SemanticRelations->OverwriteLesion(m_CaseID, selectedLesion); +} + +void QmitkLesionInfoWidget::OnRemoveLesion(const mitk::SemanticTypes::ID& selectedLesionUID) { if (m_CaseID.empty()) { QMessageBox msgBox; msgBox.setWindowTitle("No case ID set."); msgBox.setText("In order to remove a lesion, please specify the current case / patient."); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } + // retrieve the full lesion with its lesion class using the given lesion UID + mitk::SemanticTypes::Lesion selectedLesion = mitk::GetLesionByUID(selectedLesionUID, m_SemanticRelations->GetAllLesionsOfCase(m_CaseID)); + if (selectedLesion.UID.empty()) + { + // could not find lesion information for the selected lesion + return; + } + try { m_SemanticRelations->RemoveLesion(m_CaseID, selectedLesion); } - catch (mitk::Exception& e) + catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox; msgBox.setWindowTitle("Could not remove the selected lesion."); msgBox.setText("The program wasn't able to correctly remove the selected lesion from the semantic relations model.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); } } -void QmitkLesionInfoWidget::OnSetLesionClass(const mitk::SemanticTypes::Lesion& selectedLesion) -{ - // get lesion class from user input and generate UID for lesion class - //m_SemanticRelations->OverwriteLesion() -} - void QmitkLesionInfoWidget::OnUnlinkFromLesion(const mitk::DataNode* selectedSegmentation) { try { m_SemanticRelations->UnlinkSegmentationFromLesion(selectedSegmentation); } - catch (mitk::Exception& e) + catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox; msgBox.setWindowTitle("Could not unlink a lesion from the selected segmentation."); msgBox.setText("The program wasn't able to correctly unlink the lesion from the selected segmentation.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); } } void QmitkLesionInfoWidget::OnRemoveSegmentation(const mitk::DataNode* selectedSegmentation) { try { m_SemanticRelations->RemoveSegmentation(selectedSegmentation); } - catch (mitk::Exception& e) + catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox; msgBox.setWindowTitle("Could not remove the selected segmentation."); msgBox.setText("The program wasn't able to correctly remove the selected segmentation from the semantic relations model.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); } } void QmitkLesionInfoWidget::OnRemoveImage(const mitk::DataNode* selectedImage) { try { m_SemanticRelations->RemoveImage(selectedImage); emit ImageRemoved(selectedImage); } - catch (mitk::Exception& e) + catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox; msgBox.setWindowTitle("Could not remove the selected image."); msgBox.setText("The program wasn't able to correctly remove the selected image from the semantic relations model.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); } } void QmitkLesionInfoWidget::ResetLesionListWidget() { m_Controls.lesionListWidget->clear(); m_CurrentLesion.UID = ""; + m_CurrentLesion.name = ""; // create lesion list widget entries with the current lesions mitk::SemanticRelations::LesionVector allLesionsOfCase = m_SemanticRelations->GetAllLesionsOfCase(m_CaseID); if (allLesionsOfCase.empty()) { m_Controls.lesionListWidget->addItem("No lesions found"); } for (const auto& lesion : allLesionsOfCase) { - m_Controls.lesionListWidget->addItem(QString::fromStdString(lesion.UID)); + // store the UID as 'UserRole' data in the widget item + QListWidgetItem* lesionItem = new QListWidgetItem; + lesionItem->setData(Qt::UserRole, QString::fromStdString(lesion.UID)); + + // use the lesion UID for the item text, if the lesion name is non-existent + if (lesion.name.empty()) + { + lesionItem->setText(QString::fromStdString(lesion.UID)); + } + else + { + lesionItem->setText(QString::fromStdString(lesion.name)); + } + + m_Controls.lesionListWidget->addItem(lesionItem); } } void QmitkLesionInfoWidget::ResetSegmentationListWidget() { m_Controls.segmentationListWidget->clear(); m_CurrentSegmentation = nullptr; try { // create segmentation list widget entries with the current segmentations mitk::SemanticRelations::DataNodeVector allSegmentationsOfCase = m_SemanticRelations->GetAllSegmentationsOfCase(m_CaseID); if (allSegmentationsOfCase.empty()) { m_Controls.segmentationListWidget->addItem("No segmentations found"); } for (const auto& segmentation : allSegmentationsOfCase) { m_Controls.segmentationListWidget->addItem(QString::fromStdString(segmentation->GetName())); } } - catch (mitk::Exception& e) + catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox; msgBox.setWindowTitle("No segmentations retrieved"); msgBox.setText("Could not automatically retrieve all segmentations of the current case.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } } void QmitkLesionInfoWidget::ResetImageListWidget() { m_Controls.imageListWidget->clear(); m_CurrentImage = nullptr; try { // create image list widget entries with the current images mitk::SemanticRelations::DataNodeVector allImagesOfCase = m_SemanticRelations->GetAllImagesOfCase(m_CaseID); if (allImagesOfCase.empty()) { m_Controls.imageListWidget->addItem("No images found"); } for (const auto& image : allImagesOfCase) { m_Controls.imageListWidget->addItem(QString::fromStdString(image->GetName())); } } - catch (mitk::Exception& e) + catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox; msgBox.setWindowTitle("No images retrieved"); msgBox.setText("Could not automatically retrieve all images of the current case.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } } void QmitkLesionInfoWidget::ResetBackgroundColors() { // reset all lesion list widget items to original background color for (int i = 0; i < m_Controls.lesionListWidget->count(); ++i) { QListWidgetItem* item = m_Controls.lesionListWidget->item(i); item->setBackground(DEFAULT_BACKGROUND_COLOR); } // reset all segmentation list widget items to original background color for (int i = 0; i < m_Controls.segmentationListWidget->count(); ++i) { QListWidgetItem* item = m_Controls.segmentationListWidget->item(i); item->setBackground(DEFAULT_BACKGROUND_COLOR); } // reset all image list widget items to original background color for (int i = 0; i < m_Controls.imageListWidget->count(); ++i) { QListWidgetItem* item = m_Controls.imageListWidget->item(i); item->setBackground(DEFAULT_BACKGROUND_COLOR); } } void QmitkLesionInfoWidget::DarkenBackgroundColors() { // reset all lesion list widget items to original background color for (int i = 0; i < m_Controls.lesionListWidget->count(); ++i) { QListWidgetItem* item = m_Controls.lesionListWidget->item(i); if (DEFAULT_BACKGROUND_COLOR != item->background()) { item->setBackground(CONNECTED_BACKGROUND_COLOR); } } // reset all segmentation list widget items to original background color for (int i = 0; i < m_Controls.segmentationListWidget->count(); ++i) { QListWidgetItem* item = m_Controls.segmentationListWidget->item(i); if (DEFAULT_BACKGROUND_COLOR != item->background()) { item->setBackground(CONNECTED_BACKGROUND_COLOR); } } // reset all image list widget items to original background color for (int i = 0; i < m_Controls.imageListWidget->count(); ++i) { QListWidgetItem* item = m_Controls.imageListWidget->item(i); if (DEFAULT_BACKGROUND_COLOR != item->background()) { item->setBackground(CONNECTED_BACKGROUND_COLOR); } } } diff --git a/Modules/SemanticRelationsUI/include/QmitkLesionInfoWidget.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.h similarity index 75% rename from Modules/SemanticRelationsUI/include/QmitkLesionInfoWidget.h rename to Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.h index 86d17e6d85..01c80c3ba5 100644 --- a/Modules/SemanticRelationsUI/include/QmitkLesionInfoWidget.h +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.h @@ -1,131 +1,151 @@ /*=================================================================== 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 QMITKLESIONINFOWIDGET_H #define QMITKLESIONINFOWIDGET_H // semantic relations UI module -#include "MitkSemanticRelationsUIExports.h" #include -#include -#include // semantic relations module #include #include // mitk #include // qt #include /* -* @brief The QmitkLesionInfoWidget is a widget that shows the currently available lesion data of the semantic relations model +* @brief The QmitkLesionInfoWidget is a widget that shows and modifies the currently available lesion data of the semantic relations model. +* +* The widget provides a dialogs to add nodes from the data storage to the semantic relations model. +* It provides functionality to create new lesions and link them with segmentation nodes. * * The QmitkLesionInfoWidget provides three QListWidgets, that show the lesion data and the referenced segmentation data, as * well as the connected image data, depending on the selected lesion. * * The QmitkLesionInfoWidget implements the 'ISemanticRelationsObserver', so that it is automatically updated, if the * semantic relations model changes. Updating means freshly getting all lesion data and filling the lesion-ListWidget with the lesion data. */ -class MITKSEMANTICRELATIONSUI_EXPORT QmitkLesionInfoWidget : public QWidget, public mitk::ISemanticRelationsObserver +class QmitkLesionInfoWidget : public QWidget, public mitk::ISemanticRelationsObserver { Q_OBJECT public: + static const QBrush DEFAULT_BACKGROUND_COLOR; static const QBrush SELECTED_BACKGROUND_COLOR; static const QBrush CONNECTED_BACKGROUND_COLOR; QmitkLesionInfoWidget::QmitkLesionInfoWidget(mitk::DataStorage* dataStorage, QWidget* parent = nullptr); ~QmitkLesionInfoWidget(); void SetCurrentCaseID(const mitk::SemanticTypes::CaseID& caseID); /* * @brief Updates the 'lesionListWidget' of the GUI with the current lesion-data from the semantic relations model. * * Overridden from 'ISemanticRelationsObserver'. * In order for Update-function to be called, this widget has to be added as a observer of SemanticRelation * (e.g. m_SemanticRelations->AddObserver(m_LesionInfoWidget);) * * @par caseID The current case ID to identify the currently active patient / case. */ virtual void Update(const mitk::SemanticTypes::CaseID& caseID) override; const mitk::SemanticTypes::Lesion& GetSelectedLesion() const { return m_CurrentLesion; } const mitk::DataNode* GetSelectedSegmentation() const { return m_CurrentSegmentation; } const mitk::DataNode* GetSelectedImage() const { return m_CurrentImage; } /* * @brief Resets all items from the lesion list widget. */ void ResetLesionListWidget(); /* * @brief Resets all items from the the segmentation list widget. */ void ResetSegmentationListWidget(); /* * @brief Resets all items from the the segmentation list widget. */ void ResetImageListWidget(); /* * @brief Resets the background color of all items in each list widget. */ void ResetBackgroundColors(); void DarkenBackgroundColors(); Q_SIGNALS: + void JumpToPosition(const mitk::Point3D&); + void ImageAdded(const mitk::SemanticTypes::CaseID&); void ImageRemoved(const mitk::DataNode*); private Q_SLOTS: + /* + * @brief Generates a new, empty lesion to add to the semantic relations model for the current case ID. + */ + void OnAddLesionButtonClicked(); + void OnAddSegmentationButtonClicked(); + void OnAddImageButtonClicked(); + + // slots for the mouse click events of the list widgets void OnCurrentLesionItemChanged(QListWidgetItem*, QListWidgetItem*); void OnLesionItemDoubleClicked(QListWidgetItem*); void OnCurrentSegmentationItemChanged(QListWidgetItem*, QListWidgetItem*); void OnSegmentationItemDoubleClicked(QListWidgetItem*); void OnCurrentImageItemChanged(QListWidgetItem*, QListWidgetItem*); void OnImageItemDoubleClicked(QListWidgetItem*); + void OnLesionListContextMenuRequested(const QPoint&); void OnSegmentationListContextMenuRequested(const QPoint&); void OnImageListContextMenuRequested(const QPoint&); - void OnLinkToSegmentation(const mitk::SemanticTypes::Lesion&); - void OnRemoveLesion(const mitk::SemanticTypes::Lesion&); - void OnSetLesionClass(const mitk::SemanticTypes::Lesion&); + + // slots for the context menu actions of the lesion list widget + void OnLinkToSegmentation(const mitk::SemanticTypes::ID&); + void OnSetLesionName(const mitk::SemanticTypes::ID&); + void OnSetLesionClass(const mitk::SemanticTypes::ID&); + void OnRemoveLesion(const mitk::SemanticTypes::ID&); + + // slots for the context menu actions of the segmentation list widget void OnUnlinkFromLesion(const mitk::DataNode*); + + +public Q_SLOTS: + + // slots for the context menu action of the image / segmentation list widget + // the slots are also used from the semantic relations view if a node in the data storage is removed void OnRemoveSegmentation(const mitk::DataNode*); void OnRemoveImage(const mitk::DataNode*); private: void Init(); void SetUpConnections(); Ui::QmitkLesionInfoWidgetControls m_Controls; mitk::DataStorage* m_DataStorage; std::unique_ptr m_SemanticRelations; mitk::SemanticTypes::CaseID m_CaseID; - QmitkSimpleDatamanagerWidget* m_SimpleDatamanagerWidget; - QmitkSelectNodeDialog* m_SimpleDatamanagerNodeDialog; - mitk::SemanticTypes::Lesion m_CurrentLesion; mitk::DataNode::Pointer m_CurrentSegmentation; mitk::DataNode::Pointer m_CurrentImage; }; #endif // QMITKLESIONINFOWIDGET_H diff --git a/Modules/SemanticRelationsUI/src/QmitkLesionInfoWidgetControls.ui b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidgetControls.ui similarity index 68% rename from Modules/SemanticRelationsUI/src/QmitkLesionInfoWidgetControls.ui rename to Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidgetControls.ui index 4f3ed1d771..6d0d31456a 100644 --- a/Modules/SemanticRelationsUI/src/QmitkLesionInfoWidgetControls.ui +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidgetControls.ui @@ -1,51 +1,72 @@ QmitkLesionInfoWidgetControls true 0 0 350 300 - + Available lesions: Available segmentations: Available images: - + + + Add lesion + + - + + + Add segmentation + + + + + Add image + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsControls.ui b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsControls.ui index d8c77c31b9..bb178d3af5 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsControls.ui +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsControls.ui @@ -1,68 +1,37 @@ QmitkSemanticRelationsControls 0 0 350 300 Semantic relations plugin - + 0 0 Currently selected patient: - + - - - - Select patient node - - - - - - - Add segmentation - - - - - - - - - - Add image - - - - - - - Add lesion - - - diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsNodeSelectionDialog.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsNodeSelectionDialog.cpp index 61a6094772..346e265571 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsNodeSelectionDialog.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsNodeSelectionDialog.cpp @@ -1,50 +1,53 @@ /*=================================================================== 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 "QmitkSemanticRelationsNodeSelectionDialog.h" // semantic relations ui module #include +// semantic relations ui module +#include + QmitkSemanticRelationsNodeSelectionDialog::QmitkSemanticRelationsNodeSelectionDialog(QWidget* parent, QString title, QString hint) : QmitkNodeSelectionDialog(parent, title, hint) { // nothing here } void QmitkSemanticRelationsNodeSelectionDialog::SetCaseID(const mitk::SemanticTypes::CaseID& caseID) { for (auto panel : m_Panels) { QmitkAbstractSemanticRelationsStorageInspector* semanticRelationsStorageInspector = dynamic_cast(panel); if (nullptr != semanticRelationsStorageInspector) { semanticRelationsStorageInspector->SetCaseID(caseID); } } } void QmitkSemanticRelationsNodeSelectionDialog::SetLesion(const mitk::SemanticTypes::Lesion& lesion) { for (auto panel : m_Panels) { QmitkAbstractSemanticRelationsStorageInspector* semanticRelationsStorageInspector = dynamic_cast(panel); if (nullptr != semanticRelationsStorageInspector) { semanticRelationsStorageInspector->SetLesion(lesion); } } } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.cpp index 7e8cbea675..dec945ae84 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.cpp @@ -1,289 +1,112 @@ /*=================================================================== 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 "QmitkSemanticRelationsView.h" #include "QmitkSemanticRelationsNodeSelectionDialog.h" // semantic relations module #include #include #include // blueberry #include #include // qt #include const std::string QmitkSemanticRelationsView::VIEW_ID = "org.mitk.views.semanticrelations"; -QmitkSemanticRelationsView::QmitkSemanticRelationsView() - : m_SemanticRelations(nullptr) -{ - // nothing here -} - void QmitkSemanticRelationsView::SetFocus() { // nothing here } void QmitkSemanticRelationsView::CreateQtPartControl(QWidget* parent) { // create GUI widgets - m_Parent = parent; m_Controls.setupUi(parent); // initialize the semantic relations m_SemanticRelations = std::make_unique(GetDataStorage()); - connect(m_Controls.caseIDComboBox, SIGNAL(currentIndexChanged(const QString&)), SLOT(OnCaseIDSelectionChanged(const QString&))); - connect(m_Controls.selectPatientNodePushButton, SIGNAL(clicked()), SLOT(OnSelectPatientNodeButtonClicked())); + m_LesionInfoWidget = new QmitkLesionInfoWidget(GetDataStorage(), parent); + m_Controls.gridLayout->addWidget(m_LesionInfoWidget); - m_LesionInfoWidget = new QmitkLesionInfoWidget(GetDataStorage()); - m_Controls.gridLayout->addWidget(m_LesionInfoWidget, 3, 0, 1, 3); - connect(m_LesionInfoWidget, SIGNAL(JumpToPosition(const mitk::Point3D&)), SLOT(OnJumpToPosition(const mitk::Point3D&))); - connect(m_LesionInfoWidget, SIGNAL(ImageRemoved(const mitk::DataNode*)), SLOT(OnImageRemoved(const mitk::DataNode*))); + connect(m_Controls.caseIDComboBox, static_cast(&QComboBox::currentIndexChanged), this, &QmitkSemanticRelationsView::OnCaseIDSelectionChanged); - // connect buttons to modify semantic relations - connect(m_Controls.addLesionPushButton, SIGNAL(clicked()), SLOT(OnAddLesionButtonClicked())); - connect(m_Controls.addSegmentationPushButton, SIGNAL(clicked()), SLOT(OnAddSegmentationButtonClicked())); - connect(m_Controls.addImagePushButton, SIGNAL(clicked()), SLOT(OnAddImageButtonClicked())); + connect(m_LesionInfoWidget, &QmitkLesionInfoWidget::ImageAdded, this, &QmitkSemanticRelationsView::AddToComboBox); + connect(m_LesionInfoWidget, &QmitkLesionInfoWidget::JumpToPosition, this, &QmitkSemanticRelationsView::OnJumpToPosition); + connect(m_LesionInfoWidget, &QmitkLesionInfoWidget::ImageRemoved, this, &QmitkSemanticRelationsView::OnImageRemoved); } void QmitkSemanticRelationsView::NodeRemoved(const mitk::DataNode* node) { - OnImageRemoved(node); + if (mitk::NodePredicates::GetImagePredicate()->CheckNode(node)) + { + m_LesionInfoWidget->OnRemoveImage(node); + } + else if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(node)) + { + m_LesionInfoWidget->OnRemoveSegmentation(node); + } } void QmitkSemanticRelationsView::OnCaseIDSelectionChanged(const QString& caseID) { - m_CaseID = caseID.toStdString(); - m_LesionInfoWidget->SetCurrentCaseID(m_CaseID); -} - -void QmitkSemanticRelationsView::OnSelectPatientNodeButtonClicked() -{ - QmitkSemanticRelationsNodeSelectionDialog* dialog = new QmitkSemanticRelationsNodeSelectionDialog(m_Parent, "Select patient image to set a current patient", ""); - dialog->SetDataStorage(GetDataStorage()); - dialog->setWindowTitle("Select patient image node"); - dialog->SetNodePredicate(mitk::NodePredicates::GetImagePredicate()); - dialog->SetSelectOnlyVisibleNodes(true); - dialog->SetSelectionMode(QAbstractItemView::SingleSelection); - dialog->SetCaseID(m_CaseID); - dialog->SetLesion(m_LesionInfoWidget->GetSelectedLesion()); - - if (QDialog::Accepted == dialog->exec()) - { - auto nodes = dialog->GetSelectedNodes(); - for (mitk::DataNode* dataNode : nodes) - { - if (nullptr != dataNode) - { - m_Controls.selectedPatientNodeLineEdit->setText(QString::fromStdString(dataNode->GetName())); - //m_PatientInfoWidget->SetPatientInfo(node); - //m_PatientInfoWidget->show(); - } - else - { - m_Controls.selectedPatientNodeLineEdit->clear(); - } - } - } + m_LesionInfoWidget->SetCurrentCaseID(caseID.toStdString()); } void QmitkSemanticRelationsView::OnJumpToPosition(const mitk::Point3D& position) { mitk::IRenderWindowPart* renderWindowPart = GetRenderWindowPart(); if (nullptr != renderWindowPart) { renderWindowPart->SetSelectedPosition(position); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } -void QmitkSemanticRelationsView::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); - msgBox.exec(); - return; - } - - mitk::SemanticTypes::Lesion newLesion; - newLesion.UID = mitk::UIDGeneratorBoost::GenerateUID(); - newLesion.lesionClass = mitk::SemanticTypes::LesionClass(); - newLesion.lesionClass.UID = mitk::UIDGeneratorBoost::GenerateUID(); - - try - { - m_SemanticRelations->AddLesion(m_CaseID, newLesion); - } - catch (mitk::SemanticRelationException& e) - { - MITK_INFO << "Could not add a new lesion. " << e; - } -} - -void QmitkSemanticRelationsView::OnAddSegmentationButtonClicked() -{ - if (m_CaseID.empty()) - { - QMessageBox msgBox; - msgBox.setWindowTitle("No case ID set."); - msgBox.setText("In order to add a segmentation, please specify the current case / patient."); - msgBox.setIcon(QMessageBox::Warning); - msgBox.exec(); - return; - } - - QmitkSemanticRelationsNodeSelectionDialog* dialog = new QmitkSemanticRelationsNodeSelectionDialog(m_Parent, "Select segmentations to add to the semantic relations storage", ""); - dialog->SetDataStorage(GetDataStorage()); - dialog->setWindowTitle("Select segmentation node"); - dialog->SetNodePredicate(mitk::NodePredicates::GetSegmentationPredicate()); - dialog->SetSelectOnlyVisibleNodes(true); - dialog->SetSelectionMode(QAbstractItemView::MultiSelection); - dialog->SetCaseID(m_CaseID); - - if (QDialog::Accepted == dialog->exec()) - { - auto nodes = dialog->GetSelectedNodes(); - for (mitk::DataNode* dataNode : nodes) - { - if (nullptr == dataNode) - { - continue; - } - - mitk::BaseData* baseData = dataNode->GetData(); - if (nullptr == baseData) - { - continue; - } - - // continue with valid segmentation data - // get parent node of the current segmentation node with the node predicate - mitk::DataStorage::SetOfObjects::ConstPointer parentNodes = GetDataStorage()->GetSources(dataNode, 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"); - 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" - - // 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" - } - - try - { - m_SemanticRelations->AddSegmentation(dataNode, parentNodes->front()); - } - catch (mitk::Exception& 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" - "Reason:\n" + QString::fromStdString(exceptionMessage.str())); - msgBox.setIcon(QMessageBox::Warning); - msgBox.exec(); - } - } - } -} - -void QmitkSemanticRelationsView::OnAddImageButtonClicked() -{ - QmitkSemanticRelationsNodeSelectionDialog* dialog = new QmitkSemanticRelationsNodeSelectionDialog(m_Parent, "Select images to add to the semantic relations storage", ""); - dialog->SetDataStorage(GetDataStorage()); - dialog->setWindowTitle("Select image node"); - dialog->SetNodePredicate(mitk::NodePredicates::GetImagePredicate()); - dialog->SetSelectOnlyVisibleNodes(true); - dialog->SetSelectionMode(QAbstractItemView::MultiSelection); - dialog->SetCaseID(m_CaseID); - - if (QDialog::Accepted == dialog->exec()) - { - auto nodes = dialog->GetSelectedNodes(); - for (mitk::DataNode* dataNode : nodes) - { - if (nullptr != dataNode) - { - try - { - // add the image to the semantic relations storage - m_SemanticRelations->AddImage(dataNode); - mitk::SemanticTypes::CaseID caseID = mitk::GetCaseIDFromDataNode(dataNode); - AddToComboBox(caseID); - } - catch (mitk::Exception& 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" - "Reason:\n" + QString::fromStdString(exceptionMessage.str())); - msgBox.setIcon(QMessageBox::Warning); - msgBox.exec(); - } - } - } - } -} - void QmitkSemanticRelationsView::OnImageRemoved(const mitk::DataNode* imageNode) { mitk::SemanticTypes::CaseID caseID = mitk::GetCaseIDFromDataNode(imageNode); RemoveFromComboBox(caseID); } void QmitkSemanticRelationsView::AddToComboBox(const mitk::SemanticTypes::CaseID& caseID) { int foundIndex = m_Controls.caseIDComboBox->findText(QString::fromStdString(caseID)); if (-1 == foundIndex) { // add the caseID to the combo box, as it is not already contained m_Controls.caseIDComboBox->addItem(QString::fromStdString(caseID)); } } void QmitkSemanticRelationsView::RemoveFromComboBox(const mitk::SemanticTypes::CaseID& caseID) { std::vector allControlPoints = m_SemanticRelations->GetAllControlPointsOfCase(caseID); int foundIndex = m_Controls.caseIDComboBox->findText(QString::fromStdString(caseID)); if (allControlPoints.empty() && -1 != foundIndex) { // TODO: find new way to check for empty case id // caseID does not contain any control points and therefore no data // remove the caseID, if it is still contained m_Controls.caseIDComboBox->removeItem(foundIndex); } -} \ No newline at end of file +} diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.h index 9e6d3cdf8a..e5a40c7602 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.h +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.h @@ -1,91 +1,78 @@ /*=================================================================== 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 QMITKSEMANTICRELATIONSVIEW_H #define QMITKSEMANTICRELATIONSVIEW_H // semantic relations plugin #include "ui_QmitkSemanticRelationsControls.h" +#include "QmitkLesionInfoWidget.h" // semantic relations module #include #include -// semantic relations UI module -#include - // blueberry #include // mitk qt #include /* -* @brief The QmitkSemanticRelationsView is a MITK view to combine and show the widgets of the 'SemanticRelationsUI'-module. +* @brief The QmitkSemanticRelationsView is an MITK view to combine and show the widgets of the 'SemanticRelationsUI'-module and this semantic relations plugin. * * It allows the MITK user to see and modify the content of the SemanticRelations-session. -* It provides a dialog to add images from the data storage to the semantic relations model and a dialog to select -* data nodes that were added to the semantic relations model before. -* If provides functionality to create new lesions and link them with segmentation nodes. * A combo box is used to select and show the current patient. * * SIDE NOTE: Modifying the control points and information types of data in the semantic relations model is currently done * by using the right-click context-menu of the "PatientTableWidget" in the "Select Patient Node"-Dialog. * This is a leftover from when the widget was not used as a selection widget. Those functionality will be moved * to this main GUI soon. */ class QmitkSemanticRelationsView : public QmitkAbstractView { Q_OBJECT public: static const std::string VIEW_ID; - QmitkSemanticRelationsView(); protected: virtual void SetFocus() override; virtual void CreateQtPartControl(QWidget* parent) override; private Q_SLOTS: void OnCaseIDSelectionChanged(const QString&); - void OnSelectPatientNodeButtonClicked(); + + void AddToComboBox(const mitk::SemanticTypes::CaseID&); void OnJumpToPosition(const mitk::Point3D&); - /* - * @brief Generates a new, empty lesion to add to the semantic relations model for the current case ID. - */ - void OnAddLesionButtonClicked(); - void OnAddSegmentationButtonClicked(); - void OnAddImageButtonClicked(); void OnImageRemoved(const mitk::DataNode*); private: virtual void NodeRemoved(const mitk::DataNode* node) override; - void AddToComboBox(const mitk::SemanticTypes::CaseID& caseID); + void RemoveFromComboBox(const mitk::SemanticTypes::CaseID& caseID); - QWidget* m_Parent; Ui::QmitkSemanticRelationsControls m_Controls; QmitkLesionInfoWidget* m_LesionInfoWidget; - mitk::SemanticTypes::CaseID m_CaseID; std::unique_ptr m_SemanticRelations; }; #endif // QMITKSEMANTICRELATIONSVIEW_H