diff --git a/Modules/SemanticRelations/include/mitkLesionData.h b/Modules/SemanticRelations/include/mitkLesionData.h index 7f1beec103..b16f5e8c55 100644 --- a/Modules/SemanticRelations/include/mitkLesionData.h +++ b/Modules/SemanticRelations/include/mitkLesionData.h @@ -1,63 +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. ===================================================================*/ #ifndef MITKLESIONDATA_H #define MITKLESIONDATA_H #include // mitk semantic relations module #include "mitkSemanticTypes.h" // c++ #include namespace mitk { /** * @brief This class holds the data of each lesion in the lesion tree view. + * The data is the lesion itself with its UID, name and lesion class + * as well as two vectors for + * - lesion presence: bool value for each control-point + * inside the semantic relations storage + * - lesion volume: double value for each control-point - information type pair + * inside the semantic relations storage * */ class MITKSEMANTICRELATIONS_EXPORT LesionData { public: /** * @brief sets the data members to their initial values */ LesionData(const SemanticTypes::Lesion& lesion = SemanticTypes::Lesion()); ~LesionData(); SemanticTypes::Lesion GetLesion() const { return m_Lesion; }; SemanticTypes::ID GetLesionUID() const { return m_Lesion.UID; } std::string GetLesionName() const { return m_Lesion.name; } const std::vector& GetLesionPresence() const { return m_LesionPresence; }; const std::vector& GetLesionVolume() const { return m_LesionVolume; }; void SetLesion(const SemanticTypes::Lesion& lesion); void SetLesionPresence(const std::vector& lesionPresence); void SetLesionVolume(const std::vector& lesionVolume); private: SemanticTypes::Lesion m_Lesion; std::vector m_LesionPresence; std::vector m_LesionVolume; }; } // end namespace #endif // MITKLESIONDATA_H diff --git a/Modules/SemanticRelationsUI/include/QmitkLesionTreeItem.h b/Modules/SemanticRelationsUI/include/QmitkLesionTreeItem.h index 4c9c1afe8f..306454f0f7 100644 --- a/Modules/SemanticRelationsUI/include/QmitkLesionTreeItem.h +++ b/Modules/SemanticRelationsUI/include/QmitkLesionTreeItem.h @@ -1,122 +1,124 @@ /*=================================================================== 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 QMITKLESIONTREEITEM_H #define QMITKLESIONTREEITEM_H // mitk semantic relations UI #include "mitkLesionData.h" // mitk semantic relations #include // qt #include // c++ #include #include /* -* @brief +* @brief This class is used by custom tree models to create their tree items. +* It provides functions to traverse and modify the tree. +* Additionally it holds some 'LesionData' that is used to display lesion properties inside a tree view. */ class QmitkLesionTreeItem : public std::enable_shared_from_this { public: using ChildPointer = std::shared_ptr; using ChildConstPointer = std::shared_ptr; using ParentPointer = std::weak_ptr; QmitkLesionTreeItem(mitk::LesionData lesionData = mitk::LesionData()); /** * @brief Return the child of this item at a specific position. * * @param row Determines the position of a child item to return. * * @return The child of this item at a specific position. */ ChildPointer GetChildInRow(int row) const { return m_Children.at(row); }; /** * @brief Return the parent item. * * @return The parent item as std::weak_ptr. */ ParentPointer GetParent() const { return m_ParentItem; }; /** * @brief Set the parent item of this item. * * @param parent The new parent item of this item. */ void SetParent(ParentPointer parent); /** * @brief Return the item data, which contains ... * * see mitk::LesionItemData */ mitk::LesionData& GetData() { return m_ItemData; }; /** * @brief Get the row of this item relative to its parent item using 'GetRowOfChild'. * * @return The row of this item relative to its parent item. */ int GetRow() const; /** * @brief Get the row of the given child item relative to this item. * * @param child The child item whose row is to be determined. * * @return The row of the child item. */ int GetRowOfChild(ChildConstPointer child) const; /** * @brief Return the number of child items. * * @return Number of child items. */ size_t ChildCount() const { return m_Children.size(); }; /** * @brief Add a new child to the list of children of this item if it is not already a child item. * * @param child The child item to add to this item. */ void AddChild(ChildPointer child); /** * @brief Remove a child from the list of children of this item. * * @param child The child item to remove from this item. */ void RemoveChild(ChildPointer child); /** * @brief Set the item data of this item. * * @param value LesionData that provides information about this item. */ void SetData(const mitk::LesionData& lesionData); private: ParentPointer m_ParentItem; std::vector m_Children; mitk::LesionData m_ItemData; }; Q_DECLARE_METATYPE(QmitkLesionTreeItem) Q_DECLARE_METATYPE(QmitkLesionTreeItem*) #endif // QMITKLESIONTREEITEM_H diff --git a/Modules/SemanticRelationsUI/include/QmitkLesionTreeModel.h b/Modules/SemanticRelationsUI/include/QmitkLesionTreeModel.h index d159ec0405..f7653ec75d 100644 --- a/Modules/SemanticRelationsUI/include/QmitkLesionTreeModel.h +++ b/Modules/SemanticRelationsUI/include/QmitkLesionTreeModel.h @@ -1,100 +1,110 @@ /*=================================================================== 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 QMITKLESIONTREEMODEL_H #define QMITKLESIONTREEMODEL_H // mitk semantic relations UI #include "MitkSemanticRelationsUIExports.h" #include "QmitkAbstractSemanticRelationsStorageModel.h" #include "QmitkLesionTreeItem.h" -// c++ -#include - /* -* @brief +* @brief The 'QmitkLesionTreeModel' is a subclass of 'QmitkAbstractSemanticRelationsStorageModel' and provides +* functionality to serve as a tree model. +* The tree model creates a new top-level tree item for each lesion that is stored inside the semantic relations storage. +* Each lesion tree item contains lesion data that can be display inside a tree view. The lesion data +* consists of a lesion with with its UID, name and lesion class. The name or UID is used for the top-level tree items. +* Additionally the lesion data contains two vectors which define the lesion presence (bool) and the lesion volume (double) +* for each control-point - information type pair. The lesion presence will be used inside this model for the tree items. +* The volume is used inside another tree model. +* +* The model holds the last segmentation that is added to the data storage to support the process of defining a new lesion +* (and linking it with the latest segmentation) (see 'NodeAdded'). +* Furthermore the model is able to accept a 'QList' of currently selected data nodes and to use it to change the background +* color of each lesion tree item that is connected to this data node(s). This helps to see which lesion is already found and +* defined for a given (set of) data node(s). */ class MITKSEMANTICRELATIONSUI_EXPORT QmitkLesionTreeModel : public QmitkAbstractSemanticRelationsStorageModel { Q_OBJECT public: /** * @brief Initialize the root item of the model. The root item does not have a parent item. */ QmitkLesionTreeModel(QObject* parent = nullptr); ////////////////////////////////////////////////////////////////////////// // overridden virtual functions from QAbstractItemModel ////////////////////////////////////////////////////////////////////////// virtual QModelIndex index(int row, int column, const QModelIndex& itemIndex = QModelIndex()) const override; virtual QModelIndex parent(const QModelIndex& itemIndex) const override; virtual int rowCount(const QModelIndex& itemIndex = QModelIndex()) const override; virtual int columnCount(const QModelIndex& itemIndex = QModelIndex()) const override; virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; ////////////////////////////////////////////////////////////////////////// // end override ////////////////////////////////////////////////////////////////////////// const mitk::DataNode* GetLastSegmentation() const; protected: // the following functions have to be overridden but are not implemented in this model virtual void NodePredicateChanged() override { } virtual void NodeAdded(const mitk::DataNode*) override; virtual void NodeChanged(const mitk::DataNode*) override { } virtual void NodeRemoved(const mitk::DataNode*) override { } /** * @brief Overridden from 'QmitkAbstractSemanticRelationsStorageModel': This function retrieves all control points * of the current case and stores them to define the header of the tree. * Furthermore all lesions are retrieved and the lesion data is stored and show in the tree view. */ virtual void SetData() override; private: void SetLesionData(); void AddLesion(const mitk::SemanticTypes::Lesion& lesion); void SetSelectedDataNodesPresence(); /** * @brief The function uses the ID of the lesion to see if a data node presence was already set. * If not, the given bool value is used and stored inside a member variable. If the lesion presence * was already set, it will be overwritten. * The function is used by the 'SetSelectedDataNodesPresence' function. * * @param lesion The lesion whose data node presence should be set * @param dataNodePresence The bool value that defines the data node presence of the given lesion */ void SetDataNodePresenceOfLesion(const mitk::SemanticTypes::Lesion* lesion, bool dataNodePresence); QmitkLesionTreeItem* GetItemByIndex(const QModelIndex& index) const; std::map m_DataNodePresence; const mitk::DataNode* m_LastSegmentation; std::shared_ptr m_RootItem; mitk::SemanticTypes::ControlPointVector m_ControlPoints; mitk::SemanticTypes::LesionVector m_CurrentLesions; }; #endif // QMITKLESIONTREEMODEL_H diff --git a/Modules/SemanticRelationsUI/include/QmitkStatisticsCalculator.h b/Modules/SemanticRelationsUI/include/QmitkStatisticsCalculator.h index 47e08b453d..9861660b0a 100644 --- a/Modules/SemanticRelationsUI/include/QmitkStatisticsCalculator.h +++ b/Modules/SemanticRelationsUI/include/QmitkStatisticsCalculator.h @@ -1,78 +1,80 @@ /*=================================================================== 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 QMITKSTATISTICSCALCULATOR_H #define QMITKSTATISTICSCALCULATOR_H // mitk semantic relations UI #include "MitkSemanticRelationsUIExports.h" // mitk semantic relations module #include #include // mitk core #include -#include #include // mitk image statistics ui module #include -// itk -#include - /* -* @brief +* @brief This class provides functions to compute the lesion volume of a given lesion. +* A lesion can be defined by a specific segmentation at each control-point - information type pair. +* This segmentation and its parent image will be used inside the private 'GetSegmentationMaskVolume' function. +* This function in turn uses the image statistics module (the 'ImageStatisticsContainerManager') to retrieve +* the specific statistics values from a statistics node. However, if the statistics are not found, +* a new 'QmitkImageStatisticsCalculationJob' is started. If the job is finished and the statistics are calculated, +* a new statistics node is added to the data storage (or an existing statistics data node is updated). */ class MITKSEMANTICRELATIONSUI_EXPORT QmitkStatisticsCalculator : public QObject { Q_OBJECT public: QmitkStatisticsCalculator(); ~QmitkStatisticsCalculator(); void SetDataStorage(mitk::DataStorage* dataStorage) { m_DataStorage = dataStorage; } /** * @brief Compute and store lesion volume for all available control points and information types. * * @param lesionData The lesion data that holds the lesion and will hold the additional lesion data. * @param caseID The current case ID. */ void ComputeLesionVolume(mitk::LesionData& lesionData, const mitk::SemanticTypes::CaseID& caseID); private: /** * @brief * * */ double GetSegmentationMaskVolume(mitk::DataNode::Pointer imageNode, mitk::DataNode::Pointer segmentationNode); void OnStatisticsCalculationEnds(); - QmitkImageStatisticsCalculationJob * m_CalculationJob; + QmitkImageStatisticsCalculationJob* m_CalculationJob; mitk::WeakPointer m_DataStorage; mitk::DataNode::Pointer m_ImageNode; mitk::DataNode::Pointer m_SegmentationNode; double m_MaskVolume; }; #endif // QMITKSTATISTICSCALCULATOR_H diff --git a/Modules/SemanticRelationsUI/include/QmitkStatisticsTreeModel.h b/Modules/SemanticRelationsUI/include/QmitkStatisticsTreeModel.h index c1cc85c3e0..1c54bde693 100644 --- a/Modules/SemanticRelationsUI/include/QmitkStatisticsTreeModel.h +++ b/Modules/SemanticRelationsUI/include/QmitkStatisticsTreeModel.h @@ -1,84 +1,98 @@ /*=================================================================== 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 QMITKSTATISTICSTREEMODEL_H #define QMITKSTATISTICSTREEMODEL_H // mitk semantic relations UI #include "MitkSemanticRelationsUIExports.h" #include "QmitkAbstractSemanticRelationsStorageModel.h" #include "QmitkLesionTreeItem.h" -#include +#include "QmitkStatisticsCalculator.h" /* -* @brief +* @brief The 'QmitkStatisticsTreeModel' is a subclass of 'QmitkAbstractSemanticRelationsStorageModel' and provides +* functionality to serve as a tree model. +* The tree model creates a new top-level tree item for each lesion that is stored inside the semantic relations storage. +* The top-level lesion tree items hold child items for each information type. +* Each lesion tree item contains lesion data that can be display inside a tree view. The lesion data +* consists of a lesion with with its UID, name and lesion class. The name or UID is used for the top-level tree items. +* Additionally the lesion data contains two vectors which define the lesion presence (bool) and the lesion volume (double) +* for each control-point - information type pair. The lesion volume will be used inside this model for the child items. +* The presence is used inside another tree model. +* +* The model uses the 'QmitkStatisticsCalculator' to start the lesion volume calculation for each lesion. +* This calculator is able to find an existing lesion volume or to trigger the computation of the required statistics. +* If the required statistics are newly computed and added as a statistics container to the data storage, +* this model will be notified about this event (see 'NodeAdded', 'NodeChanged' and 'NodeRemoved') and will update +* its lesion tree items. */ class MITKSEMANTICRELATIONSUI_EXPORT QmitkStatisticsTreeModel : public QmitkAbstractSemanticRelationsStorageModel { Q_OBJECT public: /** * @brief Initialize the root item of the model. The root item does not have a parent item. */ QmitkStatisticsTreeModel(QObject* parent = nullptr); ////////////////////////////////////////////////////////////////////////// // overridden virtual functions from QAbstractItemModel ////////////////////////////////////////////////////////////////////////// virtual QModelIndex index(int row, int column, const QModelIndex& itemIndex = QModelIndex()) const override; virtual QModelIndex parent(const QModelIndex& itemIndex) const override; virtual int rowCount(const QModelIndex& itemIndex = QModelIndex()) const override; virtual int columnCount(const QModelIndex& itemIndex = QModelIndex()) const override; virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; ////////////////////////////////////////////////////////////////////////// // end override ////////////////////////////////////////////////////////////////////////// protected: virtual void DataStorageChanged() override; virtual void NodePredicateChanged() override { } virtual void NodeAdded(const mitk::DataNode*) override; virtual void NodeChanged(const mitk::DataNode*) override; virtual void NodeRemoved(const mitk::DataNode*) override; /** * @brief Overridden from 'QmitkAbstractSemanticRelationsStorageModel': This function retrieves all control points * of the current case and stores them to define the header of the tree. * Furthermore all lesions are retrieved and the lesion data is stored and show in the tree view. */ virtual void SetData() override; private: void SetLesionData(); void AddLesion(const mitk::SemanticTypes::Lesion& lesion); QmitkLesionTreeItem* GetItemByIndex(const QModelIndex& index) const; std::unique_ptr m_StatisticsCalculator; std::shared_ptr m_RootItem; mitk::SemanticTypes::ControlPointVector m_ControlPoints; mitk::SemanticTypes::InformationTypeVector m_InformationTypes; mitk::SemanticTypes::LesionVector m_CurrentLesions; }; #endif // QMITKSTATISTICSTREEMODEL_H diff --git a/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp b/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp index 25a0f51395..be4259127b 100644 --- a/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp @@ -1,296 +1,296 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // semantic relations UI module #include "QmitkLesionTreeModel.h" // semantic relations module #include #include #include #include #include #include // qt #include QmitkLesionTreeModel::QmitkLesionTreeModel(QObject* parent/* = nullptr*/) : QmitkAbstractSemanticRelationsStorageModel(parent) , m_RootItem(std::make_shared(mitk::LesionData())) { // nothing here } ////////////////////////////////////////////////////////////////////////// // overridden virtual functions from QAbstractItemModel ////////////////////////////////////////////////////////////////////////// QModelIndex QmitkLesionTreeModel::index(int row, int column, const QModelIndex& itemIndex) const { if (!hasIndex(row, column, itemIndex)) { return QModelIndex(); } auto childItem = GetItemByIndex(itemIndex)->GetChildInRow(row); if (nullptr == childItem) { return QModelIndex(); } return createIndex(row, column, childItem.get()); } QModelIndex QmitkLesionTreeModel::parent(const QModelIndex& itemIndex) const { if (!itemIndex.isValid()) { return QModelIndex(); } auto parentItem = GetItemByIndex(itemIndex)->GetParent(); if (parentItem.expired()) { return QModelIndex(); } auto sharedParent = parentItem.lock(); if (sharedParent == m_RootItem) { return QModelIndex(); } return createIndex(sharedParent->GetRow(), 0, sharedParent.get()); } int QmitkLesionTreeModel::rowCount(const QModelIndex& itemIndex/* = QModelIndex()*/) const { return GetItemByIndex(itemIndex)->ChildCount(); } int QmitkLesionTreeModel::columnCount(const QModelIndex&/* itemIndex = QModelIndex() */) const { if (0 == m_RootItem->ChildCount()) { // no lesion items stored, no need to display columns return 0; } return m_ControlPoints.size() + 1; } QVariant QmitkLesionTreeModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return QVariant(); } if (index.column() < 0 || index.column() > static_cast(m_ControlPoints.size())) { return QVariant(); } QmitkLesionTreeItem* currentItem = GetItemByIndex(index); if (Qt::DisplayRole == role) { if (currentItem->GetParent().expired()) { return QVariant(); } auto parentItem = currentItem->GetParent().lock(); // parent exists and is the root item -> 1. item of a lesion entry if (m_RootItem == parentItem) { // display role fills the first columns with the lesion UID / name if (0 == index.column()) { std::string itemString = currentItem->GetData().GetLesionName(); if (itemString.empty()) { itemString = currentItem->GetData().GetLesionUID(); } return QString::fromStdString(itemString); } else { // display role fills other columns with the lesion presence info const auto lesionPresence = currentItem->GetData().GetLesionPresence(); if (index.column() - 1 > static_cast(lesionPresence.size())) { - return ""; + return "N/A"; } return QVariant(lesionPresence.at(index.column() - 1)); } } } if (Qt::BackgroundColorRole == role) { auto it = m_DataNodePresence.find(currentItem->GetData().GetLesion().UID); if (it != m_DataNodePresence.end()) { return it->second ? QVariant(QColor(Qt::darkGreen)) : QVariant(QColor(Qt::transparent)); } return QVariant(QColor(Qt::transparent)); } if (Qt::UserRole == role) { return QVariant::fromValue(currentItem); } return QVariant(); } QVariant QmitkLesionTreeModel::headerData(int section, Qt::Orientation orientation, int role) const { if (0 == m_RootItem->ChildCount()) { // no lesion items stored, no need to display the header return QVariant(); } if (Qt::Horizontal == orientation && Qt::DisplayRole == role) { if (0 == section) { return QVariant("Lesion"); } if (static_cast(m_ControlPoints.size()) >= section) { mitk::SemanticTypes::ControlPoint currentControlPoint = m_ControlPoints.at(section-1); return QVariant(QString::fromStdString(currentControlPoint.ToString())); } } return QVariant(); } const mitk::DataNode* QmitkLesionTreeModel::GetLastSegmentation() const { return m_LastSegmentation; } void QmitkLesionTreeModel::NodeAdded(const mitk::DataNode* dataNode) { if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { m_LastSegmentation = dataNode; } } void QmitkLesionTreeModel::SetData() { m_RootItem = std::make_shared(mitk::LesionData()); // get all control points of current case m_ControlPoints = mitk::RelationStorage::GetAllControlPointsOfCase(m_CaseID); // sort the vector of control points for the timeline std::sort(m_ControlPoints.begin(), m_ControlPoints.end()); SetLesionData(); SetSelectedDataNodesPresence(); } void QmitkLesionTreeModel::SetLesionData() { m_CurrentLesions = mitk::RelationStorage::GetAllLesionsOfCase(m_CaseID); for (auto& lesion : m_CurrentLesions) { AddLesion(lesion); } } void QmitkLesionTreeModel::AddLesion(const mitk::SemanticTypes::Lesion& lesion) { if (m_DataStorage.IsExpired()) { return; } auto dataStorage = m_DataStorage.Lock(); // create new lesion tree item data and modify it according to the control point data mitk::LesionData lesionData(lesion); mitk::ComputeLesionPresence(lesionData, m_CaseID); - // add the 1. level lesion item to the root item + // add the top-level lesion item to the root item std::shared_ptr newLesionTreeItem = std::make_shared(lesionData); m_RootItem->AddChild(newLesionTreeItem); } void QmitkLesionTreeModel::SetSelectedDataNodesPresence() { m_DataNodePresence.clear(); for (const auto& dataNode : m_SelectedDataNodes) { if (!mitk::SemanticRelationsInference::InstanceExists(dataNode)) { continue; } for (const auto& lesion : m_CurrentLesions) { if (!mitk::SemanticRelationsInference::InstanceExists(m_CaseID, lesion)) { continue; } try { // set the lesion presence for the current node bool dataNodePresence = mitk::SemanticRelationsInference::IsLesionPresent(lesion, dataNode); SetDataNodePresenceOfLesion(&lesion, dataNodePresence); } catch (const mitk::SemanticRelationException&) { continue; } } } } void QmitkLesionTreeModel::SetDataNodePresenceOfLesion(const mitk::SemanticTypes::Lesion* lesion, bool dataNodePresence) { std::map::iterator iter = m_DataNodePresence.find(lesion->UID); if (iter != m_DataNodePresence.end()) { // key already existing, overwrite already stored bool value iter->second = dataNodePresence; } else { m_DataNodePresence.insert(std::make_pair(lesion->UID, dataNodePresence)); } } QmitkLesionTreeItem* QmitkLesionTreeModel::GetItemByIndex(const QModelIndex& index) const { if (index.isValid()) { auto item = static_cast(index.internalPointer()); if (nullptr != item) { return item; } } return m_RootItem.get(); } diff --git a/Modules/SemanticRelationsUI/src/QmitkStatisticsCalculator.cpp b/Modules/SemanticRelationsUI/src/QmitkStatisticsCalculator.cpp index f646341ec6..ea1892430e 100644 --- a/Modules/SemanticRelationsUI/src/QmitkStatisticsCalculator.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkStatisticsCalculator.cpp @@ -1,240 +1,237 @@ /*=================================================================== 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 "QmitkStatisticsCalculator.h" // semantic relations module #include #include #include #include #include -// mitk core module -#include - // mitk image statistics module #include #include #include #include QmitkStatisticsCalculator::QmitkStatisticsCalculator() : m_CalculationJob(nullptr) , m_DataStorage(nullptr) , m_MaskVolume(0.0) { m_CalculationJob = new QmitkImageStatisticsCalculationJob(); connect(m_CalculationJob, &QmitkImageStatisticsCalculationJob::finished, this, &QmitkStatisticsCalculator::OnStatisticsCalculationEnds, Qt::QueuedConnection); } QmitkStatisticsCalculator::~QmitkStatisticsCalculator() { if (!m_CalculationJob->isFinished()) { m_CalculationJob->terminate(); m_CalculationJob->wait(); } m_CalculationJob->deleteLater(); } void QmitkStatisticsCalculator::ComputeLesionVolume(mitk::LesionData& lesionData, const mitk::SemanticTypes::CaseID& caseID) { if (m_DataStorage.IsExpired()) { return; } auto dataStorage = m_DataStorage.Lock(); std::vector lesionVolume; mitk::SemanticTypes::Lesion lesion = lesionData.GetLesion(); double volume = 0.0; mitk::SemanticTypes::ControlPointVector controlPoints = mitk::RelationStorage::GetAllControlPointsOfCase(caseID); // sort the vector of control points for the timeline std::sort(controlPoints.begin(), controlPoints.end()); mitk::SemanticTypes::InformationTypeVector informationTypes = mitk::RelationStorage::GetAllInformationTypesOfCase(caseID); for (const auto& informationType : informationTypes) { for (const auto& controlPoint : controlPoints) { mitk::SemanticRelationsDataStorageAccess semanticRelationsDataStorageAccess(dataStorage); mitk::DataNode::Pointer specificImage; mitk::DataNode::Pointer specificSegmentation; try { specificSegmentation = semanticRelationsDataStorageAccess.GetSpecificSegmentation(caseID, controlPoint, informationType, lesion); if (nullptr == specificSegmentation) { volume = 0.0; } else { // get parent node of the specific segmentation node with the node predicate auto parentNodes = dataStorage->GetSources(specificSegmentation, mitk::NodePredicates::GetImagePredicate(), false); for (auto it = parentNodes->Begin(); it != parentNodes->End(); ++it) { specificImage = it->Value(); } volume = GetSegmentationMaskVolume(specificImage, specificSegmentation); } } catch (mitk::SemanticRelationException&) { volume = 0.0; } lesionVolume.push_back(volume); } } lesionData.SetLesionVolume(lesionVolume); } double QmitkStatisticsCalculator::GetSegmentationMaskVolume(mitk::DataNode::Pointer imageNode, mitk::DataNode::Pointer segmentationNode) { m_MaskVolume = 0.0; if (m_DataStorage.IsExpired()) { return m_MaskVolume; } auto dataStorage = m_DataStorage.Lock(); if (imageNode.IsNull() || segmentationNode.IsNull()) { return m_MaskVolume; } m_ImageNode = imageNode; m_SegmentationNode = segmentationNode; auto image = dynamic_cast(m_ImageNode->GetData()); auto segmentation = dynamic_cast(m_SegmentationNode->GetData()); if (nullptr == image || nullptr == segmentation) { return m_MaskVolume; } // all nodes and images are valid, retrieve statistics mitk::ImageStatisticsContainer::ConstPointer imageStatistics = mitk::ImageStatisticsContainerManager::GetImageStatistics(dataStorage, image, segmentation); bool imageStatisticsOlderThanInputs = false; if (imageStatistics && (imageStatistics->GetMTime() < image->GetMTime() || (imageStatistics->GetMTime() < segmentation->GetMTime()))) { imageStatisticsOlderThanInputs = true; } // statistics need to be (re)computed if (!imageStatistics || imageStatisticsOlderThanInputs) { m_CalculationJob->Initialize(image, segmentation, nullptr); try { m_CalculationJob->start(); return m_MaskVolume; } catch (const std::exception&) { return m_MaskVolume; } } // use a valid statistics object to get the volume of the image-segmentation pair mitk::ImageStatisticsContainer::ImageStatisticsObject statisticsObject; try { statisticsObject = imageStatistics->GetStatisticsForTimeStep(0); } catch (mitk::Exception&) { return m_MaskVolume; } try { if (statisticsObject.HasStatistic(mitk::ImageStatisticsConstants::VOLUME())) { auto valueVariant = statisticsObject.GetValueNonConverted(mitk::ImageStatisticsConstants::VOLUME()); m_MaskVolume = boost::get(valueVariant); } } catch (mitk::Exception&) { return m_MaskVolume; } return m_MaskVolume; } void QmitkStatisticsCalculator::OnStatisticsCalculationEnds() { // taken from 'QmitkImageStatisticsView' (see measurementtoolbox plugin) if (m_DataStorage.IsExpired()) { return; } auto dataStorage = m_DataStorage.Lock(); if (m_CalculationJob->GetStatisticsUpdateSuccessFlag()) { auto statistic = m_CalculationJob->GetStatisticsData(); auto image = m_CalculationJob->GetStatisticsImage(); mitk::BaseData::ConstPointer mask = nullptr; auto imageRule = mitk::StatisticsToImageRelationRule::New(); imageRule->Connect(statistic, image); if (m_CalculationJob->GetMaskImage()) { auto maskRule = mitk::StatisticsToMaskRelationRule::New(); mask = m_CalculationJob->GetMaskImage(); maskRule->Connect(statistic, mask); } auto imageStatistics = mitk::ImageStatisticsContainerManager::GetImageStatistics(dataStorage, image, mask); // if statistics base data already exist: add to existing node if (nullptr != imageStatistics) { auto allDataNodes = dataStorage->GetAll()->CastToSTLConstContainer(); for (auto node : allDataNodes) { auto nodeData = node->GetData(); if (nullptr != nodeData && nodeData->GetUID() == imageStatistics->GetUID()) { node->SetData(statistic); } } } // statistics base data does not exist: add new node else { auto statisticsNodeName = m_ImageNode->GetName(); if (m_SegmentationNode) { statisticsNodeName += "_" + m_SegmentationNode->GetName(); } statisticsNodeName += "_statistics"; auto statisticsNode = mitk::CreateImageStatisticsNode(statistic, statisticsNodeName); dataStorage->Add(statisticsNode); } } } diff --git a/Modules/SemanticRelationsUI/src/QmitkStatisticsTreeModel.cpp b/Modules/SemanticRelationsUI/src/QmitkStatisticsTreeModel.cpp index 7157668873..af1f4bf2b5 100644 --- a/Modules/SemanticRelationsUI/src/QmitkStatisticsTreeModel.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkStatisticsTreeModel.cpp @@ -1,272 +1,272 @@ /*=================================================================== 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 "QmitkStatisticsTreeModel.h" // semantic relations module #include #include #include #include #include QmitkStatisticsTreeModel::QmitkStatisticsTreeModel(QObject* parent/* = nullptr*/) : QmitkAbstractSemanticRelationsStorageModel(parent) , m_RootItem(std::make_shared(mitk::LesionData())) { m_StatisticsCalculator = std::make_unique(); } ////////////////////////////////////////////////////////////////////////// // overridden virtual functions from QAbstractItemModel ////////////////////////////////////////////////////////////////////////// QModelIndex QmitkStatisticsTreeModel::index(int row, int column, const QModelIndex& itemIndex) const { if (!hasIndex(row, column, itemIndex)) { return QModelIndex(); } auto childItem = GetItemByIndex(itemIndex)->GetChildInRow(row); if (nullptr == childItem) { return QModelIndex(); } return createIndex(row, column, childItem.get()); } QModelIndex QmitkStatisticsTreeModel::parent(const QModelIndex& itemIndex) const { if (!itemIndex.isValid()) { return QModelIndex(); } auto parentItemWeakPtr = GetItemByIndex(itemIndex)->GetParent(); if (parentItemWeakPtr.expired()) { return QModelIndex(); } auto parentItem = parentItemWeakPtr.lock(); if (parentItem == m_RootItem) { return QModelIndex(); } return createIndex(parentItem->GetRow(), 0, parentItem.get()); } int QmitkStatisticsTreeModel::rowCount(const QModelIndex& itemIndex/* = QModelIndex()*/) const { return GetItemByIndex(itemIndex)->ChildCount(); } int QmitkStatisticsTreeModel::columnCount(const QModelIndex&/* itemIndex = QModelIndex() */) const { if (0 == m_RootItem->ChildCount()) { // no lesion items stored, no need to display columns return 0; } return m_ControlPoints.size() + 1; } QVariant QmitkStatisticsTreeModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return QVariant(); } if (index.column() < 0 || index.column() > static_cast(m_ControlPoints.size())) { return QVariant(); } QmitkLesionTreeItem* currentItem = GetItemByIndex(index); if (Qt::DisplayRole == role) { if (currentItem->GetParent().expired()) { return QVariant(); } auto parentItem = currentItem->GetParent().lock(); // parent exists and is the root item -> top level item if (m_RootItem == parentItem) { // display role fills the first columns with the lesion UID / name if (0 == index.column()) { std::string itemString = currentItem->GetData().GetLesionName(); if (itemString.empty()) { itemString = currentItem->GetData().GetLesionUID(); } return QString::fromStdString(itemString); } } // parent is not the root item -> volume item else { // display role fills the first columns with the information type if (0 == index.column()) { if (index.row() < static_cast(m_InformationTypes.size())) { return QString::fromStdString(m_InformationTypes.at(index.row())); } - return "No information type"; + return "N/A"; } else { // display role fills other columns with the lesion volume info const auto lesionVolume = currentItem->GetData().GetLesionVolume(); if ((index.column() - 1) * index.row() < static_cast(lesionVolume.size())) { return QVariant(lesionVolume.at(index.row()*m_ControlPoints.size() + (index.column() - 1))); } - return "No lesion volume"; + return "N/A"; } } } return QVariant(); } QVariant QmitkStatisticsTreeModel::headerData(int section, Qt::Orientation orientation, int role) const { if (0 == m_RootItem->ChildCount()) { // no lesion items stored, no need to display the header return QVariant(); } if (Qt::Horizontal == orientation && Qt::DisplayRole == role) { if (0 == section) { return QVariant("Lesion"); } if (static_cast(m_ControlPoints.size()) >= section) { mitk::SemanticTypes::ControlPoint currentControlPoint = m_ControlPoints.at(section-1); return QVariant(QString::fromStdString(currentControlPoint.ToString())); } } return QVariant(); } void QmitkStatisticsTreeModel::DataStorageChanged() { if (!m_DataStorage.IsExpired()) { auto dataStorage = m_DataStorage.Lock(); m_SemanticRelationsDataStorageAccess = std::make_unique(dataStorage); m_StatisticsCalculator->SetDataStorage(dataStorage); UpdateModelData(); } } -void QmitkStatisticsTreeModel::NodeRemoved(const mitk::DataNode*) +void QmitkStatisticsTreeModel::NodeAdded(const mitk::DataNode*) { emit beginResetModel(); UpdateModelData(); emit endResetModel(); } -void QmitkStatisticsTreeModel::NodeAdded(const mitk::DataNode*) +void QmitkStatisticsTreeModel::NodeChanged(const mitk::DataNode*) { emit beginResetModel(); UpdateModelData(); emit endResetModel(); } -void QmitkStatisticsTreeModel::NodeChanged(const mitk::DataNode*) +void QmitkStatisticsTreeModel::NodeRemoved(const mitk::DataNode*) { emit beginResetModel(); UpdateModelData(); emit endResetModel(); } void QmitkStatisticsTreeModel::SetData() { m_RootItem = std::make_shared(mitk::LesionData()); // get all control points of current case m_ControlPoints = mitk::RelationStorage::GetAllControlPointsOfCase(m_CaseID); // sort the vector of control points for the timeline std::sort(m_ControlPoints.begin(), m_ControlPoints.end()); // get all information types points of current case m_InformationTypes = mitk::RelationStorage::GetAllInformationTypesOfCase(m_CaseID); SetLesionData(); } void QmitkStatisticsTreeModel::SetLesionData() { m_CurrentLesions = mitk::RelationStorage::GetAllLesionsOfCase(m_CaseID); for (auto& lesion : m_CurrentLesions) { AddLesion(lesion); } } void QmitkStatisticsTreeModel::AddLesion(const mitk::SemanticTypes::Lesion& lesion) { if (m_DataStorage.IsExpired()) { return; } auto dataStorage = m_DataStorage.Lock(); // create new lesion tree item data and modify it according to the control point data mitk::LesionData lesionData(lesion); m_StatisticsCalculator->ComputeLesionVolume(lesionData, m_CaseID); // add the 1. level lesion item to the root item std::shared_ptr newLesionTreeItem = std::make_shared(lesionData); m_RootItem->AddChild(newLesionTreeItem); auto informationTypeSize = m_InformationTypes.size(); for (int i = 0; i < informationTypeSize; ++i) { std::shared_ptr volumeItem = std::make_shared(lesionData); newLesionTreeItem->AddChild(volumeItem); } } QmitkLesionTreeItem* QmitkStatisticsTreeModel::GetItemByIndex(const QModelIndex& index) const { if (index.isValid()) { auto item = static_cast(index.internalPointer()); if (nullptr != item) { return item; } } return m_RootItem.get(); } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsStatisticsView.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsStatisticsView.cpp index e233be83e0..cf5a0f862e 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsStatisticsView.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsStatisticsView.cpp @@ -1,100 +1,91 @@ /*=================================================================== 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 "QmitkSemanticRelationsStatisticsView.h" // semantic relations module -#include -#include -#include #include #include -// qt -#include -#include -#include - const std::string QmitkSemanticRelationsStatisticsView::VIEW_ID = "org.mitk.views.semanticrelationsstatistics"; QmitkSemanticRelationsStatisticsView::~QmitkSemanticRelationsStatisticsView() { auto semanticRelationsIntegration = std::make_unique(); semanticRelationsIntegration->RemoveObserver(this); } void QmitkSemanticRelationsStatisticsView::Update(const mitk::SemanticTypes::CaseID& caseID) { AddToComboBox(caseID); } void QmitkSemanticRelationsStatisticsView::SetFocus() { // nothing here } void QmitkSemanticRelationsStatisticsView::CreateQtPartControl(QWidget* parent) { // create GUI elements m_Controls.setupUi(parent); m_StatisticsTreeModel = new QmitkStatisticsTreeModel(parent); m_StatisticsTreeModel->SetDataStorage(GetDataStorage()); m_Controls.statisticsTreeView->setModel(m_StatisticsTreeModel); auto semanticRelationsIntegration = std::make_unique(); semanticRelationsIntegration->AddObserver(this); SetUpConnections(); const auto& allCaseIDs = mitk::RelationStorage::GetAllCaseIDs(); for (const auto& caseID : allCaseIDs) { AddToComboBox(caseID); } } void QmitkSemanticRelationsStatisticsView::SetUpConnections() { connect(m_Controls.caseIDComboBox, static_cast(&QComboBox::currentIndexChanged), this, &QmitkSemanticRelationsStatisticsView::OnCaseIDSelectionChanged); connect(m_StatisticsTreeModel, &QmitkStatisticsTreeModel::ModelUpdated, this, &QmitkSemanticRelationsStatisticsView::OnModelUpdated); - } void QmitkSemanticRelationsStatisticsView::OnCaseIDSelectionChanged(const QString& caseID) { m_StatisticsTreeModel->SetCaseID(caseID.toStdString()); } void QmitkSemanticRelationsStatisticsView::OnModelUpdated() { m_Controls.statisticsTreeView->expandAll(); int columns = m_Controls.statisticsTreeView->model()->columnCount(); for (int i = 0; i < columns; ++i) { m_Controls.statisticsTreeView->resizeColumnToContents(i); } } void QmitkSemanticRelationsStatisticsView::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)); } } \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsStatisticsView.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsStatisticsView.h index 2fb586c512..64c6ec3c0d 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsStatisticsView.h +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsStatisticsView.h @@ -1,74 +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 QMITKSEMANTICRELATIONSSTATISTICSVIEW_H #define QMITKSEMANTICRELATIONSSTATISTICSVIEW_H // semantic relations plugin #include "ui_QmitkSemanticRelationsStatisticsControls.h" -#include "QmitkStatisticsTreeModel.h" // semantic relations module #include +// semantic relations ui module +#include "QmitkStatisticsTreeModel.h" + // mitk qt gui common plugin #include /* -* @brief +* @brief The QmitkSemanticRelationsStatisticsView is an MITK view to combine and show the statistics tree view of the 'SemanticRelationsUI'-module. +* It observes the semantic relations storage and displays the currently available case IDs in a combo box. +* A 'QmitkStatisticsTreeModel' is created and set as the model of a QTreeView. */ class QmitkSemanticRelationsStatisticsView : public QmitkAbstractView, public mitk::ISemanticRelationsObserver { Q_OBJECT public: static const std::string VIEW_ID; virtual ~QmitkSemanticRelationsStatisticsView() override; /* * @brief Update the view with the data from the semantic relations. * * Overridden from 'ISemanticRelationsObserver'. * In order for the Update-function to be called, this view has to be added as an observer of SemanticRelation * (e.g. m_SemanticRelations->AddObserver(this);) * * @par caseID The current case ID to identify the currently active patient / case. */ virtual void Update(const mitk::SemanticTypes::CaseID& caseID) override; protected: virtual void SetFocus() override; virtual void CreateQtPartControl(QWidget* parent) override; private Q_SLOTS: void OnCaseIDSelectionChanged(const QString&); void OnModelUpdated(); private: void SetUpConnections(); void AddToComboBox(const mitk::SemanticTypes::CaseID& caseID); Ui::QmitkSemanticRelationsStatisticsControls m_Controls; QmitkStatisticsTreeModel* m_StatisticsTreeModel; }; #endif // QMITKSEMANTICRELATIONSSTATISTICSVIEW_H