diff --git a/Modules/QtWidgets/src/QmitkMultiWidgetConfigurationToolBar.cpp b/Modules/QtWidgets/src/QmitkMultiWidgetConfigurationToolBar.cpp index a078239b81..e5a695957e 100644 --- a/Modules/QtWidgets/src/QmitkMultiWidgetConfigurationToolBar.cpp +++ b/Modules/QtWidgets/src/QmitkMultiWidgetConfigurationToolBar.cpp @@ -1,82 +1,82 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical Image Computing. 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 "QmitkMultiWidgetConfigurationToolBar.h" QmitkMultiWidgetConfigurationToolBar::QmitkMultiWidgetConfigurationToolBar() : QToolBar() { QToolBar::setOrientation(Qt::Vertical); QToolBar::setIconSize(QSize(17, 17)); InitializeToolBar(); } QmitkMultiWidgetConfigurationToolBar::~QmitkMultiWidgetConfigurationToolBar() { // nothing here } void QmitkMultiWidgetConfigurationToolBar::InitializeToolBar() { // create popup to show a widget to modify the multi widget layout m_LayoutSelectionPopup = new QmitkMultiWidgetLayoutSelectionWidget(this); m_LayoutSelectionPopup->hide(); AddButtons(); connect(m_LayoutSelectionPopup, &QmitkMultiWidgetLayoutSelectionWidget::LayoutSet, this, &QmitkMultiWidgetConfigurationToolBar::LayoutSet); } void QmitkMultiWidgetConfigurationToolBar::AddButtons() { QAction* setLayoutAction = new QAction(QIcon(":/Qmitk/mwLayout.png"), tr("Set multi widget layout"), this); connect(setLayoutAction, &QAction::triggered, this, &QmitkMultiWidgetConfigurationToolBar::OnSetLayout); QToolBar::addAction(setLayoutAction); m_SynchronizeAction = new QAction(QIcon(":/Qmitk/mwSynchronized.png"), tr("Desynchronize render windows"), this); m_SynchronizeAction->setCheckable(true); m_SynchronizeAction->setChecked(true); connect(m_SynchronizeAction, &QAction::triggered, this, &QmitkMultiWidgetConfigurationToolBar::OnSynchronize); QToolBar::addAction(m_SynchronizeAction); } void QmitkMultiWidgetConfigurationToolBar::OnSetLayout() { m_LayoutSelectionPopup->setWindowFlags(Qt::Popup); - m_LayoutSelectionPopup->move(this->cursor().pos()); + m_LayoutSelectionPopup->move(this->cursor().pos().x() - m_LayoutSelectionPopup->width(), this->cursor().pos().y()); m_LayoutSelectionPopup->show(); } void QmitkMultiWidgetConfigurationToolBar::OnSynchronize() { bool synchronized = m_SynchronizeAction->isChecked(); if (synchronized) { m_SynchronizeAction->setIcon(QIcon(":/Qmitk/mwSynchronized.png")); m_SynchronizeAction->setText(tr("Desynchronize render windows")); } else { m_SynchronizeAction->setIcon(QIcon(":/Qmitk/mwDesynchronized.png")); m_SynchronizeAction->setText(tr("Synchronize render windows")); } m_SynchronizeAction->setChecked(synchronized); emit Synchronized(synchronized); } diff --git a/Modules/QtWidgets/src/QmitkMultiWidgetLayoutSelectionWidget.cpp b/Modules/QtWidgets/src/QmitkMultiWidgetLayoutSelectionWidget.cpp index 687ad5a934..17081206c4 100644 --- a/Modules/QtWidgets/src/QmitkMultiWidgetLayoutSelectionWidget.cpp +++ b/Modules/QtWidgets/src/QmitkMultiWidgetLayoutSelectionWidget.cpp @@ -1,76 +1,80 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical Image Computing. 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 "QmitkMultiWidgetLayoutSelectionWidget.h" QmitkMultiWidgetLayoutSelectionWidget::QmitkMultiWidgetLayoutSelectionWidget(QWidget* parent/* = 0*/) : QWidget(parent) { Init(); } void QmitkMultiWidgetLayoutSelectionWidget::Init() { ui.setupUi(this); + + auto stylesheet = "QTableWidget::item{background-color: white;}\nQTableWidget::item:selected{background-color: #1C97EA;}"; + ui.tableWidget->setStyleSheet(stylesheet); + connect(ui.tableWidget, &QTableWidget::itemSelectionChanged, this, &QmitkMultiWidgetLayoutSelectionWidget::OnTableItemSelectionChanged); connect(ui.setLayoutPushButton, &QPushButton::clicked, this, &QmitkMultiWidgetLayoutSelectionWidget::OnSetLayoutButtonClicked); } void QmitkMultiWidgetLayoutSelectionWidget::OnTableItemSelectionChanged() { QItemSelectionModel* selectionModel = ui.tableWidget->selectionModel(); int row = 0; int column = 0; QModelIndexList indices = selectionModel->selectedIndexes(); if (indices.size() > 0) { row = indices[0].row(); column = indices[0].column(); QModelIndex topLeft = ui.tableWidget->model()->index(0, 0, QModelIndex()); QModelIndex bottomRight = ui.tableWidget->model()->index(row, column, QModelIndex()); QItemSelection cellSelection; cellSelection.select(topLeft, bottomRight); selectionModel->select(cellSelection, QItemSelectionModel::Select); } } void QmitkMultiWidgetLayoutSelectionWidget::OnSetLayoutButtonClicked() { int row = 0; int column = 0; QModelIndexList indices = ui.tableWidget->selectionModel()->selectedIndexes(); if (indices.size() > 0) { // find largest row and column for (const QModelIndex modelIndex : indices) { if (modelIndex.row() > row) { row = modelIndex.row(); } if (modelIndex.column() > column) { column = modelIndex.column(); } } close(); emit LayoutSet(row+1, column+1); } } diff --git a/Modules/QtWidgets/src/QmitkMultiWidgetLayoutSelectionWidget.ui b/Modules/QtWidgets/src/QmitkMultiWidgetLayoutSelectionWidget.ui index 34d3e62dfd..5c2d9e4af1 100644 --- a/Modules/QtWidgets/src/QmitkMultiWidgetLayoutSelectionWidget.ui +++ b/Modules/QtWidgets/src/QmitkMultiWidgetLayoutSelectionWidget.ui @@ -1,72 +1,70 @@ QmitkMultiWidgetLayoutSelectionWidget 0 0 230 230 QAbstractItemView::NoEditTriggers false false 3 4 false 50 50 false 50 50 - - Set multi widget layout diff --git a/Modules/RenderWindowManager/include/mitkRenderWindowLayerUtilities.h b/Modules/RenderWindowManager/include/mitkRenderWindowLayerUtilities.h index 6e30a38584..23af38d3c3 100644 --- a/Modules/RenderWindowManager/include/mitkRenderWindowLayerUtilities.h +++ b/Modules/RenderWindowManager/include/mitkRenderWindowLayerUtilities.h @@ -1,71 +1,79 @@ /*=================================================================== 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 MITKRENDERWINDOWLAYERUTILITIES_H #define MITKRENDERWINDOWLAYERUTILITIES_H // render window manager module #include "MitkRenderWindowManagerExports.h" // mitk core #include #include #include #include /** * @brief Render window layer helper functions to retrieve the currently valid layer stack */ namespace mitk { namespace RenderWindowLayerUtilities { typedef std::vector RendererVector; typedef std::map> LayerStack; /** * The base data node of a renderer is supposed to be on layer 0 (zero), which should be the lowest layer in a render window. */ const int BASE_LAYER_INDEX = 0; /** * The top layer index, denoting that no valid (positive) layer index is given and therefore the index should be resolved into the topmost layer index. */ const int TOP_LAYER_INDEX = -1; /** * @brief Return the stack of layers of the given renderer as std::map, which guarantees ordering of the layers. * Stacked layers are only included if they have their "fixedLayer" property set to true and their "layer" property set. * * If "renderer" = nullptr: a layer stack won't be created and an empty "LayerStack" will be returned. * If "withBaseNode" = true: include the base node in the layer stack, if existing. * If "withBaseNode" = false: exclude the base node from the layer stack. * * @param dataStorage Pointer to a data storage instance whose data nodes should be checked and possibly be included. * @param renderer Pointer to the renderer instance for which the layer stack should be generated. * @param withBaseNode Boolean to decide whether the base node should be included in or excluded from the layer stack. */ MITKRENDERWINDOWMANAGER_EXPORT LayerStack GetLayerStack(const DataStorage* dataStorage, const BaseRenderer* renderer, bool withBaseNode); /** * @brief Helper function to get a node predicate that can be used to filter render window specific data nodes. * * The data nodes must not be 'helper objects'. The must have set a 'fixed layer' property for the given renderer. */ MITKRENDERWINDOWMANAGER_EXPORT NodePredicateAnd::Pointer GetRenderWindowPredicate(const BaseRenderer* renderer); - + /** + * @brief Set renderer-specific properties to mark a data node as 'managed by the specific renderer'. + * In order for a renderer to manage a data node, the 'fixedLayer' property has to be set for the given renderer. + * Additionally, the 'visible' and the 'layer' property are set and allow to individually render a set of nodes + * with a specific renderer. + * The last two mentioned properties are set so that they initially have the same value as the corresponding + * global property. + */ + MITKRENDERWINDOWMANAGER_EXPORT void SetRenderWindowProperties(mitk::DataNode* dataNode, const BaseRenderer* renderer); } // namespace RenderWindowLayerUtilities } // namespace mitk #endif // MITKRENDERWINDOWLAYERUTILITIES_H diff --git a/Modules/RenderWindowManager/src/mitkRenderWindowLayerController.cpp b/Modules/RenderWindowManager/src/mitkRenderWindowLayerController.cpp index 2ba1043c82..4ca3aa6af3 100644 --- a/Modules/RenderWindowManager/src/mitkRenderWindowLayerController.cpp +++ b/Modules/RenderWindowManager/src/mitkRenderWindowLayerController.cpp @@ -1,575 +1,567 @@ /*=================================================================== 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. ===================================================================*/ // render window manager module #include "mitkRenderWindowLayerController.h" mitk::RenderWindowLayerController::RenderWindowLayerController() : m_DataStorage(nullptr) { // nothing here } void mitk::RenderWindowLayerController::SetDataStorage(DataStorage::Pointer dataStorage) { if (m_DataStorage != dataStorage) { // set the new data storage m_DataStorage = dataStorage; } } void mitk::RenderWindowLayerController::SetControlledRenderer(RenderWindowLayerUtilities::RendererVector controlledRenderer) { if (m_ControlledRenderer != controlledRenderer) { // set the new set of controlled renderer m_ControlledRenderer = controlledRenderer; } } void mitk::RenderWindowLayerController::SetBaseDataNode(DataNode* dataNode, const BaseRenderer* renderer /*= nullptr*/) { if (nullptr == dataNode) { return; } if (nullptr == renderer) { // set the data node as base data node in all controlled renderer for (const auto& renderer : m_ControlledRenderer) { if (nullptr != renderer) { SetBaseDataNode(dataNode, renderer); } } } else { // get the layer stack with the base data node of the current renderer RenderWindowLayerUtilities::LayerStack stackedLayers = RenderWindowLayerUtilities::GetLayerStack(m_DataStorage, renderer, true); if (!stackedLayers.empty()) { // see if base layer exists RenderWindowLayerUtilities::LayerStack::iterator layerStackIterator = stackedLayers.find(RenderWindowLayerUtilities::BASE_LAYER_INDEX); if (layerStackIterator != stackedLayers.end()) { // remove the current base data node from the current renderer layerStackIterator->second->GetPropertyList(renderer)->DeleteProperty("layer"); layerStackIterator->second->SetBoolProperty("fixedLayer", false, renderer); layerStackIterator->second->SetVisibility(false, renderer); } } // "RenderWindowLayerUtilities::BASE_LAYER_INDEX" indicates the base data node --> set as new background dataNode->SetIntProperty("layer", RenderWindowLayerUtilities::BASE_LAYER_INDEX, renderer); dataNode->SetBoolProperty("fixedLayer", true, renderer); dataNode->SetVisibility(true, renderer); dataNode->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdate(renderer->GetRenderWindow()); } } void mitk::RenderWindowLayerController::InsertLayerNode(DataNode* dataNode, int layer /*= RenderWindowLayerUtilities::TOP_LAYER_INDEX*/, const BaseRenderer* renderer /*= nullptr*/) { if (nullptr == dataNode) { return; } if (nullptr == renderer) { // insert data node in all controlled renderer for (const auto& renderer : m_ControlledRenderer) { if (nullptr != renderer) { InsertLayerNode(dataNode, layer, renderer); } } } else { if (RenderWindowLayerUtilities::BASE_LAYER_INDEX == layer) { // "RenderWindowLayerUtilities::BASE_LAYER_INDEX" indicates the base data node --> set as new background (overwrite current base node if needed) SetBaseDataNode(dataNode, renderer); } else { InsertLayerNodeInternal(dataNode, layer, renderer); } } } void mitk::RenderWindowLayerController::InsertLayerNodeInternal(DataNode* dataNode, int newLayer, const BaseRenderer* renderer /*= nullptr*/) { - dataNode->SetBoolProperty("fixedLayer", true, renderer); - // use visibility of existing renderer or common renderer - bool visible = false; - bool visibilityProperty = dataNode->GetVisibility(visible, renderer); - if (true == visibilityProperty) - { - // found a visibility property - dataNode->SetVisibility(visible, renderer); - } + RenderWindowLayerUtilities::SetRenderWindowProperties(dataNode, renderer); // get the layer stack without the base node of the current renderer RenderWindowLayerUtilities::LayerStack stackedLayers = RenderWindowLayerUtilities::GetLayerStack(m_DataStorage, renderer, false); if (stackedLayers.empty()) { // no layer stack for the current renderer if (RenderWindowLayerUtilities::TOP_LAYER_INDEX == newLayer) { // set given layer as first layer above base layer (= 1) newLayer = 1; // alternatively: no layer stack for the current renderer -> insert as background node //SetBaseDataNode(dataNode, renderer); } } else { if (RenderWindowLayerUtilities::TOP_LAYER_INDEX == newLayer) { // get the first value (highest int-key -> topmost layer) // + 1 indicates inserting the node above the topmost layer newLayer = stackedLayers.begin()->first + 1; } else { MoveNodeToPosition(dataNode, newLayer, renderer); return; } } // update data storage (the "data node model") dataNode->SetIntProperty("layer", newLayer, renderer); dataNode->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdate(renderer->GetRenderWindow()); } void mitk::RenderWindowLayerController::RemoveLayerNode(DataNode* dataNode, const BaseRenderer* renderer /*= nullptr*/) { if (nullptr == dataNode) { return; } if (nullptr == renderer) { // remove data node from all controlled renderer for (const auto& renderer : m_ControlledRenderer) { if (nullptr != renderer) { RemoveLayerNode(dataNode, renderer); } } } else { // "remove" node from the renderer list dataNode->GetPropertyList(renderer)->DeleteProperty("layer"); dataNode->SetBoolProperty("fixedLayer", false, renderer); dataNode->SetVisibility(false, renderer); dataNode->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdate(renderer->GetRenderWindow()); } } bool mitk::RenderWindowLayerController::MoveNodeToPosition(DataNode* dataNode, int newLayer, const BaseRenderer* renderer /*= nullptr*/) { if (nullptr == dataNode) { return false; } if (nullptr == renderer) { // move data node to position in all controlled renderer for (const auto& renderer : m_ControlledRenderer) { if (nullptr != renderer) { MoveNodeToPosition(dataNode, newLayer, renderer); // we don't store/need the returned boolean value return false; } } } else { // get the layer stack without the base node of the current renderer RenderWindowLayerUtilities::LayerStack stackedLayers = RenderWindowLayerUtilities::GetLayerStack(m_DataStorage, renderer, false); if (!stackedLayers.empty()) { // get the current layer value of the given data node int currentLayer; bool wasFound = dataNode->GetIntProperty("layer", currentLayer, renderer); if (wasFound && currentLayer != newLayer) { // move the given data node to the specified layer dataNode->SetIntProperty("layer", newLayer, renderer); int upperBound; int lowerBound; int step; if (currentLayer < newLayer) { // move node up upperBound = newLayer + 1; lowerBound = currentLayer + 1; step = -1; // move all other nodes one step down } else { upperBound = currentLayer; lowerBound = newLayer; step = 1; // move all other nodes one step up } // move all other data nodes between the upper and the lower bound for (auto& layer : stackedLayers) { if (layer.second != dataNode && layer.first < upperBound && layer.first >= lowerBound) { layer.second->SetIntProperty("layer", layer.first + step, renderer); } // else: current data node is the selected data node or // was previously already above the selected data node or // was previously already below the new layer position } dataNode->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdate(renderer->GetRenderWindow()); return true; } // else: data node has no layer information or is already at the specified position } // else: do not work with empty layer stack } return false; } bool mitk::RenderWindowLayerController::MoveNodeToFront(DataNode* dataNode, const BaseRenderer* renderer /*= nullptr*/) { if (nullptr == dataNode) { return false; } if (nullptr == renderer) { // move data node to front in all controlled renderer for (const auto& renderer : m_ControlledRenderer) { if (nullptr != renderer) { MoveNodeToFront(dataNode, renderer); // we don't store/need the returned boolean value return false; } } } else { // get the layer stack without the base node of the current renderer RenderWindowLayerUtilities::LayerStack stackedLayers = RenderWindowLayerUtilities::GetLayerStack(m_DataStorage, renderer, false); if (!stackedLayers.empty()) { // get the first value (highest int-key -> topmost layer) int topmostLayer = stackedLayers.begin()->first; // get the current layer value of the given data node int currentLayer; bool wasFound = dataNode->GetIntProperty("layer", currentLayer, renderer); if (wasFound && currentLayer < topmostLayer) { // move the current data node above the current topmost layer dataNode->SetIntProperty("layer", topmostLayer+1, renderer); dataNode->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdate(renderer->GetRenderWindow()); return true; } // else: data node has no layer information or is already the topmost layer node } // else: do not work with empty layer stack } return false; } bool mitk::RenderWindowLayerController::MoveNodeToBack(DataNode* dataNode, const BaseRenderer* renderer /*= nullptr*/) { if (nullptr == dataNode) { return false; } if (nullptr == renderer) { // move data node to back in all controlled renderer for (const auto& renderer : m_ControlledRenderer) { if (nullptr != renderer) { MoveNodeToBack(dataNode, renderer); // we don't store/need the returned boolean value return false; } } } else { // get the layer stack without the base node of the current renderer RenderWindowLayerUtilities::LayerStack stackedLayers = RenderWindowLayerUtilities::GetLayerStack(m_DataStorage, renderer, false); if (!stackedLayers.empty()) { // get the last value (lowest int-key) // cannot be the base layer as the base node was excluded by the 'GetLayerStack'-function int lowermostLayer = stackedLayers.rbegin()->first; // get the current layer value of the given data node int currentLayer; bool wasFound = dataNode->GetIntProperty("layer", currentLayer, renderer); if (wasFound && currentLayer > lowermostLayer) { // move the current data node to the current lowermost layer dataNode->SetIntProperty("layer", lowermostLayer, renderer); // move all other data nodes one layer up for (auto& layer : stackedLayers) { if (layer.second != dataNode && layer.first < currentLayer) { layer.second->SetIntProperty("layer", layer.first + 1, renderer); } // else: current data node is the selected data node or // was previously already above the selected data node } dataNode->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdate(renderer->GetRenderWindow()); return true; } // else: data node has no layer information or is already the lowermost layer node } // else: do not work with empty layer stack } return false; } bool mitk::RenderWindowLayerController::MoveNodeUp(DataNode* dataNode, const BaseRenderer* renderer /*= nullptr*/) { if (nullptr == dataNode) { return false; } if (nullptr == renderer) { // move data node down in all controlled renderer for (const auto& renderer : m_ControlledRenderer) { if (nullptr != renderer) { MoveNodeUp(dataNode, renderer); // we don't store/need the returned boolean value return false; } } } else { // get the layer stack without the base node of the current renderer RenderWindowLayerUtilities::LayerStack stackedLayers = RenderWindowLayerUtilities::GetLayerStack(m_DataStorage, renderer, false); if (!stackedLayers.empty()) { // get the current layer value of the given data node int currentLayer; bool wasFound = dataNode->GetIntProperty("layer", currentLayer, renderer); if (wasFound) { // get the current layer in the map of stacked layers RenderWindowLayerUtilities::LayerStack::const_iterator layerStackIterator = stackedLayers.find(currentLayer); if (layerStackIterator != stackedLayers.end() && layerStackIterator != stackedLayers.begin()) { // found the element in the map, at different position than 'begin' -> // current node is not on the topmost layer and therefore can be moved one layer up // swap the layers of the dataNode and the dataNode on the next higher layer (previous map element) RenderWindowLayerUtilities::LayerStack::const_iterator prevLayerStackIterator = std::prev(layerStackIterator); dataNode->SetIntProperty("layer", prevLayerStackIterator->first, renderer); prevLayerStackIterator->second->SetIntProperty("layer", currentLayer, renderer); dataNode->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdate(renderer->GetRenderWindow()); return true; } // else: layer stack does not contain a layer with the 'currentLayer'data node or // layer is already the topmost layer node } // else: data node has no layer information } // else: do not work with empty layer stack } return false; } bool mitk::RenderWindowLayerController::MoveNodeDown(DataNode* dataNode, const BaseRenderer* renderer /*= nullptr*/) { if (nullptr == dataNode) { return false; } if (nullptr == renderer) { // move data node up in all controlled renderer for (const auto& renderer : m_ControlledRenderer) { if (nullptr != renderer) { MoveNodeDown(dataNode, renderer); // we don't store/need the returned boolean value return false; } } } else { // get the layer stack without the base node of the current renderer RenderWindowLayerUtilities::LayerStack stackedLayers = RenderWindowLayerUtilities::GetLayerStack(m_DataStorage, renderer, false); if (!stackedLayers.empty()) { // get the current layer value of the given data node int currentLayer; bool wasFound = dataNode->GetIntProperty("layer", currentLayer, renderer); if (wasFound) { // get the current layer in the map of stacked layers RenderWindowLayerUtilities::LayerStack::const_iterator layerStackIterator = stackedLayers.find(currentLayer); if (layerStackIterator != stackedLayers.end()) { // found the element in the map ... RenderWindowLayerUtilities::LayerStack::const_iterator nextLayerStackIterator = std::next(layerStackIterator); if (nextLayerStackIterator != stackedLayers.end()) { // ... and found a successor -> // current node is not on the lowermost layer and therefore can be moved one layer down // swap the layers of the dataNode and the dataNode on the next lower layer (next map element) dataNode->SetIntProperty("layer", nextLayerStackIterator->first, renderer); nextLayerStackIterator->second->SetIntProperty("layer", currentLayer, renderer); dataNode->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdate(renderer->GetRenderWindow()); return true; } // else: data node is already the lowermost layer node } // else: layer stack does not contain a layer with the 'currentLayer' } // else: data node has no layer information } // else: do not work with empty layer stack } return false; } void mitk::RenderWindowLayerController::SetVisibilityOfDataNode(bool visibility, DataNode* dataNode, const BaseRenderer* renderer /*=nullptr*/) { if (nullptr == dataNode) { return; } if (nullptr == renderer) { // set visibility of data node in all controlled renderer for (const auto& renderer : m_ControlledRenderer) { if (nullptr != renderer) { SetVisibilityOfDataNode(visibility, dataNode, renderer); } } } else { dataNode->SetVisibility(visibility, renderer); dataNode->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdate(renderer->GetRenderWindow()); } } void mitk::RenderWindowLayerController::HideDataNodeInAllRenderer(const DataNode* dataNode) { if (nullptr == dataNode) { return; } for (const auto& renderer : m_ControlledRenderer) { if (nullptr != renderer) { dataNode->GetPropertyList(renderer)->SetBoolProperty("visible", false); } } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::RenderWindowLayerController::ResetRenderer(bool onlyVisibility /*= true*/, const BaseRenderer* renderer /*= nullptr*/) { if (nullptr == renderer) { // reset all controlled renderer for (const auto& renderer : m_ControlledRenderer) { if (nullptr != renderer) { ResetRenderer(onlyVisibility, renderer); } } } else { // get the layer stack with the base node of the current renderer RenderWindowLayerUtilities::LayerStack stackedLayers = RenderWindowLayerUtilities::GetLayerStack(m_DataStorage, renderer, true); if (!stackedLayers.empty()) { for (const auto& layer : stackedLayers) { int layerLevel; layer.second->GetIntProperty("layer", layerLevel, renderer); if (RenderWindowLayerUtilities::BASE_LAYER_INDEX == layerLevel) { // set base data node visibility to true layer.second->SetVisibility(true, renderer); } else { // set visibility of all other data nodes to false layer.second->SetVisibility(false, renderer); // modify layer node if (!onlyVisibility) { // clear mode: additionally remove layer node from current renderer layer.second->GetPropertyList(renderer)->DeleteProperty("layer"); layer.second->SetBoolProperty("fixedLayer", false, renderer); } } layer.second->Modified(); } mitk::RenderingManager::GetInstance()->RequestUpdate(renderer->GetRenderWindow()); } } } diff --git a/Modules/RenderWindowManager/src/mitkRenderWindowLayerUtilities.cpp b/Modules/RenderWindowManager/src/mitkRenderWindowLayerUtilities.cpp index b3fd2e8a12..fd34b0de43 100644 --- a/Modules/RenderWindowManager/src/mitkRenderWindowLayerUtilities.cpp +++ b/Modules/RenderWindowManager/src/mitkRenderWindowLayerUtilities.cpp @@ -1,68 +1,92 @@ /*=================================================================== 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. ===================================================================*/ // render window manager module #include "mitkRenderWindowLayerUtilities.h" // mitk core #include #include mitk::RenderWindowLayerUtilities::LayerStack mitk::RenderWindowLayerUtilities::GetLayerStack(const DataStorage* dataStorage, const BaseRenderer* renderer, bool withBaseNode) { LayerStack stackedLayers; if (nullptr == dataStorage || nullptr == renderer) { // no nodes to stack or no renderer selected return stackedLayers; } int layer = -1; NodePredicateAnd::Pointer combinedNodePredicate = GetRenderWindowPredicate(renderer); DataStorage::SetOfObjects::ConstPointer filteredDataNodes = dataStorage->GetSubset(combinedNodePredicate); for (DataStorage::SetOfObjects::ConstIterator it = filteredDataNodes->Begin(); it != filteredDataNodes->End(); ++it) { DataNode::Pointer dataNode = it->Value(); if (dataNode.IsNull()) { continue; } bool layerFound = dataNode->GetIntProperty("layer", layer, renderer); if (layerFound) { if (BASE_LAYER_INDEX != layer|| withBaseNode) { // data node is not on the base layer or the base layer should be included anyway stackedLayers.insert(std::make_pair(layer, dataNode)); } } } return stackedLayers; } mitk::NodePredicateAnd::Pointer mitk::RenderWindowLayerUtilities::GetRenderWindowPredicate(const BaseRenderer* renderer) { NodePredicateAnd::Pointer renderWindowPredicate = NodePredicateAnd::New(); NodePredicateProperty::Pointer helperObject = NodePredicateProperty::New("helper object", BoolProperty::New(true)); NodePredicateProperty::Pointer fixedLayer = NodePredicateProperty::New("fixedLayer", BoolProperty::New(true), renderer); renderWindowPredicate->AddPredicate(NodePredicateNot::New(helperObject)); renderWindowPredicate->AddPredicate(fixedLayer); return renderWindowPredicate; } + +void mitk::RenderWindowLayerUtilities::SetRenderWindowProperties(mitk::DataNode* dataNode, const BaseRenderer* renderer) +{ + dataNode->SetBoolProperty("fixedLayer", true, renderer); + // use visibility of existing renderer or common renderer + // common renderer is used if renderer-specific property does not exist + bool visible = false; + bool visibilityProperty = dataNode->GetVisibility(visible, renderer); + if (true == visibilityProperty) + { + // found a visibility property + dataNode->SetVisibility(visible, renderer); + } + + // use layer of existing renderer or common renderer + // common renderer is used if renderer-specific property does not exist + int layer = -1; + bool layerProperty = dataNode->GetIntProperty("layer", layer, renderer); + if (true == layerProperty) + { + // found a layer property + dataNode->SetIntProperty("layer", layer, renderer); + } +} diff --git a/Modules/RenderWindowManagerUI/files.cmake b/Modules/RenderWindowManagerUI/files.cmake index 1637b1a861..bb0d58e96c 100644 --- a/Modules/RenderWindowManagerUI/files.cmake +++ b/Modules/RenderWindowManagerUI/files.cmake @@ -1,18 +1,21 @@ set(H_FILES include/QmitkRenderWindowDataStorageInspector.h include/QmitkRenderWindowDataStorageListModel.h + include/QmitkRenderWindowDataStorageTreeModel.h ) set(CPP_FILES QmitkRenderWindowDataStorageInspector.cpp QmitkRenderWindowDataStorageListModel.cpp + QmitkRenderWindowDataStorageTreeModel.cpp ) set(MOC_H_FILES include/QmitkRenderWindowDataStorageInspector.h include/QmitkRenderWindowDataStorageListModel.h + include/QmitkRenderWindowDataStorageTreeModel.h ) set(UI_FILES src/QmitkRenderWindowDataStorageInspector.ui ) diff --git a/Modules/RenderWindowManagerUI/include/QmitkRenderWindowDataStorageInspector.h b/Modules/RenderWindowManagerUI/include/QmitkRenderWindowDataStorageInspector.h index d0078bb2dc..03e54c8a8e 100644 --- a/Modules/RenderWindowManagerUI/include/QmitkRenderWindowDataStorageInspector.h +++ b/Modules/RenderWindowManagerUI/include/QmitkRenderWindowDataStorageInspector.h @@ -1,94 +1,96 @@ /*=================================================================== 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 QMITKRENDERWINDOWDATASTORAGEINSPECTOR_H -#define QMITKRENDERWINDOWDATASTORAGEWINSPECTOR_H +#define QMITKRENDERWINDOWDATASTORAGEINSPECTOR_H // render window manager UI module #include "MitkRenderWindowManagerUIExports.h" #include "ui_QmitkRenderWindowDataStorageInspector.h" // render window manager module #include #include -#include +#include // qt widgets module #include /** * The 'QmitkRenderWindowDataStorageInspector' offers a GUI to manipulate the base renderer / render windows of the MITK workbench. * * In order to use this widget, a (e.g.) plugin has to set the controlled renderer, which will be forwarded to * a render window view direction controller. */ class MITKRENDERWINDOWMANAGERUI_EXPORT QmitkRenderWindowDataStorageInspector : public QmitkAbstractDataStorageInspector { Q_OBJECT public: QmitkRenderWindowDataStorageInspector(QWidget* parent = nullptr); // override from 'QmitkAbstractDataStorageInspector' /** * @brief See 'QmitkAbstractDataStorageInspector' */ virtual QAbstractItemView* GetView() override; /** * @brief See 'QmitkAbstractDataStorageInspector' */ virtual const QAbstractItemView* GetView() const override; /** * @brief See 'QmitkAbstractDataStorageInspector' */ virtual void SetSelectionMode(SelectionMode mode) override; /** * @brief See 'QmitkAbstractDataStorageInspector' */ virtual SelectionMode GetSelectionMode() const override; /** * @brief Set the controlled base renderer. */ void SetControlledRenderer(mitk::RenderWindowLayerUtilities::RendererVector controlledRenderer); /** * @brief Set the currently selected render window. * * @param renderWindowId the text inside the combo box */ void SetActiveRenderWindow(const QString& renderWindowId); private Q_SLOTS: + void ModelRowsInserted(const QModelIndex& parent, int start, int end); + void SetAsBaseLayer(); void ResetRenderer(); void ChangeViewDirection(const QString& viewDirection); private: virtual void Initialize() override; void SetUpConnections(); Ui::QmitkRenderWindowDataStorageInspector m_Controls; - std::unique_ptr m_StorageModel; + std::unique_ptr m_StorageModel; std::unique_ptr m_RenderWindowLayerController; std::unique_ptr m_RenderWindowViewDirectionController; }; #endif // QMITKRENDERWINDOWDATASTORAGEINSPECTOR_H diff --git a/Modules/RenderWindowManagerUI/include/QmitkRenderWindowDataStorageTreeModel.h b/Modules/RenderWindowManagerUI/include/QmitkRenderWindowDataStorageTreeModel.h new file mode 100644 index 0000000000..44a37fbf55 --- /dev/null +++ b/Modules/RenderWindowManagerUI/include/QmitkRenderWindowDataStorageTreeModel.h @@ -0,0 +1,136 @@ +/*=================================================================== + +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 QMITKDATASTORAGERENDERWINDOWTREEMODEL_H +#define QMITKDATASTORAGERENDERWINDOWTREEMODEL_H + +// render window manager UI model +#include "MitkRenderWindowManagerUIExports.h" + +// render window manager module +#include "mitkRenderWindowLayerController.h" +#include "mitkRenderWindowLayerUtilities.h" + +//mitk core +#include + +// qt widgets module +#include +#include + +/* +* @brief The 'QmitkRenderWindowDataStorageTreeModel' is a tree model derived from the 'QmitkAbstractDataStorageModel'. +*/ +class MITKRENDERWINDOWMANAGERUI_EXPORT QmitkRenderWindowDataStorageTreeModel : public QmitkAbstractDataStorageModel +{ + Q_OBJECT + +public: + + QmitkRenderWindowDataStorageTreeModel(QObject* parent = nullptr); + + // override from 'QmitkAbstractDataStorageModel' + /** + * @brief See 'QmitkAbstractDataStorageModel' + */ + void DataStorageChanged() override; + /** + * @brief See 'QmitkAbstractDataStorageModel' + */ + void NodePredicateChanged() override; + /** + * @brief See 'QmitkAbstractDataStorageModel' + */ + void NodeAdded(const mitk::DataNode* node) override; + /** + * @brief See 'QmitkAbstractDataStorageModel' + */ + void NodeChanged(const mitk::DataNode* node) override; + /** + * @brief See 'QmitkAbstractDataStorageModel' + */ + void NodeRemoved(const mitk::DataNode* node) override; + + // override from 'QAbstractItemModel' + QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex& parent) const override; + + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + int columnCount(const QModelIndex& parent = QModelIndex()) const override; + + QVariant data(const QModelIndex& index, int role) const override; + bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; + + Qt::ItemFlags flags(const QModelIndex& index) const override; + + Qt::DropActions supportedDropActions() const override; + Qt::DropActions supportedDragActions() const override; + QStringList mimeTypes() const override; + QMimeData* mimeData(const QModelIndexList& indexes) const override; + + bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) override; + + void SetControlledRenderer(mitk::RenderWindowLayerUtilities::RendererVector controlledRenderer); + + void SetCurrentRenderer(mitk::BaseRenderer* baseRenderer); + mitk::BaseRenderer* GetCurrentRenderer() const; + +private: + + void ResetTree(); + void UpdateModelData(); + /** + * @brief Adjust the layer property according to the current tree. + * The function will set the "layer" property of each underlying data node so that it fits the + * the actual hierarchy represented by the current tree. + */ + void AdjustLayerProperty(); + /** + * @brief Fill a vector of tree items in a depth-first order (child-first). + */ + void TreeToVector(QmitkDataStorageTreeModelInternalItem* parent, std::vector& treeAsVector) const; + /** + * @brief Add the given data node to the tree of the given renderer. + * The given renderer specifies the "layer"-property that is used for adding the new tree item + * to the tree. The "layer"-property may be different for each renderer resulting in a + * different tree for each renderer. + * + * @param dataNode The data node that should be added. + * @param renderer The base renderer to which the data node should be added. + */ + void AddNodeInternal(const mitk::DataNode* dataNode, const mitk::BaseRenderer* renderer); + /** + * @brief Remove the tree item that contains the given data node. Removing an item may + * leave the child items of the removed item without a parent. In this case + * the children have to be moved inside the tree so the tree has to be rebuild + * according to the current status of the data storage. + * + * @param dataNode The data node that should be removed. + */ + void RemoveNodeInternal(const mitk::DataNode* dataNode); + + mitk::DataNode* GetParentNode(const mitk::DataNode* node) const; + QmitkDataStorageTreeModelInternalItem* GetItemByIndex(const QModelIndex& index) const; + QModelIndex GetIndexByItem(QmitkDataStorageTreeModelInternalItem* item) const; + + std::unique_ptr m_RenderWindowLayerController; + mitk::RenderWindowLayerUtilities::RendererVector m_ControlledRenderer; + QmitkDataStorageTreeModelInternalItem* m_Root; + mitk::WeakPointer m_BaseRenderer; + +}; + +#endif // QMITKDATASTORAGERENDERWINDOWTREEMODEL_H diff --git a/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageInspector.cpp b/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageInspector.cpp index 018d3441f0..152497dc09 100644 --- a/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageInspector.cpp +++ b/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageInspector.cpp @@ -1,165 +1,173 @@ /*=================================================================== 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. ===================================================================*/ // render window manager UI module #include "QmitkRenderWindowDataStorageInspector.h" #include "QmitkCustomVariants.h" // mitk core #include // qt #include QmitkRenderWindowDataStorageInspector::QmitkRenderWindowDataStorageInspector(QWidget* parent /*=nullptr*/) : QmitkAbstractDataStorageInspector(parent) { m_Controls.setupUi(this); // initialize the render window layer controller and the render window view direction controller m_RenderWindowLayerController = std::make_unique(); m_RenderWindowViewDirectionController = std::make_unique(); - m_StorageModel = std::make_unique(this); - - m_Controls.renderWindowListView->setModel(m_StorageModel.get()); - m_Controls.renderWindowListView->setEditTriggers(QAbstractItemView::NoEditTriggers); - m_Controls.renderWindowListView->setSelectionBehavior(QAbstractItemView::SelectRows); - m_Controls.renderWindowListView->setSelectionMode(QAbstractItemView::ExtendedSelection); - m_Controls.renderWindowListView->setAlternatingRowColors(true); - m_Controls.renderWindowListView->setDragEnabled(true); - m_Controls.renderWindowListView->setDropIndicatorShown(true); - m_Controls.renderWindowListView->setAcceptDrops(true); - m_Controls.renderWindowListView->setContextMenuPolicy(Qt::CustomContextMenu); + m_StorageModel = std::make_unique(this); + + m_Controls.renderWindowTreeView->setModel(m_StorageModel.get()); + m_Controls.renderWindowTreeView->setHeaderHidden(true); + m_Controls.renderWindowTreeView->setEditTriggers(QAbstractItemView::NoEditTriggers); + m_Controls.renderWindowTreeView->setSelectionBehavior(QAbstractItemView::SelectRows); + m_Controls.renderWindowTreeView->setSelectionMode(QAbstractItemView::ExtendedSelection); + m_Controls.renderWindowTreeView->setAlternatingRowColors(true); + m_Controls.renderWindowTreeView->setDragEnabled(true); + m_Controls.renderWindowTreeView->setDropIndicatorShown(true); + m_Controls.renderWindowTreeView->setAcceptDrops(true); + m_Controls.renderWindowTreeView->setContextMenuPolicy(Qt::CustomContextMenu); SetUpConnections(); } QAbstractItemView* QmitkRenderWindowDataStorageInspector::GetView() { - return m_Controls.renderWindowListView; + return m_Controls.renderWindowTreeView; } const QAbstractItemView* QmitkRenderWindowDataStorageInspector::GetView() const { - return m_Controls.renderWindowListView; + return m_Controls.renderWindowTreeView; } void QmitkRenderWindowDataStorageInspector::SetSelectionMode(SelectionMode mode) { - m_Controls.renderWindowListView->setSelectionMode(mode); + m_Controls.renderWindowTreeView->setSelectionMode(mode); } QmitkRenderWindowDataStorageInspector::SelectionMode QmitkRenderWindowDataStorageInspector::GetSelectionMode() const { - return m_Controls.renderWindowListView->selectionMode(); + return m_Controls.renderWindowTreeView->selectionMode(); } void QmitkRenderWindowDataStorageInspector::Initialize() { if (m_DataStorage.IsExpired()) { return; } auto dataStorage = m_DataStorage.Lock(); m_StorageModel->SetDataStorage(dataStorage); m_StorageModel->SetNodePredicate(m_NodePredicate); m_RenderWindowLayerController->SetDataStorage(dataStorage); m_RenderWindowViewDirectionController->SetDataStorage(dataStorage); - m_Connector->SetView(m_Controls.renderWindowListView); + m_Connector->SetView(m_Controls.renderWindowTreeView); } void QmitkRenderWindowDataStorageInspector::SetUpConnections() { + connect(m_StorageModel.get(), &QAbstractItemModel::rowsInserted, this, &QmitkRenderWindowDataStorageInspector::ModelRowsInserted); + connect(m_Controls.pushButtonSetAsBaseLayer, &QPushButton::clicked, this, &QmitkRenderWindowDataStorageInspector::SetAsBaseLayer); connect(m_Controls.pushButtonResetRenderer, &QPushButton::clicked, this, &QmitkRenderWindowDataStorageInspector::ResetRenderer); QSignalMapper* changeViewDirectionSignalMapper = new QSignalMapper(this); changeViewDirectionSignalMapper->setMapping(m_Controls.radioButtonAxial, QString("axial")); changeViewDirectionSignalMapper->setMapping(m_Controls.radioButtonCoronal, QString("coronal")); changeViewDirectionSignalMapper->setMapping(m_Controls.radioButtonSagittal, QString("sagittal")); changeViewDirectionSignalMapper->setMapping(m_Controls.radioButton3D, QString("3D")); connect(changeViewDirectionSignalMapper, static_cast(&QSignalMapper::mapped), this, &QmitkRenderWindowDataStorageInspector::ChangeViewDirection); connect(m_Controls.radioButtonAxial, &QPushButton::clicked, changeViewDirectionSignalMapper, static_cast(&QSignalMapper::map)); connect(m_Controls.radioButtonCoronal, &QPushButton::clicked, changeViewDirectionSignalMapper, static_cast(&QSignalMapper::map)); connect(m_Controls.radioButtonSagittal, &QPushButton::clicked, changeViewDirectionSignalMapper, static_cast(&QSignalMapper::map)); connect(m_Controls.radioButton3D, &QPushButton::clicked, changeViewDirectionSignalMapper, static_cast(&QSignalMapper::map)); } void QmitkRenderWindowDataStorageInspector::SetControlledRenderer(mitk::RenderWindowLayerUtilities::RendererVector controlledRenderer) { m_StorageModel->SetControlledRenderer(controlledRenderer); m_RenderWindowLayerController->SetControlledRenderer(controlledRenderer); m_RenderWindowViewDirectionController->SetControlledRenderer(controlledRenderer); } void QmitkRenderWindowDataStorageInspector::SetActiveRenderWindow(const QString& renderWindowId) { mitk::BaseRenderer* selectedRenderer = mitk::BaseRenderer::GetByName(renderWindowId.toStdString()); if (nullptr == selectedRenderer) { return; } m_StorageModel->SetCurrentRenderer(selectedRenderer); mitk::SliceNavigationController::ViewDirection viewDirection = selectedRenderer->GetSliceNavigationController()->GetDefaultViewDirection(); switch (viewDirection) { case mitk::SliceNavigationController::Axial: m_Controls.radioButtonAxial->setChecked(true); break; case mitk::SliceNavigationController::Frontal: m_Controls.radioButtonCoronal->setChecked(true); break; case mitk::SliceNavigationController::Sagittal: m_Controls.radioButtonSagittal->setChecked(true); break; default: break; } } +void QmitkRenderWindowDataStorageInspector::ModelRowsInserted(const QModelIndex& parent, int start, int end) +{ + m_Controls.renderWindowTreeView->setExpanded(parent, true); +} + void QmitkRenderWindowDataStorageInspector::SetAsBaseLayer() { - QModelIndex selectedIndex = m_Controls.renderWindowListView->currentIndex(); + QModelIndex selectedIndex = m_Controls.renderWindowTreeView->currentIndex(); if (selectedIndex.isValid()) { QVariant qvariantDataNode = m_StorageModel->data(selectedIndex, Qt::UserRole); if (qvariantDataNode.canConvert()) { mitk::DataNode* dataNode = qvariantDataNode.value(); m_RenderWindowLayerController->SetBaseDataNode(dataNode, m_StorageModel->GetCurrentRenderer()); - m_Controls.renderWindowListView->clearSelection(); + m_Controls.renderWindowTreeView->clearSelection(); } } } void QmitkRenderWindowDataStorageInspector::ResetRenderer() { m_RenderWindowLayerController->ResetRenderer(true, m_StorageModel->GetCurrentRenderer()); - m_Controls.renderWindowListView->clearSelection(); + m_Controls.renderWindowTreeView->clearSelection(); } void QmitkRenderWindowDataStorageInspector::ChangeViewDirection(const QString& viewDirection) { m_RenderWindowViewDirectionController->SetViewDirectionOfRenderer(viewDirection.toStdString(), m_StorageModel->GetCurrentRenderer()); } diff --git a/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageInspector.ui b/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageInspector.ui index 64c507a340..785e20c50c 100644 --- a/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageInspector.ui +++ b/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageInspector.ui @@ -1,92 +1,92 @@ QmitkRenderWindowDataStorageInspector 0 0 340 246 0 0 0 0 Render window manager Render window overview - + Reset render window - + Set as base layer - + Axial true - + Coronal Sagittal 3D diff --git a/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageTreeModel.cpp b/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageTreeModel.cpp new file mode 100644 index 0000000000..87214ffa53 --- /dev/null +++ b/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageTreeModel.cpp @@ -0,0 +1,605 @@ +/*=================================================================== + +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. + +===================================================================*/ + +// render window manager UI module +#include "QmitkRenderWindowDataStorageTreeModel.h" + +// mitk core +#include + +// qt widgets module +#include "QmitkCustomVariants.h" +#include "QmitkEnums.h" +#include "QmitkMimeTypes.h" +#include "QmitkNodeDescriptorManager.h" + +QmitkRenderWindowDataStorageTreeModel::QmitkRenderWindowDataStorageTreeModel(QObject* parent /*= nullptr*/) + : QmitkAbstractDataStorageModel(parent) + , m_Root(nullptr) +{ + m_RenderWindowLayerController = std::make_unique(); + ResetTree(); +} + +void QmitkRenderWindowDataStorageTreeModel::DataStorageChanged() +{ + m_RenderWindowLayerController->SetDataStorage(m_DataStorage.Lock()); + ResetTree(); + UpdateModelData(); +} + +void QmitkRenderWindowDataStorageTreeModel::NodePredicateChanged() +{ + ResetTree(); + UpdateModelData(); +} + +void QmitkRenderWindowDataStorageTreeModel::NodeAdded(const mitk::DataNode* node) +{ + for (const auto renderer : m_ControlledRenderer) + { + // add the node to each render window + mitk::RenderWindowLayerUtilities::SetRenderWindowProperties(const_cast(node), renderer); + } + + if (!m_BaseRenderer.IsExpired()) + { + auto baseRenderer = m_BaseRenderer.Lock(); + AddNodeInternal(node, baseRenderer); + } +} + +void QmitkRenderWindowDataStorageTreeModel::NodeChanged(const mitk::DataNode* node) +{ + auto item = m_Root->Find(node); + if (nullptr != item) + { + auto parentItem = item->GetParent(); + // as the root node should not be removed one should always have a parent item + if (nullptr == parentItem) + { + return; + } + + auto index = createIndex(item->GetIndex(), 0, item); + emit dataChanged(index, index); + } +} + +void QmitkRenderWindowDataStorageTreeModel::NodeRemoved(const mitk::DataNode* node) +{ + RemoveNodeInternal(node); +} + +QModelIndex QmitkRenderWindowDataStorageTreeModel::index(int row, int column, const QModelIndex& parent) const +{ + auto item = GetItemByIndex(parent); + if (nullptr != item) + { + item = item->GetChild(row); + } + + if (nullptr == item) + { + return QModelIndex(); + } + + return createIndex(row, column, item); +} + +QModelIndex QmitkRenderWindowDataStorageTreeModel::parent(const QModelIndex& parent) const +{ + auto item = GetItemByIndex(parent); + if (nullptr != item) + { + item = item->GetParent(); + } + + if(nullptr == item) + { + return QModelIndex(); + } + + if (item == m_Root) + { + return QModelIndex(); + } + + return createIndex(item->GetIndex(), 0, item); +} + +int QmitkRenderWindowDataStorageTreeModel::rowCount(const QModelIndex& parent /*= QModelIndex()*/) const +{ + auto item = GetItemByIndex(parent); + if (nullptr == item) + { + return 0; + } + + return item->GetChildCount(); +} + +int QmitkRenderWindowDataStorageTreeModel::columnCount(const QModelIndex&/* parent = QModelIndex()*/) const +{ + if (0 == m_Root->GetChildCount()) + { + // no items stored, no need to display columns + return 0; + } + + return 1; +} + +QVariant QmitkRenderWindowDataStorageTreeModel::data(const QModelIndex& index, int role) const +{ + if (m_BaseRenderer.IsExpired()) + { + return QVariant(); + } + + auto baseRenderer = m_BaseRenderer.Lock(); + + if (!index.isValid() || this != index.model()) + { + return QVariant(); + } + + auto item = GetItemByIndex(index); + if (nullptr == item) + { + return QVariant(); + } + + auto dataNode = item->GetDataNode(); + if (nullptr == dataNode) + { + return QVariant(); + } + + if (Qt::CheckStateRole == role) + { + bool visibility = false; + dataNode->GetVisibility(visibility, baseRenderer); + if (visibility) + { + return Qt::Checked; + } + else + { + return Qt::Unchecked; + } + } + else if (Qt::DisplayRole == role) + { + return QVariant(QString::fromStdString(dataNode->GetName())); + } + else if (Qt::ToolTipRole == role) + { + return QVariant("Name of the data node."); + } + else if (Qt::DecorationRole == role) + { + QmitkNodeDescriptor* nodeDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor(dataNode); + return nodeDescriptor->GetIcon(dataNode); + } + else if (Qt::UserRole == role || QmitkDataNodeRawPointerRole == role) + { + // user role always returns a reference to the data node, + // which can be used to modify the data node in the data storage + return QVariant::fromValue(dataNode); + } + else if (QmitkDataNodeRole == role) + { + return QVariant::fromValue(mitk::DataNode::Pointer(dataNode)); + } + + return QVariant(); +} + +bool QmitkRenderWindowDataStorageTreeModel::setData(const QModelIndex& index, const QVariant& value, int role /*= Qt::EditRole*/) +{ + if (m_BaseRenderer.IsExpired()) + { + return false; + } + + auto baseRenderer = m_BaseRenderer.Lock(); + + if (!index.isValid() || this != index.model()) + { + return false; + } + + auto item = GetItemByIndex(index); + if (nullptr == item) + { + return false; + } + + auto dataNode = item->GetDataNode(); + if (nullptr == dataNode) + { + return false; + } + + if (Qt::EditRole == role && !value.toString().isEmpty()) + { + dataNode->SetName(value.toString().toStdString().c_str()); + emit dataChanged(index, index); + return true; + } + if (Qt::CheckStateRole == role) + { + Qt::CheckState newCheckState = static_cast(value.toInt()); + bool isVisible = newCheckState; + dataNode->SetVisibility(isVisible, baseRenderer); + + emit dataChanged(index, index); + mitk::RenderingManager::GetInstance()->RequestUpdate(baseRenderer->GetRenderWindow()); + return true; + } + return false; +} + +Qt::ItemFlags QmitkRenderWindowDataStorageTreeModel::flags(const QModelIndex& index) const +{ + if (this != index.model()) + { + return Qt::NoItemFlags; + } + + if (!index.isValid()) + { + return Qt::ItemIsDropEnabled; + } + + auto item = GetItemByIndex(index); + if (nullptr == item) + { + return Qt::NoItemFlags; + } + + const auto dataNode = item->GetDataNode(); + if (m_NodePredicate.IsNull() || m_NodePredicate->CheckNode(dataNode)) + { + return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; + } + + return Qt::NoItemFlags; +} + +Qt::DropActions QmitkRenderWindowDataStorageTreeModel::supportedDropActions() const +{ + return Qt::CopyAction | Qt::MoveAction; +} + +Qt::DropActions QmitkRenderWindowDataStorageTreeModel::supportedDragActions() const +{ + return Qt::CopyAction | Qt::MoveAction; +} + +QStringList QmitkRenderWindowDataStorageTreeModel::mimeTypes() const +{ + QStringList types = QAbstractItemModel::mimeTypes(); + types << QmitkMimeTypes::DataNodePtrs; + return types; +} + +QMimeData* QmitkRenderWindowDataStorageTreeModel::mimeData(const QModelIndexList& indexes) const +{ + QMimeData* mimeData = new QMimeData(); + QByteArray encodedData; + + QDataStream stream(&encodedData, QIODevice::WriteOnly); + + for (const auto& index : indexes) + { + if (index.isValid()) + { + auto dataNode = data(index, QmitkDataNodeRawPointerRole).value(); + stream << reinterpret_cast(dataNode); + } + } + + mimeData->setData(QmitkMimeTypes::DataNodePtrs, encodedData); + return mimeData; +} + +bool QmitkRenderWindowDataStorageTreeModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int /*row*/, int /*column*/, const QModelIndex& parent) +{ + if (m_BaseRenderer.IsExpired()) + { + return false; + } + + auto baseRenderer = m_BaseRenderer.Lock(); + + if (action == Qt::IgnoreAction) + { + return true; + } + + if (!data->hasFormat(QmitkMimeTypes::DataNodePtrs)) + { + return false; + } + + if (!parent.isValid()) + { + return false; + } + + int layer = -1; + auto dataNode = this->data(parent, QmitkDataNodeRawPointerRole).value(); + if (nullptr != dataNode) + { + dataNode->GetIntProperty("layer", layer, baseRenderer); + } + + auto dataNodeList = QmitkMimeTypes::ToDataNodePtrList(data); + for (const auto& dataNode : dataNodeList) + { + m_RenderWindowLayerController->MoveNodeToPosition(dataNode, layer, baseRenderer); + } + + ResetTree(); + UpdateModelData(); + AdjustLayerProperty(); + return true; +} + +void QmitkRenderWindowDataStorageTreeModel::SetControlledRenderer(mitk::RenderWindowLayerUtilities::RendererVector controlledRenderer) +{ + m_RenderWindowLayerController->SetControlledRenderer(controlledRenderer); + m_ControlledRenderer = controlledRenderer; + + ResetTree(); + if (m_DataStorage.IsExpired()) + { + return; + } + + auto dataStorage = m_DataStorage.Lock(); + + for (const auto& renderer : controlledRenderer) + { + if (nullptr == renderer) + { + continue; + } + + auto allDataNodes = dataStorage->GetAll(); + for (const auto& dataNode : *allDataNodes) + { + // add the node to each render window + mitk::RenderWindowLayerUtilities::SetRenderWindowProperties(dataNode, renderer); + } + } +} + +void QmitkRenderWindowDataStorageTreeModel::SetCurrentRenderer(mitk::BaseRenderer* baseRenderer) +{ + if (m_BaseRenderer == baseRenderer) + { + return; + } + + // base renderer changed + // reset tree to build a new renderer-specific item hierarchy + m_BaseRenderer = baseRenderer; + ResetTree(); + UpdateModelData(); +} + +mitk::BaseRenderer* QmitkRenderWindowDataStorageTreeModel::GetCurrentRenderer() const +{ + if (m_BaseRenderer.IsExpired()) + { + return nullptr; + } + + return m_BaseRenderer.Lock().GetPointer(); +} + +void QmitkRenderWindowDataStorageTreeModel::ResetTree() +{ + beginResetModel(); + if (nullptr != m_Root) + { + m_Root->Delete(); + } + endResetModel(); + + mitk::DataNode::Pointer rootDataNode = mitk::DataNode::New(); + rootDataNode->SetName("Data Storage"); + m_Root = new QmitkDataStorageTreeModelInternalItem(rootDataNode); +} + +void QmitkRenderWindowDataStorageTreeModel::UpdateModelData() +{ + if (!m_DataStorage.IsExpired()) + { + auto dataStorage = m_DataStorage.Lock(); + if (!m_BaseRenderer.IsExpired()) + { + auto baseRenderer = m_BaseRenderer.Lock(); + + mitk::NodePredicateAnd::Pointer combinedNodePredicate = mitk::RenderWindowLayerUtilities::GetRenderWindowPredicate(baseRenderer); + auto filteredDataNodes = dataStorage->GetSubset(combinedNodePredicate); + for (const auto& dataNode : *filteredDataNodes) + { + AddNodeInternal(dataNode, baseRenderer); + } + } + } +} + +void QmitkRenderWindowDataStorageTreeModel::AdjustLayerProperty() +{ + if (m_BaseRenderer.IsExpired()) + { + return; + } + + auto baseRenderer = m_BaseRenderer.Lock(); + + std::vector treeAsVector; + TreeToVector(m_Root, treeAsVector); + + int i = treeAsVector.size() - 1; + for (auto it = treeAsVector.begin(); it != treeAsVector.end(); ++it) + { + auto dataNode = (*it)->GetDataNode(); + dataNode->SetIntProperty("layer", i, baseRenderer); + --i; + } +} + +void QmitkRenderWindowDataStorageTreeModel::TreeToVector(QmitkDataStorageTreeModelInternalItem* parent, std::vector& treeAsVector) const +{ + QmitkDataStorageTreeModelInternalItem* item; + for (int i = 0; i < parent->GetChildCount(); ++i) + { + item = parent->GetChild(i); + TreeToVector(item, treeAsVector); + treeAsVector.push_back(item); + } +} + +void QmitkRenderWindowDataStorageTreeModel::AddNodeInternal(const mitk::DataNode* dataNode, const mitk::BaseRenderer* renderer) +{ + if (nullptr == dataNode + || m_DataStorage.IsExpired() + || nullptr != m_Root->Find(dataNode)) + { + return; + } + + // find out if we have a root node + auto parentItem = m_Root; + QModelIndex index; + auto parentDataNode = GetParentNode(dataNode); + + if (nullptr != parentDataNode) // no top level data node + { + parentItem = m_Root->Find(parentDataNode); + if (nullptr == parentItem) + { + // parent node not contained in the tree; add it + NodeAdded(parentDataNode); + parentItem = m_Root->Find(parentDataNode); + if (nullptr == parentItem) + { + // could not find and add the parent tree; abort + return; + } + } + + // get the index of this parent with the help of the grand parent + index = createIndex(parentItem->GetIndex(), 0, parentItem); + } + + int firstRowWithASiblingBelow = 0; + int nodeLayer = -1; + dataNode->GetIntProperty("layer", nodeLayer, renderer); + for (const auto& siblingItem : parentItem->GetChildren()) + { + int siblingLayer = -1; + auto siblingNode = siblingItem->GetDataNode(); + if (nullptr != siblingNode) + { + siblingNode->GetIntProperty("layer", siblingLayer, renderer); + } + if (nodeLayer > siblingLayer) + { + break; + } + ++firstRowWithASiblingBelow; + } + + beginInsertRows(index, firstRowWithASiblingBelow, firstRowWithASiblingBelow); + auto newNode = new QmitkDataStorageTreeModelInternalItem(const_cast(dataNode)); + parentItem->InsertChild(newNode, firstRowWithASiblingBelow); + endInsertRows(); +} + +void QmitkRenderWindowDataStorageTreeModel::RemoveNodeInternal(const mitk::DataNode* dataNode) +{ + if (nullptr == dataNode + || nullptr == m_Root) + { + return; + } + + auto item = m_Root->Find(dataNode); + if (nullptr == item) + { + return; + } + + auto parentItem = item->GetParent(); + auto parentIndex = GetIndexByItem(parentItem); + + auto children = item->GetChildren(); + beginRemoveRows(parentIndex, item->GetIndex(), item->GetIndex()); + parentItem->RemoveChild(item); + delete item; + endRemoveRows(); + + if (!children.empty()) + { + // rebuild tree because children could not be at the top level + ResetTree(); + UpdateModelData(); + } +} + +mitk::DataNode* QmitkRenderWindowDataStorageTreeModel::GetParentNode(const mitk::DataNode* node) const +{ + mitk::DataNode* dataNode = nullptr; + if (m_DataStorage.IsExpired()) + { + return dataNode; + } + + auto sources = m_DataStorage.Lock()->GetSources(node); + if (sources->empty()) + { + return dataNode; + } + + return sources->front(); +} + +QmitkDataStorageTreeModelInternalItem* QmitkRenderWindowDataStorageTreeModel::GetItemByIndex(const QModelIndex& index) const +{ + if (index.isValid()) + { + return static_cast(index.internalPointer()); + } + + return m_Root; +} + +QModelIndex QmitkRenderWindowDataStorageTreeModel::GetIndexByItem(QmitkDataStorageTreeModelInternalItem* item) const +{ + if (item == m_Root) + { + return QModelIndex(); + } + + return createIndex(item->GetIndex(), 0, item); +} diff --git a/Modules/SemanticRelations/CMakeLists.txt b/Modules/SemanticRelations/CMakeLists.txt index 72db95214f..aa6819b240 100644 --- a/Modules/SemanticRelations/CMakeLists.txt +++ b/Modules/SemanticRelations/CMakeLists.txt @@ -1,7 +1,7 @@ MITK_CREATE_MODULE( - DEPENDS MitkSceneSerializationBase MitkDICOMReader MitkMultilabel MitkPersistence + DEPENDS MitkSceneSerializationBase MitkDICOMReader MitkMultilabel MitkPersistence MitkImageStatisticsUI ) if(BUILD_TESTING) ADD_SUBDIRECTORY(Test) endif(BUILD_TESTING) diff --git a/Modules/SemanticRelations/include/mitkControlPointManager.h b/Modules/SemanticRelations/include/mitkControlPointManager.h index c1343ebd8f..d51ac37f9f 100644 --- a/Modules/SemanticRelations/include/mitkControlPointManager.h +++ b/Modules/SemanticRelations/include/mitkControlPointManager.h @@ -1,93 +1,123 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKCONTROLPOINTMANAGER_H #define MITKCONTROLPOINTMANAGER_H #include // semantic relations module #include "mitkSemanticTypes.h" // mitk core #include namespace mitk { /** * @brief Provides helper functions that are needed to work with control points. * * These functions help to generate new control points, check for overlapping / containing control points or provide functionality * to find a fitting control point or even extend an already existing control point. */ /** * @brief Generates a control point from a given data node. * The date is extracted from the data node by using the 'DICOMHelper::GetDICOMDateFromDataNode'-function. * * @param datanode A data node pointer, whose date should be included in the newly generated control point. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint GenerateControlPoint(const mitk::DataNode* datanode); /** * @brief Find and return a whole control point including its date given a specific control point UID. * - * @param controlPointUID The control point UID as string. - * @param allControlPoints All currently known control points of a specific case. + * @param caseID The current case identifier is defined by the given string. + * @param controlPointUID The control point UID as string. * * @return The control point with its UID and the date. */ - MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint GetControlPointByUID(const SemanticTypes::ID& controlPointUID, const SemanticTypes::ControlPointVector& allControlPoints); + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint GetControlPointByUID(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& controlPointUID); /** * @brief Returns an already existing control point from the given vector of control points. This existing control point has the * the same date (year, month, day) as the given single control point. * If no existing control point can be found an empty control point is returned. * - * @param controlPoint The control point to check for existence. - * @param allControlPoints The vector of already existing control points. + * @param caseID The current case identifier is defined by the given string. + * @param controlPoint The control point to check for existence. + * + * @return The existing control point. */ - MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint FindExistingControlPoint(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ControlPointVector& allControlPoints); + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint FindExistingControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); /** - * @brief Returns an already existing close control point from the given vector of control points. This closest control point has a date + * @brief Returns an already existing close control point from the given vector of control points. This closest control point has a * date that is within a certain distance-in-days to the given control point. * If no closest control point can be found within the distance threshold an empty control point is returned. * - * @param controlPoint The control point to check for distance. - * @param allControlPoints The vector of already existing control points. + * @param caseID The current case identifier is defined by the given string. + * @param controlPoint The control point to check for distance. + * + * @return The closest control point. */ - MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint FindClosestControlPoint(const SemanticTypes::ControlPoint& controlPoint, SemanticTypes::ControlPointVector& allControlPoints); + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint FindClosestControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); /** * @brief Returns the examination period to which the given control point belongs. * Each examination point holds a vector of control point UIDs so that the UID of the given control point can be compared against the UIDs of the vector. * An empty examination period is returned if, * - the given vector of examination periods is empty * - the examination periods do not contain any control point UIDs * - the UID of the given control point is not contained in any examination period * - * @param controlPoint The control point of which the examination period should be found. - * @param allExaminationPeriods All currently known examination periods of a specific case. + * @param caseID The current case identifier is defined by the given string. + * @param controlPoint The control point of which the examination period should be found. + * + * @return The examination period that contains the given control point. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ExaminationPeriod FindContainingExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); + /** + * @brief Return the examination period to which the given data node belongs. + * The control point is used to find an already existing or the closest control point in the semantic relations storage. + * If such a control point is found, the 'FindClosestControlPoint'-function with this control point as an argument is used + * to actually find the corresponding examination period. + * + * @param caseID The current case identifier is defined by the given string. + * @param controlPoint The control point of which the examination period should be found. + * + * @return The examination period that fits the given data node. */ - MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ExaminationPeriod FindExaminationPeriod(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriodVector& allExaminationPeriods); + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ExaminationPeriod FindFittingExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); + /** + * @brief Return the examination period to which the given data node belongs. + * The DICOM date of the data node is used to find an already existing or the closest control point in the semantic relations storage. + * If such a control point is found, the 'FindFittingExaminationPeriod'-function with this control point as an argument is used + * to actually find the corresponding examination period. + * + * @param datanode A data node pointer, whose date should be included in the newly generated control point. + * + * @return The examination period that contains the given data node. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ExaminationPeriod FindFittingExaminationPeriod(const DataNode* dataNode); /** * @brief Sort the given vector of examination periods. * Each examination period has a vector of control point UIDs (stored in chronological order). * The examination periods can be sorted by comparing the first control points of the examination periods. * - * @param allExaminationPeriods All currently known examination periods of a specific case. - * @param allControlPoints All currently known control points of a specific case. + * @param caseID The current case identifier is defined by the given string. + * @param allExaminationPeriods The examination periods to sort. */ - MITKSEMANTICRELATIONS_EXPORT void SortExaminationPeriods(SemanticTypes::ExaminationPeriodVector& allExaminationPeriods, const SemanticTypes::ControlPointVector& allControlPoints); + MITKSEMANTICRELATIONS_EXPORT void SortAllExaminationPeriods(const SemanticTypes::CaseID& caseID, SemanticTypes::ExaminationPeriodVector& allExaminationPeriods); + } // namespace mitk #endif // MITKCONTROLPOINTMANAGER_H diff --git a/Modules/SemanticRelations/include/mitkISemanticRelationsObservable.h b/Modules/SemanticRelations/include/mitkISemanticRelationsObservable.h index 08e50b37ac..a9c78e0f07 100644 --- a/Modules/SemanticRelations/include/mitkISemanticRelationsObservable.h +++ b/Modules/SemanticRelations/include/mitkISemanticRelationsObservable.h @@ -1,57 +1,55 @@ /*=================================================================== 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 MITKISEMANTICRELATIONSOBSERVABLE_H #define MITKISEMANTICRELATIONSOBSERVABLE_H #include "mitkISemanticRelationsObserver.h" -#include "mitkSemanticTypes.h" - namespace mitk { /* * @brief This interface declares three functions each observable subject has to implement * in order to be observed in the 'Observer pattern' sense. * The concrete observable class has to store its observer. */ class ISemanticRelationsObservable { public: /* * @brief Adds the given concrete observer to a container that holds all currently registered observer. * - * @par observer The concrete observer to register. + * @param observer The concrete observer to register. */ virtual void AddObserver(ISemanticRelationsObserver* observer) = 0; /* * @brief Removes the given concrete observer from the container that holds all currently registered observer. * - * @par observer The concrete observer to unregister. + * @param observer The concrete observer to unregister. */ virtual void RemoveObserver(ISemanticRelationsObserver* observer) = 0; /* * @brief Updates all concrete observer in the container that holds all currently registered observer. * The caseID can be used to only update the observer, if the caseID fulfills a certain condition. * - * @par caseID A caseID that identifies the currently active patient / case. + * @param caseID A caseID that identifies the currently active patient / case. */ - virtual void NotifyObserver(const mitk::SemanticTypes::CaseID& caseID) const = 0; + virtual void NotifyObserver(const SemanticTypes::CaseID& caseID) const = 0; }; // class ISemanticRelationsObservable } // namespace mitk #endif // MITKISEMANTICRELATIONSOBSERVABLE_H diff --git a/Modules/SemanticRelations/include/mitkISemanticRelationsObserver.h b/Modules/SemanticRelations/include/mitkISemanticRelationsObserver.h index 3fd8651456..d7df63e1f5 100644 --- a/Modules/SemanticRelations/include/mitkISemanticRelationsObserver.h +++ b/Modules/SemanticRelations/include/mitkISemanticRelationsObserver.h @@ -1,43 +1,43 @@ /*=================================================================== 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 MITKISEMANTICRELATIONSOBSERVER_H #define MITKISEMANTICRELATIONSOBSERVER_H #include "mitkSemanticTypes.h" namespace mitk { /* * @brief This interface declares a functions each observer has to implement * in order to be notified in the 'Observer pattern' sense. */ class ISemanticRelationsObserver { public: /* * @brief Updates the concrete observer. * The caseID can be used to get access to a certain patient (case), * whose data should be used for updating. * - * @par caseID The current case ID to identify the currently active patient / case. + * @param caseID The current case ID to identify the currently active patient / case. */ virtual void Update(const mitk::SemanticTypes::CaseID& caseID) = 0; }; // class ISemanticRelationsObserver } // namespace mitk #endif // MITKISEMANTICRELATIONSOBSERVER_H diff --git a/Modules/SemanticRelations/include/mitkLesionData.h b/Modules/SemanticRelations/include/mitkLesionData.h index 7f1beec103..a2818d3422 100644 --- a/Modules/SemanticRelations/include/mitkLesionData.h +++ b/Modules/SemanticRelations/include/mitkLesionData.h @@ -1,63 +1,68 @@ /*=================================================================== 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/SemanticRelations/include/mitkLesionManager.h b/Modules/SemanticRelations/include/mitkLesionManager.h index 790382bc0e..976d8f43de 100644 --- a/Modules/SemanticRelations/include/mitkLesionManager.h +++ b/Modules/SemanticRelations/include/mitkLesionManager.h @@ -1,78 +1,75 @@ /*=================================================================== 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 #include // semantic relations module -#include "mitkSemanticTypes.h" #include "mitkLesionData.h" -// 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. +* to generate new and 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); /** - * @brief Generate and store additional lesion data such as lesion presence and lesion volume for each control point. + * @brief Compute and store lesion presence 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. */ - MITKSEMANTICRELATIONS_EXPORT void GenerateAdditionalLesionData(LesionData& lesionData, const SemanticTypes::CaseID& caseID); + MITKSEMANTICRELATIONS_EXPORT void ComputeLesionPresence(LesionData& lesionData, const SemanticTypes::CaseID& caseID); + } // namespace mitk #endif // MITKLESIONMANAGER_H diff --git a/Modules/SemanticRelations/include/mitkNodePredicates.h b/Modules/SemanticRelations/include/mitkNodePredicates.h index 554653b21e..201e9e3838 100644 --- a/Modules/SemanticRelations/include/mitkNodePredicates.h +++ b/Modules/SemanticRelations/include/mitkNodePredicates.h @@ -1,47 +1,47 @@ /*=================================================================== 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 MITKNODEPREDICATES_H #define MITKNODEPREDICATES_H #include // mitk core #include namespace mitk { namespace NodePredicates { /* * @brief Helper function to get a node predicate that can be used to filter images. * * The images are of type 'mitk::Image' but must not be 'helper objects' or 'segmentation nodes'. * For the definition of 'segmentation nodes' see 'GetSegmentationPredicate'. */ - MITKSEMANTICRELATIONS_EXPORT mitk::NodePredicateAnd::Pointer GetImagePredicate(); + MITKSEMANTICRELATIONS_EXPORT NodePredicateAnd::Pointer GetImagePredicate(); /* * @brief Helper function to get a node predicate that can be used to filter segmentations. * * The segmentations are of type 'mitk::LabelSetImage' or nodes that have their 'binary' property set to true. * Segmentations must not be 'helper objects'. */ - MITKSEMANTICRELATIONS_EXPORT mitk::NodePredicateAnd::Pointer GetSegmentationPredicate(); + MITKSEMANTICRELATIONS_EXPORT NodePredicateAnd::Pointer GetSegmentationPredicate(); } // namespace NodePredicates } // namespace mitk #endif // MITKNODEPREDICATES_H diff --git a/Modules/SemanticRelations/include/mitkRelationStorage.h b/Modules/SemanticRelations/include/mitkRelationStorage.h index 08f6aecc12..0bfc45cbd3 100644 --- a/Modules/SemanticRelations/include/mitkRelationStorage.h +++ b/Modules/SemanticRelations/include/mitkRelationStorage.h @@ -1,82 +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. ===================================================================*/ #ifndef MITKRELATIONSTORAGE_H #define MITKRELATIONSTORAGE_H #include // semantic relations module #include "mitkSemanticTypes.h" namespace mitk { namespace RelationStorage { MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionVector GetAllLesionsOfCase(const SemanticTypes::CaseID& caseID); SemanticTypes::Lesion GetLesionOfSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID); MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPointVector GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID); SemanticTypes::ControlPoint GetControlPointOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID); MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ExaminationPeriodVector GetAllExaminationPeriodsOfCase(const SemanticTypes::CaseID& caseID); MITKSEMANTICRELATIONS_EXPORT SemanticTypes::InformationTypeVector GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID); SemanticTypes::InformationType GetInformationTypeOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID); MITKSEMANTICRELATIONS_EXPORT SemanticTypes::IDVector GetAllImageIDsOfCase(const SemanticTypes::CaseID& caseID); MITKSEMANTICRELATIONS_EXPORT SemanticTypes::IDVector GetAllImageIDsOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); MITKSEMANTICRELATIONS_EXPORT SemanticTypes::IDVector GetAllImageIDsOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType); MITKSEMANTICRELATIONS_EXPORT SemanticTypes::IDVector GetAllSegmentationIDsOfCase(const SemanticTypes::CaseID& caseID); MITKSEMANTICRELATIONS_EXPORT SemanticTypes::IDVector GetAllSegmentationIDsOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID); MITKSEMANTICRELATIONS_EXPORT SemanticTypes::IDVector GetAllSegmentationIDsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); SemanticTypes::ID GetImageIDOfSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID); MITKSEMANTICRELATIONS_EXPORT std::vector GetAllCaseIDs(); MITKSEMANTICRELATIONS_EXPORT bool InstanceExists(const SemanticTypes::CaseID& caseID); void AddCase(const SemanticTypes::CaseID& caseID); void AddImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID); void RemoveImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID); void AddSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID, const SemanticTypes::ID& parentID); void RemoveSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID); 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 LinkImageToControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID, const SemanticTypes::ControlPoint& controlPoint); void UnlinkImageFromControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID); void RemoveControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); void AddExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod); + MITKSEMANTICRELATIONS_EXPORT void RenameExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod); void AddControlPointToExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod& examinationPeriod); void RemoveControlPointFromExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod& examinationPeriod); void RemoveExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod); 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 RemoveInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType); } // namespace RelationStorage } // namespace mitk #endif // MITKRELATIONSTORAGE_H diff --git a/Modules/SemanticRelations/include/mitkSemanticRelationsDataStorageAccess.h b/Modules/SemanticRelations/include/mitkSemanticRelationsDataStorageAccess.h index 07b2854aaf..e02de8ba66 100644 --- a/Modules/SemanticRelations/include/mitkSemanticRelationsDataStorageAccess.h +++ b/Modules/SemanticRelations/include/mitkSemanticRelationsDataStorageAccess.h @@ -1,140 +1,187 @@ /*=================================================================== 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 MITKSEMANTICRELATIONSDATASTORAGEACCESS_H #define MITKSEMANTICRELATIONSDATASTORAGEACCESS_H #include // semantic relations module #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 order for most functions to work the case ID has to be used as a parameter. * If not, these functions do nothing. */ class MITKSEMANTICRELATIONS_EXPORT SemanticRelationsDataStorageAccess { public: using DataNodeVector = std::vector; SemanticRelationsDataStorageAccess(DataStorage* dataStorage); - ~SemanticRelationsDataStorageAccess(); /************************************************************************/ /* functions to get instances / attributes */ /************************************************************************/ /** * @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 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. */ 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 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. + * @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 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 specified by the given vector of image IDs. + * + * @pre The data storage member has to be valid (!nullptr). + * @throw SemanticRelationException, if the data storage member is invalid (==nullptr). + * + * @param imageIDs A vector of image IDs that represent the images in the data storage. + * + * @return A vector of data nodes representing images. + */ + DataNodeVector GetAllImagesByID(const SemanticTypes::IDVector& imageIDs) 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. + * @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 Return a vector of all image nodes that are defined with the given information type and with the given control point. + * @brief Return a vector of all image nodes that are defined with the given control point and the given information type. * * @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. + * + * @return A vector of image nodes that are defined with the given control point and the given information type. */ 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. + * @brief Return a vector of all image nodes that are defined with the given information type and the given examination period. + * The function uses the 'SemanticRelationsInference::GetAllImageIDsOfExaminationPeriod'-function to retrieve the imageIDs of the examination period and + * then compares the information type of all these images against the given information type. + * + * @param informationType An information type that identifies the corresponding information type instance. + * @param examinationPeriod An examination period that identifies the corresponding examination period 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::InformationType& informationType, const SemanticTypes::ExaminationPeriod& examinationPeriod) const; + /** + * @brief Return a vector of all segmentation nodes that are defined with the given control point and the given information type. * 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. + * + * @return A vector of segmentation nodes that are defined with the given control point and the given information type. */ DataNodeVector GetAllSpecificSegmentations(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const; + /** + * @brief Return the single segmentation node that is defined with the given information type, the given control point and is representing the given lesion. + * The function uses the 'GetAllSpecificSegmentations'-function to retrieve the specific segmentations and then checks for the represented lesion. + * + * @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). + * The lesion has to exist for the given case. + * @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') or + * if the lesion does not exist for the given case (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. + * @param lesion A lesion with a UID that identifies the corresponding lesion instance. + * + * @return A single segmentation node that is defined with the given information type, the given control point and is representing the given lesion. + */ + DataNode::Pointer GetSpecificSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, + const SemanticTypes::InformationType& informationType, const SemanticTypes::Lesion& lesion) const; private: WeakPointer m_DataStorage; }; } // namespace mitk #endif // MITKSEMANTICRELATIONSDATASTORAGEACCESS_H diff --git a/Modules/SemanticRelations/include/mitkSemanticRelationsInference.h b/Modules/SemanticRelations/include/mitkSemanticRelationsInference.h index fa9faeda4d..404edad885 100644 --- a/Modules/SemanticRelations/include/mitkSemanticRelationsInference.h +++ b/Modules/SemanticRelations/include/mitkSemanticRelationsInference.h @@ -1,292 +1,351 @@ /*=================================================================== 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 MITKSEMANTICRELATIONSINFERENCE_H #define MITKSEMANTICRELATIONSINFERENCE_H #include // semantic relations module #include "mitkSemanticTypes.h" // mitk core #include namespace mitk { /** * @brief The API provides functions to query 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 order for most functions to work the case ID has to be used as a parameter. * If not, these functions do nothing. */ namespace SemanticRelationsInference { /************************************************************************/ /* functions to get instances / attributes */ /************************************************************************/ /** * @brief Return a vector of lesion classes that are currently available for the given case. * * @param caseID The current case identifier is defined by the given string. + * * @return A vector of lesion classes. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionClassVector GetAllLesionClassesOfCase(const SemanticTypes::CaseID& caseID); /** * @brief Return the lesion that is defined by the given segmentation. * * @pre The given segmentation data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given segmentation 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. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::Lesion GetLesionOfSegmentation(const DataNode* segmentationNode); /** * @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 segmentations 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 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 A vector of lesions. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionVector GetAllLesionsOfImage(const DataNode* imageNode); /** * @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. + * + * @return A vector of lesions. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionVector GetAllLesionsOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); /** + * @brief Returns a vector of all lesions 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) lesion: + * Only those lesions are returned for which the image of the associated segmentation is of the given information type. + * If the information type instance does not exist, an empty vector is returned. + * + * @return A vector of lesions. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionVector GetAllLesionsOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType); + /** + * @brief Returns a vector of all lesions that are valid for the given case, given a specific control point and a specific information type. + * + * @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. + * @param informationType A specific information type which has to be available at a returned (found) lesion: + * Only those lesions are returned for which the image of the associated segmentation is of the given information type. + * If the information type instance does not exist, an empty vector is returned. + * + * @return A vector of lesions. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionVector GetAllSpecificLesions(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType); + /** * @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. */ MITKSEMANTICRELATIONS_EXPORT bool IsRepresentingALesion(const DataNode* segmentationNode); /** * @brief Check if the segmentation identified by the given segmentation ID refers to an existing lesion instance. * This function can be used before calling 'GetRepresentedLesion' in order to avoid a possible exception. * * @param caseID The current case identifier is defined by the given string. * @param segmentationID The segmentation node identifier is defined by the given string. + * * @return True, if the segmentation refers to an existing lesion; false otherwise. */ MITKSEMANTICRELATIONS_EXPORT bool IsRepresentingALesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID); /** * @brief Check if the given lesion is present on the given data node. * The function receives the case- and the node-ID from the DICOM tags of the node itself. * It uses node predicates to decide if the node is an image or a segmentation node. * * @param lesion A lesion with a UID that identifies the corresponding lesion instance. * @param dataNode A data node to check. + * * @return True, if the lesion is present on the data node; false otherwise. */ MITKSEMANTICRELATIONS_EXPORT bool IsLesionPresent(const SemanticTypes::Lesion& lesion, const DataNode* dataNode); /** * @brief Check if the given lesion is related to the image identified by the given image ID. * Each lesion is represented by a segmentation which is connected to its parent image. * * @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 imageID The image node identifier is defined by the given string. + * * @return True, if the lesion is related to image identified by the given image ID; false otherwise. */ MITKSEMANTICRELATIONS_EXPORT bool IsLesionPresentOnImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ID& imageID); /** * @brief Check if the given lesion is present on the segmentation identified by the given segmentation ID. * * @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 segmentationID The segmentation node identifier is defined by the given string. + * * @return True, if the lesion is present on the segmentation identified by the given segmentation ID; false otherwise. */ MITKSEMANTICRELATIONS_EXPORT bool IsLesionPresentOnSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ID& segmentationID); /** * @brief Check if the given lesion is present at the given control point. * * @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 lesion is present at the given control point; false otherwise. */ MITKSEMANTICRELATIONS_EXPORT bool IsLesionPresentAtControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ControlPoint& controlPoint); /** * @brief Check if the given data node exists in the relation storage. * The function receives the case- and the node-ID from the DICOM tags of the node itself. * It uses node predicates to decide if the node is an image or a segmentation node and searches * through the corresponding relations. * * @param dataNode A data node to check. + * * @return True, if the data node exists; false otherwise. */ MITKSEMANTICRELATIONS_EXPORT bool InstanceExists(const DataNode* dataNode); /** * @brief Check if the given lesion instance exists. * 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. */ MITKSEMANTICRELATIONS_EXPORT bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); /** * @brief Return a vector of all image IDs that identify images that are related to the given lesion. * Each lesion is represented by a segmentation which is connected to its parent image. * If the lesion is not represented 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 IDs identifying images that identify images that are related to the given lesion. + * + * @return A vector of IDs identifying images that are related to the given lesion. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::IDVector GetAllImageIDsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); /** + * @brief Return a vector of all image IDs that identify images that are related to the given examination period. + * If the examination period is not used by and image, an empty vector is returned. + * + * @pre The UID of the examination period has to exist for an examination period instance. + * @throw SemanticRelationException, if UID of the examination period does not exist for an examination period instance (this can be checked via 'InstanceExists'). + * + * @param caseID The current case identifier is defined by the given string. + * @param examinationPeriod An examination period with a UID that identifies the corresponding examination period instance. + * + * @return A vector of IDs identifying images that are related to the given examination period. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::IDVector GetAllImageIDsOfExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod); + /** * @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 image data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given image 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. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint GetControlPointOfImage(const DataNode* dataNode); /** * @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. + * @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. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPointVector GetAllControlPointsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); /** * @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. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPointVector GetAllControlPointsOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType); /** * @brief Check if the given control point instance exists. * 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. */ MITKSEMANTICRELATIONS_EXPORT bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); /** * @brief Check if the given examination period instance exists. * This function can be used before calling 'AddExaminationPeriod' in order to avoid a possible exception. * * @param caseID The current case identifier is defined by the given string. * @param examinationPeriod An examination period with a UID that identifies the corresponding examination period instance. + * * @return True, if the examination period instance exists; false otherwise. */ MITKSEMANTICRELATIONS_EXPORT bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod); /** * @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 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. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::InformationType GetInformationTypeOfImage(const DataNode* imageNode); /** * @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. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::InformationTypeVector GetAllInformationTypesOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); /** * @brief Check if the given information type exists. * * @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. */ MITKSEMANTICRELATIONS_EXPORT bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType); /** * @brief Determine if the given information type contains images, which are connected to segmentations that represent the given lesion. * If the lesion or the information type 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 informationType An information type that identifies the corresponding information type instance. * * @return True, if the given information type contains data that is related to the given lesion; false otherwise. */ MITKSEMANTICRELATIONS_EXPORT bool SpecificImageExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::InformationType& informationType); /** * @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. */ MITKSEMANTICRELATIONS_EXPORT bool SpecificImageExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ControlPoint& controlPoint); /** * @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. */ MITKSEMANTICRELATIONS_EXPORT bool SpecificImageExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType, const SemanticTypes::ControlPoint& controlPoint); } // namespace SemanticRelationsInference } // namespace mitk #endif // MITKSEMANTICRELATIONSINFERENCE_H diff --git a/Modules/SemanticRelations/include/mitkSemanticRelationsIntegration.h b/Modules/SemanticRelations/include/mitkSemanticRelationsIntegration.h index f770210fda..0d4426c3d6 100644 --- a/Modules/SemanticRelations/include/mitkSemanticRelationsIntegration.h +++ b/Modules/SemanticRelations/include/mitkSemanticRelationsIntegration.h @@ -1,316 +1,326 @@ /*=================================================================== 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 MITKSEMANTICRELATIONSINTEGRATION_H #define MITKSEMANTICRELATIONSINTEGRATION_H #include // semantic relations module #include "mitkISemanticRelationsObservable.h" #include "mitkISemanticRelationsObserver.h" #include "mitkSemanticTypes.h" // mitk core #include namespace mitk { /** * @brief The API provides functions to 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 order for most functions to work the case ID has to be used as a parameter. * If not, these functions do nothing. * * The class implements the ISemanticRelationsObservable interface to allow observers to * be informed about changes in the semantic relation storage. */ class MITKSEMANTICRELATIONS_EXPORT SemanticRelationsIntegration : public ISemanticRelationsObservable { public: /************************************************************************/ /* 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; virtual ~SemanticRelationsIntegration() {} /************************************************************************/ /* 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 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 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. 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 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 segmentation 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 segmentation 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 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 segmentation 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 SemanticRelationException, if the given segmentation data node is invalid (==nullptr). * * @param segmentationNode The segmentation identifier is extracted from the given segmentation 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 SemanticRelationException, if the given segmentation data node is invalid (==nullptr). * * @param segmentationNode The segmentation identifier is extracted from the given segmentation data node. The segmentation node has DICOM information from its parent node. */ void RemoveSegmentation(const DataNode* segmentationNode); /** * @brief Set the control point for the given image. * * @pre The given image data node has to be valid (!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. * @param controlPoint The control point instance which is used for the given image. */ void SetControlPointOfImage(const DataNode* imageNode, const SemanticTypes::ControlPoint& controlPoint); /** * @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 image. * This function combines adding a control point and linking it, since a control point with no associated data is not allowed. * * @pre The given image data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given image 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 image data node (if parameter 'checkConsistence = true'). * @throw SemanticRelationException, if the given control point does not contain the date of the given image data node and 'checkConsistence = true' (this can be checked via 'ControlPointManager::InsideControlPoint'). * * @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. * @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 image data node actually lies inside the control point to link. */ void AddControlPointAndLinkImage(const DataNode* imageNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence = true); /** * @brief Link the given image to an already existing control point. * * @pre The given image data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given image 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 image data node (if parameter 'checkConsistence = true'). * @throw SemanticRelationException, if the given control point does not contain the date of the given image data node and 'checkConsistence = true' (this can be checked via 'ControlPointManager::InsideControlPoint'). * * @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. * @param controlPoint The control point instance to link. * @param checkConsistence If true, the function checks, whether the date of the image data node actually lies inside the control point to link. */ void LinkImageToControlPoint(const DataNode* imageNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence = true); /** * @brief Unlink the given image from the linked control point. * If an image is unlinked from a control point, the function needs to check whether the control point is still linked to any other image: * - if not, the control point instance will be removed (has to be removed since a control point with no associated image 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 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 UnlinkImageFromControlPoint(const DataNode* imageNode); /** * @brief Add an examination period instance to the set of already existing examination periods - with no connection to a specific control point. * * @pre The UID of the examination period must not already exist for an examination period instance. * @throw SemanticRelationException, if the UID of the examination period already exists for a examination period instance (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param examinationPeriod The examination period to add. */ void AddExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod); /** + * @brief Rename an already existing examination period instance. + * + * @pre The UID of the examination period has to exist for an examination period instance. + * @throw SemanticRelationException, if the UID of the examination period does not exist for an examination period instance (this can be checked via 'InstanceExists'). + * + * @param caseID The current case identifier is defined by the given string. + * @param lesion The examination period instance that renames an existing examination period. + */ + void RenameExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod); + /** * @brief Add a control point to the vector of control point UIDs of an existing examination period. * * @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 UID of the examination period must not already exist for an examination period instance. * @throw SemanticRelationException, if the UID of the examination period already exists for a examination period instance (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param controlPoint The control point instance to add to the examination period. * @param examinationPeriod The examination period to which the control point should be added. */ void AddControlPointToExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod& examinationPeriod); /** * @brief Set (and possibly overwrite) the information type of the given image. * An already associated information type might be removed if is not referenced by any other image: * * @pre The given image data node has to be valid (!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 image data node, which contains DICOM information about the case. * @param informationType An information type that identifies the corresponding information type instance. */ void SetInformationType(const DataNode* imageNode, const SemanticTypes::InformationType& informationType); /** * @brief Set the information type of the given image. * * @pre The given image data node has to be valid (!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 image 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 SemanticRelationException, if the given image data node is invalid (==nullptr). * * @param imageNode The current case identifier is extracted from the given image data node, which contains DICOM information about the case. */ void RemoveInformationTypeFromImage(const DataNode* imageNode); private: /** * @brief A vector that stores the currently registered observer of this observable subject. */ static std::vector m_ObserverVector; /** * @brief The class 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 Remove all control points from the storage that are not referenced by any image anymore. * This might happen if an image has been removed (and unlinked from the corresponding control point) * or if the user sets a new control point for an image manually in the GUI. * * @param caseID The current case identifier is defined by the given string. */ void ClearControlPoints(const SemanticTypes::CaseID& caseID); }; } // namespace mitk #endif // MITKSEMANTICRELATIONSINTEGRATION_H diff --git a/Modules/SemanticRelations/src/mitkControlPointManager.cpp b/Modules/SemanticRelations/src/mitkControlPointManager.cpp index 98f1486379..01c041f871 100644 --- a/Modules/SemanticRelations/src/mitkControlPointManager.cpp +++ b/Modules/SemanticRelations/src/mitkControlPointManager.cpp @@ -1,179 +1,228 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // semantic relations module #include "mitkControlPointManager.h" #include "mitkDICOMHelper.h" +#include "mitkRelationStorage.h" #include "mitkSemanticRelationException.h" #include "mitkUIDGeneratorBoost.h" // mitk core #include mitk::SemanticTypes::ControlPoint mitk::GenerateControlPoint(const DataNode* datanode) { SemanticTypes::ControlPoint controlPoint; try { controlPoint = GetDICOMDateFromDataNode(datanode); } catch (SemanticRelationException& e) { - mitkReThrow(e) << "Cannot generate a control point from the DICOM tag of the given data node"; + mitkReThrow(e) << "Cannot generate a control point from the DICOM tag of the given data node."; } controlPoint.UID = UIDGeneratorBoost::GenerateUID(); return controlPoint; } -mitk::SemanticTypes::ControlPoint mitk::GetControlPointByUID(const SemanticTypes::ID& controlPointUID, const std::vector& allControlPoints) +mitk::SemanticTypes::ControlPoint mitk::GetControlPointByUID(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& controlPointUID) { auto lambda = [&controlPointUID](const SemanticTypes::ControlPoint& currentControlPoint) { return currentControlPoint.UID == controlPointUID; }; + SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); const auto existingControlPoint = std::find_if(allControlPoints.begin(), allControlPoints.end(), lambda); mitk::SemanticTypes::ControlPoint controlPoint; if (existingControlPoint != allControlPoints.end()) { controlPoint = *existingControlPoint; } return controlPoint; } -mitk::SemanticTypes::ControlPoint mitk::FindExistingControlPoint(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ControlPointVector& allControlPoints) +mitk::SemanticTypes::ControlPoint mitk::FindExistingControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { + SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); for (const auto& currentControlPoint : allControlPoints) { if (controlPoint.date == currentControlPoint.date) { return currentControlPoint; } } return SemanticTypes::ControlPoint(); } -mitk::SemanticTypes::ControlPoint mitk::FindClosestControlPoint(const SemanticTypes::ControlPoint& controlPoint, SemanticTypes::ControlPointVector& allControlPoints) +mitk::SemanticTypes::ControlPoint mitk::FindClosestControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { + SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); if (allControlPoints.empty()) { return SemanticTypes::ControlPoint(); } // sort the vector of control points for easier lookup std::sort(allControlPoints.begin(), allControlPoints.end()); // new control point does not match an existing control point // check if the control point is close to an already existing control point - std::vector::const_iterator it; + SemanticTypes::ControlPointVector::const_iterator it; for (it = allControlPoints.begin(); it != allControlPoints.end(); ++it) { if (controlPoint.date < it->date) { break; } } SemanticTypes::ControlPoint nextControlPoint; SemanticTypes::ControlPoint previousControlPoint; if (it == allControlPoints.begin()) { // new date is smaller ("older") than the smallest already existing control point nextControlPoint = *it; } else if (it != allControlPoints.end()) { // new date is greater ("newer") than an already existing control point, // but smaller ("older") than another already existing control point nextControlPoint = *it; previousControlPoint = *(--it); } else { // new date is greater ("newer") than the greatest already existing control point previousControlPoint = *(--it); } // test distance to next and previous time period double distanceToNextExaminationPeriod = nextControlPoint.DistanceInDays(controlPoint); double distanceToPreviousExaminationPeriod = previousControlPoint.DistanceInDays(controlPoint); SemanticTypes::ControlPoint closestControlPoint; int closestDistanceInDays = 0; if (distanceToNextExaminationPeriod < distanceToPreviousExaminationPeriod) { // control point is closer to the next control point closestControlPoint = nextControlPoint; closestDistanceInDays = distanceToNextExaminationPeriod; } else { // control point is closer to the previous control point closestControlPoint = previousControlPoint; closestDistanceInDays = distanceToPreviousExaminationPeriod; } int THRESHOLD_DISTANCE_IN_DAYS = 30; if (closestDistanceInDays <= THRESHOLD_DISTANCE_IN_DAYS) { return closestControlPoint; } return SemanticTypes::ControlPoint(); } -mitk::SemanticTypes::ExaminationPeriod mitk::FindExaminationPeriod(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriodVector& allExaminationPeriods) +mitk::SemanticTypes::ExaminationPeriod mitk::FindContainingExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { + SemanticTypes::ExaminationPeriodVector allExaminationPeriods = RelationStorage::GetAllExaminationPeriodsOfCase(caseID); for (const auto& examinationPeriod : allExaminationPeriods) { for (const auto& UID : examinationPeriod.controlPointUIDs) { if (controlPoint.UID == UID) { return examinationPeriod; } } } return SemanticTypes::ExaminationPeriod(); } -void mitk::SortExaminationPeriods(SemanticTypes::ExaminationPeriodVector& allExaminationPeriods, const SemanticTypes::ControlPointVector& allControlPoints) +mitk::SemanticTypes::ExaminationPeriod mitk::FindFittingExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { - auto lambda = [allControlPoints](const SemanticTypes::ExaminationPeriod& leftExaminationPeriod, const SemanticTypes::ExaminationPeriod& rightExaminationPeriod) + SemanticTypes::ExaminationPeriod specificExaminationPeriod; + SemanticTypes::ControlPoint specificControlPoint; + // find the closest control point + SemanticTypes::ControlPoint existingControlPoint = FindExistingControlPoint(caseID, controlPoint); + if (!existingControlPoint.UID.empty()) + { + specificControlPoint = existingControlPoint; + } + else + { + auto closestControlPoint = FindClosestControlPoint(caseID, controlPoint); + if (!closestControlPoint.UID.empty()) + { + specificControlPoint = closestControlPoint; + } + } + + // find the containing examination period + return FindContainingExaminationPeriod(caseID, specificControlPoint); +} + +mitk::SemanticTypes::ExaminationPeriod mitk::FindFittingExaminationPeriod(const DataNode* dataNode) +{ + SemanticTypes::CaseID caseID = ""; + SemanticTypes::ControlPoint controlPoint; + try + { + caseID = GetCaseIDFromDataNode(dataNode); + controlPoint = GetDICOMDateFromDataNode(dataNode); + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot find an examination period."; + } + + return FindFittingExaminationPeriod(caseID, controlPoint); +} + +void mitk::SortAllExaminationPeriods(const SemanticTypes::CaseID& caseID, SemanticTypes::ExaminationPeriodVector& allExaminationPeriods) +{ + SemanticTypes::ControlPointVector controlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); + // sort the vector of control points for the timeline + std::sort(controlPoints.begin(), controlPoints.end()); + + auto lambda = [&caseID](const SemanticTypes::ExaminationPeriod& leftExaminationPeriod, const SemanticTypes::ExaminationPeriod& rightExaminationPeriod) { if (leftExaminationPeriod.controlPointUIDs.empty()) { return true; } if (rightExaminationPeriod.controlPointUIDs.empty()) { return false; } const auto leftUID = leftExaminationPeriod.controlPointUIDs.front(); const auto rightUID = rightExaminationPeriod.controlPointUIDs.front(); - const auto& leftControlPoint = GetControlPointByUID(leftUID, allControlPoints); - const auto& rightControlPoint = GetControlPointByUID(rightUID, allControlPoints); + const auto& leftControlPoint = GetControlPointByUID(caseID, leftUID); + const auto& rightControlPoint = GetControlPointByUID(caseID, rightUID); return leftControlPoint.date < rightControlPoint.date; }; std::sort(allExaminationPeriods.begin(), allExaminationPeriods.end(), lambda); } diff --git a/Modules/SemanticRelations/src/mitkDICOMHelper.cpp b/Modules/SemanticRelations/src/mitkDICOMHelper.cpp index 7be974ef13..21e2f1fc21 100644 --- a/Modules/SemanticRelations/src/mitkDICOMHelper.cpp +++ b/Modules/SemanticRelations/src/mitkDICOMHelper.cpp @@ -1,190 +1,190 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // semantic relations module #include "mitkDICOMHelper.h" #include "mitkSemanticRelationException.h" #include "mitkUIDGeneratorBoost.h" // mitk core #include // c++ #include mitk::SemanticTypes::ControlPoint GetControlPointFromString(const std::string& dateAsString) { // date expected to be YYYYMMDD (8 characters) if (dateAsString.size() != 8) { // string does not represent a DICOM date mitkThrowException(mitk::SemanticRelationException) << "Not a valid DICOM date format."; } mitk::SemanticTypes::ControlPoint controlPoint; controlPoint.SetDateFromString(dateAsString); return controlPoint; } std::string mitk::GetCaseIDDICOMProperty() { // extract suitable DICOM tag to use as the case id // two alternatives can be used: // - DICOM tag "0x0010, 0x0010" is PatientName // - DICOM tag "0x0010, 0x0020" is PatientID // in the current implementation the PatientName (0x0010, 0x0010) is used return GeneratePropertyNameForDICOMTag(0x0010, 0x0010); } std::string mitk::GetNodeIDDICOMProperty() { // extract suitable DICOM tag to use as the data node id // DICOM tag "0x0020, 0x000e" is SeriesInstanceUID return GeneratePropertyNameForDICOMTag(0x0020, 0x000e); } std::string mitk::GetDateDICOMProperty() { // extract suitable DICOM tag to use as the data node id // DICOM tag "0x0008, 0x0022" is AcquisitionDate return GeneratePropertyNameForDICOMTag(0x0008, 0x0022); } std::string mitk::GetModalityDICOMProperty() { // extract suitable DICOM tag to use as the information type // DICOM tag "0x0008, 0x0060" is Modality return GeneratePropertyNameForDICOMTag(0x0008, 0x0060); } -mitk::SemanticTypes::CaseID mitk::GetCaseIDFromDataNode(const mitk::DataNode* dataNode) +mitk::SemanticTypes::CaseID mitk::GetCaseIDFromDataNode(const DataNode* dataNode) { if (nullptr == dataNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } - mitk::BaseData* baseData = dataNode->GetData(); + BaseData* baseData = dataNode->GetData(); if (nullptr == baseData) { mitkThrowException(SemanticRelationException) << "No valid base data."; } - mitk::BaseProperty* dicomTag = baseData->GetProperty(GetCaseIDDICOMProperty().c_str()); + BaseProperty* dicomTag = baseData->GetProperty(GetCaseIDDICOMProperty().c_str()); if (nullptr == dicomTag) { mitkThrowException(SemanticRelationException) << "Not a valid DICOM property."; } std::string dicomTagAsString = dicomTag->GetValueAsString(); return dicomTagAsString; } -mitk::SemanticTypes::ID mitk::GetIDFromDataNode(const mitk::DataNode* dataNode) +mitk::SemanticTypes::ID mitk::GetIDFromDataNode(const DataNode* dataNode) { if (nullptr == dataNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } - mitk::BaseData* baseData = dataNode->GetData(); + BaseData* baseData = dataNode->GetData(); if (nullptr == baseData) { mitkThrowException(SemanticRelationException) << "No valid base data."; } - mitk::BaseProperty* dicomTag = baseData->GetProperty(GetNodeIDDICOMProperty().c_str()); + BaseProperty* dicomTag = baseData->GetProperty(GetNodeIDDICOMProperty().c_str()); if (nullptr == dicomTag) { mitkThrowException(SemanticRelationException) << "Not a valid DICOM property."; } std::string dicomTagAsString = dicomTag->GetValueAsString(); return dicomTagAsString; } -mitk::SemanticTypes::ControlPoint mitk::GetDICOMDateFromDataNode(const mitk::DataNode* dataNode) +mitk::SemanticTypes::ControlPoint mitk::GetDICOMDateFromDataNode(const DataNode* dataNode) { if (nullptr == dataNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } - mitk::BaseData* baseData = dataNode->GetData(); + BaseData* baseData = dataNode->GetData(); if (nullptr == baseData) { mitkThrowException(SemanticRelationException) << "No valid base data."; } - mitk::BaseProperty* acquisitionDateProperty = baseData->GetProperty(GetDateDICOMProperty().c_str()); + BaseProperty* acquisitionDateProperty = baseData->GetProperty(GetDateDICOMProperty().c_str()); if (nullptr == acquisitionDateProperty) { mitkThrowException(SemanticRelationException) << "Not a valid DICOM property."; } std::string acquisitionDateAsString = acquisitionDateProperty->GetValueAsString(); SemanticTypes::ControlPoint controlPoint; try { controlPoint = GetControlPointFromString(acquisitionDateAsString); } catch (SemanticRelationException &e) { mitkReThrow(e) << "Cannot retrieve a valid DICOM date."; } return controlPoint; } -mitk::SemanticTypes::InformationType mitk::GetDICOMModalityFromDataNode(const mitk::DataNode* dataNode) +mitk::SemanticTypes::InformationType mitk::GetDICOMModalityFromDataNode(const DataNode* dataNode) { if (nullptr == dataNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } - mitk::BaseData* baseData = dataNode->GetData(); + BaseData* baseData = dataNode->GetData(); if (nullptr == baseData) { mitkThrowException(SemanticRelationException) << "No valid base data."; } - mitk::BaseProperty* dicomTag = baseData->GetProperty(GetModalityDICOMProperty().c_str()); + BaseProperty* dicomTag = baseData->GetProperty(GetModalityDICOMProperty().c_str()); if (nullptr == dicomTag) { mitkThrowException(SemanticRelationException) << "Not a valid DICOM property."; } std::string dicomTagAsString = dicomTag->GetValueAsString(); return dicomTagAsString; } std::string mitk::TrimDICOM(const std::string& identifier) { if (identifier.empty()) { return identifier; } // leading whitespace std::size_t first = identifier.find_first_not_of(' '); if (std::string::npos == first) { return ""; } // trailing whitespace std::size_t last = identifier.find_last_not_of(' '); return identifier.substr(first, last - first + 1); } diff --git a/Modules/SemanticRelations/src/mitkLesionData.cpp b/Modules/SemanticRelations/src/mitkLesionData.cpp index 172b364f65..d2c8ced6e4 100644 --- a/Modules/SemanticRelations/src/mitkLesionData.cpp +++ b/Modules/SemanticRelations/src/mitkLesionData.cpp @@ -1,43 +1,38 @@ /*=================================================================== 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 "mitkLesionData.h" mitk::LesionData::LesionData(const SemanticTypes::Lesion& lesion/* = SemanticTypes::Lesion()*/) { m_Lesion = lesion; } -mitk::LesionData::~LesionData() -{ - // nothing here -} - void mitk::LesionData::SetLesion(const SemanticTypes::Lesion& lesion) { m_Lesion = lesion; } void mitk::LesionData::SetLesionPresence(const std::vector& lesionPresence) { m_LesionPresence = lesionPresence; } void mitk::LesionData::SetLesionVolume(const std::vector& lesionVolume) { m_LesionVolume = lesionVolume; } diff --git a/Modules/SemanticRelations/src/mitkLesionManager.cpp b/Modules/SemanticRelations/src/mitkLesionManager.cpp index a9e83d88e2..6c623ad2c9 100644 --- a/Modules/SemanticRelations/src/mitkLesionManager.cpp +++ b/Modules/SemanticRelations/src/mitkLesionManager.cpp @@ -1,123 +1,106 @@ /*=================================================================== 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 "mitkRelationStorage.h" #include "mitkSemanticRelationException.h" #include "mitkSemanticRelationsInference.h" -#include "mitkRelationStorage.h" #include "mitkUIDGeneratorBoost.h" -double GetLesionVolume(const mitk::SemanticTypes::CaseID& caseID, const mitk::SemanticTypes::Lesion& lesion, const mitk::SemanticTypes::ControlPoint& controlPoint); - mitk::SemanticTypes::Lesion mitk::GenerateNewLesion(const std::string& lesionClassType/* = ""*/) { SemanticTypes::Lesion lesion; - lesion.UID = mitk::UIDGeneratorBoost::GenerateUID(); + lesion.UID = UIDGeneratorBoost::GenerateUID(); lesion.name = "New lesion"; lesion.lesionClass = GenerateNewLesionClass(lesionClassType); return lesion; } mitk::SemanticTypes::LesionClass mitk::GenerateNewLesionClass(const std::string& lesionClassType/* = ""*/) { SemanticTypes::LesionClass lesionClass; - lesionClass.UID = mitk::UIDGeneratorBoost::GenerateUID(); + lesionClass.UID = UIDGeneratorBoost::GenerateUID(); lesionClass.classType = lesionClassType; return lesionClass; } mitk::SemanticTypes::Lesion mitk::GetLesionByUID(const SemanticTypes::ID& lesionUID, const std::vector& allLesions) { auto lambda = [&lesionUID](const SemanticTypes::Lesion& currentLesion) { return currentLesion.UID == lesionUID; }; const auto existingLesion = std::find_if(allLesions.begin(), allLesions.end(), lambda); SemanticTypes::Lesion lesion; if (existingLesion != allLesions.end()) { lesion = *existingLesion; } return lesion; } mitk::SemanticTypes::LesionClass mitk::FindExistingLesionClass(const std::string& lesionClassType, const std::vector& allLesionClasses) { auto lambda = [&lesionClassType](const SemanticTypes::LesionClass& currentLesionClass) { return currentLesionClass.classType == lesionClassType; }; const auto existingLesionClass = std::find_if(allLesionClasses.begin(), allLesionClasses.end(), lambda); SemanticTypes::LesionClass lesionClass; if (existingLesionClass != allLesionClasses.end()) { lesionClass = *existingLesionClass; } return lesionClass; } -void mitk::GenerateAdditionalLesionData(LesionData& lesionData, const SemanticTypes::CaseID& caseID) +void mitk::ComputeLesionPresence(LesionData& lesionData, const SemanticTypes::CaseID& caseID) { std::vector lesionPresence; - std::vector lesionVolume; - SemanticTypes::Lesion lesion = lesionData.GetLesion(); + auto lesion = lesionData.GetLesion(); bool presence = false; - double volume = 0.0; - - SemanticTypes::ControlPointVector controlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); + auto controlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); // sort the vector of control points for the timeline std::sort(controlPoints.begin(), controlPoints.end()); - for (const auto& controlPoint : controlPoints) + auto informationTypes = RelationStorage::GetAllInformationTypesOfCase(caseID); + for (const auto& informationType : informationTypes) { - try + for (const auto& controlPoint : controlPoints) { - presence = SemanticRelationsInference::IsLesionPresentAtControlPoint(caseID, lesion, controlPoint); + try + { + presence = SemanticRelationsInference::IsLesionPresentAtControlPoint(caseID, lesion, controlPoint); + } + catch (SemanticRelationException&) + { + presence = false; + } + + lesionPresence.push_back(presence); } - catch (SemanticRelationException& e) - { - mitkReThrow(e) << "Cannot determine the lesion presence for generating additional lesion data."; - } - - lesionPresence.push_back(presence); - volume = GetLesionVolume(caseID, lesion, controlPoint); - lesionVolume.push_back(volume); } lesionData.SetLesionPresence(lesionPresence); - lesionData.SetLesionVolume(lesionVolume); -} - -double GetLesionVolume(const mitk::SemanticTypes::CaseID& caseID, const mitk::SemanticTypes::Lesion& lesion, const mitk::SemanticTypes::ControlPoint& controlPoint) -{ - bool presence = mitk::SemanticRelationsInference::IsLesionPresentAtControlPoint(caseID, lesion, controlPoint); - if (presence) - { - return 1.0; - } - else - { - return 0.0; - } } diff --git a/Modules/SemanticRelations/src/mitkRelationStorage.cpp b/Modules/SemanticRelations/src/mitkRelationStorage.cpp index 0a1f1c9e40..32156b5760 100644 --- a/Modules/SemanticRelations/src/mitkRelationStorage.cpp +++ b/Modules/SemanticRelations/src/mitkRelationStorage.cpp @@ -1,1539 +1,1571 @@ /*=================================================================== 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 "mitkRelationStorage.h" // semantic relations module #include "mitkDICOMHelper.h" // multi label module #include // mitk core #include #include // c++ #include #include namespace { std::vector GetCaseIDs() { PERSISTENCE_GET_SERVICE_MACRO if (nullptr == persistenceService) { MITK_DEBUG << "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_DEBUG << "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_DEBUG << "Could not find the property " << listIdentifier << " for the " << listIdentifier << " property list."; return std::vector(); } return caseIDsVectorProperty->GetValue(); } bool CaseIDExists(const mitk::SemanticTypes::CaseID& caseID) { auto allCaseIDs = GetCaseIDs(); auto existingCase = std::find(allCaseIDs.begin(), allCaseIDs.end(), caseID); if (existingCase == allCaseIDs.end()) { return false; } return true; } mitk::PropertyList::Pointer GetStorageData(const mitk::SemanticTypes::CaseID& caseID) { // access the storage PERSISTENCE_GET_SERVICE_MACRO if (nullptr == persistenceService) { MITK_DEBUG << "Persistence service could not be loaded"; return nullptr; } // The persistence service may create a new property list with the given ID, if no property list is found. // Since we don't want to return a new property list but rather inform the user that the given case // is not a valid, stored case, we will return nullptr in that case. if (CaseIDExists(caseID)) { // the property list is valid for a whole case and contains all the properties for the current case return persistenceService->GetPropertyList(const_cast(caseID)); } return nullptr; } mitk::SemanticTypes::Lesion GenerateLesion(const mitk::SemanticTypes::CaseID& caseID, const mitk::SemanticTypes::ID& lesionID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return mitk::SemanticTypes::Lesion(); } mitk::VectorProperty* lesionDataProperty = dynamic_cast*>(propertyList->GetProperty(lesionID)); if (nullptr == lesionDataProperty) { MITK_DEBUG << "Lesion " << lesionID << " not found. Lesion can not be retrieved."; return mitk::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_DEBUG << "Incorrect lesion data storage. Not two (2) strings of the lesion name and the lesion UID are stored."; return mitk::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) { mitk::SemanticTypes::LesionClass generatedLesionClass; generatedLesionClass.UID = lesionClassID; generatedLesionClass.classType = lesionClassProperty->GetValue(); mitk::SemanticTypes::Lesion generatedLesion; generatedLesion.UID = lesionID; generatedLesion.name = lesionData[0]; generatedLesion.lesionClass = generatedLesionClass; return generatedLesion; } MITK_DEBUG << "Incorrect lesion class storage. Lesion " << lesionID << " can not be retrieved."; return mitk::SemanticTypes::Lesion(); } mitk::SemanticTypes::ControlPoint GenerateControlpoint(const mitk::SemanticTypes::CaseID& caseID, const mitk::SemanticTypes::ID& controlPointUID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return mitk::SemanticTypes::ControlPoint(); } // retrieve a vector property that contains the integer values of the date of a control point (0. year 1. month 2. day) mitk::VectorProperty* controlPointVectorProperty = dynamic_cast*>(propertyList->GetProperty(controlPointUID)); if (nullptr == controlPointVectorProperty) { MITK_DEBUG << "Could not find the control point " << controlPointUID << " in the storage."; return mitk::SemanticTypes::ControlPoint(); } std::vector controlPointVectorPropertyValue = controlPointVectorProperty->GetValue(); // a control point has to have exactly three integer values (year, month and day) if (controlPointVectorPropertyValue.size() != 3) { MITK_DEBUG << "Incorrect control point storage. Not three (3) values of the date are stored."; return mitk::SemanticTypes::ControlPoint(); } // set the values of the control point mitk::SemanticTypes::ControlPoint generatedControlPoint; generatedControlPoint.UID = controlPointUID; generatedControlPoint.date = boost::gregorian::date(controlPointVectorPropertyValue[0], controlPointVectorPropertyValue[1], controlPointVectorPropertyValue[2]); return generatedControlPoint; } } mitk::SemanticTypes::LesionVector mitk::RelationStorage::GetAllLesionsOfCase(const SemanticTypes::CaseID& caseID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return SemanticTypes::LesionVector(); } // retrieve a vector property that contains the valid lesion-IDs for the current case VectorProperty* lesionsVectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); if (nullptr == lesionsVectorProperty) { MITK_DEBUG << "Could not find any lesion in the storage."; return SemanticTypes::LesionVector(); } std::vector lesionsVectorPropertyValue = lesionsVectorProperty->GetValue(); SemanticTypes::LesionVector allLesionsOfCase; for (const auto& lesionID : lesionsVectorPropertyValue) { SemanticTypes::Lesion generatedLesion = GenerateLesion(caseID, lesionID); if (!generatedLesion.UID.empty()) { allLesionsOfCase.push_back(generatedLesion); } } return allLesionsOfCase; } mitk::SemanticTypes::Lesion mitk::RelationStorage::GetLesionOfSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "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) VectorProperty* segmentationVectorProperty = dynamic_cast*>(propertyList->GetProperty(segmentationID)); if (nullptr == segmentationVectorProperty) { MITK_DEBUG << "Could not find the segmentation " << segmentationID << " in the storage."; return SemanticTypes::Lesion(); } std::vector segmentationVectorPropertyValue = segmentationVectorProperty->GetValue(); // the lesion ID of a segmentation is the second value in the vector if (segmentationVectorPropertyValue.size() != 2) { MITK_DEBUG << "Incorrect segmentation storage. Not two (2) IDs stored."; return SemanticTypes::Lesion(); } std::string lesionID = segmentationVectorPropertyValue[1]; if (lesionID.empty()) { // segmentation does not refer to any lesion; return empty lesion return SemanticTypes::Lesion(); } return GenerateLesion(caseID, lesionID); } mitk::SemanticTypes::ControlPointVector mitk::RelationStorage::GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return SemanticTypes::ControlPointVector(); } // retrieve a vector property that contains the valid control point-IDs for the current case VectorProperty* controlPointsVectorProperty = dynamic_cast*>(propertyList->GetProperty("controlpoints")); if (nullptr == controlPointsVectorProperty) { MITK_DEBUG << "Could not find any control points in the storage."; return SemanticTypes::ControlPointVector(); } std::vector controlPointsVectorPropertyValue = controlPointsVectorProperty->GetValue(); SemanticTypes::ControlPointVector allControlPointsOfCase; for (const auto& controlPointUID : controlPointsVectorPropertyValue) { SemanticTypes::ControlPoint generatedControlPoint = GenerateControlpoint(caseID, controlPointUID); if (!generatedControlPoint.UID.empty()) { allControlPointsOfCase.push_back(generatedControlPoint); } } return allControlPointsOfCase; } mitk::SemanticTypes::ControlPoint mitk::RelationStorage::GetControlPointOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return SemanticTypes::ControlPoint(); } // retrieve a vector property that contains the information type and the referenced ID of a control point (0. information type 1. control point ID) VectorProperty* imageVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); if (nullptr == imageVectorProperty) { MITK_DEBUG << "Could not find the image " << imageID << " in the storage."; return SemanticTypes::ControlPoint(); } std::vector imageVectorPropertyValue = imageVectorProperty->GetValue(); SemanticTypes::ControlPoint controlPoint; // an image has to have exactly two values (the information type and the ID of the control point) if (imageVectorPropertyValue.size() != 2) { MITK_DEBUG << "Incorrect data storage. Not two (2) values stored."; return SemanticTypes::ControlPoint(); } // the second value of the image vector is the ID of the referenced control point std::string controlPointID = imageVectorPropertyValue[1]; // retrieve a vector property that contains the integer values of the date of a control point (0. year 1. month 2. day) VectorProperty* controlPointVectorProperty = dynamic_cast*>(propertyList->GetProperty(controlPointID)); if (nullptr == controlPointVectorProperty) { MITK_DEBUG << "Could not find the control point " << controlPointID << " in the storage."; return SemanticTypes::ControlPoint(); } std::vector controlPointVectorPropertyValue = controlPointVectorProperty->GetValue(); // a control point has to have exactly three integer values (year, month and day) if (controlPointVectorPropertyValue.size() != 3) { MITK_DEBUG << "Incorrect control point storage. Not three (3) values of the date are stored."; return SemanticTypes::ControlPoint(); } // set the values of the control point controlPoint.UID = controlPointID; controlPoint.date = boost::gregorian::date(controlPointVectorPropertyValue[0], controlPointVectorPropertyValue[1], controlPointVectorPropertyValue[2]); return controlPoint; } mitk::SemanticTypes::ExaminationPeriodVector mitk::RelationStorage::GetAllExaminationPeriodsOfCase(const SemanticTypes::CaseID& caseID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return SemanticTypes::ExaminationPeriodVector(); } // retrieve a vector property that contains the valid examination period UIDs for the current case VectorProperty::Pointer examinationPeriodsVectorProperty = dynamic_cast*>(propertyList->GetProperty("examinationperiods")); if (nullptr == examinationPeriodsVectorProperty) { MITK_DEBUG << "Could not find any examination periods in the storage."; return SemanticTypes::ExaminationPeriodVector(); } std::vector examinationPeriodsVectorPropertyValue = examinationPeriodsVectorProperty->GetValue(); SemanticTypes::ExaminationPeriodVector allExaminationPeriods; for (const auto& examinationPeriodID : examinationPeriodsVectorPropertyValue) { // retrieve a vector property that contains the represented control point-IDs VectorProperty::Pointer examinationPeriodVectorProperty = dynamic_cast*>(propertyList->GetProperty(examinationPeriodID)); if (nullptr == examinationPeriodVectorProperty) { MITK_DEBUG << "Could not find the examination period " << examinationPeriodID << " in the storage."; continue; } std::vector examinationPeriodVectorPropertyValue = examinationPeriodVectorProperty->GetValue(); // an examination period has an arbitrary number of vector values (name and control point UIDs) (at least one for the name) if (examinationPeriodVectorPropertyValue.empty()) { MITK_DEBUG << "Incorrect examination period storage. At least one (1) value for the examination period name has to be stored."; continue; } else { // set the values of the name and the control points SemanticTypes::ExaminationPeriod generatedExaminationPeriod; generatedExaminationPeriod.UID = examinationPeriodID; generatedExaminationPeriod.name = examinationPeriodVectorPropertyValue[0]; for (size_t i = 1; i < examinationPeriodVectorPropertyValue.size(); ++i) { generatedExaminationPeriod.controlPointUIDs.push_back(examinationPeriodVectorPropertyValue[i]); } allExaminationPeriods.push_back(generatedExaminationPeriod); } } return allExaminationPeriods; } mitk::SemanticTypes::InformationTypeVector mitk::RelationStorage::GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return SemanticTypes::InformationTypeVector(); } // retrieve a vector property that contains the valid information types of the current case VectorProperty* informationTypesVectorProperty = dynamic_cast*>(propertyList->GetProperty("informationtypes")); if (nullptr == informationTypesVectorProperty) { MITK_DEBUG << "Could not find any information types in the storage."; return SemanticTypes::InformationTypeVector(); } return informationTypesVectorProperty->GetValue(); } mitk::SemanticTypes::InformationType mitk::RelationStorage::GetInformationTypeOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "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 (0. information type 1. control point ID) VectorProperty* imageVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); if (nullptr == imageVectorProperty) { MITK_DEBUG << "Could not find the image " << imageID << " in the storage."; return SemanticTypes::InformationType(); } std::vector imageVectorPropertyValue = imageVectorProperty->GetValue(); // an image has to have exactly two values (the information type and the ID of the control point) if (imageVectorPropertyValue.size() != 2) { MITK_DEBUG << "Incorrect data storage. Not two (2) values stored."; return SemanticTypes::InformationType(); } // the first value of the image vector is the information type return imageVectorPropertyValue[0]; } mitk::SemanticTypes::IDVector mitk::RelationStorage::GetAllImageIDsOfCase(const SemanticTypes::CaseID& caseID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return SemanticTypes::IDVector(); } // retrieve a vector property that contains the valid image-IDs of the current case VectorProperty* imagesVectorProperty = dynamic_cast*>(propertyList->GetProperty("images")); if (nullptr == imagesVectorProperty) { MITK_DEBUG << "Could not find any image in the storage."; return SemanticTypes::IDVector(); } return imagesVectorProperty->GetValue(); } mitk::SemanticTypes::IDVector mitk::RelationStorage::GetAllImageIDsOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return SemanticTypes::IDVector(); } // retrieve a vector property that contains the valid image-IDs of the current case VectorProperty* imagesVectorProperty = dynamic_cast*>(propertyList->GetProperty("images")); if (nullptr == imagesVectorProperty) { MITK_DEBUG << "Could not find any image in the storage."; return SemanticTypes::IDVector(); } mitk::SemanticTypes::IDVector allImageIDsOfControlPoint; std::vector imagesVectorPropertyValue = imagesVectorProperty->GetValue(); for (const auto& imageID : imagesVectorPropertyValue) { // retrieve a vector property that contains the referenced ID of an image (0. information type 1. control point ID) VectorProperty* imageVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); if (nullptr == imageVectorProperty) { continue; } std::vector imageVectorPropertyValue = imageVectorProperty->GetValue(); // an image has to have exactly two values (the information type and the ID of the control point) if (imageVectorPropertyValue.size() != 2) { continue; } // the second value of the image vector is the ID of the referenced control point if (imageVectorPropertyValue[1] == controlPoint.UID) { allImageIDsOfControlPoint.push_back(imageID); } } return allImageIDsOfControlPoint; } mitk::SemanticTypes::IDVector mitk::RelationStorage::GetAllImageIDsOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return SemanticTypes::IDVector(); } // retrieve a vector property that contains the valid image-IDs of the current case VectorProperty* imagesVectorProperty = dynamic_cast*>(propertyList->GetProperty("images")); if (nullptr == imagesVectorProperty) { MITK_DEBUG << "Could not find any image in the storage."; return SemanticTypes::IDVector(); } mitk::SemanticTypes::IDVector allImageIDsOfInformationType; std::vector imagesVectorPropertyValue = imagesVectorProperty->GetValue(); for (const auto& imageID : imagesVectorPropertyValue) { // retrieve a vector property that contains the referenced ID of an image (0. information type 1. control point ID) VectorProperty* imageVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); if (nullptr == imageVectorProperty) { continue; } std::vector imageVectorPropertyValue = imageVectorProperty->GetValue(); // an image has to have exactly two values (the information type and the ID of the control point) if (imageVectorPropertyValue.size() != 2) { continue; } // the first value of the image vector is the ID of the referenced information type if (imageVectorPropertyValue[0] == informationType) { allImageIDsOfInformationType.push_back(imageID); } } return allImageIDsOfInformationType; } mitk::SemanticTypes::IDVector mitk::RelationStorage::GetAllSegmentationIDsOfCase(const SemanticTypes::CaseID& caseID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return SemanticTypes::IDVector(); } // retrieve a vector property that contains the valid segmentation-IDs for the current case VectorProperty* segmentationsVectorProperty = dynamic_cast*>(propertyList->GetProperty("segmentations")); if (nullptr == segmentationsVectorProperty) { MITK_DEBUG << "Could not find any segmentation in the storage."; return SemanticTypes::IDVector(); } return segmentationsVectorProperty->GetValue(); } mitk::SemanticTypes::IDVector mitk::RelationStorage::GetAllSegmentationIDsOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return SemanticTypes::IDVector(); } // retrieve a vector property that contains the valid segmentation-IDs for the current case VectorProperty* segmentationsVectorProperty = dynamic_cast*>(propertyList->GetProperty("segmentations")); if (nullptr == segmentationsVectorProperty) { MITK_DEBUG << "Could not find any segmentation in the storage."; return SemanticTypes::IDVector(); } mitk::SemanticTypes::IDVector allSegmentationIDsOfImage; std::vector segmentationsVectorPropertyValue = segmentationsVectorProperty->GetValue(); for (const auto& segmentationID : segmentationsVectorPropertyValue) { // retrieve a vector property that contains the referenced ID of a segmentation (0. image ID 1. lesion ID) VectorProperty* segmentationVectorProperty = dynamic_cast*>(propertyList->GetProperty(segmentationID)); if (nullptr == segmentationVectorProperty) { continue; } std::vector segmentationVectorPropertyValue = segmentationVectorProperty->GetValue(); // a segmentation has to have exactly two values (the ID of the referenced image and the ID of the referenced lesion) if (segmentationVectorPropertyValue.size() != 2) { continue; } // the first value of the segmentation vector is the ID of the referenced image if (segmentationVectorPropertyValue[0] == imageID) { allSegmentationIDsOfImage.push_back(segmentationID); } } return allSegmentationIDsOfImage; } mitk::SemanticTypes::IDVector mitk::RelationStorage::GetAllSegmentationIDsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return SemanticTypes::IDVector(); } // retrieve a vector property that contains the valid segmentation-IDs for the current case VectorProperty* segmentationsVectorProperty = dynamic_cast*>(propertyList->GetProperty("segmentations")); if (nullptr == segmentationsVectorProperty) { MITK_DEBUG << "Could not find any segmentation in the storage."; return SemanticTypes::IDVector(); } mitk::SemanticTypes::IDVector allSegmentationIDsOfLesion; std::vector segmentationsVectorPropertyValue = segmentationsVectorProperty->GetValue(); for (const auto& segmentationID : segmentationsVectorPropertyValue) { // retrieve a vector property that contains the referenced ID of a segmentation (0. image ID 1. lesion ID) VectorProperty* segmentationVectorProperty = dynamic_cast*>(propertyList->GetProperty(segmentationID)); if (nullptr == segmentationVectorProperty) { continue; } std::vector segmentationVectorPropertyValue = segmentationVectorProperty->GetValue(); // a segmentation has to have exactly two values (the ID of the referenced image and the ID of the referenced lesion) if (segmentationVectorPropertyValue.size() != 2) { continue; } // the second value of the segmentation vector is the ID of the referenced lesion if (segmentationVectorPropertyValue[1] == lesion.UID) { allSegmentationIDsOfLesion.push_back(segmentationID); } } return allSegmentationIDsOfLesion; } mitk::SemanticTypes::ID mitk::RelationStorage::GetImageIDOfSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return SemanticTypes::ID(); } // retrieve a vector property that contains the referenced ID of a segmentation (0. image ID 1. lesion ID) VectorProperty* segmentationVectorProperty = dynamic_cast*>(propertyList->GetProperty(segmentationID)); if (nullptr == segmentationVectorProperty) { MITK_DEBUG << "Could not find the segmentation " << segmentationID << " in the storage."; return SemanticTypes::ID(); } std::vector segmentationVectorPropertyValue = segmentationVectorProperty->GetValue(); // the lesion ID of a segmentation is the second value in the vector if (segmentationVectorPropertyValue.size() != 2) { MITK_DEBUG << "Incorrect segmentation storage. Not two (2) IDs stored."; return SemanticTypes::ID(); } return segmentationVectorPropertyValue[0]; } std::vector mitk::RelationStorage::GetAllCaseIDs() { return GetCaseIDs(); } bool mitk::RelationStorage::InstanceExists(const SemanticTypes::CaseID& caseID) { return CaseIDExists(caseID); } void mitk::RelationStorage::AddCase(const SemanticTypes::CaseID& caseID) { PERSISTENCE_GET_SERVICE_MACRO if (nullptr == persistenceService) { MITK_DEBUG << "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"; PropertyList::Pointer propertyList = persistenceService->GetPropertyList(listIdentifier); if (nullptr == propertyList) { MITK_DEBUG << "Could not find the property list " << listIdentifier << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains all case IDs VectorProperty::Pointer caseIDsVectorProperty = dynamic_cast*>(propertyList->GetProperty(listIdentifier)); std::vector caseIDsVectorPropertyValue; if (nullptr == caseIDsVectorProperty) { caseIDsVectorProperty = VectorProperty::New(); } else { caseIDsVectorPropertyValue = caseIDsVectorProperty->GetValue(); } auto existingCase = std::find(caseIDsVectorPropertyValue.begin(), caseIDsVectorPropertyValue.end(), caseID); if (existingCase != caseIDsVectorPropertyValue.end()) { return; } // add case to the "caseIDs" property list caseIDsVectorPropertyValue.push_back(caseID); caseIDsVectorProperty->SetValue(caseIDsVectorPropertyValue); propertyList->SetProperty(listIdentifier, caseIDsVectorProperty); } void mitk::RelationStorage::AddImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "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 VectorProperty::Pointer imagesVectorProperty = dynamic_cast*>(propertyList->GetProperty("images")); std::vector imagesVectorPropertyValue; if (nullptr == imagesVectorProperty) { imagesVectorProperty = VectorProperty::New(); } else { imagesVectorPropertyValue = imagesVectorProperty->GetValue(); } auto existingImage = std::find(imagesVectorPropertyValue.begin(), imagesVectorPropertyValue.end(), imageID); if (existingImage != imagesVectorPropertyValue.end()) { return; } // add image to the "images" property list imagesVectorPropertyValue.push_back(imageID); imagesVectorProperty->SetValue(imagesVectorPropertyValue); propertyList->SetProperty("images", imagesVectorProperty); // add the image itself VectorProperty::Pointer imageVectorProperty = VectorProperty::New(); // an image has to have exactly two values (the information type and the ID of the control point) std::vector imageVectorPropertyValue(2); imageVectorProperty->SetValue(imageVectorPropertyValue); propertyList->SetProperty(imageID, imageVectorProperty); } void mitk::RelationStorage::RemoveImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "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 VectorProperty::Pointer imagesVectorProperty = dynamic_cast*>(propertyList->GetProperty("images")); if (nullptr == imagesVectorProperty) { MITK_DEBUG << "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 imagesVectorPropertyValue = imagesVectorProperty->GetValue(); imagesVectorPropertyValue.erase(std::remove(imagesVectorPropertyValue.begin(), imagesVectorPropertyValue.end(), imageID), imagesVectorPropertyValue.end()); if (imagesVectorPropertyValue.empty()) { // no more images stored -> remove the images property list propertyList->DeleteProperty("images"); } else { // or store the modified vector value imagesVectorProperty->SetValue(imagesVectorPropertyValue); } // remove the image instance itself propertyList->DeleteProperty(imageID); } void mitk::RelationStorage::AddSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID, const SemanticTypes::ID& parentID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "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 VectorProperty::Pointer segmentationsVectorProperty = dynamic_cast*>(propertyList->GetProperty("segmentations")); std::vector segmentationsVectorPropertyValue; if (nullptr == segmentationsVectorProperty) { segmentationsVectorProperty = VectorProperty::New(); } else { segmentationsVectorPropertyValue = segmentationsVectorProperty->GetValue(); } auto existingSegmentation = std::find(segmentationsVectorPropertyValue.begin(), segmentationsVectorPropertyValue.end(), segmentationID); if (existingSegmentation != segmentationsVectorPropertyValue.end()) { return; } // add segmentation to the "segmentations" property list segmentationsVectorPropertyValue.push_back(segmentationID); segmentationsVectorProperty->SetValue(segmentationsVectorPropertyValue); propertyList->SetProperty("segmentations", segmentationsVectorProperty); // add the segmentation itself VectorProperty::Pointer segmentationVectorProperty = VectorProperty::New(); // a segmentation has to have exactly two values (the ID of the referenced image and the ID of the referenced lesion) std::vector segmentationVectorPropertyValue(2); segmentationVectorPropertyValue[0] = parentID; segmentationVectorProperty->SetValue(segmentationVectorPropertyValue); propertyList->SetProperty(segmentationID, segmentationVectorProperty); } void mitk::RelationStorage::RemoveSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "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 VectorProperty::Pointer segmentationsVectorProperty = dynamic_cast*>(propertyList->GetProperty("segmentations")); if (nullptr == segmentationsVectorProperty) { MITK_DEBUG << "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 segmentationsVectorPropertyValue = segmentationsVectorProperty->GetValue(); segmentationsVectorPropertyValue.erase(std::remove(segmentationsVectorPropertyValue.begin(), segmentationsVectorPropertyValue.end(), segmentationID), segmentationsVectorPropertyValue.end()); if (segmentationsVectorPropertyValue.empty()) { // no more segmentations stored -> remove the segmentations property list propertyList->DeleteProperty("segmentations"); } else { // or store the modified vector value segmentationsVectorProperty->SetValue(segmentationsVectorPropertyValue); } // remove the lesion instance itself propertyList->DeleteProperty(segmentationID); } void mitk::RelationStorage::AddLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "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 VectorProperty::Pointer lesionsVectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); std::vector lesionsVectorPropertyValue; if (nullptr == lesionsVectorProperty) { lesionsVectorProperty = VectorProperty::New(); } else { lesionsVectorPropertyValue = lesionsVectorProperty->GetValue(); } const auto& existingIndex = std::find(lesionsVectorPropertyValue.begin(), lesionsVectorPropertyValue.end(), lesion.UID); if (existingIndex != lesionsVectorPropertyValue.end()) { return; } // add the new lesion id from the given lesion to the vector of all current lesion IDs lesionsVectorPropertyValue.push_back(lesion.UID); // overwrite the current vector property with the new, extended string vector lesionsVectorProperty->SetValue(lesionsVectorPropertyValue); propertyList->SetProperty("lesions", lesionsVectorProperty); // add the lesion with the lesion UID as the key and the lesion information as value std::vector lesionData; lesionData.push_back(lesion.name); lesionData.push_back(lesion.lesionClass.UID); VectorProperty::Pointer newLesionVectorProperty = VectorProperty::New(); newLesionVectorProperty->SetValue(lesionData); propertyList->SetProperty(lesion.UID, newLesionVectorProperty); // add the lesion class with the lesion class UID as key and the class type as value std::string lesionClassType = lesion.lesionClass.classType; propertyList->SetStringProperty(lesion.lesionClass.UID.c_str(), lesionClassType.c_str()); } void mitk::RelationStorage::OverwriteLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "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 VectorProperty* lesionVectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); if (nullptr == lesionVectorProperty) { MITK_DEBUG << "Could not find any lesion in the storage."; return; } std::vector lesionVectorPropertyValue = lesionVectorProperty->GetValue(); const auto existingLesion = std::find(lesionVectorPropertyValue.begin(), lesionVectorPropertyValue.end(), lesion.UID); if (existingLesion != lesionVectorPropertyValue.end()) { // overwrite the referenced lesion class UID with the new, given lesion class data std::vector lesionData; lesionData.push_back(lesion.name); lesionData.push_back(lesion.lesionClass.UID); VectorProperty::Pointer newLesionVectorProperty = VectorProperty::New(); newLesionVectorProperty->SetValue(lesionData); propertyList->SetProperty(lesion.UID, newLesionVectorProperty); // overwrite the lesion class with the lesion class UID as key and the new, given class type as value std::string lesionClassType = lesion.lesionClass.classType; propertyList->SetStringProperty(lesion.lesionClass.UID.c_str(), lesionClassType.c_str()); } else { MITK_DEBUG << "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) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "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 VectorProperty* lesionVectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); if (nullptr == lesionVectorProperty) { MITK_DEBUG << "Could not find any lesion in the storage."; return; } std::vector lesionVectorPropertyValue = lesionVectorProperty->GetValue(); const auto existingLesion = std::find(lesionVectorPropertyValue.begin(), lesionVectorPropertyValue.end(), lesion.UID); if (existingLesion != lesionVectorPropertyValue.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) VectorProperty* segmentationVectorProperty = dynamic_cast*>(propertyList->GetProperty(segmentationID)); if (nullptr == segmentationVectorProperty) { MITK_DEBUG << "Could not find the segmentation " << segmentationID << " in the storage. Cannot link segmentation to lesion."; return; } std::vector segmentationVectorPropertyValue = segmentationVectorProperty->GetValue(); if (segmentationVectorPropertyValue.size() != 2) { MITK_DEBUG << "Incorrect segmentation storage. Not two (2) IDs stored."; return; } // the lesion ID of a segmentation is the second value in the vector segmentationVectorPropertyValue[1] = lesion.UID; segmentationVectorProperty->SetValue(segmentationVectorPropertyValue); return; } MITK_DEBUG << "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) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "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) VectorProperty* segmentationVectorProperty = dynamic_cast*>(propertyList->GetProperty(segmentationID)); if (nullptr == segmentationVectorProperty) { MITK_DEBUG << "Could not find the segmentation " << segmentationID << " in the storage. Cannot unlink lesion from segmentation."; return; } std::vector segmentationVectorPropertyValue = segmentationVectorProperty->GetValue(); // a segmentation has to have exactly two values (the ID of the linked image and the ID of the lesion) if (segmentationVectorPropertyValue.size() != 2) { MITK_DEBUG << "Incorrect data storage. Not two (2) values stored."; return; } // the second value of the segmentation vector is the ID of the referenced lesion // set the lesion reference to an empty string for removal segmentationVectorPropertyValue[1] = ""; segmentationVectorProperty->SetValue(segmentationVectorPropertyValue); } void mitk::RelationStorage::RemoveLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "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 VectorProperty* lesionVectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); if (nullptr == lesionVectorProperty) { MITK_DEBUG << "Could not find any lesion in the storage."; return; } // remove the lesion reference from the list of all lesions of the current case std::vector lesionsVectorPropertyValue = lesionVectorProperty->GetValue(); lesionsVectorPropertyValue.erase(std::remove(lesionsVectorPropertyValue.begin(), lesionsVectorPropertyValue.end(), lesion.UID), lesionsVectorPropertyValue.end()); if (lesionsVectorPropertyValue.empty()) { // no more lesions stored -> remove the lesions property list propertyList->DeleteProperty("lesions"); } else { // or store the modified vector value lesionVectorProperty->SetValue(lesionsVectorPropertyValue); } // remove the lesion instance itself // the lesion data is stored under the lesion ID VectorProperty* lesionDataProperty = dynamic_cast*>(propertyList->GetProperty(lesion.UID)); if (nullptr == lesionDataProperty) { MITK_DEBUG << "Lesion " << lesion.UID << " not found (already removed?). Cannot remove the lesion."; return; } std::vector lesionData = lesionDataProperty->GetValue(); // a lesion date has to have exactly two values (the name of the lesion and the UID of the lesion class) if (lesionData.size() != 2) { MITK_DEBUG << "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) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the lesion class StringProperty* lesionClassProperty = dynamic_cast(propertyList->GetProperty(lesionClassID)); if (nullptr == lesionClassProperty) { MITK_DEBUG << "Lesion class " << lesionClassID << " not found (already removed?). Cannot remove the lesion class."; return; } // retrieve a vector property that contains the valid lesions of the current case VectorProperty* lesionVectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); if (nullptr == lesionVectorProperty) { return; } // check if the lesion class ID is referenced by any other lesion std::vector lesionsVectorPropertyValue = lesionVectorProperty->GetValue(); const auto existingLesionClass = std::find_if(lesionsVectorPropertyValue.begin(), lesionsVectorPropertyValue.end(), [&propertyList, &lesionClassID](const std::string& lesionID) { VectorProperty* lesionDataProperty = dynamic_cast*>(propertyList->GetProperty(lesionID)); if (nullptr == lesionDataProperty) { return false; } 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; } return lesionData[1] == lesionClassID; }); if (existingLesionClass == lesionsVectorPropertyValue.end()) { // lesion class ID not referenced; remove lesion class propertyList->DeleteProperty(lesionClassID); } } void mitk::RelationStorage::AddControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "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 VectorProperty::Pointer controlPointsVectorProperty = dynamic_cast*>(propertyList->GetProperty("controlpoints")); std::vector controlPointsVectorPropertyValue; if (nullptr == controlPointsVectorProperty) { controlPointsVectorProperty = VectorProperty::New(); } else { controlPointsVectorPropertyValue = controlPointsVectorProperty->GetValue(); } const auto existingControlPoint = std::find(controlPointsVectorPropertyValue.begin(), controlPointsVectorPropertyValue.end(), controlPoint.UID); if (existingControlPoint != controlPointsVectorPropertyValue.end()) { return; } // add the new control point UID from the given control point to the vector of all current control point UIDs controlPointsVectorPropertyValue.push_back(controlPoint.UID); // overwrite the current vector property with the new, extended string vector controlPointsVectorProperty->SetValue(controlPointsVectorPropertyValue); propertyList->SetProperty("controlpoints", controlPointsVectorProperty); // store the control point values (the three integer values of a date) std::vector controlPointDate; controlPointDate.push_back(controlPoint.date.year()); controlPointDate.push_back(controlPoint.date.month()); controlPointDate.push_back(controlPoint.date.day()); VectorProperty::Pointer newControlPointVectorProperty = VectorProperty::New(); newControlPointVectorProperty->SetValue(controlPointDate); propertyList->SetProperty(controlPoint.UID, newControlPointVectorProperty); } void mitk::RelationStorage::LinkImageToControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID, const SemanticTypes::ControlPoint& controlPoint) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "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 VectorProperty* controlPointsVectorProperty = dynamic_cast*>(propertyList->GetProperty("controlpoints")); if (nullptr == controlPointsVectorProperty) { MITK_DEBUG << "Could not find any control point in the storage."; return; } std::vector controlPointsVectorPropertyValue = controlPointsVectorProperty->GetValue(); const auto existingControlPoint = std::find(controlPointsVectorPropertyValue.begin(), controlPointsVectorPropertyValue.end(), controlPoint.UID); if (existingControlPoint != controlPointsVectorPropertyValue.end()) { // set / overwrite the control point reference of the given data // retrieve a vector property that contains the referenced ID of a image (0. information type 1. control point ID) VectorProperty* imageVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); if (nullptr == imageVectorProperty) { MITK_DEBUG << "Could not find the image " << imageID << " in the storage. Cannot link data to control point."; return; } std::vector imageVectorPropertyValue = imageVectorProperty->GetValue(); // an image has to have exactly two values (the information type and the ID of the control point) if (imageVectorPropertyValue.size() != 2) { MITK_DEBUG << "Incorrect data storage. Not two (2) values stored."; return; } // the second value of the image vector is the ID of the referenced control point imageVectorPropertyValue[1] = controlPoint.UID; imageVectorProperty->SetValue(imageVectorPropertyValue); return; } MITK_DEBUG << "Could not find control point " << controlPoint.UID << " in the storage. Cannot link data to control point."; } void mitk::RelationStorage::UnlinkImageFromControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "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) VectorProperty* imageVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); if (nullptr == imageVectorProperty) { MITK_DEBUG << "Could not find the date " << imageID << " in the storage. Cannot unlink control point from date."; return; } std::vector imageVectorPropertyValue = imageVectorProperty->GetValue(); // an image has to have exactly two values (the information type and the ID of the control point) if (imageVectorPropertyValue.size() != 2) { MITK_DEBUG << "Incorrect data storage. Not two (2) values stored."; return; } // the second value of the image vector is the ID of the referenced control point // set the control point reference to an empty string for removal imageVectorPropertyValue[1] = ""; imageVectorProperty->SetValue(imageVectorPropertyValue); } void mitk::RelationStorage::RemoveControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "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 VectorProperty* controlPointsVectorProperty = dynamic_cast*>(propertyList->GetProperty("controlpoints")); if (nullptr == controlPointsVectorProperty) { MITK_DEBUG << "Could not find any control point in the storage."; return; } // remove the control point reference from the list of all control points of the current case std::vector controlPointsVectorPropertyValue = controlPointsVectorProperty->GetValue(); controlPointsVectorPropertyValue.erase(std::remove(controlPointsVectorPropertyValue.begin(), controlPointsVectorPropertyValue.end(), controlPoint.UID), controlPointsVectorPropertyValue.end()); if (controlPointsVectorPropertyValue.empty()) { // no more control points stored -> remove the control point property list propertyList->DeleteProperty("controlpoints"); } else { // or store the modified vector value controlPointsVectorProperty->SetValue(controlPointsVectorPropertyValue); } // remove the control point instance itself propertyList->DeleteProperty(controlPoint.UID); } void mitk::RelationStorage::AddExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid examination period UIDs for the current case VectorProperty::Pointer examinationPeriodsVectorProperty = dynamic_cast*>(propertyList->GetProperty("examinationperiods")); std::vector examinationPeriodsVectorPropertyValue; if (nullptr == examinationPeriodsVectorProperty) { examinationPeriodsVectorProperty = VectorProperty::New(); } else { examinationPeriodsVectorPropertyValue = examinationPeriodsVectorProperty->GetValue(); } const auto& existingIndex = std::find(examinationPeriodsVectorPropertyValue.begin(), examinationPeriodsVectorPropertyValue.end(), examinationPeriod.UID); if (existingIndex != examinationPeriodsVectorPropertyValue.end()) { return; } // add the new examination period id from the given examination period to the vector of all current examination period UIDs examinationPeriodsVectorPropertyValue.push_back(examinationPeriod.UID); // overwrite the current vector property with the new, extended string vector examinationPeriodsVectorProperty->SetValue(examinationPeriodsVectorPropertyValue); propertyList->SetProperty("examinationperiods", examinationPeriodsVectorProperty); // add the examination period with the UID as the key and the name as as the vector value std::vector examinationPeriodData; examinationPeriodData.push_back(examinationPeriod.name); VectorProperty::Pointer newExaminationPeriodVectorProperty = VectorProperty::New(); newExaminationPeriodVectorProperty->SetValue(examinationPeriodData); propertyList->SetProperty(examinationPeriod.UID, newExaminationPeriodVectorProperty); } +void mitk::RelationStorage::RenameExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) +{ + PropertyList::Pointer propertyList = GetStorageData(caseID); + if (nullptr == propertyList) + { + MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; + return; + } + // retrieve a vector property that contains the data of the given examination period + VectorProperty* examinationPeriodDataVectorProperty = dynamic_cast*>(propertyList->GetProperty(examinationPeriod.UID)); + if (nullptr == examinationPeriodDataVectorProperty) + { + MITK_DEBUG << "Could not find examination period " << examinationPeriod.UID << " in the storage. Cannot rename the examination period."; + return; + } + + std::vector examinationPeriodDataVectorPropertyValue = examinationPeriodDataVectorProperty->GetValue(); + // an examination period has an arbitrary number of vector values (name and control point UIDs) (at least one for the name) + if (examinationPeriodDataVectorPropertyValue.size() < 1) + { + MITK_DEBUG << "Incorrect examination period storage. At least one (1) name has to be stored."; + return; + } + else + { + // set the first vector value - the name + examinationPeriodDataVectorPropertyValue[0] = examinationPeriod.name; + // store the modified vector value + examinationPeriodDataVectorProperty->SetValue(examinationPeriodDataVectorPropertyValue); + } +} + void mitk::RelationStorage::AddControlPointToExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod& examinationPeriod) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the represented control point UIDs of the given examination period VectorProperty* controlPointUIDsVectorProperty = dynamic_cast*>(propertyList->GetProperty(examinationPeriod.UID)); if (nullptr == controlPointUIDsVectorProperty) { MITK_DEBUG << "Could not find the examination period " << examinationPeriod.UID << " in the storage. Cannot add the control point to the examination period."; return; } std::vector controlPointUIDsVectorPropertyValue = controlPointUIDsVectorProperty->GetValue(); // store the control point UID controlPointUIDsVectorPropertyValue.push_back(controlPoint.UID); // sort the vector according to the date of the control points referenced by the UIDs auto lambda = [&caseID](const SemanticTypes::ID& leftControlPointUID, const SemanticTypes::ID& rightControlPointUID) { const auto& leftControlPoint = GenerateControlpoint(caseID, leftControlPointUID); const auto& rightControlPoint = GenerateControlpoint(caseID, rightControlPointUID); return leftControlPoint.date <= rightControlPoint.date; }; std::sort(controlPointUIDsVectorPropertyValue.begin(), controlPointUIDsVectorPropertyValue.end(), lambda); // store the modified and sorted control point UID vector of this examination period controlPointUIDsVectorProperty->SetValue(controlPointUIDsVectorPropertyValue); } void mitk::RelationStorage::RemoveControlPointFromExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod& examinationPeriod) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the represented control point UIDs of the given examination period VectorProperty* controlPointUIDsVectorProperty = dynamic_cast*>(propertyList->GetProperty(examinationPeriod.UID)); if (nullptr == controlPointUIDsVectorProperty) { MITK_DEBUG << "Could not find examination period " << examinationPeriod.UID << " in the storage. Cannot add the control point to the examination period."; return; } std::vector controlPointUIDsVectorPropertyValue = controlPointUIDsVectorProperty->GetValue(); // an examination period has an arbitrary number of vector values (name and control point UIDs) (at least one for the name) if (controlPointUIDsVectorPropertyValue.size() < 2) { MITK_DEBUG << "Incorrect examination period storage. At least one (1) control point ID has to be stored."; return; } else { controlPointUIDsVectorPropertyValue.erase(std::remove(controlPointUIDsVectorPropertyValue.begin(), controlPointUIDsVectorPropertyValue.end(), controlPoint.UID), controlPointUIDsVectorPropertyValue.end()); if (controlPointUIDsVectorPropertyValue.size() < 2) { RemoveExaminationPeriod(caseID, examinationPeriod); } else { // store the modified vector value controlPointUIDsVectorProperty->SetValue(controlPointUIDsVectorPropertyValue); } } } void mitk::RelationStorage::RemoveExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid examination period UIDs for the current case VectorProperty::Pointer examinationPeriodsVectorProperty = dynamic_cast*>(propertyList->GetProperty("examinationperiods")); if (nullptr == examinationPeriodsVectorProperty) { MITK_DEBUG << "Could not find any examination periods in the storage."; return; } std::vector examinationPeriodsVectorPropertyValue = examinationPeriodsVectorProperty->GetValue(); examinationPeriodsVectorPropertyValue.erase(std::remove(examinationPeriodsVectorPropertyValue.begin(), examinationPeriodsVectorPropertyValue.end(), examinationPeriod.UID), examinationPeriodsVectorPropertyValue.end()); if (examinationPeriodsVectorPropertyValue.empty()) { // no more examination periods stored -> remove the examination period property list propertyList->DeleteProperty("examinationperiods"); } else { // or store the modified vector value examinationPeriodsVectorProperty->SetValue(examinationPeriodsVectorPropertyValue); } // remove the examination period instance itself propertyList->DeleteProperty(examinationPeriod.UID); } void mitk::RelationStorage::AddInformationTypeToImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID, const SemanticTypes::InformationType& informationType) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "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 VectorProperty::Pointer informationTypesVectorProperty = dynamic_cast*>(propertyList->GetProperty("informationtypes")); std::vector informationTypesVectorPropertyValue; if (nullptr == informationTypesVectorProperty) { informationTypesVectorProperty = VectorProperty::New(); } else { informationTypesVectorPropertyValue = informationTypesVectorProperty->GetValue(); } const auto existingInformationType = std::find(informationTypesVectorPropertyValue.begin(), informationTypesVectorPropertyValue.end(), informationType); if (existingInformationType == informationTypesVectorPropertyValue.end()) { // at first: add the information type to the storage informationTypesVectorPropertyValue.push_back(informationType); informationTypesVectorProperty->SetValue(informationTypesVectorPropertyValue); propertyList->SetProperty("informationtypes", informationTypesVectorProperty); } // 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) VectorProperty* imageVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); if (nullptr == imageVectorProperty) { MITK_DEBUG << "Could not find the image " << imageID << " in the storage. Cannot add information type to image."; return; } std::vector imageVectorPropertyValue = imageVectorProperty->GetValue(); // an image has to have exactly two values (the information type and the ID of the control point) if (imageVectorPropertyValue.size() != 2) { MITK_DEBUG << "Incorrect data storage. Not two (2) values stored."; return; } // the first value of the image vector is the information type imageVectorPropertyValue[0] = informationType; imageVectorProperty->SetValue(imageVectorPropertyValue); } void mitk::RelationStorage::RemoveInformationTypeFromImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "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) VectorProperty* imageVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); if (nullptr == imageVectorProperty) { MITK_DEBUG << "Could not find the image " << imageID << " in the storage. Cannot remove information type from image."; return; } std::vector imageVectorPropertyValue = imageVectorProperty->GetValue(); // an image has to have exactly two values (the information type and the ID of the control point) if (imageVectorPropertyValue.size() != 2) { MITK_DEBUG << "Incorrect data storage. Not two (2) values stored."; return; } // the first value of the image vector is the information type // set the information type to an empty string for removal imageVectorPropertyValue[0] = ""; imageVectorProperty->SetValue(imageVectorPropertyValue); } void mitk::RelationStorage::RemoveInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) { PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_DEBUG << "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 VectorProperty* informationTypesVectorProperty = dynamic_cast*>(propertyList->GetProperty("informationtypes")); if (nullptr == informationTypesVectorProperty) { MITK_DEBUG << "Could not find any information type in the storage."; return; } std::vector informationTypesVectorPropertyValue = informationTypesVectorProperty->GetValue(); informationTypesVectorPropertyValue.erase(std::remove(informationTypesVectorPropertyValue.begin(), informationTypesVectorPropertyValue.end(), informationType), informationTypesVectorPropertyValue.end()); if (informationTypesVectorPropertyValue.empty()) { // no more information types stored -> remove the information types property list propertyList->DeleteProperty("informationtypes"); } else { // or store the modified vector value informationTypesVectorProperty->SetValue(informationTypesVectorPropertyValue); } } diff --git a/Modules/SemanticRelations/src/mitkSemanticRelationsDataStorageAccess.cpp b/Modules/SemanticRelations/src/mitkSemanticRelationsDataStorageAccess.cpp index cad0d29a7f..c4e1c15821 100644 --- a/Modules/SemanticRelations/src/mitkSemanticRelationsDataStorageAccess.cpp +++ b/Modules/SemanticRelations/src/mitkSemanticRelationsDataStorageAccess.cpp @@ -1,236 +1,342 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSemanticRelationsDataStorageAccess.h" // semantic relations module #include "mitkControlPointManager.h" #include "mitkDICOMHelper.h" #include "mitkNodePredicates.h" #include "mitkRelationStorage.h" #include "mitkSemanticRelationException.h" #include "mitkSemanticRelationsInference.h" // c++ #include #include mitk::SemanticRelationsDataStorageAccess::SemanticRelationsDataStorageAccess(DataStorage* dataStorage) : m_DataStorage(dataStorage) { // nothing here } -mitk::SemanticRelationsDataStorageAccess::~SemanticRelationsDataStorageAccess() -{ - // nothing here -} - /************************************************************************/ /* functions to get instances / attributes */ /************************************************************************/ mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllSegmentationsOfCase(const SemanticTypes::CaseID& caseID) const { if (m_DataStorage.IsExpired()) { mitkThrowException(SemanticRelationException) << "Not a valid data storage."; } - std::vector allSegmentationIDsOfCase = RelationStorage::GetAllSegmentationIDsOfCase(caseID); - std::vector allSegmentationsOfCase; + SemanticTypes::IDVector allSegmentationIDsOfCase = RelationStorage::GetAllSegmentationIDsOfCase(caseID); + DataNodeVector allSegmentationsOfCase; // get all segmentation nodes of the current data storage // only those nodes are respected, that are currently held in the data storage DataStorage::SetOfObjects::ConstPointer segmentationNodes = m_DataStorage.Lock()->GetSubset(NodePredicates::GetSegmentationPredicate()); for (auto it = segmentationNodes->Begin(); it != segmentationNodes->End(); ++it) { DataNode* segmentationNode = it->Value(); - std::string caseID; - std::string segmentationID; + SemanticTypes::CaseID currentCaseID; + SemanticTypes::ID segmentationID; try { // find the corresponding segmentation node for the given segmentation ID - caseID = GetCaseIDFromDataNode(segmentationNode); + currentCaseID = GetCaseIDFromDataNode(segmentationNode); segmentationID = GetIDFromDataNode(segmentationNode); } catch (SemanticRelationException&) { // found a segmentation node that is not stored in the semantic relations // this segmentation node does not have any DICOM information --> exception thrown // continue with the next segmentation to compare IDs continue; } - if (caseID == caseID && (std::find(allSegmentationIDsOfCase.begin(), allSegmentationIDsOfCase.end(), segmentationID) != allSegmentationIDsOfCase.end())) + if (caseID == currentCaseID && (std::find(allSegmentationIDsOfCase.begin(), allSegmentationIDsOfCase.end(), segmentationID) != allSegmentationIDsOfCase.end())) { // found current image node in the storage, add it to the return vector allSegmentationsOfCase.push_back(segmentationNode); } } return allSegmentationsOfCase; } mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllSegmentationsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const { if (SemanticRelationsInference::InstanceExists(caseID, lesion)) { // lesion exists, retrieve all case segmentations from the storage DataNodeVector allSegmentationsOfLesion = GetAllSegmentationsOfCase(caseID); // filter all segmentations: check for semantic relation with the given lesion using a lambda function auto lambda = [&lesion](DataNode::Pointer segmentation) { try { SemanticTypes::Lesion representedLesion = SemanticRelationsInference::GetLesionOfSegmentation(segmentation); return lesion.UID != representedLesion.UID; } catch (const SemanticRelationException&) { return true; } }; allSegmentationsOfLesion.erase(std::remove_if(allSegmentationsOfLesion.begin(), allSegmentationsOfLesion.end(), lambda), allSegmentationsOfLesion.end()); return allSegmentationsOfLesion; } else { mitkThrowException(SemanticRelationException) << "Could not find an existing lesion instance for the given caseID " << caseID << " and lesion " << lesion.UID << "."; } } mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllImagesOfCase(const SemanticTypes::CaseID& caseID) const { if (m_DataStorage.IsExpired()) { mitkThrowException(SemanticRelationException) << "Not a valid data storage."; } - std::vector allImageIDsOfCase = RelationStorage::GetAllImageIDsOfCase(caseID); - std::vector allImagesOfCase; + SemanticTypes::IDVector allImageIDsOfCase = RelationStorage::GetAllImageIDsOfCase(caseID); + DataNodeVector allImagesOfCase; // get all image nodes of the current data storage // only those nodes are respected, that are currently held in the data storage DataStorage::SetOfObjects::ConstPointer imageNodes = m_DataStorage.Lock()->GetSubset(NodePredicates::GetImagePredicate()); for (auto it = imageNodes->Begin(); it != imageNodes->End(); ++it) { DataNode* imageNode = it->Value(); - std::string caseID; - std::string imageID; + SemanticTypes::CaseID currentCaseID; + SemanticTypes::ID imageID; try { // find the corresponding image node for the given segmentation ID - caseID = GetCaseIDFromDataNode(imageNode); + currentCaseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException&) { // found an image node that is not stored in the semantic relations // this image node does not have any DICOM information --> exception thrown // continue with the next image to compare IDs continue; } - if (caseID == caseID && (std::find(allImageIDsOfCase.begin(), allImageIDsOfCase.end(), imageID) != allImageIDsOfCase.end())) + if (caseID == currentCaseID && (std::find(allImageIDsOfCase.begin(), allImageIDsOfCase.end(), imageID) != allImageIDsOfCase.end())) + { + // found current image node in the storage, add it to the return vector + allImagesOfCase.push_back(imageNode); + } + } + + return allImagesOfCase; +} + +mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllImagesByID(const SemanticTypes::IDVector& imageIDs) const +{ + if (m_DataStorage.IsExpired()) + { + mitkThrowException(SemanticRelationException) << "Not a valid data storage."; + } + + DataNodeVector allImagesOfCase; + // get all image nodes of the current data storage + // only those nodes are respected, that are currently held in the data storage + DataStorage::SetOfObjects::ConstPointer imageNodes = m_DataStorage.Lock()->GetSubset(NodePredicates::GetImagePredicate()); + for (auto it = imageNodes->Begin(); it != imageNodes->End(); ++it) + { + DataNode* imageNode = it->Value(); + + SemanticTypes::CaseID currentCaseID; + SemanticTypes::ID imageID; + try + { + // find the corresponding image node for the given segmentation ID + imageID = GetIDFromDataNode(imageNode); + } + catch (SemanticRelationException&) + { + // found an image node that is not stored in the semantic relations + // this image node does not have any DICOM information --> exception thrown + // continue with the next image to compare IDs + continue; + } + + if (std::find(imageIDs.begin(), imageIDs.end(), imageID) != imageIDs.end()) { // found current image node in the storage, add it to the return vector allImagesOfCase.push_back(imageNode); } } return allImagesOfCase; } mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllImagesOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const { if (m_DataStorage.IsExpired()) { mitkThrowException(SemanticRelationException) << "Not a valid data storage."; } DataNodeVector allImagesOfLesion; // 1. get all segmentations that define the lesion // 2. retrieve the parent node (source) of the found segmentation node DataNodeVector allSegmentationsOfLesion = GetAllSegmentationsOfLesion(caseID, lesion); for (const auto& segmentationNode : allSegmentationsOfLesion) { // get parent node of the current segmentation node with the node predicate DataStorage::SetOfObjects::ConstPointer parentNodes = m_DataStorage.Lock()->GetSources(segmentationNode, NodePredicates::GetImagePredicate(), false); for (auto it = parentNodes->Begin(); it != parentNodes->End(); ++it) { DataNode::Pointer dataNode = it->Value(); allImagesOfLesion.push_back(it->Value()); } } std::sort(allImagesOfLesion.begin(), allImagesOfLesion.end()); allImagesOfLesion.erase(std::unique(allImagesOfLesion.begin(), allImagesOfLesion.end()), allImagesOfLesion.end()); return allImagesOfLesion; } mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllSpecificImages(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const { if (SemanticRelationsInference::InstanceExists(caseID, controlPoint)) { if (SemanticRelationsInference::InstanceExists(caseID, informationType)) { // control point exists, information type exists, retrieve all images from the storage DataNodeVector allImagesOfCase = GetAllImagesOfCase(caseID); // filter all images to remove the ones with a different control point and information type using a lambda function auto lambda = [&controlPoint, &informationType](DataNode::Pointer imageNode) { return (informationType != SemanticRelationsInference::GetInformationTypeOfImage(imageNode)) || (controlPoint.date != SemanticRelationsInference::GetControlPointOfImage(imageNode).date); }; allImagesOfCase.erase(std::remove_if(allImagesOfCase.begin(), allImagesOfCase.end(), lambda), allImagesOfCase.end()); return allImagesOfCase; } else { - mitkThrowException(SemanticRelationException) << "Could not find an existing information type for the given caseID " << caseID << " and information type " << informationType; + mitkThrowException(SemanticRelationException) << "Could not find an existing information type for the given caseID " << caseID << " and information type " << informationType << "."; + } + } + else + { + mitkThrowException(SemanticRelationException) << "Could not find an existing control point for the given caseID " << caseID << " and control point " << controlPoint.UID << "."; + } +} + +mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllSpecificImages(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType, const SemanticTypes::ExaminationPeriod& examinationPeriod) const +{ + if (SemanticRelationsInference::InstanceExists(caseID, informationType)) + { + if (SemanticRelationsInference::InstanceExists(caseID, examinationPeriod)) + { + // examination period exists, information type exists, retrieve all imageIDs from the storage + auto allImageIDsOfExaminationPeriod = SemanticRelationsInference::GetAllImageIDsOfExaminationPeriod(caseID, examinationPeriod); + // filter all images to remove the ones with a different information type using a lambda function + auto lambda = [&caseID, &informationType, this](SemanticTypes::ID imageID) + { + return (informationType != RelationStorage::GetInformationTypeOfImage(caseID, imageID)); + }; + + allImageIDsOfExaminationPeriod.erase(std::remove_if(allImageIDsOfExaminationPeriod.begin(), allImageIDsOfExaminationPeriod.end(), lambda), allImageIDsOfExaminationPeriod.end()); + + auto allImagesOfExaminationPeriod = GetAllImagesByID(allImageIDsOfExaminationPeriod); + return allImagesOfExaminationPeriod; + } + else + { + mitkThrowException(SemanticRelationException) << "Could not find an existing examination period for the given caseID " << caseID << " and examination period " << examinationPeriod.name << "."; } } else { - mitkThrowException(SemanticRelationException) << "Could not find an existing control point for the given caseID " << caseID << " and control point " << controlPoint.UID; + mitkThrowException(SemanticRelationException) << "Could not find an existing information type for the given caseID " << caseID << " and information type " << informationType << "."; } } mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllSpecificSegmentations(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const { if (m_DataStorage.IsExpired()) { mitkThrow() << "Not a valid data storage."; } - DataNodeVector allSpecificImages = GetAllSpecificImages(caseID, controlPoint, informationType); + DataNodeVector allSpecificImages; + try + { + allSpecificImages = GetAllSpecificImages(caseID, controlPoint, informationType); + + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot get the specific segmentation."; + } + DataNodeVector allSpecificSegmentations; for (const auto& imageNode : allSpecificImages) { DataStorage::SetOfObjects::ConstPointer segmentationNodes = m_DataStorage.Lock()->GetDerivations(imageNode, NodePredicates::GetSegmentationPredicate(), false); for (auto it = segmentationNodes->Begin(); it != segmentationNodes->End(); ++it) { allSpecificSegmentations.push_back(it->Value()); } } return allSpecificSegmentations; } + +mitk::DataNode::Pointer mitk::SemanticRelationsDataStorageAccess::GetSpecificSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, + const SemanticTypes::InformationType& informationType, const SemanticTypes::Lesion& lesion) const +{ + if (m_DataStorage.IsExpired()) + { + mitkThrow() << "Not a valid data storage."; + } + + DataNodeVector allSpecificSegmentations; + try + { + allSpecificSegmentations = GetAllSpecificSegmentations(caseID, controlPoint, informationType); + + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot get the specific segmentation."; + } + + for (const auto& segmentationNode : allSpecificSegmentations) + { + SemanticTypes::Lesion representedLesion = SemanticRelationsInference::GetLesionOfSegmentation(segmentationNode); + if (representedLesion.UID == lesion.UID) + { + return segmentationNode; + } + } + + return mitk::DataNode::Pointer(); +} diff --git a/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp b/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp index 21dfdb3301..a974ed4571 100644 --- a/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp +++ b/Modules/SemanticRelations/src/mitkSemanticRelationsInference.cpp @@ -1,528 +1,591 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSemanticRelationsInference.h" // semantic relations module #include "mitkControlPointManager.h" #include "mitkDICOMHelper.h" #include "mitkNodePredicates.h" #include "mitkRelationStorage.h" #include "mitkSemanticRelationException.h" /************************************************************************/ /* functions to get instances / attributes */ /************************************************************************/ mitk::SemanticTypes::LesionClassVector mitk::SemanticRelationsInference::GetAllLesionClassesOfCase(const SemanticTypes::CaseID& caseID) { SemanticTypes::LesionVector allLesionsOfCase = RelationStorage::GetAllLesionsOfCase(caseID); SemanticTypes::LesionClassVector allLesionClassesOfCase; for (const auto& lesion : allLesionsOfCase) { allLesionClassesOfCase.push_back(lesion.lesionClass); } // remove duplicate entries auto lessThan = [](const SemanticTypes::LesionClass& lesionClassLeft, const SemanticTypes::LesionClass& lesionClassRight) { return lesionClassLeft.UID < lesionClassRight.UID; }; auto equal = [](const SemanticTypes::LesionClass& lesionClassLeft, const SemanticTypes::LesionClass& lesionClassRight) { return lesionClassLeft.UID == lesionClassRight.UID; }; std::sort(allLesionClassesOfCase.begin(), allLesionClassesOfCase.end(), lessThan); allLesionClassesOfCase.erase(std::unique(allLesionClassesOfCase.begin(), allLesionClassesOfCase.end(), equal), allLesionClassesOfCase.end()); return allLesionClassesOfCase; } mitk::SemanticTypes::Lesion mitk::SemanticRelationsInference::GetLesionOfSegmentation(const DataNode* segmentationNode) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } SemanticTypes::CaseID caseID = ""; SemanticTypes::ID segmentationID = ""; try { caseID = GetCaseIDFromDataNode(segmentationNode); segmentationID = GetIDFromDataNode(segmentationNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot get the lesion of the given segmentation data node."; } return RelationStorage::GetLesionOfSegmentation(caseID, segmentationID); } mitk::SemanticTypes::LesionVector mitk::SemanticRelationsInference::GetAllLesionsOfImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID = ""; SemanticTypes::ID imageID = ""; try { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot get all lesions of the given image data node."; } SemanticTypes::LesionVector allLesionsOfImage; // 1. get all segmentations that are connected to the given image // 2. get the lesion of each segmentation // 3. guarantee uniqueness of lesions SemanticTypes::IDVector allSegmentationIDsOfImage = RelationStorage::GetAllSegmentationIDsOfImage(caseID, imageID); for (const auto& segmentationID : allSegmentationIDsOfImage) { // get represented lesion of the current segmentation SemanticTypes::Lesion representedLesion = RelationStorage::GetLesionOfSegmentation(caseID, segmentationID); if (!representedLesion.UID.empty()) { allLesionsOfImage.push_back(representedLesion); } } // remove duplicate entries auto lessThan = [](const SemanticTypes::Lesion& lesionLeft, const SemanticTypes::Lesion& lesionRight) { return lesionLeft.UID < lesionRight.UID; }; auto equal = [](const SemanticTypes::Lesion& lesionLeft, const SemanticTypes::Lesion& lesionRight) { return lesionLeft.UID == lesionRight.UID; }; std::sort(allLesionsOfImage.begin(), allLesionsOfImage.end(), lessThan); allLesionsOfImage.erase(std::unique(allLesionsOfImage.begin(), allLesionsOfImage.end(), equal), allLesionsOfImage.end()); return allLesionsOfImage; } mitk::SemanticTypes::LesionVector mitk::SemanticRelationsInference::GetAllLesionsOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { SemanticTypes::LesionVector allLesions = RelationStorage::GetAllLesionsOfCase(caseID); // filter the lesions: use only those, where the associated data is connected to image data that refers to the given control point using a lambda function auto lambda = [&caseID, &controlPoint](const SemanticTypes::Lesion& lesion) { return !SpecificImageExists(caseID, lesion, controlPoint); }; allLesions.erase(std::remove_if(allLesions.begin(), allLesions.end(), lambda), allLesions.end()); return allLesions; } +mitk::SemanticTypes::LesionVector mitk::SemanticRelationsInference::GetAllLesionsOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) +{ + SemanticTypes::LesionVector allLesions = RelationStorage::GetAllLesionsOfCase(caseID); + + // filter the lesions: use only those, where the associated data is connected to image data that refers to the given information type using a lambda function + auto lambda = [&caseID, &informationType](const SemanticTypes::Lesion& lesion) + { + return !SpecificImageExists(caseID, lesion, informationType); + }; + + allLesions.erase(std::remove_if(allLesions.begin(), allLesions.end(), lambda), allLesions.end()); + + return allLesions; +} + +mitk::SemanticTypes::LesionVector mitk::SemanticRelationsInference::GetAllSpecificLesions(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) +{ + auto allLesionsOfControlPoint = GetAllLesionsOfControlPoint(caseID, controlPoint); + auto allLesionsOfInformationType = GetAllLesionsOfInformationType(caseID, informationType); + SemanticTypes::LesionVector allLesionsIntersection; + + auto lessThan = [](const SemanticTypes::Lesion& lesionLeft, const SemanticTypes::Lesion& lesionRight) + { + return lesionLeft.UID < lesionRight.UID; + }; + + auto equal = [](const SemanticTypes::Lesion& lesionLeft, const SemanticTypes::Lesion& lesionRight) + { + return lesionLeft.UID == lesionRight.UID; + }; + + std::sort(allLesionsOfControlPoint.begin(), allLesionsOfControlPoint.end(), lessThan); + std::sort(allLesionsOfInformationType.begin(), allLesionsOfInformationType.end(), lessThan); + SemanticTypes::IDVector allImageIDsIntersection; + // set_intersection removes duplicated nodes + std::set_intersection(allLesionsOfControlPoint.begin(), allLesionsOfControlPoint.end(), + allLesionsOfInformationType.begin(), allLesionsOfInformationType.end(), + std::back_inserter(allLesionsIntersection), equal); + + return allLesionsIntersection; +} + bool mitk::SemanticRelationsInference::IsRepresentingALesion(const DataNode* segmentationNode) { SemanticTypes::Lesion representedLesion; try { representedLesion = GetLesionOfSegmentation(segmentationNode); } catch (const SemanticRelationException&) { return false; } return !representedLesion.UID.empty(); } bool mitk::SemanticRelationsInference::IsRepresentingALesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID) { SemanticTypes::Lesion representedLesion = RelationStorage::GetLesionOfSegmentation(caseID, segmentationID); return !representedLesion.UID.empty(); } bool mitk::SemanticRelationsInference::IsLesionPresent(const SemanticTypes::Lesion& lesion, const DataNode* dataNode) { SemanticTypes::CaseID caseID = ""; SemanticTypes::ID dataNodeID = ""; try { caseID = GetCaseIDFromDataNode(dataNode); dataNodeID = GetIDFromDataNode(dataNode); } catch (const SemanticRelationException&) { return false; } if (NodePredicates::GetImagePredicate()->CheckNode(dataNode)) { return IsLesionPresentOnImage(caseID, lesion, dataNodeID); } if (NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { return IsLesionPresentOnSegmentation(caseID, lesion, dataNodeID); } return false; } bool mitk::SemanticRelationsInference::IsLesionPresentOnImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ID& imageID) { SemanticTypes::IDVector allImageIDsOfLesion; try { allImageIDsOfLesion = GetAllImageIDsOfLesion(caseID, lesion); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot get all image IDs of the given lesion to determine the lesion presence."; } for (const auto& imageIDOfLesion : allImageIDsOfLesion) { if (imageIDOfLesion == imageID) { return true; } } return false; } bool mitk::SemanticRelationsInference::IsLesionPresentOnSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ID& segmentationID) { const auto representedLesion = RelationStorage::GetLesionOfSegmentation(caseID, segmentationID); return lesion.UID == representedLesion.UID; } bool mitk::SemanticRelationsInference::IsLesionPresentAtControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ControlPoint& controlPoint) { SemanticTypes::IDVector allImageIDsOfLesion; try { allImageIDsOfLesion = GetAllImageIDsOfLesion(caseID, lesion); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot get all image IDs of the given lesion to determine the lesion presence."; } for (const auto& imageIDOfLesion : allImageIDsOfLesion) { - auto imageControlPoint = mitk::RelationStorage::GetControlPointOfImage(caseID, imageIDOfLesion); + auto imageControlPoint = RelationStorage::GetControlPointOfImage(caseID, imageIDOfLesion); if (imageControlPoint.date == controlPoint.date) { return true; } } return false; } bool mitk::SemanticRelationsInference::InstanceExists(const DataNode* dataNode) { SemanticTypes::CaseID caseID = ""; SemanticTypes::ID dataNodeID = ""; try { caseID = GetCaseIDFromDataNode(dataNode); dataNodeID = GetIDFromDataNode(dataNode); } catch (const SemanticRelationException&) { return false; } if (NodePredicates::GetImagePredicate()->CheckNode(dataNode)) { - std::vector allImageIDsOfCase = RelationStorage::GetAllImageIDsOfCase(caseID); + SemanticTypes::IDVector allImageIDsOfCase = RelationStorage::GetAllImageIDsOfCase(caseID); return std::find(allImageIDsOfCase.begin(), allImageIDsOfCase.end(), dataNodeID) != allImageIDsOfCase.end(); } if (NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { - std::vector allSegmentationIDsOfCase = RelationStorage::GetAllSegmentationIDsOfCase(caseID); + SemanticTypes::IDVector allSegmentationIDsOfCase = RelationStorage::GetAllSegmentationIDsOfCase(caseID); return std::find(allSegmentationIDsOfCase.begin(), allSegmentationIDsOfCase.end(), dataNodeID) != allSegmentationIDsOfCase.end(); } return false; } bool mitk::SemanticRelationsInference::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { SemanticTypes::LesionVector allLesions = RelationStorage::GetAllLesionsOfCase(caseID); // filter all lesions: check for equality with the given lesion using a lambda function auto lambda = [&lesion](const SemanticTypes::Lesion& currentLesion) { return currentLesion.UID == lesion.UID; }; const auto existingLesion = std::find_if(allLesions.begin(), allLesions.end(), lambda); return existingLesion != allLesions.end(); } mitk::SemanticTypes::IDVector mitk::SemanticRelationsInference::GetAllImageIDsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { if (!InstanceExists(caseID, lesion)) { mitkThrowException(SemanticRelationException) << "Could not find an existing lesion instance for the given caseID " << caseID << " and lesion " << lesion.UID << "."; } SemanticTypes::IDVector allImageIDsOfLesion; // 1. get all segmentations that define the lesion // 2. get the parentID (imageID) of each segmentation // 3. guarantee uniqueness of image IDs SemanticTypes::IDVector allSegmentationIDsOfLesion = RelationStorage::GetAllSegmentationIDsOfLesion(caseID, lesion); for (const auto& segmentationID : allSegmentationIDsOfLesion) { // get parent ID of the current segmentation ID SemanticTypes::ID imageID = RelationStorage::GetImageIDOfSegmentation(caseID, segmentationID); if(!imageID.empty()) { allImageIDsOfLesion.push_back(imageID); } } std::sort(allImageIDsOfLesion.begin(), allImageIDsOfLesion.end()); allImageIDsOfLesion.erase(std::unique(allImageIDsOfLesion.begin(), allImageIDsOfLesion.end()), allImageIDsOfLesion.end()); return allImageIDsOfLesion; } +mitk::SemanticTypes::IDVector mitk::SemanticRelationsInference::GetAllImageIDsOfExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) +{ + if (!InstanceExists(caseID, examinationPeriod)) + { + mitkThrowException(SemanticRelationException) << "Could not find an existing examination period for the given caseID " << caseID << " and examination period " << examinationPeriod.name << "."; + } + + SemanticTypes::IDVector allImageIDsOfExaminationPeriod; + // 1. get all control point UIDs of the examination period + // 2. get all images of each control points to find all images of the examination period + SemanticTypes::ControlPoint controlPoint; + for (const auto& controlPointUID : examinationPeriod.controlPointUIDs) + { + controlPoint = GetControlPointByUID(caseID, controlPointUID); + auto allImageIDsOfControlPoint = RelationStorage::GetAllImageIDsOfControlPoint(caseID, controlPoint); + allImageIDsOfExaminationPeriod.insert(allImageIDsOfExaminationPeriod.end(), allImageIDsOfControlPoint.begin(), allImageIDsOfControlPoint.end()); + } + + return allImageIDsOfExaminationPeriod; +} + mitk::SemanticTypes::ControlPoint mitk::SemanticRelationsInference::GetControlPointOfImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } SemanticTypes::CaseID caseID = ""; SemanticTypes::ID imageID = ""; try { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot get the control point of the given image data node."; } return RelationStorage::GetControlPointOfImage(caseID, imageID); } mitk::SemanticTypes::ControlPointVector mitk::SemanticRelationsInference::GetAllControlPointsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); // filter the control points: use only those, where the associated image data has a segmentation that refers to the given lesion using a lambda function auto lambda = [&caseID, &lesion](const SemanticTypes::ControlPoint& controlPoint) { return !SpecificImageExists(caseID, lesion, controlPoint); }; allControlPoints.erase(std::remove_if(allControlPoints.begin(), allControlPoints.end(), lambda), allControlPoints.end()); return allControlPoints; } mitk::SemanticTypes::ControlPointVector mitk::SemanticRelationsInference::GetAllControlPointsOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) { SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); // filter the control points: use only those, where the associated image data refers to the given information type using a lambda function auto lambda = [&caseID, &informationType](const SemanticTypes::ControlPoint& controlPoint) { return !SpecificImageExists(caseID, informationType, controlPoint); }; allControlPoints.erase(std::remove_if(allControlPoints.begin(), allControlPoints.end(), lambda), allControlPoints.end()); return allControlPoints; } bool mitk::SemanticRelationsInference::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); // filter all control points: check for equality with the given control point using a lambda function auto lambda = [&controlPoint](const SemanticTypes::ControlPoint& currentControlPoint) { return currentControlPoint.UID == controlPoint.UID; }; const auto existingControlPoint = std::find_if(allControlPoints.begin(), allControlPoints.end(), lambda); if (existingControlPoint != allControlPoints.end()) { return true; } else { return false; } } bool mitk::SemanticRelationsInference::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) { SemanticTypes::ExaminationPeriodVector allExaminationPeriods = RelationStorage::GetAllExaminationPeriodsOfCase(caseID); // filter all examination periods: check for equality with the given examination period using a lambda function auto lambda = [&examinationPeriod](const SemanticTypes::ExaminationPeriod& currentExaminationPeriod) { return currentExaminationPeriod.UID == examinationPeriod.UID; }; const auto existingExaminationPeriod = std::find_if(allExaminationPeriods.begin(), allExaminationPeriods.end(), lambda); if (existingExaminationPeriod != allExaminationPeriods.end()) { return true; } else { return false; } } mitk::SemanticTypes::InformationType mitk::SemanticRelationsInference::GetInformationTypeOfImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID = ""; SemanticTypes::ID imageID = ""; try { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot get the information type of the given image data node."; } return RelationStorage::GetInformationTypeOfImage(caseID, imageID); } mitk::SemanticTypes::InformationTypeVector mitk::SemanticRelationsInference::GetAllInformationTypesOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { SemanticTypes::InformationTypeVector allInformationTypes = RelationStorage::GetAllInformationTypesOfCase(caseID); // filter the information types: use only those, where the associated data refers to the given control point using a lambda function auto lambda = [&caseID, &controlPoint](const SemanticTypes::InformationType& informationType) { return !SpecificImageExists(caseID, informationType, controlPoint); }; allInformationTypes.erase(std::remove_if(allInformationTypes.begin(), allInformationTypes.end(), lambda), allInformationTypes.end()); return allInformationTypes; } bool mitk::SemanticRelationsInference::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) { SemanticTypes::InformationTypeVector allInformationTypes = RelationStorage::GetAllInformationTypesOfCase(caseID); // filter all information types: check for equality with the given information type using a lambda function auto lambda = [&informationType](const SemanticTypes::InformationType& currentInformationType) { return currentInformationType == informationType; }; const auto existingInformationType = std::find_if(allInformationTypes.begin(), allInformationTypes.end(), lambda); if (existingInformationType != allInformationTypes.end()) { return true; } else { return false; } } bool mitk::SemanticRelationsInference::SpecificImageExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::InformationType& informationType) { SemanticTypes::IDVector allImageIDsOfLesion; try { allImageIDsOfLesion = GetAllImageIDsOfLesion(caseID, lesion); } catch (const SemanticRelationException&) { return false; } SemanticTypes::IDVector allImageIDsOfInformationType = RelationStorage::GetAllImageIDsOfInformationType(caseID, informationType); std::sort(allImageIDsOfLesion.begin(), allImageIDsOfLesion.end()); std::sort(allImageIDsOfInformationType.begin(), allImageIDsOfInformationType.end()); SemanticTypes::IDVector allImageIDsIntersection; // set_intersection removes duplicated nodes, since 'GetAllImageIDsOfInformationType' only contains at most one of each node std::set_intersection(allImageIDsOfLesion.begin(), allImageIDsOfLesion.end(), allImageIDsOfInformationType.begin(), allImageIDsOfInformationType.end(), std::back_inserter(allImageIDsIntersection)); // if the vector of intersecting image IDs is empty, the information type does not contain the lesion return !allImageIDsIntersection.empty(); } bool mitk::SemanticRelationsInference::SpecificImageExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ControlPoint& controlPoint) { SemanticTypes::IDVector allImageIDsOfLesion; try { allImageIDsOfLesion = GetAllImageIDsOfLesion(caseID, lesion); } catch (const SemanticRelationException&) { return false; } SemanticTypes::IDVector allImageIDsOfControlPoint = RelationStorage::GetAllImageIDsOfControlPoint(caseID, controlPoint); std::sort(allImageIDsOfLesion.begin(), allImageIDsOfLesion.end()); std::sort(allImageIDsOfControlPoint.begin(), allImageIDsOfControlPoint.end()); SemanticTypes::IDVector allImageIDsIntersection; // set_intersection removes duplicated nodes, since 'GetAllImageIDsOfControlPoint' only contains at most one of each node std::set_intersection(allImageIDsOfLesion.begin(), allImageIDsOfLesion.end(), allImageIDsOfControlPoint.begin(), allImageIDsOfControlPoint.end(), std::back_inserter(allImageIDsIntersection)); // if the vector of intersecting image IDs is empty, the control point does not contain the lesion return !allImageIDsIntersection.empty(); } bool mitk::SemanticRelationsInference::SpecificImageExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType, const SemanticTypes::ControlPoint& controlPoint) { SemanticTypes::IDVector allImageIDsOfInformationType = RelationStorage::GetAllImageIDsOfInformationType(caseID, informationType); SemanticTypes::IDVector allImageIDsOfControlPoint = RelationStorage::GetAllImageIDsOfControlPoint(caseID, controlPoint); std::sort(allImageIDsOfInformationType.begin(), allImageIDsOfInformationType.end()); std::sort(allImageIDsOfControlPoint.begin(), allImageIDsOfControlPoint.end()); SemanticTypes::IDVector allImageIDsIntersection; // set_intersection removes duplicated nodes std::set_intersection(allImageIDsOfInformationType.begin(), allImageIDsOfInformationType.end(), allImageIDsOfControlPoint.begin(), allImageIDsOfControlPoint.end(), std::back_inserter(allImageIDsIntersection)); // if the vector of intersecting image IDs is empty no image exists for the given information type and control point return !allImageIDsIntersection.empty(); } diff --git a/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp b/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp index 6e65122cb3..7173530030 100644 --- a/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp +++ b/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp @@ -1,592 +1,610 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSemanticRelationsIntegration.h" // semantic relations module #include "mitkControlPointManager.h" #include "mitkDICOMHelper.h" #include "mitkNodePredicates.h" #include "mitkRelationStorage.h" #include "mitkSemanticRelationException.h" #include "mitkSemanticRelationsInference.h" #include "mitkUIDGeneratorBoost.h" // multi label module #include // c++ #include #include std::vector mitk::SemanticRelationsIntegration::m_ObserverVector; void mitk::SemanticRelationsIntegration::AddObserver(ISemanticRelationsObserver* observer) { std::vector::iterator existingObserver = std::find(m_ObserverVector.begin(), m_ObserverVector.end(), observer); if (existingObserver != m_ObserverVector.end()) { // no need to add the already existing observer return; } m_ObserverVector.push_back(observer); } void mitk::SemanticRelationsIntegration::RemoveObserver(ISemanticRelationsObserver* observer) { m_ObserverVector.erase(std::remove(m_ObserverVector.begin(), m_ObserverVector.end(), observer), m_ObserverVector.end()); } /************************************************************************/ /* functions to add / remove instances / attributes */ /************************************************************************/ void mitk::SemanticRelationsIntegration::AddImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID; SemanticTypes::ID imageID; SemanticTypes::InformationType informationType; SemanticTypes::ControlPoint controlPoint; try // retrieve information { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); informationType = GetDICOMModalityFromDataNode(imageNode); controlPoint = GenerateControlPoint(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot add the given image data node."; } try // add and set information { RelationStorage::AddCase(caseID); RelationStorage::AddImage(caseID, imageID); AddInformationTypeToImage(imageNode, informationType); SetControlPointOfImage(imageNode, controlPoint); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot add the given image data node."; } } void mitk::SemanticRelationsIntegration::RemoveImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID; SemanticTypes::ID imageID; try // retrieve information { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot remove the given image data node."; } - try // add and set information + try { RemoveInformationTypeFromImage(imageNode); UnlinkImageFromControlPoint(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot remove the given image data node."; } RelationStorage::RemoveImage(caseID, imageID); NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::AddLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { if (SemanticRelationsInference::InstanceExists(caseID, lesion)) { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to add already exists for the given case."; } RelationStorage::AddLesion(caseID, lesion); NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::OverwriteLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { if (SemanticRelationsInference::InstanceExists(caseID, lesion)) { RelationStorage::OverwriteLesion(caseID, lesion); NotifyObserver(caseID); } else { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to overwrite does not exist for the given case."; } } void mitk::SemanticRelationsIntegration::AddLesionAndLinkSegmentation(const DataNode* segmentationNode, const SemanticTypes::Lesion& lesion) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } SemanticTypes::CaseID caseID; try { caseID = GetCaseIDFromDataNode(segmentationNode); AddLesion(caseID, lesion); LinkSegmentationToLesion(segmentationNode, lesion); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot add given lesion and link the given segmentation data node."; } NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::RemoveLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { if (SemanticRelationsInference::InstanceExists(caseID, lesion)) { SemanticTypes::IDVector allSegmentationIDsOfLesion = RelationStorage::GetAllSegmentationIDsOfLesion(caseID, lesion); if (allSegmentationIDsOfLesion.empty()) { // no more segmentations are linked to the specific lesion // the lesion can be removed from the storage RelationStorage::RemoveLesion(caseID, lesion); NotifyObserver(caseID); } else { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to remove is still referred to by a segmentation node. Lesion will not be removed."; } } else { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to remove does not exist for the given case."; } } void mitk::SemanticRelationsIntegration::AddSegmentation(const DataNode* segmentationNode, const DataNode* parentNode) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } if (nullptr == parentNode) { mitkThrowException(SemanticRelationException) << "Not a valid parent data node."; } SemanticTypes::CaseID caseID; SemanticTypes::ID segmentationNodeID; SemanticTypes::ID parentNodeID; try { caseID = GetCaseIDFromDataNode(segmentationNode); segmentationNodeID = GetIDFromDataNode(segmentationNode); parentNodeID = GetIDFromDataNode(parentNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot add the given segmentation data node."; } RelationStorage::AddSegmentation(caseID, segmentationNodeID, parentNodeID); NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::LinkSegmentationToLesion(const DataNode* segmentationNode, const SemanticTypes::Lesion& lesion) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } SemanticTypes::CaseID caseID; SemanticTypes::ID segmentationID; try { caseID = GetCaseIDFromDataNode(segmentationNode); segmentationID = GetIDFromDataNode(segmentationNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot link the given segmentation data node to the given lesion."; } if (SemanticRelationsInference::InstanceExists(caseID, lesion)) { RelationStorage::LinkSegmentationToLesion(caseID, segmentationID, lesion); NotifyObserver(caseID); } else { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to link does not exist for the given case."; } } void mitk::SemanticRelationsIntegration::UnlinkSegmentationFromLesion(const DataNode* segmentationNode) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } SemanticTypes::CaseID caseID; SemanticTypes::ID segmentationID; try { caseID = GetCaseIDFromDataNode(segmentationNode); segmentationID = GetIDFromDataNode(segmentationNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot unlink the given segmentation data node from its lesion."; } RelationStorage::UnlinkSegmentationFromLesion(caseID, segmentationID); NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::RemoveSegmentation(const DataNode* segmentationNode) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } SemanticTypes::CaseID caseID; SemanticTypes::ID segmentationNodeID; try { caseID = GetCaseIDFromDataNode(segmentationNode); segmentationNodeID = GetIDFromDataNode(segmentationNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot remove the given segmentation data node."; } + try + { + UnlinkSegmentationFromLesion(segmentationNode); + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot remove the given segmentation data node."; + } + RelationStorage::RemoveSegmentation(caseID, segmentationNodeID); NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::SetControlPointOfImage(const DataNode* imageNode, const SemanticTypes::ControlPoint& controlPoint) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID; try { caseID = GetCaseIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot set the given control point for the given image data node."; } SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); // need to check if an already existing control point fits/contains the user control point - SemanticTypes::ControlPoint existingControlPoint = FindExistingControlPoint(controlPoint, allControlPoints); + SemanticTypes::ControlPoint existingControlPoint = FindExistingControlPoint(caseID, controlPoint); try { if (!existingControlPoint.UID.empty()) { // found an already existing control point LinkImageToControlPoint(imageNode, existingControlPoint, false); } else { - AddControlPointAndLinkImage(imageNode, controlPoint, false); - // added a new control point // find closest control point to add the new control point to the correct examination period - SemanticTypes::ControlPoint closestControlPoint = FindClosestControlPoint(controlPoint, allControlPoints); - SemanticTypes::ExaminationPeriodVector allExaminationPeriods = RelationStorage::GetAllExaminationPeriodsOfCase(caseID); - SemanticTypes::ExaminationPeriod examinationPeriod = FindExaminationPeriod(closestControlPoint, allExaminationPeriods); + SemanticTypes::ControlPoint closestControlPoint = FindClosestControlPoint(caseID, controlPoint); + SemanticTypes::ExaminationPeriod examinationPeriod = FindContainingExaminationPeriod(caseID, closestControlPoint); if (examinationPeriod.UID.empty()) { // no closest control point (exceed threshold) or no examination period found // create a new examination period for this control point and add it to the storage examinationPeriod.UID = UIDGeneratorBoost::GenerateUID(); - examinationPeriod.name = "New examination period " + std::to_string(allExaminationPeriods.size()); + examinationPeriod.name = "New examination period"; AddExaminationPeriod(caseID, examinationPeriod); } + // added a new control point + AddControlPointAndLinkImage(imageNode, controlPoint, false); // add the control point to the (newly created or found / close) examination period AddControlPointToExaminationPeriod(caseID, controlPoint, examinationPeriod); } } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot set the given control point for the given image data node."; } ClearControlPoints(caseID); NotifyObserver(caseID); - } void mitk::SemanticRelationsIntegration::AddControlPointAndLinkImage(const DataNode* imageNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID; try { caseID = GetCaseIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot add the given control point and link the given image data node."; } if (SemanticRelationsInference::InstanceExists(caseID, controlPoint)) { mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to add already exists for the given case. \n Use 'LinkImageToControlPoint' instead."; } RelationStorage::AddControlPoint(caseID, controlPoint); try { LinkImageToControlPoint(imageNode, controlPoint, checkConsistence); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot add the given control point and link the given image data node."; } } void mitk::SemanticRelationsIntegration::LinkImageToControlPoint(const DataNode* imageNode, const SemanticTypes::ControlPoint& controlPoint, bool /*checkConsistence*/) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID; SemanticTypes::ID imageID; try { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot link the image data node to the given control point."; } if (SemanticRelationsInference::InstanceExists(caseID, controlPoint)) { RelationStorage::LinkImageToControlPoint(caseID, imageID, controlPoint); } else { mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to link does not exist for the given case."; } } void mitk::SemanticRelationsIntegration::UnlinkImageFromControlPoint(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID = ""; SemanticTypes::ID imageID = ""; try { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot unlink the given image data node from its control point."; } - SemanticTypes::ControlPoint controlPoint = RelationStorage::GetControlPointOfImage(caseID, imageID); RelationStorage::UnlinkImageFromControlPoint(caseID, imageID); ClearControlPoints(caseID); } void mitk::SemanticRelationsIntegration::AddExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) { if (SemanticRelationsInference::InstanceExists(caseID, examinationPeriod)) { mitkThrowException(SemanticRelationException) << "The examination period " << examinationPeriod.UID << " to add already exists for the given case."; } else { RelationStorage::AddExaminationPeriod(caseID, examinationPeriod); } } +void mitk::SemanticRelationsIntegration::RenameExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) +{ + if (SemanticRelationsInference::InstanceExists(caseID, examinationPeriod)) + { + RelationStorage::RenameExaminationPeriod(caseID, examinationPeriod); + NotifyObserver(caseID); + } + else + { + mitkThrowException(SemanticRelationException) << "The examination period " << examinationPeriod.UID << " to overwrite does not exist for the given case."; + } +} + void mitk::SemanticRelationsIntegration::AddControlPointToExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod& examinationPeriod) { if (!SemanticRelationsInference::InstanceExists(caseID, controlPoint)) { mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to add does not exist for the given case."; } if (!SemanticRelationsInference::InstanceExists(caseID, examinationPeriod)) { mitkThrowException(SemanticRelationException) << "The examination period " << examinationPeriod.UID << " does not exist for the given case. \n Use 'AddExaminationPeriod' before."; } RelationStorage::AddControlPointToExaminationPeriod(caseID, controlPoint, examinationPeriod); } void mitk::SemanticRelationsIntegration::SetInformationType(const DataNode* imageNode, const SemanticTypes::InformationType& informationType) { SemanticTypes::CaseID caseID; try { caseID = GetCaseIDFromDataNode(imageNode); RemoveInformationTypeFromImage(imageNode); AddInformationTypeToImage(imageNode, informationType); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot set the given information type for the given image data node."; } NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::AddInformationTypeToImage(const DataNode* imageNode, const SemanticTypes::InformationType& informationType) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID = ""; SemanticTypes::ID imageID = ""; try { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot add the given information type to the given image data node."; } RelationStorage::AddInformationTypeToImage(caseID, imageID, informationType); } void mitk::SemanticRelationsIntegration::RemoveInformationTypeFromImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID = ""; SemanticTypes::ID imageID = ""; try { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot remove the information type from the given image data node."; } SemanticTypes::InformationType originalInformationType = RelationStorage::GetInformationTypeOfImage(caseID, imageID); RelationStorage::RemoveInformationTypeFromImage(caseID, imageID); // check for further references to the removed information type - std::vector allImageIDsVectorValue = RelationStorage::GetAllImageIDsOfCase(caseID); + SemanticTypes::IDVector allImageIDsVectorValue = RelationStorage::GetAllImageIDsOfCase(caseID); for (const auto& otherImageID : allImageIDsVectorValue) { SemanticTypes::InformationType otherInformationType = RelationStorage::GetInformationTypeOfImage(caseID, otherImageID); if (otherInformationType == originalInformationType) { // found the information type in another image -> cannot remove the information type from the case return; } } // given information type was not referred by any other image of the case -> the information type can be removed from the case RelationStorage::RemoveInformationType(caseID, originalInformationType); } /************************************************************************/ /* private functions */ /************************************************************************/ void mitk::SemanticRelationsIntegration::NotifyObserver(const SemanticTypes::CaseID& caseID) const { for (auto& observer : m_ObserverVector) { observer->Update(caseID); } } void mitk::SemanticRelationsIntegration::ClearControlPoints(const SemanticTypes::CaseID& caseID) { SemanticTypes::ControlPointVector allControlPointsOfCase = RelationStorage::GetAllControlPointsOfCase(caseID); - std::vector allImageIDsVectorValue = RelationStorage::GetAllImageIDsOfCase(caseID); + SemanticTypes::IDVector allImageIDsVectorValue = RelationStorage::GetAllImageIDsOfCase(caseID); SemanticTypes::ControlPointVector referencedControlPoints; for (const auto& imageID : allImageIDsVectorValue) { - auto controlPointOfImage = RelationStorage::GetControlPointOfImage(caseID, imageID); + SemanticTypes::ControlPoint controlPointOfImage = RelationStorage::GetControlPointOfImage(caseID, imageID); referencedControlPoints.push_back(controlPointOfImage); } std::sort(allControlPointsOfCase.begin(), allControlPointsOfCase.end()); std::sort(referencedControlPoints.begin(), referencedControlPoints.end()); SemanticTypes::ControlPointVector nonReferencedControlPoints; std::set_difference(allControlPointsOfCase.begin(), allControlPointsOfCase.end(), referencedControlPoints.begin(), referencedControlPoints.end(), std::inserter(nonReferencedControlPoints, nonReferencedControlPoints.begin())); - auto allExaminationPeriods = RelationStorage::GetAllExaminationPeriodsOfCase(caseID); for (const auto& controlPoint : nonReferencedControlPoints) { - const auto& examinationPeriod = FindExaminationPeriod(controlPoint, allExaminationPeriods); + const SemanticTypes::ExaminationPeriod& examinationPeriod = FindContainingExaminationPeriod(caseID, controlPoint); RelationStorage::RemoveControlPointFromExaminationPeriod(caseID, controlPoint, examinationPeriod); RelationStorage::RemoveControlPoint(caseID, controlPoint); } } diff --git a/Modules/SemanticRelationsUI/files.cmake b/Modules/SemanticRelationsUI/files.cmake index 8208ef2d16..a10162f616 100644 --- a/Modules/SemanticRelationsUI/files.cmake +++ b/Modules/SemanticRelationsUI/files.cmake @@ -1,32 +1,36 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkModuleActivator.cpp QmitkAbstractSemanticRelationsStorageInspector.cpp QmitkAbstractSemanticRelationsStorageModel.cpp QmitkControlPointDialog.cpp QmitkLesionTextDialog.cpp QmitkLesionTreeItem.cpp QmitkLesionTreeModel.cpp QmitkPatientTableHeaderView.cpp QmitkPatientTableInspector.cpp QmitkPatientTableModel.cpp QmitkSemanticRelationsUIHelper.cpp + QmitkStatisticsCalculator.cpp + QmitkStatisticsTreeModel.cpp QmitkTableItemThumbnailDelegate.cpp ) set(MOC_H_FILES include/QmitkAbstractSemanticRelationsStorageInspector.h include/QmitkAbstractSemanticRelationsStorageModel.h include/QmitkControlPointDialog.h include/QmitkLesionTextDialog.h include/QmitkLesionTreeModel.h include/QmitkPatientTableHeaderView.h include/QmitkPatientTableInspector.h include/QmitkPatientTableModel.h + include/QmitkStatisticsCalculator.h + include/QmitkStatisticsTreeModel.h include/QmitkTableItemThumbnailDelegate.h ) set(UI_FILES src/QmitkPatientTableInspector.ui ) diff --git a/Modules/SemanticRelationsUI/include/QmitkAbstractSemanticRelationsStorageModel.h b/Modules/SemanticRelationsUI/include/QmitkAbstractSemanticRelationsStorageModel.h index b2e4ac65ad..543ff5a007 100644 --- a/Modules/SemanticRelationsUI/include/QmitkAbstractSemanticRelationsStorageModel.h +++ b/Modules/SemanticRelationsUI/include/QmitkAbstractSemanticRelationsStorageModel.h @@ -1,119 +1,115 @@ /*=================================================================== 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 QMITKABSTRACTSEMANTICRELATIONSSTORAGEMODEL_H #define QMITKABSTRACTSEMANTICRELATIONSSTORAGEMODEL_H // mitk semantic relations UI #include "MitkSemanticRelationsUIExports.h" // semantic relations module #include #include #include #include // qt widgets module #include "QmitkAbstractDataStorageModel.h" /* * @brief The QmitkAbstractSemanticRelationsStorageModel is a subclass of 'QmitkAbstractDataStorageModel' and provides additional * functionality to set and store a semantic relations instance, the current case ID and the current lesion. */ class MITKSEMANTICRELATIONSUI_EXPORT QmitkAbstractSemanticRelationsStorageModel : public QmitkAbstractDataStorageModel, public mitk::ISemanticRelationsObserver { Q_OBJECT public: QmitkAbstractSemanticRelationsStorageModel(QObject* parent = nullptr); virtual ~QmitkAbstractSemanticRelationsStorageModel(); /* - * @brief Update this model with the data from the semantic relations. + * @brief Update this model with the data from the semantic relations, + * if the case ID is equal to the currently selected case ID of the table model. * * Overridden from 'ISemanticRelationsObserver'. - * In order for the Update-function to be called, this model has to be added as a observer of SemanticRelation + * In order for the Update-function to be called, this model has to be added as an observer of SemanticRelation * (e.g. m_SemanticRelations->AddObserver(m_SemanticRelationsStorageModel);) * * @par caseID The current case ID to identify the currently active patient / case. */ virtual void Update(const mitk::SemanticTypes::CaseID& caseID) override; /** * @brief Set the current case ID which is needed to access the semantic relations storage. * * @param caseID A case ID as string */ void SetCaseID(const mitk::SemanticTypes::CaseID& caseID); const mitk::SemanticTypes::CaseID& GetCaseID() const { return m_CaseID; } /** * @brief Set the current lesion which can be used to show on which images the lesion is visible. * * @param lesion The selected lesion */ void SetLesion(const mitk::SemanticTypes::Lesion& lesion); const mitk::SemanticTypes::Lesion& GetLesion() const { return m_Lesion; } /** * @brief Set the current data node selection which can be used to show which lesions * are visible on the node selection. * * @param dataNodeSelection The selected data nodes */ void SetDataNodeSelection(const QList& dataNodeSelection); const QList& GetSelectedDataNodes() const { return m_SelectedDataNodes; }; /* - * @brief Update the semantic relations storage model with the current data from the semantic relations model, - * if the case ID is equal to the currently selected case ID of the table model. - */ - void UpdateModelData(const mitk::SemanticTypes::CaseID& caseID); - /* * @brief Update the semantic relations storage model with the current data from the semantic relations model * and the current case ID. */ void UpdateModelData(); Q_SIGNALS: void ModelUpdated(); protected: /** * @brief Create a new 'SemanticRelationsDataStorageAccess' instance with the new data storage and * update the model data. * This functions is called inside the 'SetDataStorage'-function from the parent class. */ virtual void DataStorageChanged() override; /** * @brief This function is called if the model data is updated. It can be used by subclasses to define * the way the data of a specific model is generated. It typically consists of access to the * semantic relations storage to retrieve certain information. */ virtual void SetData() = 0; std::unique_ptr m_SemanticRelationsDataStorageAccess; std::unique_ptr m_SemanticRelationsIntegration; mitk::SemanticTypes::CaseID m_CaseID; QList m_SelectedDataNodes; mitk::SemanticTypes::Lesion m_Lesion; }; #endif // QMITKABSTRACTSEMANTICRELATIONSSTORAGEMODEL_H diff --git a/Modules/SemanticRelationsUI/include/QmitkControlPointDialog.h b/Modules/SemanticRelationsUI/include/QmitkControlPointDialog.h index f8c23395da..10ff4208b5 100644 --- a/Modules/SemanticRelationsUI/include/QmitkControlPointDialog.h +++ b/Modules/SemanticRelationsUI/include/QmitkControlPointDialog.h @@ -1,46 +1,44 @@ /*=================================================================== 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 QMITKCONTROLPOINTDIALOG_H #define QMITKCONTROLPOINTDIALOG_H #include // semantic relations module #include "mitkSemanticTypes.h" #include #include class MITKSEMANTICRELATIONSUI_EXPORT QmitkControlPointDialog : public QDialog { Q_OBJECT public: QmitkControlPointDialog(QWidget *parent = nullptr); - virtual ~QmitkControlPointDialog(); - void SetCurrentDate(mitk::SemanticTypes::ControlPoint currentControlPoint); QDate GetCurrentDate() const; private: QDateEdit* m_DateEdit; }; #endif // QMITKCONTROLPOINTDIALOG_H diff --git a/Modules/SemanticRelationsUI/include/QmitkLesionTextDialog.h b/Modules/SemanticRelationsUI/include/QmitkLesionTextDialog.h index b50806adf9..b6a14a4429 100644 --- a/Modules/SemanticRelationsUI/include/QmitkLesionTextDialog.h +++ b/Modules/SemanticRelationsUI/include/QmitkLesionTextDialog.h @@ -1,49 +1,47 @@ /*=================================================================== 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/QmitkLesionTreeItem.h b/Modules/SemanticRelationsUI/include/QmitkLesionTreeItem.h index 2cd05eec35..306454f0f7 100644 --- a/Modules/SemanticRelationsUI/include/QmitkLesionTreeItem.h +++ b/Modules/SemanticRelationsUI/include/QmitkLesionTreeItem.h @@ -1,123 +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()); - ~QmitkLesionTreeItem(); /** * @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 899d912e61..f7653ec75d 100644 --- a/Modules/SemanticRelationsUI/include/QmitkLesionTreeModel.h +++ b/Modules/SemanticRelationsUI/include/QmitkLesionTreeModel.h @@ -1,98 +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); - ~QmitkLesionTreeModel(); ////////////////////////////////////////////////////////////////////////// // 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 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/QmitkPatientTableModel.h b/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h index 16a38ef02b..966f97a35e 100644 --- a/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h +++ b/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h @@ -1,134 +1,133 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QMITKPATIENTTABLEMODEL_H #define QMITKPATIENTTABLEMODEL_H // semantic relations UI module #include "QmitkAbstractSemanticRelationsStorageModel.h" // semantic relations module #include #include // mitk core #include // qt #include #include /** * @brief The QmitkPatientTableModel is a subclass of the QmitkAbstractSemanticRelationsStorageModel and holds the semantic relations data of the currently selected case. * * The QmitkPatientTableModel uses the 'data' function to return either the data node of a table cell or the thumbnail of the underlying image. * The horizontal header of the table shows the control points of the current case and the vertical header of the table shows the information types of the current case. * Using the 'GetFilteredData'-function of the SemanticRelations-class the model is able to retrieve the correct data node for each table entry. * * Additionally the model creates and holds the QPixmaps of the known data nodes in order to return a thumbnail, if needed. */ class QmitkPatientTableModel : public QmitkAbstractSemanticRelationsStorageModel { Q_OBJECT public: QmitkPatientTableModel(QObject* parent = nullptr); ~QmitkPatientTableModel(); ////////////////////////////////////////////////////////////////////////// // overridden functions from QAbstractItemModel ////////////////////////////////////////////////////////////////////////// virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; virtual QModelIndex parent(const QModelIndex& child) const override; virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override; virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; virtual Qt::ItemFlags flags(const QModelIndex& index) const override; ////////////////////////////////////////////////////////////////////////// /// end override ///////////////////////////////////////////////////////////////////////// void SetNodeType(const std::string& nodeType); protected: // the following functions have to be overridden... virtual void NodePredicateChanged() override; // but are not implemented in this model virtual void NodeAdded(const mitk::DataNode*) override { } virtual void NodeChanged(const mitk::DataNode*) override { } virtual void NodeRemoved(const mitk::DataNode*) override { } /** * @brief Overridden from 'QmitkAbstractSemanticRelationsStorageModel': This function retrieves all control points * and information types of the current case and stores them to define the header of the table. * Furthermore all images are retrieved and the pixmap of the images are generated and stored. */ virtual void SetData() override; private: void SetHeaderModel(); void SetPixmaps(); void SetLesionPresences(); /** * @brief The function uses the ID of the node to see if a pixmap was already set. If not, the given pixmap * is used and stored inside a member variable. If the pixmap was already set, it will be overwritten. * Using 'nullptr' as a pixmap will erase the entry for the given data node. * * @param dataNode The data node whose pixmap should be set * @param pixmapFromImage The pixmap that shows an image of the content of the data node */ void SetPixmapOfNode(const mitk::DataNode* dataNode, QPixmap* pixmapFromImage); /** * @brief The function uses the ID of the node to see if a lesion presence was already set. If not, the given * bool value is used and stored inside a member variable. If the lesion presence was already set, it * will be overwritten. * The function is used by the 'SetLesionPresences' function. * * @param dataNode The data node whose lesion presence should be set * @param lesionPresence The bool value that defines the lesion presence of the given data node */ void SetLesionPresenceOfNode(const mitk::DataNode* dataNode, bool lesionPresence); /** * @brief Returns the data node that is associated with the given table entry (index). * * The function uses the SemanticRelations-class and the current control point data and information type data to * filter the nodes of the current case. * The index is used to access the correct row in the table (information type) and the correct column in the table (control point). * * @par index The QModelIndex of the table entry */ mitk::DataNode* GetCurrentDataNode(const QModelIndex &index) const; std::map m_PixmapMap; std::map m_LesionPresence; - mitk::SemanticTypes::InformationTypeVector m_InformationTypes; - mitk::SemanticTypes::ControlPointVector m_ControlPoints; mitk::SemanticTypes::ExaminationPeriodVector m_ExaminationPeriods; + mitk::SemanticTypes::InformationTypeVector m_InformationTypes; mitk::SemanticRelationsDataStorageAccess::DataNodeVector m_CurrentDataNodes; std::string m_SelectedNodeType; QStandardItemModel* m_HeaderModel; }; #endif // QMITKPATIENTTABLEMODEL_H diff --git a/Modules/SemanticRelationsUI/include/QmitkStatisticsCalculator.h b/Modules/SemanticRelationsUI/include/QmitkStatisticsCalculator.h new file mode 100644 index 0000000000..9861660b0a --- /dev/null +++ b/Modules/SemanticRelationsUI/include/QmitkStatisticsCalculator.h @@ -0,0 +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 + +// mitk image statistics ui module +#include + +/* +* @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; + 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/QmitkLesionTreeModel.h b/Modules/SemanticRelationsUI/include/QmitkStatisticsTreeModel.h similarity index 54% copy from Modules/SemanticRelationsUI/include/QmitkLesionTreeModel.h copy to Modules/SemanticRelationsUI/include/QmitkStatisticsTreeModel.h index 899d912e61..1c54bde693 100644 --- a/Modules/SemanticRelationsUI/include/QmitkLesionTreeModel.h +++ b/Modules/SemanticRelationsUI/include/QmitkStatisticsTreeModel.h @@ -1,98 +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 QMITKLESIONTREEMODEL_H -#define QMITKLESIONTREEMODEL_H +#ifndef QMITKSTATISTICSTREEMODEL_H +#define QMITKSTATISTICSTREEMODEL_H // mitk semantic relations UI #include "MitkSemanticRelationsUIExports.h" #include "QmitkAbstractSemanticRelationsStorageModel.h" #include "QmitkLesionTreeItem.h" - -// c++ -#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 QmitkLesionTreeModel : public QmitkAbstractSemanticRelationsStorageModel +class MITKSEMANTICRELATIONSUI_EXPORT QmitkStatisticsTreeModel : public QmitkAbstractSemanticRelationsStorageModel { - Q_OBJECT - + Q_OBJECT + public: /** * @brief Initialize the root item of the model. The root item does not have a parent item. */ - QmitkLesionTreeModel(QObject* parent = nullptr); - ~QmitkLesionTreeModel(); + 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: - - // the following functions have to be overridden but are not implemented in this model + + 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 { } + 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; + 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 // QMITKLESIONTREEMODEL_H +#endif // QMITKSTATISTICSTREEMODEL_H diff --git a/Modules/SemanticRelationsUI/src/QmitkAbstractSemanticRelationsStorageModel.cpp b/Modules/SemanticRelationsUI/src/QmitkAbstractSemanticRelationsStorageModel.cpp index 8b973ef1db..ce6b86e446 100644 --- a/Modules/SemanticRelationsUI/src/QmitkAbstractSemanticRelationsStorageModel.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkAbstractSemanticRelationsStorageModel.cpp @@ -1,90 +1,85 @@ /*=================================================================== 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 "QmitkAbstractSemanticRelationsStorageModel.h" #include "QmitkCustomVariants.h" QmitkAbstractSemanticRelationsStorageModel::QmitkAbstractSemanticRelationsStorageModel(QObject* parent /*= nullptr*/) : QmitkAbstractDataStorageModel(parent) , m_SemanticRelationsDataStorageAccess(nullptr) { m_SemanticRelationsIntegration = std::make_unique(); m_SemanticRelationsIntegration->AddObserver(this); } QmitkAbstractSemanticRelationsStorageModel::~QmitkAbstractSemanticRelationsStorageModel() { if (nullptr != m_SemanticRelationsIntegration) { m_SemanticRelationsIntegration->RemoveObserver(this); } } void QmitkAbstractSemanticRelationsStorageModel::Update(const mitk::SemanticTypes::CaseID& caseID) { - UpdateModelData(caseID); + // if the case ID of updated instance is equal to the currently active caseID + if (caseID == m_CaseID) + { + UpdateModelData(); + } } void QmitkAbstractSemanticRelationsStorageModel::SetCaseID(const mitk::SemanticTypes::CaseID& caseID) { m_CaseID = caseID; UpdateModelData(); } void QmitkAbstractSemanticRelationsStorageModel::SetLesion(const mitk::SemanticTypes::Lesion& lesion) { m_Lesion = lesion; UpdateModelData(); } void QmitkAbstractSemanticRelationsStorageModel::SetDataNodeSelection(const QList& dataNodeSelection) { m_SelectedDataNodes = dataNodeSelection; UpdateModelData(); } -void QmitkAbstractSemanticRelationsStorageModel::UpdateModelData(const mitk::SemanticTypes::CaseID& caseID) -{ - // if the case ID of updated instance is equal to the currently active caseID - if (caseID == m_CaseID) - { - UpdateModelData(); - } -} - void QmitkAbstractSemanticRelationsStorageModel::UpdateModelData() { if (nullptr == m_SemanticRelationsDataStorageAccess) { return; } // update the model, so that the table will be filled with the new patient information beginResetModel(); SetData(); endResetModel(); emit ModelUpdated(); } void QmitkAbstractSemanticRelationsStorageModel::DataStorageChanged() { m_SemanticRelationsDataStorageAccess = std::make_unique(m_DataStorage.Lock()); UpdateModelData(); } diff --git a/Modules/SemanticRelationsUI/src/QmitkControlPointDialog.cpp b/Modules/SemanticRelationsUI/src/QmitkControlPointDialog.cpp index 348aade318..0e4a199258 100644 --- a/Modules/SemanticRelationsUI/src/QmitkControlPointDialog.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkControlPointDialog.cpp @@ -1,69 +1,65 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkControlPointDialog.h" #include #include #include #include #include QmitkControlPointDialog::QmitkControlPointDialog(QWidget* parent) : QDialog(parent) { QBoxLayout* verticalLayout = new QVBoxLayout(this); verticalLayout->setMargin(5); verticalLayout->setSpacing(5); QLabel* dateLabel = new QLabel(tr("Set date"), this); verticalLayout->addWidget(dateLabel); m_DateEdit = new QDateEdit(this); m_DateEdit->setDisplayFormat("yyyy-MM-dd"); m_DateEdit->setFocus(); verticalLayout->addWidget(m_DateEdit); QPushButton* acceptButton = new QPushButton(tr("Ok"), this); QPushButton* cancelButton = new QPushButton(tr("Cancel"), this); acceptButton->setDefault(true); connect(acceptButton, &QPushButton::clicked, this, &QmitkControlPointDialog::accept); connect(cancelButton, &QPushButton::clicked, this, &QmitkControlPointDialog::reject); QBoxLayout* horizontalLayout = new QHBoxLayout(); horizontalLayout->setSpacing(5); horizontalLayout->addStretch(); horizontalLayout->addWidget(acceptButton); horizontalLayout->addWidget(cancelButton); verticalLayout->addLayout(horizontalLayout); setMinimumSize(250, 100); } -QmitkControlPointDialog::~QmitkControlPointDialog() -{ -} - void QmitkControlPointDialog::SetCurrentDate(mitk::SemanticTypes::ControlPoint currentControlPoint) { m_DateEdit->setDate(QDate(currentControlPoint.date.year(), currentControlPoint.date.month(), currentControlPoint.date.day())); } QDate QmitkControlPointDialog::GetCurrentDate() const { return m_DateEdit->date(); } diff --git a/Modules/SemanticRelationsUI/src/QmitkLesionTextDialog.cpp b/Modules/SemanticRelationsUI/src/QmitkLesionTextDialog.cpp index 3ca3416a27..220b06898d 100644 --- a/Modules/SemanticRelationsUI/src/QmitkLesionTextDialog.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkLesionTextDialog.cpp @@ -1,72 +1,67 @@ /*=================================================================== 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 "QmitkLesionTextDialog.h" #include #include #include QmitkLesionTextDialog::QmitkLesionTextDialog(QWidget* parent) : QDialog(parent) { QBoxLayout* verticalLayout = new QVBoxLayout(this); verticalLayout->setMargin(5); verticalLayout->setSpacing(5); QLabel* dialogLabel = new QLabel(tr("Set lesion information"), this); verticalLayout->addWidget(dialogLabel); 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, &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); setMinimumSize(250, 100); } -QmitkLesionTextDialog::~QmitkLesionTextDialog() -{ - // nothing here -} - void QmitkLesionTextDialog::SetLineEditText(const std::string& lineEditText) { m_LineEdit->setText(QString::fromStdString(lineEditText)); } QString QmitkLesionTextDialog::GetLineEditText() const { return m_LineEdit->text(); } QLineEdit* QmitkLesionTextDialog::GetLineEdit() const { return m_LineEdit; } diff --git a/Modules/SemanticRelationsUI/src/QmitkLesionTreeItem.cpp b/Modules/SemanticRelationsUI/src/QmitkLesionTreeItem.cpp index a698270871..291a7d31f9 100644 --- a/Modules/SemanticRelationsUI/src/QmitkLesionTreeItem.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkLesionTreeItem.cpp @@ -1,86 +1,81 @@ /*=================================================================== 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 "QmitkLesionTreeItem.h" // qt #include QmitkLesionTreeItem::QmitkLesionTreeItem(mitk::LesionData lesionData/* = mitk::LesionTreeItemData()*/) { m_ItemData = lesionData; } -QmitkLesionTreeItem::~QmitkLesionTreeItem() -{ - // nothing here -} - void QmitkLesionTreeItem::SetParent(ParentPointer parent) { m_ParentItem = parent; } int QmitkLesionTreeItem::GetRow() const { if (m_ParentItem.expired()) { return 0; } auto parentItem = m_ParentItem.lock(); return parentItem->GetRowOfChild(this->shared_from_this()); } int QmitkLesionTreeItem::GetRowOfChild(ChildConstPointer child) const { auto it = std::find(m_Children.begin(), m_Children.end(), child); if (it == m_Children.end()) { return -1; } else { return std::distance(m_Children.begin(), it); } } void QmitkLesionTreeItem::AddChild(ChildPointer child) { auto it = std::find(m_Children.begin(), m_Children.end(), child); if (it == m_Children.end()) { // child does not already exist; add to vector of children m_Children.push_back(child); // add parent item child->SetParent(this->shared_from_this()); } } void QmitkLesionTreeItem::RemoveChild(ChildPointer child) { auto it = std::find(m_Children.begin(), m_Children.end(), child); if (it != m_Children.end()) { m_Children.erase(it); } } void QmitkLesionTreeItem::SetData(const mitk::LesionData& lesionData) { m_ItemData = lesionData; } diff --git a/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp b/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp index 07c1071fc3..44adbf74ab 100644 --- a/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp @@ -1,303 +1,302 @@ /*=================================================================== 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_LastSegmentation(nullptr) , m_RootItem(std::make_shared(mitk::LesionData())) { // nothing here } -QmitkLesionTreeModel::~QmitkLesionTreeModel() -{ - // nothing here -} - ////////////////////////////////////////////////////////////////////////// // overridden virtual functions from QAbstractItemModel ////////////////////////////////////////////////////////////////////////// QModelIndex QmitkLesionTreeModel::index(int row, int column, const QModelIndex& itemIndex) const { if (!hasIndex(row, column, itemIndex)) { return QModelIndex(); } auto childItem = GetItemByIndex(itemIndex)->GetChildInRow(row); if (nullptr == childItem) { return QModelIndex(); } return createIndex(row, column, childItem.get()); } QModelIndex QmitkLesionTreeModel::parent(const QModelIndex& itemIndex) const { if (!itemIndex.isValid()) { return QModelIndex(); } auto parentItem = GetItemByIndex(itemIndex)->GetParent(); if (parentItem.expired()) { return QModelIndex(); } auto sharedParent = parentItem.lock(); if (sharedParent == m_RootItem) { return QModelIndex(); } return createIndex(sharedParent->GetRow(), 0, sharedParent.get()); } int QmitkLesionTreeModel::rowCount(const QModelIndex& itemIndex/* = QModelIndex()*/) const { return GetItemByIndex(itemIndex)->ChildCount(); } int QmitkLesionTreeModel::columnCount(const QModelIndex&/* itemIndex = QModelIndex() */) const { if (0 == m_RootItem->ChildCount()) { // no lesion items stored, no need to display columns return 0; } return m_ControlPoints.size() + 1; } QVariant QmitkLesionTreeModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return QVariant(); } if (index.column() < 0 || index.column() > static_cast(m_ControlPoints.size())) { return QVariant(); } QmitkLesionTreeItem* currentItem = GetItemByIndex(index); if (Qt::DisplayRole == role) { if (currentItem->GetParent().expired()) { return QVariant(); } auto parentItem = currentItem->GetParent().lock(); // parent exists and is the root item -> 1. item of a lesion entry if (m_RootItem == parentItem) { // display role fills the first columns with the lesion UID / name if (0 == index.column()) { std::string itemString = currentItem->GetData().GetLesionName(); if (itemString.empty()) { itemString = currentItem->GetData().GetLesionUID(); } return QString::fromStdString(itemString); } else { // display role fills other columns with the lesion presence info const auto lesionPresence = currentItem->GetData().GetLesionPresence(); if (index.column() - 1 > static_cast(lesionPresence.size())) { - return ""; + return "N/A"; } - return QVariant(lesionPresence.at(index.column() - 1)); - } - } - // parent is not the root item -> 2. item of a lesion entry - else - { - // display role fills the first columns with the property name "Volume" - if (0 == index.column()) - { - return "Volume"; - } - else - { - // display role fills other columns with the lesion volume info - const auto lesionVolume= currentItem->GetData().GetLesionVolume(); - if (index.column() - 1 > static_cast(lesionVolume.size())) + if (lesionPresence.at(index.column() - 1)) { - return ""; + return QString::fromStdString("present"); } - return QVariant(lesionVolume.at(index.column() - 1)); + + return QString::fromStdString("not present"); } } } 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::GenerateAdditionalLesionData(lesionData, m_CaseID); + 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); - - // add the 2. level lesion item to the 1. level lesion item - std::shared_ptr newChildItem = std::make_shared(lesionData); - newLesionTreeItem->AddChild(newChildItem); } 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/QmitkPatientTableModel.cpp b/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp index 80b857e1f7..04d9e0fc44 100644 --- a/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp @@ -1,342 +1,350 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // semantic relations UI module #include "QmitkPatientTableModel.h" #include "QmitkPatientTableHeaderView.h" #include "QmitkSemanticRelationsUIHelper.h" // semantic relations module #include #include #include #include #include #include #include // qt #include // c++ #include #include QmitkPatientTableModel::QmitkPatientTableModel(QObject* parent /*= nullptr*/) : QmitkAbstractSemanticRelationsStorageModel(parent) , m_SelectedNodeType("Image") { m_HeaderModel = new QStandardItemModel(this); } QmitkPatientTableModel::~QmitkPatientTableModel() { // nothing here } QModelIndex QmitkPatientTableModel::index(int row, int column, const QModelIndex& parent/* = QModelIndex()*/) const { if (hasIndex(row, column, parent)) { return createIndex(row, column); } return QModelIndex(); } QModelIndex QmitkPatientTableModel::parent(const QModelIndex& /*child*/) const { return QModelIndex(); } int QmitkPatientTableModel::rowCount(const QModelIndex& parent/* = QModelIndex()*/) const { if (parent.isValid()) { return 0; } return m_InformationTypes.size(); } int QmitkPatientTableModel::columnCount(const QModelIndex& parent/* = QModelIndex()*/) const { if (parent.isValid()) { return 0; } - return m_ControlPoints.size(); + return m_ExaminationPeriods.size(); } QVariant QmitkPatientTableModel::data(const QModelIndex& index, int role/* = Qt::DisplayRole*/) const { // special role for returning the horizontal header if (QmitkPatientTableHeaderView::HorizontalHeaderDataRole == role) { return QVariant::fromValue(m_HeaderModel); } if (!index.isValid()) { return QVariant(); } if (index.row() < 0 || index.row() >= static_cast(m_InformationTypes.size()) - || index.column() < 0 || index.column() >= static_cast(m_ControlPoints.size())) + || index.column() < 0 || index.column() >= static_cast(m_ExaminationPeriods.size())) { return QVariant(); } mitk::DataNode* dataNode = GetCurrentDataNode(index); - if (nullptr == dataNode) - { - return QVariant(); - } if (Qt::DecorationRole == role) { auto it = m_PixmapMap.find(dataNode); if (it != m_PixmapMap.end()) { return QVariant(it->second); } - } - if (QmitkDataNodeRole == role) - { - return QVariant::fromValue(mitk::DataNode::Pointer(dataNode)); - } - - if (QmitkDataNodeRawPointerRole == role) - { - return QVariant::fromValue(dataNode); + auto emptyPixmap = QPixmap(120, 120); + emptyPixmap.fill(Qt::transparent); + return emptyPixmap; } if (Qt::BackgroundColorRole == role) { auto it = m_LesionPresence.find(dataNode); if (it != m_LesionPresence.end()) { return it->second ? QVariant(QColor(Qt::darkGreen)) : QVariant(QColor(Qt::transparent)); } return QVariant(QColor(Qt::transparent)); } + if (QmitkDataNodeRole == role) + { + return QVariant::fromValue(mitk::DataNode::Pointer(dataNode)); + } + + if (QmitkDataNodeRawPointerRole == role) + { + return QVariant::fromValue(dataNode); + } + return QVariant(); } QVariant QmitkPatientTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (Qt::Vertical == orientation && Qt::DisplayRole == role) { if (static_cast(m_InformationTypes.size()) > section) { mitk::SemanticTypes::InformationType currentInformationType = m_InformationTypes.at(section); return QVariant(QString::fromStdString(currentInformationType)); } } return QVariant(); } Qt::ItemFlags QmitkPatientTableModel::flags(const QModelIndex& index) const { Qt::ItemFlags flags; mitk::DataNode* dataNode = GetCurrentDataNode(index); if (nullptr != dataNode) { flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; } return flags; } void QmitkPatientTableModel::SetNodeType(const std::string& nodeType) { m_SelectedNodeType = nodeType; UpdateModelData(); } void QmitkPatientTableModel::NodePredicateChanged() { UpdateModelData(); } void QmitkPatientTableModel::SetData() { - // get all control points of current case - m_ControlPoints = mitk::RelationStorage::GetAllControlPointsOfCase(m_CaseID); - // sort the vector of control points for the timeline - std::sort(m_ControlPoints.begin(), m_ControlPoints.end()); - // get all examination periods of current case m_ExaminationPeriods = mitk::RelationStorage::GetAllExaminationPeriodsOfCase(m_CaseID); - // sort the vector of examination periods for the timeline - mitk::SortExaminationPeriods(m_ExaminationPeriods, m_ControlPoints); + + // sort all examination periods for the timeline + mitk::SortAllExaminationPeriods(m_CaseID, m_ExaminationPeriods); + + // rename examination periods according to their new order + std::string examinationPeriodName = "Baseline"; + for (int i = 0; i < m_ExaminationPeriods.size(); ++i) + { + auto& examinationPeriod = m_ExaminationPeriods.at(i); + examinationPeriod.name = examinationPeriodName; + mitk::RelationStorage::RenameExaminationPeriod(m_CaseID, examinationPeriod); + examinationPeriodName = "Follow-up " + std::to_string(i); + } // get all information types points of current case m_InformationTypes = mitk::RelationStorage::GetAllInformationTypesOfCase(m_CaseID); if ("Image" == m_SelectedNodeType) { m_CurrentDataNodes = m_SemanticRelationsDataStorageAccess->GetAllImagesOfCase(m_CaseID); } else if ("Segmentation" == m_SelectedNodeType) { m_CurrentDataNodes = m_SemanticRelationsDataStorageAccess->GetAllSegmentationsOfCase(m_CaseID); } SetHeaderModel(); SetPixmaps(); SetLesionPresences(); } void QmitkPatientTableModel::SetHeaderModel() { m_HeaderModel->clear(); QStandardItem* rootItem = new QStandardItem("Timeline"); QList standardItems; for (const auto& examinationPeriod : m_ExaminationPeriods) { QStandardItem* examinationPeriodItem = new QStandardItem(QString::fromStdString(examinationPeriod.name)); standardItems.push_back(examinationPeriodItem); rootItem->appendColumn(standardItems); standardItems.clear(); - - const auto& currentControlPoints = examinationPeriod.controlPointUIDs; - for (const auto& controlPointUID : currentControlPoints) - { - const auto& controlPoint = mitk::GetControlPointByUID(controlPointUID, m_ControlPoints); - QStandardItem* controlPointItem = new QStandardItem(QString::fromStdString(controlPoint.ToString())); - standardItems.push_back(controlPointItem); - examinationPeriodItem->appendColumn(standardItems); - standardItems.clear(); - } } m_HeaderModel->setItem(0, 0, rootItem); } void QmitkPatientTableModel::SetPixmaps() { m_PixmapMap.clear(); for (const auto& dataNode : m_CurrentDataNodes) { // set the pixmap for the current node QPixmap pixmapFromImage = QmitkSemanticRelationsUIHelper::GetPixmapFromImageNode(dataNode); SetPixmapOfNode(dataNode, &pixmapFromImage); } } void QmitkPatientTableModel::SetPixmapOfNode(const mitk::DataNode* dataNode, QPixmap* pixmapFromImage) { if (nullptr == dataNode) { return; } std::map::iterator iter = m_PixmapMap.find(dataNode); if (iter != m_PixmapMap.end()) { // key already existing if (nullptr != pixmapFromImage) { // overwrite already stored pixmap iter->second = pixmapFromImage->scaled(120, 120, Qt::IgnoreAspectRatio); } else { // remove key if no pixmap is given m_PixmapMap.erase(iter); } } else { m_PixmapMap.insert(std::make_pair(dataNode, pixmapFromImage->scaled(120, 120, Qt::IgnoreAspectRatio))); } } void QmitkPatientTableModel::SetLesionPresences() { m_LesionPresence.clear(); if (!mitk::SemanticRelationsInference::InstanceExists(m_CaseID, m_Lesion)) { return; } for (const auto& dataNode : m_CurrentDataNodes) { if (!mitk::SemanticRelationsInference::InstanceExists(dataNode)) { continue; } // set the lesion presence for the current node bool lesionPresence = mitk::SemanticRelationsInference::IsLesionPresent(m_Lesion, dataNode); SetLesionPresenceOfNode(dataNode, lesionPresence); } } void QmitkPatientTableModel::SetLesionPresenceOfNode(const mitk::DataNode* dataNode, bool lesionPresence) { std::map::iterator iter = m_LesionPresence.find(dataNode); if (iter != m_LesionPresence.end()) { // key already existing, overwrite already stored bool value iter->second = lesionPresence; } else { m_LesionPresence.insert(std::make_pair(dataNode, lesionPresence)); } } mitk::DataNode* QmitkPatientTableModel::GetCurrentDataNode(const QModelIndex& index) const { if (!index.isValid()) { return nullptr; } - mitk::SemanticTypes::ControlPoint currentControlPoint = m_ControlPoints.at(index.column()); - mitk::SemanticTypes::InformationType currentInformationType = m_InformationTypes.at(index.row()); - try + auto examinationPeriod = m_ExaminationPeriods.at(index.column()); + auto currentInformationType = m_InformationTypes.at(index.row()); + auto controlPointsOfExaminationPeriod = examinationPeriod.controlPointUIDs; + for (const auto& controlPointUID : controlPointsOfExaminationPeriod) { - std::vector filteredDataNodes; - if ("Image" == m_SelectedNodeType) + auto currentControlPoint = mitk::GetControlPointByUID(m_CaseID, controlPointUID); + try { - filteredDataNodes = m_SemanticRelationsDataStorageAccess->GetAllSpecificImages(m_CaseID, currentControlPoint, currentInformationType); + std::vector filteredDataNodes; + if ("Image" == m_SelectedNodeType) + { + filteredDataNodes = m_SemanticRelationsDataStorageAccess->GetAllSpecificImages(m_CaseID, currentControlPoint, currentInformationType); + } + else if ("Segmentation" == m_SelectedNodeType) + { + filteredDataNodes = m_SemanticRelationsDataStorageAccess->GetAllSpecificSegmentations(m_CaseID, currentControlPoint, currentInformationType); + } + + if (filteredDataNodes.empty()) + { + // try next control point + continue; + } + else + { + // found a specific image + return filteredDataNodes.front(); + } } - else if ("Segmentation" == m_SelectedNodeType) - { - filteredDataNodes = m_SemanticRelationsDataStorageAccess->GetAllSpecificSegmentations(m_CaseID, currentControlPoint, currentInformationType); - } - - if (filteredDataNodes.empty()) + catch (const mitk::SemanticRelationException&) { return nullptr; } - return filteredDataNodes.front(); - } - catch (const mitk::SemanticRelationException&) - { - return nullptr; } + // could not find a specif image + return nullptr; } diff --git a/Modules/SemanticRelationsUI/src/QmitkStatisticsCalculator.cpp b/Modules/SemanticRelationsUI/src/QmitkStatisticsCalculator.cpp new file mode 100644 index 0000000000..ea1892430e --- /dev/null +++ b/Modules/SemanticRelationsUI/src/QmitkStatisticsCalculator.cpp @@ -0,0 +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 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/QmitkLesionTreeModel.cpp b/Modules/SemanticRelationsUI/src/QmitkStatisticsTreeModel.cpp similarity index 53% copy from Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp copy to Modules/SemanticRelationsUI/src/QmitkStatisticsTreeModel.cpp index 07c1071fc3..af1f4bf2b5 100644 --- a/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkStatisticsTreeModel.cpp @@ -1,303 +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 "QmitkLesionTreeModel.h" +#include "QmitkStatisticsTreeModel.h" // semantic relations module #include #include #include #include #include -// qt -#include - -QmitkLesionTreeModel::QmitkLesionTreeModel(QObject* parent/* = nullptr*/) +QmitkStatisticsTreeModel::QmitkStatisticsTreeModel(QObject* parent/* = nullptr*/) : QmitkAbstractSemanticRelationsStorageModel(parent) , m_RootItem(std::make_shared(mitk::LesionData())) { - // nothing here -} - -QmitkLesionTreeModel::~QmitkLesionTreeModel() -{ - // nothing here + m_StatisticsCalculator = std::make_unique(); } ////////////////////////////////////////////////////////////////////////// // overridden virtual functions from QAbstractItemModel ////////////////////////////////////////////////////////////////////////// -QModelIndex QmitkLesionTreeModel::index(int row, int column, const QModelIndex& itemIndex) const +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 QmitkLesionTreeModel::parent(const QModelIndex& itemIndex) const +QModelIndex QmitkStatisticsTreeModel::parent(const QModelIndex& itemIndex) const { if (!itemIndex.isValid()) { return QModelIndex(); } - auto parentItem = GetItemByIndex(itemIndex)->GetParent(); - if (parentItem.expired()) + auto parentItemWeakPtr = GetItemByIndex(itemIndex)->GetParent(); + if (parentItemWeakPtr.expired()) { return QModelIndex(); } - auto sharedParent = parentItem.lock(); - if (sharedParent == m_RootItem) + auto parentItem = parentItemWeakPtr.lock(); + if (parentItem == m_RootItem) { return QModelIndex(); } - return createIndex(sharedParent->GetRow(), 0, sharedParent.get()); + return createIndex(parentItem->GetRow(), 0, parentItem.get()); } -int QmitkLesionTreeModel::rowCount(const QModelIndex& itemIndex/* = QModelIndex()*/) const +int QmitkStatisticsTreeModel::rowCount(const QModelIndex& itemIndex/* = QModelIndex()*/) const { return GetItemByIndex(itemIndex)->ChildCount(); } -int QmitkLesionTreeModel::columnCount(const QModelIndex&/* itemIndex = QModelIndex() */) const +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 QmitkLesionTreeModel::data(const QModelIndex& index, int role) const +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 -> 1. item of a lesion entry + // 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); } - else - { - // display role fills other columns with the lesion presence info - const auto lesionPresence = currentItem->GetData().GetLesionPresence(); - if (index.column() - 1 > static_cast(lesionPresence.size())) - { - return ""; - } - - return QVariant(lesionPresence.at(index.column() - 1)); - } } - // parent is not the root item -> 2. item of a lesion entry + // parent is not the root item -> volume item else { - // display role fills the first columns with the property name "Volume" + // display role fills the first columns with the information type if (0 == index.column()) { - return "Volume"; + if (index.row() < static_cast(m_InformationTypes.size())) + { + return QString::fromStdString(m_InformationTypes.at(index.row())); + } + return "N/A"; } else { // display role fills other columns with the lesion volume info - const auto lesionVolume= currentItem->GetData().GetLesionVolume(); - if (index.column() - 1 > static_cast(lesionVolume.size())) + const auto lesionVolume = currentItem->GetData().GetLesionVolume(); + if ((index.column() - 1) * index.row() < static_cast(lesionVolume.size())) { - return ""; + return QVariant(lesionVolume.at(index.row()*m_ControlPoints.size() + (index.column() - 1))); } - return QVariant(lesionVolume.at(index.column() - 1)); + return "N/A"; } } } - 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 +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 QmitkLesionTreeModel::SetData() +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::NodeAdded(const mitk::DataNode*) +{ + emit beginResetModel(); + UpdateModelData(); + emit endResetModel(); +} + +void QmitkStatisticsTreeModel::NodeChanged(const mitk::DataNode*) +{ + emit beginResetModel(); + UpdateModelData(); + emit endResetModel(); +} + +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(); - SetSelectedDataNodesPresence(); } -void QmitkLesionTreeModel::SetLesionData() +void QmitkStatisticsTreeModel::SetLesionData() { m_CurrentLesions = mitk::RelationStorage::GetAllLesionsOfCase(m_CaseID); for (auto& lesion : m_CurrentLesions) { AddLesion(lesion); } } -void QmitkLesionTreeModel::AddLesion(const mitk::SemanticTypes::Lesion& 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); - mitk::GenerateAdditionalLesionData(lesionData, m_CaseID); + 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); - // add the 2. level lesion item to the 1. level lesion item - std::shared_ptr newChildItem = std::make_shared(lesionData); - newLesionTreeItem->AddChild(newChildItem); -} - -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 + auto informationTypeSize = m_InformationTypes.size(); + for (int i = 0; i < informationTypeSize; ++i) { - m_DataNodePresence.insert(std::make_pair(lesion->UID, dataNodePresence)); + std::shared_ptr volumeItem = std::make_shared(lesionData); + newLesionTreeItem->AddChild(volumeItem); } } -QmitkLesionTreeItem* QmitkLesionTreeModel::GetItemByIndex(const QModelIndex& index) const +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.application/src/QmitkDataNodeOpenInAction.cpp b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeOpenInAction.cpp index 6f131f7f07..00bfa08f14 100644 --- a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeOpenInAction.cpp +++ b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeOpenInAction.cpp @@ -1,129 +1,126 @@ /*=================================================================== 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 +// mitk gui qt application plugin #include "QmitkDataNodeOpenInAction.h" // mitk core #include #include -// mitk gui common plugin -#include - // qt #include - -// qt #include QmitkDataNodeOpenInAction::QmitkDataNodeOpenInAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) : QAction(parent) , QmitkAbstractDataNodeAction(workbenchPartSite) { setText(tr("Open in")); InitializeAction(); } QmitkDataNodeOpenInAction::QmitkDataNodeOpenInAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) : QAction(parent) , QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) { setText(tr("Open in")); InitializeAction(); } -QmitkDataNodeOpenInAction::~QmitkDataNodeOpenInAction() -{ - // nothing here -} - void QmitkDataNodeOpenInAction::SetControlledRenderer(RendererVector controlledRenderer) { if (m_ControlledRenderer != controlledRenderer) { // set the new set of controlled renderer m_ControlledRenderer = controlledRenderer; } } void QmitkDataNodeOpenInAction::InitializeAction() { setCheckable(true); setMenu(new QMenu); connect(menu(), &QMenu::aboutToShow, this, &QmitkDataNodeOpenInAction::OnMenuAboutToShow); SetControlledRenderer(); } void QmitkDataNodeOpenInAction::OnMenuAboutToShow() { menu()->clear(); QAction* action; + QStringList rendererNames; for (const auto& renderer : m_ControlledRenderer) { - action = menu()->addAction(QString::fromStdString(renderer->GetName())); + rendererNames.append(renderer->GetName()); + } + + rendererNames.sort(); + for (const auto& rendererName : rendererNames) + { + action = menu()->addAction(rendererName); connect(action, &QAction::triggered, this, &QmitkDataNodeOpenInAction::OnActionTriggered); } } void QmitkDataNodeOpenInAction::OnActionTriggered(bool /*checked*/) { auto dataNode = GetSelectedNode(); if (dataNode.IsNull()) { return; } QAction* senderAction = qobject_cast(QObject::sender()); if (nullptr == senderAction) { return; } std::string selectedRenderer = senderAction->text().toStdString(); mitk::BaseRenderer* renderer = mitk::BaseRenderer::GetByName(selectedRenderer); if (nullptr == renderer) { return; } auto image = dynamic_cast(dataNode->GetData()); if (nullptr == image) { return; } mitk::RenderingManager::GetInstance()->InitializeView(renderer->GetRenderWindow(), image->GetTimeGeometry()); } void QmitkDataNodeOpenInAction::SetControlledRenderer() { const mitk::RenderingManager::RenderWindowVector allRegisteredRenderWindows = mitk::RenderingManager::GetInstance()->GetAllRegisteredRenderWindows(); mitk::BaseRenderer *baseRenderer = nullptr; m_ControlledRenderer.clear(); - for (const auto &renderWindow : allRegisteredRenderWindows) + for (const auto& renderWindow : allRegisteredRenderWindows) { baseRenderer = mitk::BaseRenderer::GetInstance(renderWindow); if (nullptr != baseRenderer) { m_ControlledRenderer.push_back(baseRenderer); } } } diff --git a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeOpenInAction.h b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeOpenInAction.h index 403cafc12a..88fa3f8f52 100644 --- a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeOpenInAction.h +++ b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeOpenInAction.h @@ -1,60 +1,58 @@ /*=================================================================== 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 QMITKDATANODEOPENINACTION_H #define QMITKDATANODEOPENINACTION_H #include #include "QmitkAbstractDataNodeAction.h" // mitk core #include // qt #include class MITK_QT_APP QmitkDataNodeOpenInAction : public QAction, public QmitkAbstractDataNodeAction { Q_OBJECT public: typedef std::vector RendererVector; QmitkDataNodeOpenInAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite); QmitkDataNodeOpenInAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite); - virtual ~QmitkDataNodeOpenInAction() override; - void SetControlledRenderer(RendererVector controlledRenderer); private Q_SLOTS: void OnMenuAboutToShow(); void OnActionTriggered(bool); protected: virtual void InitializeAction() override; void SetControlledRenderer(); RendererVector m_ControlledRenderer; }; #endif // QMITKDATANODEOPENINACTION_H diff --git a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeReinitAction.cpp b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeReinitAction.cpp index 9b52293f3e..0521aad9fd 100644 --- a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeReinitAction.cpp +++ b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeReinitAction.cpp @@ -1,134 +1,137 @@ /*=================================================================== 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 // mitk core #include #include #include #include #include // mitk gui common plugin #include // namespace that contains the concrete action namespace ReinitAction { void Run(berry::IWorkbenchPartSite::Pointer workbenchPartSite, mitk::DataStorage::Pointer dataStorage, const QList& selectedNodes /*= QList()*/, mitk::BaseRenderer* baseRenderer /*= nullptr*/) { if (selectedNodes.empty()) { return; } - auto renderWindow = mitk::WorkbenchUtil::GetRenderWindowPart(workbenchPartSite->GetPage(), mitk::WorkbenchUtil::NONE); - if (nullptr == renderWindow) + if (workbenchPartSite.IsNotNull()) { - renderWindow = mitk::WorkbenchUtil::OpenRenderWindowPart(workbenchPartSite->GetPage(), false); + auto renderWindow = mitk::WorkbenchUtil::GetRenderWindowPart(workbenchPartSite->GetPage(), mitk::WorkbenchUtil::NONE); if (nullptr == renderWindow) { - // no render window available - return; + renderWindow = mitk::WorkbenchUtil::OpenRenderWindowPart(workbenchPartSite->GetPage(), false); + if (nullptr == renderWindow) + { + // no render window available + return; + } } } auto boundingBoxPredicate = mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("includeInBoundingBox", mitk::BoolProperty::New(false), baseRenderer)); mitk::DataStorage::SetOfObjects::Pointer nodes = mitk::DataStorage::SetOfObjects::New(); for (const auto& dataNode : selectedNodes) { if (boundingBoxPredicate->CheckNode(dataNode)) { nodes->InsertElement(nodes->Size(), dataNode); } } if (nodes->empty()) { return; } if (1 == nodes->Size()) // Special case: If exactly one ... { auto image = dynamic_cast(nodes->ElementAt(0)->GetData()); if (nullptr != image) // ... image is selected, reinit is expected to rectify askew images. { if (nullptr == baseRenderer) { mitk::RenderingManager::GetInstance()->InitializeViews(image->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); } else { mitk::RenderingManager::GetInstance()->InitializeView(baseRenderer->GetRenderWindow(), image->GetTimeGeometry(), true); } return; } } auto boundingGeometry = dataStorage->ComputeBoundingGeometry3D(nodes, "visible", baseRenderer); if (nullptr == baseRenderer) { mitk::RenderingManager::GetInstance()->InitializeViews(boundingGeometry); } else { mitk::RenderingManager::GetInstance()->InitializeView(baseRenderer->GetRenderWindow(), boundingGeometry); } } } QmitkDataNodeReinitAction::QmitkDataNodeReinitAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchpartSite) : QAction(parent) , QmitkAbstractDataNodeAction(workbenchpartSite) { setText(tr("Reinit")); InitializeAction(); } QmitkDataNodeReinitAction::QmitkDataNodeReinitAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchpartSite) : QAction(parent) , QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite::Pointer(workbenchpartSite)) { setText(tr("Reinit")); InitializeAction(); } void QmitkDataNodeReinitAction::InitializeAction() { connect(this, &QmitkDataNodeReinitAction::triggered, this, &QmitkDataNodeReinitAction::OnActionTriggered); } void QmitkDataNodeReinitAction::OnActionTriggered(bool /*checked*/) { if (m_WorkbenchPartSite.Expired()) { return; } if (m_DataStorage.IsExpired()) { return; } mitk::BaseRenderer::Pointer baseRenderer = GetBaseRenderer(); auto selectedNodes = GetSelectedNodes(); ReinitAction::Run(m_WorkbenchPartSite.Lock(), m_DataStorage.Lock(), selectedNodes, baseRenderer); } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/CMakeLists.txt b/Plugins/org.mitk.gui.qt.semanticrelations/CMakeLists.txt index 2ffd63c2e3..970cb000dd 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/CMakeLists.txt +++ b/Plugins/org.mitk.gui.qt.semanticrelations/CMakeLists.txt @@ -1,7 +1,7 @@ project(org_mitk_gui_qt_semanticrelations) mitk_create_plugin( EXPORT_DIRECTIVE MITK_GUI_SEMANTICRELATIONS_EXPORT EXPORTED_INCLUDE_SUFFIXES src - MODULE_DEPENDS MitkPersistence MitkSemanticRelationsUI MitkRenderWindowManager + MODULE_DEPENDS MitkPersistence MitkSemanticRelationsUI MitkRenderWindowManager MitkMultilabel MitkSegmentationUI ) diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/files.cmake b/Plugins/org.mitk.gui.qt.semanticrelations/files.cmake index 8e961c0ceb..09421a5b1d 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/files.cmake +++ b/Plugins/org.mitk.gui.qt.semanticrelations/files.cmake @@ -1,43 +1,52 @@ set(INTERNAL_CPP_FILES mitkPluginActivator.cpp - QmitkAbstractSemanticRelationsAction.cpp QmitkDataNodeAddToSemanticRelationsAction.cpp QmitkDataNodeRemoveFromSemanticRelationsAction.cpp QmitkDataNodeUnlinkFromLesionAction.cpp QmitkDataNodeSetControlPointAction.cpp QmitkDataNodeSetInformationTypeAction.cpp + QmitkDataSetOpenInAction.cpp + QmitkFocusOnLesionAction.cpp + QmitkLabelSetJumpToAction.cpp QmitkLesionInfoWidget.cpp QmitkSemanticRelationsContextMenu.cpp QmitkSemanticRelationsNodeSelectionDialog.cpp + QmitkSemanticRelationsStatisticsView.cpp QmitkSemanticRelationsView.cpp ) set(UI_FILES src/internal/QmitkLesionInfoWidgetControls.ui src/internal/QmitkSemanticRelationsControls.ui + src/internal/QmitkSemanticRelationsStatisticsControls.ui ) set(MOC_H_FILES src/internal/mitkPluginActivator.h src/internal/QmitkDataNodeAddToSemanticRelationsAction.h src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.h src/internal/QmitkDataNodeUnlinkFromLesionAction.h src/internal/QmitkDataNodeSetControlPointAction.h src/internal/QmitkDataNodeSetInformationTypeAction.h + src/internal/QmitkDataSetOpenInAction.h + src/internal/QmitkFocusOnLesionAction.h + src/internal/QmitkLabelSetJumpToAction.h src/internal/QmitkLesionInfoWidget.h src/internal/QmitkSemanticRelationsContextMenu.h src/internal/QmitkSemanticRelationsNodeSelectionDialog.h + src/internal/QmitkSemanticRelationsStatisticsView.h src/internal/QmitkSemanticRelationsView.h ) set(CACHED_RESOURCE_FILES resources/SemanticRelations_48.png + resources/SemanticRelationsStatistics_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/Plugins/org.mitk.gui.qt.semanticrelations/plugin.xml b/Plugins/org.mitk.gui.qt.semanticrelations/plugin.xml index be0ea613f6..618c2e82f1 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/plugin.xml +++ b/Plugins/org.mitk.gui.qt.semanticrelations/plugin.xml @@ -1,11 +1,18 @@ + icon="resources/SemanticRelations_48.png" + category="Semantic Relations"/> + + diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/resources/SemanticRelationsStatistics_48.png b/Plugins/org.mitk.gui.qt.semanticrelations/resources/SemanticRelationsStatistics_48.png new file mode 100644 index 0000000000..182a722caf Binary files /dev/null and b/Plugins/org.mitk.gui.qt.semanticrelations/resources/SemanticRelationsStatistics_48.png differ diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkAbstractSemanticRelationsAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkAbstractSemanticRelationsAction.cpp deleted file mode 100644 index 43699e88fd..0000000000 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkAbstractSemanticRelationsAction.cpp +++ /dev/null @@ -1,30 +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 plugin -#include "QmitkAbstractSemanticRelationsAction.h" - -QmitkAbstractSemanticRelationsAction::QmitkAbstractSemanticRelationsAction(berry::IWorkbenchPartSite::Pointer workbenchPartSite) - : QmitkAbstractDataNodeAction(workbenchPartSite) -{ - m_SemanticRelationsIntegration = std::make_unique(); -} - -QmitkAbstractSemanticRelationsAction::QmitkAbstractSemanticRelationsAction(berry::IWorkbenchPartSite* workbenchPartSite) - : QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) -{ - // nothing here -} diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkAbstractSemanticRelationsAction.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkAbstractSemanticRelationsAction.h deleted file mode 100644 index 0cef975cb7..0000000000 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkAbstractSemanticRelationsAction.h +++ /dev/null @@ -1,39 +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 QMITKABSTRACTSEMANTICRELATIONSACTION_H -#define QMITKABSTRACTSEMANTICRELATIONSACTION_H - -// mitk gui qt application plugin -#include - -// semantic relations module -#include - -class QmitkAbstractSemanticRelationsAction : public QmitkAbstractDataNodeAction -{ - -public: - - QmitkAbstractSemanticRelationsAction(berry::IWorkbenchPartSite::Pointer workbenchPartSite); - QmitkAbstractSemanticRelationsAction(berry::IWorkbenchPartSite* workbenchPartSite); - -protected: - - std::unique_ptr m_SemanticRelationsIntegration; -}; - -#endif // QMITKABSTRACTSEMANTICRELATIONSACTION_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeAddToSemanticRelationsAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeAddToSemanticRelationsAction.cpp index 4d9f80a60c..02821e6490 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeAddToSemanticRelationsAction.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeAddToSemanticRelationsAction.cpp @@ -1,189 +1,251 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // semantic relations plugin #include "QmitkDataNodeAddToSemanticRelationsAction.h" +#include "QmitkDataNodeRemoveFromSemanticRelationsAction.h" // semantic relations module +#include #include #include +#include +#include +#include #include +#include #include // mitk gui common plugin #include // mitk core #include // qt #include // namespace that contains the concrete action namespace AddToSemanticRelationsAction { - void Run(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataStorage* dataStorage, const mitk::DataNode* dataNode) + void Run(mitk::DataStorage* dataStorage, const mitk::DataNode* dataNode) { - if (nullptr == dataNode) + if (nullptr == dataStorage + || nullptr == dataNode) { return; } if (mitk::NodePredicates::GetImagePredicate()->CheckNode(dataNode)) { - AddImage(semanticRelationsIntegration, dataNode); + try + { + AddImage(dataStorage, dataNode); + } + catch (mitk::SemanticRelationException& e) + { + mitkReThrow(e); + } } else if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { - AddSegmentation(semanticRelationsIntegration, dataStorage, dataNode); + try + { + AddSegmentation(dataStorage, dataNode); + } + catch (mitk::SemanticRelationException& e) + { + mitkReThrow(e); + } } } - void AddImage(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataNode* image) + void AddImage(mitk::DataStorage* dataStorage, const mitk::DataNode* image) { - if (nullptr == image) + mitk::SemanticTypes::InformationType informationType; + mitk::SemanticTypes::ExaminationPeriod examinationPeriod; + mitk::SemanticRelationsDataStorageAccess::DataNodeVector allSpecificImages; + try { - return; + mitk::SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(image); + informationType = GetDICOMModalityFromDataNode(image); + // see if the examination period - information type cell is already taken + examinationPeriod = FindFittingExaminationPeriod(image); + auto semanticRelationsDataStorageAccess = mitk::SemanticRelationsDataStorageAccess(dataStorage); + try + { + allSpecificImages = semanticRelationsDataStorageAccess.GetAllSpecificImages(caseID, informationType, examinationPeriod); + } + catch (const mitk::SemanticRelationException&) + { + // just continue since an exception means that there is no specific image + } + } + catch (mitk::SemanticRelationException& e) + { + mitkReThrow(e); + } + + if (!allSpecificImages.empty()) + { + // examination period - information type cell is already taken + // ask if cell should be overwritten + QMessageBox::StandardButton answerButton = + QMessageBox::question(nullptr, + "Specific image already exists.", + QString::fromStdString("Force overwriting existing image " + informationType + " at " + examinationPeriod.name + "?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + if (answerButton == QMessageBox::Yes) + { + try + { + // remove already existent images at specific cell + for (const auto& specificImage : allSpecificImages) + { + RemoveFromSemanticRelationsAction::Run(dataStorage, specificImage); + } + } + catch (mitk::SemanticRelationException& e) + { + mitkReThrow(e); + return; + } + } + else + { + // else case is: no overwriting + return; + } } + // specific image does not exist or has been removed; adding the image should work + mitk::SemanticRelationsIntegration semanticRelationsIntegration; try { - // add the image to the semantic relations storage - semanticRelationsIntegration->AddImage(image); + semanticRelationsIntegration.AddImage(image); } - catch (const mitk::SemanticRelationException& e) + catch (mitk::SemanticRelationException& e) { - std::stringstream exceptionMessage; exceptionMessage << e; - QMessageBox msgBox(QMessageBox::Warning, - "Could not add the selected image.", - "The program wasn't able to correctly add the selected images.\n" - "Reason:\n" + QString::fromStdString(exceptionMessage.str())); - msgBox.exec(); - return; + mitkReThrow(e); } } - void AddSegmentation(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataStorage* dataStorage, const mitk::DataNode* segmentation) + void AddSegmentation(mitk::DataStorage* dataStorage, const mitk::DataNode* segmentation) { if (nullptr == segmentation) { return; } mitk::BaseData* baseData = segmentation->GetData(); if (nullptr == baseData) { return; } // continue with valid segmentation data // get parent node of the current segmentation node with the node predicate mitk::DataStorage::SetOfObjects::ConstPointer parentNodes = dataStorage->GetSources(segmentation, mitk::NodePredicates::GetImagePredicate(), false); + if (parentNodes->empty()) + { + // segmentation without corresponding image will not be added + QMessageBox msgBox(QMessageBox::Warning, + "Could not add the selected segmentation.", + "The program wasn't able to correctly add the selected segmentation.\n" + "Reason: No parent image found"); + msgBox.exec(); + return; + } // check for already existing, identifying base properties auto caseIDPropertyName = mitk::GetCaseIDDICOMProperty(); auto nodeIDPropertyName = mitk::GetNodeIDDICOMProperty(); mitk::BaseProperty* caseIDProperty = baseData->GetProperty(caseIDPropertyName.c_str()); mitk::BaseProperty* nodeIDProperty = baseData->GetProperty(nodeIDPropertyName.c_str()); if (nullptr == caseIDProperty || nullptr == nodeIDProperty) { MITK_INFO << "No DICOM tags for case and node identification found. Transferring DICOM tags from the parent node to the selected segmentation node."; mitk::SemanticTypes::CaseID caseID; mitk::SemanticTypes::ID nodeID; try { caseID = mitk::GetCaseIDFromDataNode(parentNodes->front()); nodeID = mitk::GetIDFromDataNode(parentNodes->front()); } - catch (const mitk::SemanticRelationException& e) + catch (mitk::SemanticRelationException& e) { - std::stringstream exceptionMessage; exceptionMessage << e; - QMessageBox msgBox(QMessageBox::Warning, - "Could not add the selected segmentation.", - "The program wasn't able to correctly add the selected segmentation.\n" - "Reason:\n" + QString::fromStdString(exceptionMessage.str())); - msgBox.exec(); + mitkReThrow(e); return; } // transfer DICOM tags to the segmentation node - baseData->SetProperty(caseIDPropertyName, - mitk::TemporoSpatialStringProperty::New(caseID)); + baseData->SetProperty(caseIDPropertyName, mitk::TemporoSpatialStringProperty::New(caseID)); // add UID to distinguish between different segmentations of the same parent node - baseData->SetProperty(nodeIDPropertyName, - mitk::TemporoSpatialStringProperty::New(nodeID + mitk::UIDGeneratorBoost::GenerateUID())); + baseData->SetProperty(nodeIDPropertyName, mitk::TemporoSpatialStringProperty::New(nodeID + mitk::UIDGeneratorBoost::GenerateUID())); + } + + // add the parent node if not already existent + if (!mitk::SemanticRelationsInference::InstanceExists(parentNodes->front())) + { + AddImage(dataStorage, parentNodes->front()); } + mitk::SemanticRelationsIntegration semanticRelationsIntegration; try { // add the segmentation with its parent image to the semantic relations storage - semanticRelationsIntegration->AddSegmentation(segmentation, parentNodes->front()); + semanticRelationsIntegration.AddSegmentation(segmentation, parentNodes->front()); } - catch (const mitk::SemanticRelationException& e) + catch (mitk::SemanticRelationException& e) { - std::stringstream exceptionMessage; exceptionMessage << e; - QMessageBox msgBox(QMessageBox::Warning, - "Could not add the selected segmentation.", - "The program wasn't able to correctly add the selected segmentation.\n" - "Reason:\n" + QString::fromStdString(exceptionMessage.str())); - msgBox.exec(); - return; + mitkReThrow(e); } } } QmitkDataNodeAddToSemanticRelationsAction::QmitkDataNodeAddToSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) : QAction(parent) - , QmitkAbstractSemanticRelationsAction(workbenchPartSite) + , QmitkAbstractDataNodeAction(workbenchPartSite) { setText(tr("Add to semantic relations")); InitializeAction(); } QmitkDataNodeAddToSemanticRelationsAction::QmitkDataNodeAddToSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) : QAction(parent) - , QmitkAbstractSemanticRelationsAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) + , QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) { setText(tr("Add to semantic relations")); InitializeAction(); } -QmitkDataNodeAddToSemanticRelationsAction::~QmitkDataNodeAddToSemanticRelationsAction() -{ - // nothing here -} - void QmitkDataNodeAddToSemanticRelationsAction::InitializeAction() { connect(this, &QAction::triggered, this, &QmitkDataNodeAddToSemanticRelationsAction::OnActionTriggered); } void QmitkDataNodeAddToSemanticRelationsAction::OnActionTriggered(bool /*checked*/) { - if (nullptr == m_SemanticRelationsIntegration) - { - return; - } - if (m_DataStorage.IsExpired()) { return; } auto dataNode = GetSelectedNode(); - AddToSemanticRelationsAction::Run(m_SemanticRelationsIntegration.get(), m_DataStorage.Lock(), dataNode); + AddToSemanticRelationsAction::Run(m_DataStorage.Lock(), dataNode); } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeAddToSemanticRelationsAction.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeAddToSemanticRelationsAction.h index 2a61c5d1fe..cdb4f94585 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeAddToSemanticRelationsAction.h +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeAddToSemanticRelationsAction.h @@ -1,57 +1,73 @@ /*=================================================================== 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 QMITKDATANODEADDTOSEMANTICRELATIONSACTION_H #define QMITKDATANODEADDTOSEMANTICRELATIONSACTION_H #include // mitk gui qt application plugin -#include "QmitkAbstractSemanticRelationsAction.h" +#include // qt #include namespace AddToSemanticRelationsAction { - MITK_GUI_SEMANTICRELATIONS_EXPORT void Run(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataStorage* dataStorage, const mitk::DataNode* image); + /** + * @brief The function checks whether the given node is an image or a segmentation and calls the corresponding add function. + * The corresponding add functions will add the data node to the semantic relations storage. + * If an image is added, the 'AddImage' function will check if another image at the fitting control-point - information type cell + * already exists. If so, the user is prompted to overwrite the existing image or abort the process. + * If the user wants to overwrite the existing image, the image and it's corresponding segmentation nodes will be removed from the semantic relations storage. + * If a segmentation is added, the parent image node will also be to the semantic relations storage. If the segmentation does not contain the required DICOM information, + * the DICOM information of the parent data will be transfered to the segmentation data. + * + * @pre The given dataStorage has to be valid (!nullptr). + * @pre The given dataNode has to be valid (!nullptr). + * The function simply returns if the preconditions are not met. + * + * @throw SemanticRelationException re-thrown. + * + * @param dataStorage The data storage to use for to remove the existing image and to check for the parent image node of a segmentation. + * @param dataNode The data node to add. + */ + MITK_GUI_SEMANTICRELATIONS_EXPORT void Run(mitk::DataStorage* dataStorage, const mitk::DataNode* image); - void AddImage(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataNode* image); - void AddSegmentation(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataStorage* dataStorage, const mitk::DataNode* segmentation); + void AddImage(mitk::DataStorage* dataStorage, const mitk::DataNode* image); + void AddSegmentation(mitk::DataStorage* dataStorage, const mitk::DataNode* segmentation); } -class MITK_GUI_SEMANTICRELATIONS_EXPORT QmitkDataNodeAddToSemanticRelationsAction : public QAction, public QmitkAbstractSemanticRelationsAction +class MITK_GUI_SEMANTICRELATIONS_EXPORT QmitkDataNodeAddToSemanticRelationsAction : public QAction, public QmitkAbstractDataNodeAction { Q_OBJECT public: QmitkDataNodeAddToSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite); QmitkDataNodeAddToSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite); - virtual ~QmitkDataNodeAddToSemanticRelationsAction() override; - private Q_SLOTS: void OnActionTriggered(bool); protected: virtual void InitializeAction() override; }; #endif // QMITKDATANODEADDTOSEMANTICRELATIONSACTION_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.cpp index a1b0a04f33..30ac801473 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.cpp @@ -1,131 +1,131 @@ /*=================================================================== 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 "QmitkDataNodeRemoveFromSemanticRelationsAction.h" // semantic relations module #include #include +#include // mitk gui common plugin #include // qt #include // namespace that contains the concrete action namespace RemoveFromSemanticRelationsAction { - void Run(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataNode* dataNode) + void Run(mitk::DataStorage* dataStorage, const mitk::DataNode* dataNode) { - if (nullptr == dataNode) + if (nullptr == dataStorage + || nullptr == dataNode) { return; } if (mitk::NodePredicates::GetImagePredicate()->CheckNode(dataNode)) { - RemoveImage(semanticRelationsIntegration, dataNode); + try + { + RemoveImage(dataStorage, dataNode); + } + catch (mitk::SemanticRelationException& e) + { + mitkReThrow(e); + } } else if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { - RemoveSegmentation(semanticRelationsIntegration, dataNode); + try + { + RemoveSegmentation(dataNode); + } + catch (mitk::SemanticRelationException& e) + { + mitkReThrow(e); + } } } - void RemoveImage(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataNode* image) + void RemoveImage(mitk::DataStorage* dataStorage, const mitk::DataNode* image) { - if (nullptr == image) - { - return; - } - + mitk::SemanticRelationsIntegration semanticRelationsIntegration; try { + // remove each corresponding segmentation from the semantic relations storage + mitk::DataStorage::SetOfObjects::ConstPointer childNodes = dataStorage->GetDerivations(image, mitk::NodePredicates::GetSegmentationPredicate(), false); + for (auto it = childNodes->Begin(); it != childNodes->End(); ++it) + { + RemoveSegmentation(it->Value()); + } // remove the image from the semantic relations storage - semanticRelationsIntegration->RemoveImage(image); + semanticRelationsIntegration.RemoveImage(image); } - catch (const mitk::SemanticRelationException& e) + catch (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.\n" - "Reason:\n" + QString::fromStdString(exceptionMessage.str())); - msgBox.setIcon(QMessageBox::Warning); - msgBox.exec(); - return; + mitkReThrow(e); } } - void RemoveSegmentation(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataNode* segmentation) + void RemoveSegmentation(const mitk::DataNode* segmentation) { - if (nullptr == segmentation) - { - return; - } - + mitk::SemanticRelationsIntegration semanticRelationsIntegration; try { // remove the segmentation from the semantic relations storage - semanticRelationsIntegration->RemoveSegmentation(segmentation); + semanticRelationsIntegration.RemoveSegmentation(segmentation); } - catch (const mitk::SemanticRelationException& e) + catch (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.\n" - "Reason:\n" + QString::fromStdString(exceptionMessage.str())); - msgBox.setIcon(QMessageBox::Warning); - msgBox.exec(); - return; + mitkReThrow(e); } } } QmitkDataNodeRemoveFromSemanticRelationsAction::QmitkDataNodeRemoveFromSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) : QAction(parent) - , QmitkAbstractSemanticRelationsAction(workbenchPartSite) + , QmitkAbstractDataNodeAction(workbenchPartSite) { setText(tr("Remove from semantic relations")); InitializeAction(); } QmitkDataNodeRemoveFromSemanticRelationsAction::QmitkDataNodeRemoveFromSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) : QAction(parent) - , QmitkAbstractSemanticRelationsAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) + , QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) { setText(tr("Remove from semantic relations")); InitializeAction(); } -QmitkDataNodeRemoveFromSemanticRelationsAction::~QmitkDataNodeRemoveFromSemanticRelationsAction() -{ - // nothing here -} - void QmitkDataNodeRemoveFromSemanticRelationsAction::InitializeAction() { connect(this, &QAction::triggered, this, &QmitkDataNodeRemoveFromSemanticRelationsAction::OnActionTriggered); } void QmitkDataNodeRemoveFromSemanticRelationsAction::OnActionTriggered(bool /*checked*/) { + if (m_DataStorage.IsExpired()) + { + return; + } + auto dataNode = GetSelectedNode(); - RemoveFromSemanticRelationsAction::Run(m_SemanticRelationsIntegration.get(), dataNode); + RemoveFromSemanticRelationsAction::Run(m_DataStorage.Lock(),dataNode); } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.h index 0a3071f614..2ac42add53 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.h +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.h @@ -1,57 +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 QMITKDATANODEREMOVEFROMSEMANTICRELATIONSACTION_H #define QMITKDATANODEREMOVEFROMSEMANTICRELATIONSACTION_H #include // mitk gui qt application plugin -#include "QmitkAbstractSemanticRelationsAction.h" +#include // qt #include namespace RemoveFromSemanticRelationsAction { - MITK_GUI_SEMANTICRELATIONS_EXPORT void Run(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataNode* dataNode); - - void RemoveImage(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataNode* image); - void RemoveSegmentation(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataNode* segmentation); + /** + * @brief The function checks whether the given node is an image or a segmentation and calls the corresponding remove function. + * The corresponding remove functions will remove the data node from the semantic relations storage. + * If an image is removed, the child segmentation nodes will also be removed from the semantic relations storage. + * + * @pre The given dataStorage has to be valid (!nullptr). + * @pre The given dataNode has to be valid (!nullptr). + * The function simply returns if the preconditions are not met. + * + * @throw SemanticRelationException re-thrown. + * + * @param dataStorage The data storage to use to check for the child segmentation nodes of an image. + * @param dataNode The data node to remove. + */ + MITK_GUI_SEMANTICRELATIONS_EXPORT void Run(mitk::DataStorage* dataStorage, const mitk::DataNode* dataNode); + + void RemoveImage(mitk::DataStorage* dataStorage, const mitk::DataNode* image); + void RemoveSegmentation(const mitk::DataNode* segmentation); } -class MITK_GUI_SEMANTICRELATIONS_EXPORT QmitkDataNodeRemoveFromSemanticRelationsAction : public QAction, public QmitkAbstractSemanticRelationsAction +class MITK_GUI_SEMANTICRELATIONS_EXPORT QmitkDataNodeRemoveFromSemanticRelationsAction : public QAction, public QmitkAbstractDataNodeAction { Q_OBJECT public: QmitkDataNodeRemoveFromSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite); QmitkDataNodeRemoveFromSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite); - virtual ~QmitkDataNodeRemoveFromSemanticRelationsAction() override; - private Q_SLOTS: void OnActionTriggered(bool); protected: virtual void InitializeAction() override; }; #endif // QMITKDATANODEREMOVEFROMSEMANTICRELATIONSACTION_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetControlPointAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetControlPointAction.cpp index 825a5abf7f..070da81ebb 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetControlPointAction.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetControlPointAction.cpp @@ -1,102 +1,174 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // semantic relations plugin #include "QmitkDataNodeSetControlPointAction.h" +#include "QmitkDataNodeRemoveFromSemanticRelationsAction.h" // semantic relations module +#include #include #include +#include #include +#include #include // semantic relations UI module #include "QmitkControlPointDialog.h" -// mitk gui common plugin -#include - // qt #include +#include QmitkDataNodeSetControlPointAction::QmitkDataNodeSetControlPointAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) : QAction(parent) - , QmitkAbstractSemanticRelationsAction(workbenchPartSite) + , QmitkAbstractDataNodeAction(workbenchPartSite) { setText(tr("Set control point")); m_Parent = parent; InitializeAction(); } QmitkDataNodeSetControlPointAction::QmitkDataNodeSetControlPointAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) : QAction(parent) - , QmitkAbstractSemanticRelationsAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) + , QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) { setText(tr("Set control point")); m_Parent = parent; InitializeAction(); } -QmitkDataNodeSetControlPointAction::~QmitkDataNodeSetControlPointAction() -{ - // nothing here -} - void QmitkDataNodeSetControlPointAction::InitializeAction() { connect(this, &QAction::triggered, this, &QmitkDataNodeSetControlPointAction::OnActionTriggered); } void QmitkDataNodeSetControlPointAction::OnActionTriggered(bool /*checked*/) { - if (nullptr == m_SemanticRelationsIntegration) + if (m_DataStorage.IsExpired()) { return; } + auto dataStorage = m_DataStorage.Lock(); + auto dataNode = GetSelectedNode(); if (dataNode.IsNull()) { return; } QmitkControlPointDialog* inputDialog = new QmitkControlPointDialog(m_Parent); inputDialog->setWindowTitle("Set control point"); inputDialog->SetCurrentDate(mitk::SemanticRelationsInference::GetControlPointOfImage(dataNode)); int dialogReturnValue = inputDialog->exec(); if (QDialog::Rejected == dialogReturnValue) { return; } const QDate& userSelectedDate = inputDialog->GetCurrentDate(); mitk::SemanticTypes::ControlPoint controlPoint; controlPoint.UID = mitk::UIDGeneratorBoost::GenerateUID(); controlPoint.date = boost::gregorian::date(userSelectedDate.year(), userSelectedDate.month(), userSelectedDate.day()); + mitk::SemanticTypes::InformationType informationType; + mitk::SemanticTypes::ExaminationPeriod examinationPeriod; + mitk::SemanticRelationsDataStorageAccess::DataNodeVector allSpecificImages; try { - m_SemanticRelationsIntegration->UnlinkImageFromControlPoint(dataNode); - m_SemanticRelationsIntegration->SetControlPointOfImage(dataNode, controlPoint); + mitk::SemanticTypes::CaseID caseID = mitk::GetCaseIDFromDataNode(dataNode); + informationType = mitk::SemanticRelationsInference::GetInformationTypeOfImage(dataNode); + // see if the examination period - information type cell is already taken + examinationPeriod = mitk::FindFittingExaminationPeriod(caseID, controlPoint); + auto semanticRelationsDataStorageAccess = mitk::SemanticRelationsDataStorageAccess(dataStorage); + try + { + allSpecificImages = semanticRelationsDataStorageAccess.GetAllSpecificImages(caseID, informationType, examinationPeriod); + } + catch (const mitk::SemanticRelationException&) + { + // just continue since an exception means that there is no specific image + } } - catch (const mitk::SemanticRelationException&) + catch (const mitk::SemanticRelationException& e) { - return; + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox(QMessageBox::Warning, + "Could not set the control point.", + "The program wasn't able to correctly set the control point.\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); + msgBox.exec(); + } + + if (!allSpecificImages.empty()) + { + // examination period - information type cell is already taken + // ask if cell should be overwritten + QMessageBox::StandardButton answerButton = + QMessageBox::question(nullptr, + "Specific image already exists.", + QString::fromStdString("Force overwriting existing image " + informationType + " at " + examinationPeriod.name + "?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + if (answerButton == QMessageBox::Yes) + { + try + { + // remove already existent images at specific cell + for (const auto& specificImage : allSpecificImages) + { + RemoveFromSemanticRelationsAction::Run(dataStorage, specificImage); + } + } + catch (const mitk::SemanticRelationException& e) + { + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox(QMessageBox::Warning, + "Could not set the control point.", + "The program wasn't able to correctly set the control point.\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); + msgBox.exec(); + } + } + else + { + // else case is: no overwriting + return; + } + } + + // specific image does not exist or has been removed; setting the control point should work + mitk::SemanticRelationsIntegration semanticRelationsIntegration; + try + { + semanticRelationsIntegration.UnlinkImageFromControlPoint(dataNode); + semanticRelationsIntegration.SetControlPointOfImage(dataNode, controlPoint); + } + catch (const mitk::SemanticRelationException& e) + { + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox(QMessageBox::Warning, + "Could not set the control point.", + "The program wasn't able to correctly set the control point.\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); + msgBox.exec(); } } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetControlPointAction.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetControlPointAction.h index 625e707e92..b11eddbb9b 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetControlPointAction.h +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetControlPointAction.h @@ -1,49 +1,47 @@ /*=================================================================== 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 QMITKDATANODESETCONTROLPOINTACTION_H #define QMITKDATANODESETCONTROLPOINTACTION_H -// mitk gui qt semanticrelations plugin -#include "QmitkAbstractSemanticRelationsAction.h" +// mitk gui qt application plugin +#include // qt #include -class QmitkDataNodeSetControlPointAction : public QAction, public QmitkAbstractSemanticRelationsAction +class QmitkDataNodeSetControlPointAction : public QAction, public QmitkAbstractDataNodeAction { Q_OBJECT public: QmitkDataNodeSetControlPointAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite); QmitkDataNodeSetControlPointAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite); - virtual ~QmitkDataNodeSetControlPointAction() override; - private Q_SLOTS: void OnActionTriggered(bool); protected: virtual void InitializeAction() override; QWidget* m_Parent; }; #endif // QMITKDATANODESETCONTROLPOINTACTION_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetInformationTypeAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetInformationTypeAction.cpp index 64ea29b0e6..f5fc1816fe 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetInformationTypeAction.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetInformationTypeAction.cpp @@ -1,91 +1,165 @@ /*=================================================================== 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 "QmitkDataNodeSetInformationTypeAction.h" +#include "QmitkDataNodeRemoveFromSemanticRelationsAction.h" // semantic relations module +#include +#include #include +#include #include - -// mitk gui common plugin -#include +#include // qt #include +#include QmitkDataNodeSetInformationTypeAction::QmitkDataNodeSetInformationTypeAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) : QAction(parent) - , QmitkAbstractSemanticRelationsAction(workbenchPartSite) + , QmitkAbstractDataNodeAction(workbenchPartSite) { setText(tr("Set information type")); m_Parent = parent; InitializeAction(); } QmitkDataNodeSetInformationTypeAction::QmitkDataNodeSetInformationTypeAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) : QAction(parent) - , QmitkAbstractSemanticRelationsAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) + , QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) { setText(tr("Set information type")); m_Parent = parent; InitializeAction(); } -QmitkDataNodeSetInformationTypeAction::~QmitkDataNodeSetInformationTypeAction() -{ - // nothing here -} - void QmitkDataNodeSetInformationTypeAction::InitializeAction() { connect(this, &QAction::triggered, this, &QmitkDataNodeSetInformationTypeAction::OnActionTriggered); } void QmitkDataNodeSetInformationTypeAction::OnActionTriggered(bool /*checked*/) { - if (nullptr == m_SemanticRelationsIntegration) + if (m_DataStorage.IsExpired()) { return; } + auto dataStorage = m_DataStorage.Lock(); + auto dataNode = GetSelectedNode(); if (dataNode.IsNull()) { return; } QInputDialog* inputDialog = new QInputDialog(m_Parent); inputDialog->setWindowTitle(tr("Set information type of selected node")); inputDialog->setLabelText(tr("Information type:")); inputDialog->setTextValue(QString::fromStdString(mitk::SemanticRelationsInference::GetInformationTypeOfImage(dataNode))); inputDialog->setMinimumSize(250, 100); int dialogReturnValue = inputDialog->exec(); if (QDialog::Rejected == dialogReturnValue) { return; } + mitk::SemanticTypes::InformationType informationType = inputDialog->textValue().toStdString(); + + mitk::SemanticTypes::ExaminationPeriod examinationPeriod; + mitk::SemanticTypes::ControlPoint controlPoint; + mitk::SemanticRelationsDataStorageAccess::DataNodeVector allSpecificImages; try { - m_SemanticRelationsIntegration->SetInformationType(dataNode, inputDialog->textValue().toStdString()); + mitk::SemanticTypes::CaseID caseID = mitk::GetCaseIDFromDataNode(dataNode); + controlPoint = mitk::SemanticRelationsInference::GetControlPointOfImage(dataNode); + // see if the examination period - information type cell is already taken + examinationPeriod = mitk::FindFittingExaminationPeriod(caseID, controlPoint); + auto semanticRelationsDataStorageAccess = mitk::SemanticRelationsDataStorageAccess(dataStorage); + try + { + allSpecificImages = semanticRelationsDataStorageAccess.GetAllSpecificImages(caseID, informationType, examinationPeriod); + } + catch (const mitk::SemanticRelationException&) + { + // just continue since an exception means that there is no specific image + } } - catch (const mitk::SemanticRelationException&) + catch (const mitk::SemanticRelationException& e) { - return; + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox(QMessageBox::Warning, + "Could not set the information type.", + "The program wasn't able to correctly set the information type.\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); + msgBox.exec(); + } + + if (!allSpecificImages.empty()) + { + // examination period - information type cell is already taken + // ask if cell should be overwritten + QMessageBox::StandardButton answerButton = + QMessageBox::question(nullptr, + "Specific image already exists.", + QString::fromStdString("Force overwriting existing image " + informationType + " at " + examinationPeriod.name + "?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + if (answerButton == QMessageBox::Yes) + { + try + { + // remove already existent images at specific cell + for (const auto& specificImage : allSpecificImages) + { + RemoveFromSemanticRelationsAction::Run(dataStorage, specificImage); + } + } + catch (const mitk::SemanticRelationException& e) + { + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox(QMessageBox::Warning, + "Could not set the information type.", + "The program wasn't able to correctly set the information type.\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); + msgBox.exec(); + } + } + else + { + // else case is: no overwriting + return; + } + } + + mitk::SemanticRelationsIntegration semanticRelationsIntegration; + try + { + semanticRelationsIntegration.SetInformationType(dataNode, informationType); + } + catch (const mitk::SemanticRelationException& e) + { + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox(QMessageBox::Warning, + "Could not set the information type.", + "The program wasn't able to correctly set the information type.\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); + msgBox.exec(); } } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetInformationTypeAction.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetInformationTypeAction.h index 618b5d7f55..472def3765 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetInformationTypeAction.h +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetInformationTypeAction.h @@ -1,49 +1,47 @@ /*=================================================================== 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 QMITKDATANODESETINFORMATIONTYPEACTION_H #define QMITKDATANODESETINFORMATIONTYPEACTION_H // mitk gui qt application plugin -#include "QmitkAbstractSemanticRelationsAction.h" +#include // qt #include -class QmitkDataNodeSetInformationTypeAction : public QAction, public QmitkAbstractSemanticRelationsAction +class QmitkDataNodeSetInformationTypeAction : public QAction, public QmitkAbstractDataNodeAction { Q_OBJECT public: QmitkDataNodeSetInformationTypeAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite); QmitkDataNodeSetInformationTypeAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite); - virtual ~QmitkDataNodeSetInformationTypeAction() override; - private Q_SLOTS: void OnActionTriggered(bool); protected: virtual void InitializeAction() override; QWidget* m_Parent; }; #endif // QMITKDATANODESETINFORMATIONTYPEACTION_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeUnlinkFromLesionAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeUnlinkFromLesionAction.cpp index c01e1301b0..3c1619e8f3 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeUnlinkFromLesionAction.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeUnlinkFromLesionAction.cpp @@ -1,102 +1,94 @@ /*=================================================================== 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 "QmitkDataNodeUnlinkFromLesionAction.h" // semantic relations module #include #include +#include #include // mitk gui common plugin #include // qt #include // namespace that contains the concrete action namespace UnlinkFromLesionAction { - void Run(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, const mitk::DataNode* dataNode) + void Run(const mitk::DataNode* dataNode) { if (nullptr == dataNode) { return; } if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { + mitk::SemanticRelationsIntegration semanticRelationsIntegration; try { - semanticRelationsIntegration->UnlinkSegmentationFromLesion(dataNode); + semanticRelationsIntegration.UnlinkSegmentationFromLesion(dataNode); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox(QMessageBox::Warning, "Could not unlink the selected segmentation.", "The program wasn't able to correctly unlink the selected segmentation.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.exec(); } } else { QMessageBox msgBox(QMessageBox::Warning, "Could not unlink the selected data node.", "Please chose a valid segmentation to unlink from its represented lesion!"); msgBox.exec(); } } } QmitkDataNodeUnlinkFromLesionAction::QmitkDataNodeUnlinkFromLesionAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) : QAction(parent) - , QmitkAbstractSemanticRelationsAction(workbenchPartSite) + , QmitkAbstractDataNodeAction(workbenchPartSite) { setText(tr("Unlink from lesion")); InitializeAction(); } QmitkDataNodeUnlinkFromLesionAction::QmitkDataNodeUnlinkFromLesionAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) : QAction(parent) - , QmitkAbstractSemanticRelationsAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) + , QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) { setText(tr("Unlink from lesion")); InitializeAction(); } -QmitkDataNodeUnlinkFromLesionAction::~QmitkDataNodeUnlinkFromLesionAction() -{ - // nothing here -} - void QmitkDataNodeUnlinkFromLesionAction::InitializeAction() { connect(this, &QAction::triggered, this, &QmitkDataNodeUnlinkFromLesionAction::OnActionTriggered); } void QmitkDataNodeUnlinkFromLesionAction::OnActionTriggered(bool /*checked*/) { - if (nullptr == m_SemanticRelationsIntegration) - { - return; - } - auto dataNode = GetSelectedNode(); - UnlinkFromLesionAction::Run(m_SemanticRelationsIntegration.get(), dataNode); + UnlinkFromLesionAction::Run(dataNode); } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeUnlinkFromLesionAction.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeUnlinkFromLesionAction.h index 452d8b39fa..b9e0bf9d18 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeUnlinkFromLesionAction.h +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeUnlinkFromLesionAction.h @@ -1,54 +1,52 @@ /*=================================================================== 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 QMITKDATANODEUNLINKFROMLESIONACTION_H #define QMITKDATANODEUNLINKFROMLESIONACTION_H #include -// mitk gui qt semanticrelations plugin -#include "QmitkAbstractSemanticRelationsAction.h" +// mitk gui qt application plugin +#include // qt #include namespace UnlinkFromLesionAction { - MITK_GUI_SEMANTICRELATIONS_EXPORT void Run(const mitk::DataNode* dataNode, mitk::SemanticRelationsIntegration* semanticRelationsIntegration); + MITK_GUI_SEMANTICRELATIONS_EXPORT void Run(const mitk::DataNode* dataNode); } -class MITK_GUI_SEMANTICRELATIONS_EXPORT QmitkDataNodeUnlinkFromLesionAction : public QAction, public QmitkAbstractSemanticRelationsAction +class MITK_GUI_SEMANTICRELATIONS_EXPORT QmitkDataNodeUnlinkFromLesionAction : public QAction, public QmitkAbstractDataNodeAction { Q_OBJECT public: QmitkDataNodeUnlinkFromLesionAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite); QmitkDataNodeUnlinkFromLesionAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite); - virtual ~QmitkDataNodeUnlinkFromLesionAction() override; - private Q_SLOTS: void OnActionTriggered(bool); protected: virtual void InitializeAction() override; }; #endif // QMITKDATANODEUNLINKFROMLESIONACTION_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataSetOpenInAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataSetOpenInAction.cpp new file mode 100644 index 0000000000..6019891049 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataSetOpenInAction.cpp @@ -0,0 +1,162 @@ +/*=================================================================== + +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 "QmitkDataSetOpenInAction.h" + +// mitk core +#include +#include + +// render window manager module +#include +#include + +// semantic relations module +#include "mitkNodePredicates.h" + +// mitk gui qt application plugin +#include + +// qt +#include +#include + +namespace OpenInAction +{ + void Run(mitk::DataStorage::Pointer dataStorage, mitk::DataNode::Pointer imageNode, mitk::BaseRenderer* renderer /*= nullptr*/) + { + if (dataStorage.IsNull()) + { + return; + } + + if (imageNode.IsNull()) + { + return; + } + + auto renderWindowLayerController = std::make_unique(); + renderWindowLayerController->SetDataStorage(dataStorage); + + // get currently fixed layer nodes of the specified renderer + // remove them from the specified renderer + auto layerStack = mitk::RenderWindowLayerUtilities::GetLayerStack(dataStorage, renderer, true); + for (auto& fixedLayer : layerStack) + { + // hide all nodes of the specified renderer + fixedLayer.second->SetVisibility(false, renderer); + fixedLayer.second->Modified(); + } + + // add the selected data node to the specified renderer + // only needed in case the render window manager plugin is not used + imageNode->SetBoolProperty("fixedLayer", true, renderer); + // make is visible + imageNode->SetVisibility(true, renderer); + imageNode->Modified(); + // move node to front, which also request a render update + renderWindowLayerController->MoveNodeToFront(imageNode, renderer); + + QList visibleNodes; + visibleNodes.push_back(imageNode); + // get all corresponding segmentations of this node + mitk::DataStorage::SetOfObjects::ConstPointer segmentationNodes = dataStorage->GetDerivations(imageNode, mitk::NodePredicates::GetSegmentationPredicate(), false); + for (auto it = segmentationNodes->Begin(); it != segmentationNodes->End(); ++it) + { + auto segmentation = it->Value(); + segmentation->SetBoolProperty("fixedLayer", true, renderer); + segmentation->SetVisibility(true, renderer); + // move node to front, which also request a render update + renderWindowLayerController->MoveNodeToFront(segmentation, renderer); + visibleNodes.push_back(segmentation); + } + + ReinitAction::Run(berry::IWorkbenchPartSite::Pointer(), dataStorage, visibleNodes, renderer); + } +} + +QmitkDataSetOpenInAction::QmitkDataSetOpenInAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) + : QmitkDataNodeOpenInAction(parent, workbenchPartSite) +{ + setText(tr("Open in")); + InitializeAction(); +} + +QmitkDataSetOpenInAction::QmitkDataSetOpenInAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) + : QmitkDataNodeOpenInAction(parent, berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) +{ + setText(tr("Open in")); + InitializeAction(); +} + +void QmitkDataSetOpenInAction::InitializeAction() +{ + setCheckable(true); + + setMenu(new QMenu); + connect(menu(), &QMenu::aboutToShow, this, &QmitkDataSetOpenInAction::OnMenuAboutToShow); + + SetControlledRenderer(); +} + +void QmitkDataSetOpenInAction::OnMenuAboutToShow() +{ + menu()->clear(); + QAction* action; + + QStringList rendererNames; + for (const auto& renderer : m_ControlledRenderer) + { + rendererNames.append(renderer->GetName()); + } + + rendererNames.sort(); + for (const auto& rendererName : rendererNames) + { + action = menu()->addAction(rendererName); + connect(action, &QAction::triggered, this, &QmitkDataSetOpenInAction::OnActionTriggered); + } +} + +void QmitkDataSetOpenInAction::OnActionTriggered(bool /*checked*/) +{ + if (m_DataStorage.IsExpired()) + { + return; + } + + auto dataNode = GetSelectedNode(); + if (dataNode.IsNull()) + { + return; + } + + QAction* senderAction = qobject_cast(QObject::sender()); + if (nullptr == senderAction) + { + return; + } + + std::string selectedRenderer = senderAction->text().toStdString(); + mitk::BaseRenderer* renderer = mitk::BaseRenderer::GetByName(selectedRenderer); + if (nullptr == renderer) + { + return; + } + + OpenInAction::Run(m_DataStorage.Lock(), dataNode, renderer); +} diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataSetOpenInAction.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataSetOpenInAction.h new file mode 100644 index 0000000000..c6bf22736e --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataSetOpenInAction.h @@ -0,0 +1,48 @@ +/*=================================================================== + +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 QMITKDATASETOPENINACTION_H +#define QMITKDATASETOPENINACTION_H + +#include "QmitkDataNodeOpenInAction.h" + +namespace OpenInAction +{ + void Run(mitk::DataStorage::Pointer dataStorage, + mitk::DataNode::Pointer imageNode, + mitk::BaseRenderer* renderer = nullptr); +} + +class QmitkDataSetOpenInAction : public QmitkDataNodeOpenInAction +{ + Q_OBJECT + +public: + + QmitkDataSetOpenInAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite); + QmitkDataSetOpenInAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite); + +private Q_SLOTS: + + void OnMenuAboutToShow(); + void OnActionTriggered(bool); + +protected: + + virtual void InitializeAction() override; +}; + +#endif // QMITKDATASETOPENINACTION_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkFocusOnLesionAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkFocusOnLesionAction.cpp new file mode 100644 index 0000000000..bd507fdd94 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkFocusOnLesionAction.cpp @@ -0,0 +1,126 @@ +/*=================================================================== + +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 "QmitkFocusOnLesionAction.h" +#include "QmitkLabelSetJumpToAction.h" + +// mitk core +#include +#include + +// render window manager module +#include +#include + +// semantic relations module +#include +#include + +// qt +#include +#include + +QmitkFocusOnLesionAction::QmitkFocusOnLesionAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) + : QAction(parent) + , QmitkAbstractDataNodeAction(workbenchPartSite) +{ + setText(tr("Focus on lesion")); + InitializeAction(); +} + +QmitkFocusOnLesionAction::QmitkFocusOnLesionAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) + : QAction(parent) + , QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) +{ + setText(tr("Focus on lesion")); + InitializeAction(); +} + +void QmitkFocusOnLesionAction::SetSelectedLesion(mitk::SemanticTypes::Lesion selectedLesion) +{ + m_Lesion = selectedLesion; +} + +void QmitkFocusOnLesionAction::InitializeAction() +{ + setCheckable(true); + connect(this, &QAction::triggered, this, &QmitkFocusOnLesionAction::OnActionTriggered); + + SetControlledRenderer(); +} + +void QmitkFocusOnLesionAction::OnActionTriggered(bool /*checked*/) +{ + if (m_WorkbenchPartSite.Expired()) + { + return; + } + + if (m_DataStorage.IsExpired()) + { + return; + } + + auto dataStorage = m_DataStorage.Lock(); + auto renderWindowLayerController = std::make_unique(); + renderWindowLayerController->SetDataStorage(dataStorage); + // check each renderer to see if it contains a segmentation represented by the currently selected lesion + for (const auto& renderer : m_ControlledRenderer) + { + // get currently fixed layer nodes of the specified renderer + auto layerStack = mitk::RenderWindowLayerUtilities::GetLayerStack(dataStorage, renderer, true); + // check each node to see if it is a segmentation represented by the currently selected lesion + for (const auto& layer : layerStack) + { + auto dataNode = layer.second; + bool isVisible = false; + if (dataNode.IsNotNull()) + { + dataNode->GetBoolProperty("visible", isVisible, renderer); + } + + // only focus in renderer where the segmentation is visible + if (!isVisible) + { + continue; + } + + auto currentLesion = mitk::SemanticRelationsInference::GetLesionOfSegmentation(dataNode); + if (currentLesion.UID == m_Lesion.UID) + { + // jump to this lesion in the specified renderer + LabelSetJumpToAction::Run(m_WorkbenchPartSite.Lock(), dataNode, renderer); + } + } + } +} + +void QmitkFocusOnLesionAction::SetControlledRenderer() +{ + const mitk::RenderingManager::RenderWindowVector allRegisteredRenderWindows = + mitk::RenderingManager::GetInstance()->GetAllRegisteredRenderWindows(); + mitk::BaseRenderer *baseRenderer = nullptr; + m_ControlledRenderer.clear(); + for (const auto& renderWindow : allRegisteredRenderWindows) + { + baseRenderer = mitk::BaseRenderer::GetInstance(renderWindow); + if (nullptr != baseRenderer) + { + m_ControlledRenderer.push_back(baseRenderer); + } + } +} diff --git a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeOpenInAction.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkFocusOnLesionAction.h similarity index 54% copy from Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeOpenInAction.h copy to Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkFocusOnLesionAction.h index 403cafc12a..7f01b292fb 100644 --- a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeOpenInAction.h +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkFocusOnLesionAction.h @@ -1,60 +1,56 @@ /*=================================================================== 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 QMITKDATANODEOPENINACTION_H -#define QMITKDATANODEOPENINACTION_H +#ifndef QMITKLESIONSHOWINACTION_H +#define QMITKLESIONSHOWINACTION_H -#include +// mitk gui qt application plugin +#include -#include "QmitkAbstractDataNodeAction.h" - -// mitk core -#include +// semantic relations module +#include // qt #include -class MITK_QT_APP QmitkDataNodeOpenInAction : public QAction, public QmitkAbstractDataNodeAction +class QmitkFocusOnLesionAction : public QAction, public QmitkAbstractDataNodeAction { Q_OBJECT public: typedef std::vector RendererVector; - QmitkDataNodeOpenInAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite); - QmitkDataNodeOpenInAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite); - - virtual ~QmitkDataNodeOpenInAction() override; + QmitkFocusOnLesionAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite); + QmitkFocusOnLesionAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite); - void SetControlledRenderer(RendererVector controlledRenderer); + void SetSelectedLesion(mitk::SemanticTypes::Lesion selectedLesion); private Q_SLOTS: - void OnMenuAboutToShow(); void OnActionTriggered(bool); protected: virtual void InitializeAction() override; void SetControlledRenderer(); RendererVector m_ControlledRenderer; - + mitk::SemanticTypes::Lesion m_Lesion; }; -#endif // QMITKDATANODEOPENINACTION_H +#endif // QMITKLESIONSHOWINACTION_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLabelSetJumpToAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLabelSetJumpToAction.cpp new file mode 100644 index 0000000000..b14aa27027 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLabelSetJumpToAction.cpp @@ -0,0 +1,123 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +// mitk gui qt application plugin +#include "QmitkLabelSetJumpToAction.h" + +// mitk core +#include +#include +#include + +// mitk multi label module +#include + +// qt +#include +#include + +namespace LabelSetJumpToAction +{ + void Run(berry::IWorkbenchPartSite::Pointer workbenchPartSite, const mitk::DataNode* dataNode, mitk::BaseRenderer* baseRenderer /*= nullptr*/) + { + if (workbenchPartSite.IsNull()) + { + return; + } + + if (nullptr == dataNode) + { + return; + } + + mitk::LabelSetImage* labelSetImage = dynamic_cast(dataNode->GetData()); + if (nullptr == labelSetImage) + { + return; + } + + unsigned 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) + { + auto renderWindowPart = mitk::WorkbenchUtil::GetRenderWindowPart(workbenchPartSite->GetPage(), mitk::WorkbenchUtil::NONE); + if (nullptr == renderWindowPart) + { + renderWindowPart = mitk::WorkbenchUtil::OpenRenderWindowPart(workbenchPartSite->GetPage(), false); + if (nullptr == renderWindowPart) + { + // no render window available + return; + } + } + + auto segmentation = dynamic_cast(dataNode->GetData()); + if (nullptr != segmentation) + { + if (nullptr == baseRenderer) + { + renderWindowPart->SetSelectedPosition(centerPosition); + mitk::RenderingManager::GetInstance()->InitializeViews(segmentation->GetTimeGeometry()); + } + else + { + renderWindowPart->SetSelectedPosition(centerPosition, baseRenderer->GetName()); + mitk::RenderingManager::GetInstance()->InitializeView(baseRenderer->GetRenderWindow(), segmentation->GetTimeGeometry()); + } + } + } + } +} + +QmitkLabelSetJumpToAction::QmitkLabelSetJumpToAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) + : QAction(parent) + , QmitkAbstractDataNodeAction(workbenchPartSite) +{ + setText(tr("Jump to")); + InitializeAction(); +} + +QmitkLabelSetJumpToAction::QmitkLabelSetJumpToAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) + : QAction(parent) + , QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) +{ + setText(tr("Jump to")); + InitializeAction(); +} + +void QmitkLabelSetJumpToAction::InitializeAction() +{ + connect(this, &QAction::triggered, this, &QmitkLabelSetJumpToAction::OnActionTriggered); +} + +void QmitkLabelSetJumpToAction::OnActionTriggered(bool /*checked*/) +{ + if (m_WorkbenchPartSite.Expired()) + { + return; + } + + auto dataNode = GetSelectedNode(); + if (dataNode.IsNull()) + { + return; + } + + mitk::BaseRenderer::Pointer baseRenderer = GetBaseRenderer(); + LabelSetJumpToAction::Run(m_WorkbenchPartSite.Lock(), dataNode, baseRenderer); +} diff --git a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeOpenInAction.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLabelSetJumpToAction.h similarity index 52% copy from Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeOpenInAction.h copy to Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLabelSetJumpToAction.h index 403cafc12a..0c650ae533 100644 --- a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeOpenInAction.h +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLabelSetJumpToAction.h @@ -1,60 +1,50 @@ /*=================================================================== 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 QMITKDATANODEOPENINACTION_H -#define QMITKDATANODEOPENINACTION_H - -#include +#ifndef QMITKLABELSETJUMPTOACTION_H +#define QMITKLABELSETJUMPTOACTION_H #include "QmitkAbstractDataNodeAction.h" -// mitk core -#include - // qt #include -class MITK_QT_APP QmitkDataNodeOpenInAction : public QAction, public QmitkAbstractDataNodeAction +namespace LabelSetJumpToAction +{ + void Run(berry::IWorkbenchPartSite::Pointer workbenchPartSite, + const mitk::DataNode* dataNode, + mitk::BaseRenderer* baseRenderer = nullptr); +} + +class QmitkLabelSetJumpToAction : public QAction, public QmitkAbstractDataNodeAction { Q_OBJECT public: - typedef std::vector RendererVector; - - QmitkDataNodeOpenInAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite); - QmitkDataNodeOpenInAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite); - - virtual ~QmitkDataNodeOpenInAction() override; - - void SetControlledRenderer(RendererVector controlledRenderer); + QmitkLabelSetJumpToAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite); + QmitkLabelSetJumpToAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite); private Q_SLOTS: - void OnMenuAboutToShow(); void OnActionTriggered(bool); protected: virtual void InitializeAction() override; - - void SetControlledRenderer(); - - RendererVector m_ControlledRenderer; - }; -#endif // QMITKDATANODEOPENINACTION_H +#endif // QMITKLABELSETJUMPTOACTION_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp index 3340b471fc..4a6872c152 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp @@ -1,432 +1,496 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // semantic relations plugin #include "QmitkLesionInfoWidget.h" +#include "QmitkDataNodeAddToSemanticRelationsAction.h" +#include "QmitkFocusOnLesionAction.h" #include "QmitkSemanticRelationsNodeSelectionDialog.h" // semantic relations UI module #include // semantic relations module #include #include #include #include #include -// registration ontology module -//#include +// segmentation +#include +#include // qt #include #include #include #include #include -QmitkLesionInfoWidget::QmitkLesionInfoWidget(mitk::DataStorage* dataStorage, QWidget* parent /*= nullptr*/) +QmitkLesionInfoWidget::QmitkLesionInfoWidget(mitk::DataStorage* dataStorage, + berry::IWorkbenchPartSite::Pointer workbenchPartSite, + QWidget* parent /*= nullptr*/) : QWidget(parent) , m_DataStorage(dataStorage) + , m_WorkbenchPartSite(workbenchPartSite) , m_SemanticRelationsDataStorageAccess(std::make_unique(dataStorage)) , m_SemanticRelationsIntegration(std::make_unique()) { Initialize(); } -QmitkLesionInfoWidget::~QmitkLesionInfoWidget() -{ - // nothing here -} - void QmitkLesionInfoWidget::Initialize() { m_Controls.setupUi(this); m_Controls.lesionTreeView->setAlternatingRowColors(true); m_Controls.lesionTreeView->setSelectionMode(QAbstractItemView::SingleSelection); m_Controls.lesionTreeView->setSelectionBehavior(QAbstractItemView::SelectRows); m_Controls.lesionTreeView->setContextMenuPolicy(Qt::CustomContextMenu); m_StorageModel = new QmitkLesionTreeModel(m_Controls.lesionTreeView); if (m_DataStorage.IsExpired()) { return; } auto dataStorage = m_DataStorage.Lock(); m_StorageModel->SetDataStorage(dataStorage); m_Controls.lesionTreeView->setModel(m_StorageModel); SetUpConnections(); } void QmitkLesionInfoWidget::SetUpConnections() { connect(m_StorageModel, &QmitkLesionTreeModel::ModelUpdated, this, &QmitkLesionInfoWidget::OnModelUpdated); - - // connect buttons to modify semantic relations connect(m_Controls.addLesionPushButton, &QPushButton::clicked, this, &QmitkLesionInfoWidget::OnAddLesionButtonClicked); - - // connect each list widget with a custom slots connect(m_Controls.lesionTreeView->selectionModel(), &QItemSelectionModel::currentChanged, this, &QmitkLesionInfoWidget::OnSelectionChanged); - - // connect context menu entries connect(m_Controls.lesionTreeView, &QTreeView::customContextMenuRequested, this, &QmitkLesionInfoWidget::OnLesionListContextMenuRequested); } void QmitkLesionInfoWidget::SetCaseID(const mitk::SemanticTypes::CaseID& caseID) { m_CaseID = caseID; m_StorageModel->SetCaseID(caseID); } void QmitkLesionInfoWidget::SetDataNodeSelection(const QList& dataNodeSelection) { m_StorageModel->SetDataNodeSelection(dataNodeSelection); } ////////////////////////////////////////////////////////////////////////// // Implementation of the QT_SLOTS ////////////////////////////////////////////////////////////////////////// void QmitkLesionInfoWidget::OnModelUpdated() { m_Controls.lesionTreeView->expandAll(); int columns = m_Controls.lesionTreeView->model()->columnCount(); for (int i = 0; i < columns; ++i) { m_Controls.lesionTreeView->resizeColumnToContents(i); } } void QmitkLesionInfoWidget::OnAddLesionButtonClicked() { if (m_CaseID.empty()) { QMessageBox msgBox(QMessageBox::Warning, "No case ID set.", "In order to add a lesion, please specify the current case / patient."); msgBox.exec(); return; } mitk::SemanticTypes::Lesion newLesion = mitk::GenerateNewLesion(); try { m_SemanticRelationsIntegration->AddLesion(m_CaseID, newLesion); } catch (mitk::SemanticRelationException& e) { MITK_INFO << "Could not add a new lesion. " << e; } } void QmitkLesionInfoWidget::OnSelectionChanged(const QModelIndex& current, const QModelIndex& /*previous*/) { // only the UID is needed to identify a representing lesion QVariant data = m_StorageModel->data(current, Qt::UserRole); if (!data.canConvert()) { return; } auto lesion = data.value()->GetData().GetLesion(); if (false == mitk::SemanticRelationsInference::InstanceExists(m_CaseID, lesion)) { // no UID of a existing lesion found; cannot create a lesion return; } // if selected data nodes are set, reset to empty list to // hide "selected data nodes presence background highlighting" in the model if (!m_StorageModel->GetSelectedDataNodes().isEmpty()) { m_StorageModel->SetDataNodeSelection(QList()); } emit LesionSelectionChanged(lesion); } void QmitkLesionInfoWidget::OnLesionListContextMenuRequested(const QPoint& pos) { if (nullptr == m_SemanticRelationsIntegration) { return; } if (m_CaseID.empty()) { QMessageBox msgBox(QMessageBox::Warning, "No case ID set.", "In order to access the context menu entries a case ID has to be set."); msgBox.exec(); return; } QModelIndex index = m_Controls.lesionTreeView->indexAt(pos); if (!index.isValid()) { // no item clicked; cannot retrieve the current lesion return; } QVariant data = m_StorageModel->data(index, Qt::UserRole); mitk::SemanticTypes::Lesion selectedLesion; if (data.canConvert()) { selectedLesion = data.value()->GetData().GetLesion(); } else { return; } QMenu* menu = new QMenu(m_Controls.lesionTreeView); QAction* linkToSegmentation = new QAction("Link to segmentation", this); linkToSegmentation->setEnabled(true); connect(linkToSegmentation, &QAction::triggered, [this, selectedLesion] { OnLinkToSegmentation(selectedLesion); }); menu->addAction(linkToSegmentation); QAction* setLesionName = new QAction("Set lesion name", this); setLesionName->setEnabled(true); connect(setLesionName, &QAction::triggered, [this, selectedLesion] { OnSetLesionName(selectedLesion); }); menu->addAction(setLesionName); QAction* setLesionClass = new QAction("Set lesion class", this); setLesionClass->setEnabled(true); connect(setLesionClass, &QAction::triggered, [this, selectedLesion] { OnSetLesionClass(selectedLesion); }); menu->addAction(setLesionClass); - QAction* propageLesionToImage = new QAction("Propagate lesion to image", this); - propageLesionToImage->setEnabled(true); - connect(propageLesionToImage, &QAction::triggered, [this, selectedLesion] { OnPropagateLesion(selectedLesion); }); - menu->addAction(propageLesionToImage); + QAction* createNewSegmentation = new QAction("Create new lesion", this); + createNewSegmentation->setEnabled(true); + connect(createNewSegmentation, &QAction::triggered, [this, selectedLesion] { OnCreateNewSegmentation(selectedLesion); }); + menu->addAction(createNewSegmentation); QAction* removeLesion = new QAction("Remove lesion", this); removeLesion->setEnabled(true); connect(removeLesion, &QAction::triggered, [this, selectedLesion] { OnRemoveLesion(selectedLesion); }); menu->addAction(removeLesion); + if (!m_WorkbenchPartSite.Expired()) + { + QmitkFocusOnLesionAction* focusOnLesion = new QmitkFocusOnLesionAction(this, m_WorkbenchPartSite.Lock()); + focusOnLesion->SetDataStorage(m_DataStorage.Lock()); + focusOnLesion->SetSelectedLesion(selectedLesion); + menu->addAction(focusOnLesion); + } + menu->popup(QCursor::pos()); } void QmitkLesionInfoWidget::OnLinkToSegmentation(mitk::SemanticTypes::Lesion selectedLesion) { if (m_DataStorage.IsExpired()) { return; } auto dataStorage = m_DataStorage.Lock(); QmitkSemanticRelationsNodeSelectionDialog* dialog = new QmitkSemanticRelationsNodeSelectionDialog(this, "Select segmentation to link to the selected lesion.", ""); dialog->setWindowTitle("Select segmentation node"); dialog->SetDataStorage(dataStorage); dialog->SetNodePredicate(mitk::NodePredicates::GetSegmentationPredicate()); dialog->SetSelectOnlyVisibleNodes(true); dialog->SetCaseID(m_CaseID); + // set the last added segmentation node as pre-selected data node + const mitk::DataNode* lastSegmentation = m_StorageModel->GetLastSegmentation(); + QList selectedDataNodes; + if (nullptr != lastSegmentation) + { + selectedDataNodes.push_back(const_cast(lastSegmentation)); + dialog->SetCurrentSelection(selectedDataNodes); + } int dialogReturnValue = dialog->exec(); if (QDialog::Rejected == dialogReturnValue) { return; } - auto nodes = dialog->GetSelectedNodes(); mitk::DataNode::Pointer selectedDataNode = nullptr; - if (!nodes.isEmpty()) + selectedDataNodes = dialog->GetSelectedNodes(); + if (!selectedDataNodes.isEmpty()) { // only single selection allowed - selectedDataNode = nodes.front(); + selectedDataNode = selectedDataNodes.front(); } if (nullptr == selectedDataNode || false == mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(selectedDataNode)) { QMessageBox msgBox(QMessageBox::Warning, "No valid segmentation node selected.", "In order to link the selected lesion to a segmentation, please specify a valid segmentation node."); msgBox.exec(); return; } mitk::BaseData* baseData = selectedDataNode->GetData(); if (nullptr == baseData) { QMessageBox msgBox(QMessageBox::Warning, "No valid base data.", "In order to link the selected lesion to a segmentation, please specify a valid segmentation node."); msgBox.exec(); return; } - try - { - m_SemanticRelationsIntegration->LinkSegmentationToLesion(selectedDataNode, selectedLesion); - } - catch (const mitk::SemanticRelationException& e) - { - std::stringstream exceptionMessage; exceptionMessage << e; - QMessageBox msgBox(QMessageBox::Warning, - "Could not link the selected lesion.", - "The program wasn't able to correctly link the selected lesion with the selected segmentation.\n" - "Reason:\n" + QString::fromStdString(exceptionMessage.str())); - msgBox.exec(); - } + LinkSegmentationToLesion(selectedDataNode, selectedLesion); } void QmitkLesionInfoWidget::OnSetLesionName(mitk::SemanticTypes::Lesion selectedLesion) { // use the lesion information to set the input text for the dialog QmitkLesionTextDialog* inputDialog = new QmitkLesionTextDialog(this); inputDialog->setWindowTitle("Set lesion name"); inputDialog->SetLineEditText(selectedLesion.name); int dialogReturnValue = inputDialog->exec(); if (QDialog::Rejected == dialogReturnValue) { return; } selectedLesion.name = inputDialog->GetLineEditText().toStdString(); m_SemanticRelationsIntegration->OverwriteLesion(m_CaseID, selectedLesion); } void QmitkLesionInfoWidget::OnSetLesionClass(mitk::SemanticTypes::Lesion selectedLesion) { // use the lesion information to set the input text for the dialog QmitkLesionTextDialog* inputDialog = new QmitkLesionTextDialog(this); inputDialog->setWindowTitle("Set lesion class"); inputDialog->SetLineEditText(selectedLesion.lesionClass.classType); // prepare the completer for the dialogs input text field mitk::LesionClassVector allLesionClasses = mitk::SemanticRelationsInference::GetAllLesionClassesOfCase(m_CaseID); QStringList wordList; for (const auto& lesionClass : allLesionClasses) { wordList << QString::fromStdString(lesionClass.classType); } QCompleter* completer = new QCompleter(wordList, this); completer->setCaseSensitivity(Qt::CaseInsensitive); inputDialog->GetLineEdit()->setCompleter(completer); int dialogReturnValue = inputDialog->exec(); if (QDialog::Rejected == dialogReturnValue) { return; } // retrieve the new input lesion class type and check for an already existing lesion class types std::string newLesionClassType = inputDialog->GetLineEditText().toStdString(); mitk::SemanticTypes::LesionClass existingLesionClass = mitk::FindExistingLesionClass(newLesionClassType, allLesionClasses); if (existingLesionClass.UID.empty()) { // could not find lesion class information for the new lesion class type // create a new lesion class for the selected lesion existingLesionClass = mitk::GenerateNewLesionClass(newLesionClassType); } selectedLesion.lesionClass = existingLesionClass; m_SemanticRelationsIntegration->OverwriteLesion(m_CaseID, selectedLesion); } -void QmitkLesionInfoWidget::OnPropagateLesion(mitk::SemanticTypes::Lesion selectedLesion) +void QmitkLesionInfoWidget::OnCreateNewSegmentation(mitk::SemanticTypes::Lesion selectedLesion) { if (m_DataStorage.IsExpired()) { return; } auto dataStorage = m_DataStorage.Lock(); - QmitkSemanticRelationsNodeSelectionDialog* dialog = new QmitkSemanticRelationsNodeSelectionDialog(this, "Select data node to propagate the selected lesion.", ""); + QmitkSemanticRelationsNodeSelectionDialog* dialog = new QmitkSemanticRelationsNodeSelectionDialog(this, "Select image to segment lesion on.", ""); dialog->setWindowTitle("Select image node"); dialog->SetDataStorage(dataStorage); dialog->SetNodePredicate(mitk::NodePredicates::GetImagePredicate()); dialog->SetSelectOnlyVisibleNodes(true); dialog->SetCaseID(m_CaseID); dialog->SetLesion(selectedLesion); int dialogReturnValue = dialog->exec(); if (QDialog::Rejected == dialogReturnValue) { return; } auto nodes = dialog->GetSelectedNodes(); mitk::DataNode::Pointer selectedDataNode = nullptr; if (!nodes.isEmpty()) { // only single selection allowed selectedDataNode = nodes.front(); } if (nullptr == selectedDataNode || false == mitk::NodePredicates::GetImagePredicate()->CheckNode(selectedDataNode)) { QMessageBox msgBox(QMessageBox::Warning, "No valid image node selected.", - "In order to propagate the selected lesion to an image, please specify a valid image node."); + "In order to create a new segmentation, please specify a valid image node."); msgBox.exec(); return; } - mitk::BaseData* baseData = selectedDataNode->GetData(); - if (nullptr == baseData) + mitk::Image* selectedImage = dynamic_cast(selectedDataNode->GetData()); + if (nullptr == selectedImage) { QMessageBox msgBox(QMessageBox::Warning, - "No valid base data.", - "In order to propagate the selected lesion to an image, please specify a valid image node."); + "No valid image.", + "In order to create a new segmentation, please specify a valid image node."); msgBox.exec(); return; } + mitk::LabelSetImage::Pointer segmentation = mitk::LabelSetImage::New(); try { - /* - auto allSegmentationsOfLesion = m_SemanticRelationsDataStorageAccess->GetAllSegmentationsOfLesion(m_CaseID, selectedLesion); - mitk::FindClosestSegmentationMask(); - */ + segmentation->Initialize(selectedImage); } - catch (const mitk::SemanticRelationException& e) + catch (mitk::Exception& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox(QMessageBox::Warning, - "Could not propagate the selected lesion.", - "The program wasn't able to correctly propagate the selected lesion to the selected image.\n" + "Could not initialize segmentation.", + "The segmentation could not be correctly initialized with the selected image geometry.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.exec(); + return; } + + auto segmentationDialog = new QmitkNewSegmentationDialog(this); + segmentationDialog->setWindowTitle("New lesion segmentation"); + + dialogReturnValue = segmentationDialog->exec(); + if (dialogReturnValue == QDialog::Rejected) + { + return; + } + + QString segmentatioName = segmentationDialog->GetSegmentationName(); + if (segmentatioName.isEmpty()) + { + segmentatioName = "Unnamed"; + } + segmentation->GetActiveLabelSet()->AddLabel(segmentatioName.toStdString(), segmentationDialog->GetColor()); + + mitk::DataNode::Pointer segmentationNode = mitk::DataNode::New(); + segmentationNode->SetData(segmentation); + segmentationNode->SetName(segmentatioName.toStdString()); + dataStorage->Add(segmentationNode, selectedDataNode); + + LinkSegmentationToLesion(segmentationNode, selectedLesion); } void QmitkLesionInfoWidget::OnRemoveLesion(mitk::SemanticTypes::Lesion selectedLesion) { try { m_SemanticRelationsIntegration->RemoveLesion(m_CaseID, selectedLesion); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox(QMessageBox::Warning, "Could not remove the selected lesion.", "The program wasn't able to correctly remove the selected lesion from the semantic relations model.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.exec(); } } + +void QmitkLesionInfoWidget::LinkSegmentationToLesion(const mitk::DataNode* selectedDataNode, mitk::SemanticTypes::Lesion selectedLesion) +{ + if (m_DataStorage.IsExpired()) + { + return; + } + + auto dataStorage = m_DataStorage.Lock(); + + // if the segmentation is not contained in the semantic relations, add it + if (!mitk::SemanticRelationsInference::InstanceExists(selectedDataNode)) + { + try + { + AddToSemanticRelationsAction::Run(dataStorage, selectedDataNode); + } + catch (const mitk::SemanticRelationException& e) + { + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox(QMessageBox::Warning, + "Could not link the selected lesion.", + "The program wasn't able to correctly link the selected lesion with the selected segmentation.\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); + msgBox.exec(); + } + } + + // link the segmentation + try + { + m_SemanticRelationsIntegration->LinkSegmentationToLesion(selectedDataNode, selectedLesion); + } + catch (const mitk::SemanticRelationException& e) + { + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox(QMessageBox::Warning, + "Could not link the selected lesion.", + "The program wasn't able to correctly link the selected lesion with the selected segmentation.\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str())); + msgBox.exec(); + } +} diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.h index 9939588821..f675f664fa 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.h +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.h @@ -1,102 +1,107 @@ /*=================================================================== 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 plugin #include // semantic relations UI module #include // semantic relations module #include #include // mitk #include +// berry +#include + // qt #include /* * @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. */ class QmitkLesionInfoWidget : public QWidget { Q_OBJECT public: static const QBrush DEFAULT_BACKGROUND_COLOR; static const QBrush SELECTED_BACKGROUND_COLOR; static const QBrush CONNECTED_BACKGROUND_COLOR; - QmitkLesionInfoWidget(mitk::DataStorage* dataStorage, QWidget* parent = nullptr); - ~QmitkLesionInfoWidget(); + QmitkLesionInfoWidget(mitk::DataStorage* dataStorage, berry::IWorkbenchPartSite::Pointer workbenchPartSite, QWidget* parent = nullptr); void SetCaseID(const mitk::SemanticTypes::CaseID& caseID); void SetDataNodeSelection(const QList& dataNodeSelection); Q_SIGNALS: void LesionSelectionChanged(const mitk::SemanticTypes::Lesion&); private Q_SLOTS: void OnModelUpdated(); /* * @brief Generates a new, empty lesion to add to the semantic relations model for the current case ID. */ void OnAddLesionButtonClicked(); // slots for the mouse click events of tree view's selection model void OnSelectionChanged(const QModelIndex& current, const QModelIndex& previous); void OnLesionListContextMenuRequested(const QPoint&); // slots for the context menu actions of the lesion list widget void OnLinkToSegmentation(mitk::SemanticTypes::Lesion); void OnSetLesionName(mitk::SemanticTypes::Lesion); void OnSetLesionClass(mitk::SemanticTypes::Lesion); - void OnPropagateLesion(mitk::SemanticTypes::Lesion); + void OnCreateNewSegmentation(mitk::SemanticTypes::Lesion); void OnRemoveLesion(mitk::SemanticTypes::Lesion); private: void Initialize(); void SetUpConnections(); + void LinkSegmentationToLesion(const mitk::DataNode* selectedDataNode, mitk::SemanticTypes::Lesion selectedLesion); + Ui::QmitkLesionInfoWidgetControls m_Controls; QmitkLesionTreeModel* m_StorageModel; mitk::SemanticTypes::CaseID m_CaseID; mitk::WeakPointer m_DataStorage; + berry::IWorkbenchPartSite::WeakPtr m_WorkbenchPartSite; std::unique_ptr m_SemanticRelationsDataStorageAccess; std::unique_ptr m_SemanticRelationsIntegration; }; #endif // QMITKLESIONINFOWIDGET_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsContextMenu.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsContextMenu.cpp index b38737f62b..c96acefbbe 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsContextMenu.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsContextMenu.cpp @@ -1,77 +1,73 @@ /*=================================================================== 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 "QmitkSemanticRelationsContextMenu.h" QmitkSemanticRelationsContextMenu::QmitkSemanticRelationsContextMenu(berry::IWorkbenchPartSite::Pointer workbenchPartSite, QWidget* parent) : QMenu(parent) , m_Parent(parent) , m_WorkbenchPartSite(workbenchPartSite) { InitDefaultActions(); } -QmitkSemanticRelationsContextMenu::~QmitkSemanticRelationsContextMenu() -{ - // nothing here -} - void QmitkSemanticRelationsContextMenu::SetDataStorage(mitk::DataStorage* dataStorage) { if (m_DataStorage != dataStorage) { // set the new data storage - also for all actions m_DataStorage = dataStorage; m_ControlPointAction->SetDataStorage(m_DataStorage.Lock()); m_InformationTypeAction->SetDataStorage(m_DataStorage.Lock()); m_RemoveFromSemanticRelationsAction->SetDataStorage(m_DataStorage.Lock()); + m_DataSetOpenInAction->SetDataStorage(m_DataStorage.Lock()); m_UnlinkFromLesionAction->SetDataStorage(m_DataStorage.Lock()); } } void QmitkSemanticRelationsContextMenu::SetControlledRenderer(mitk::RenderWindowLayerUtilities::RendererVector controlledRenderer) { if (m_ControlledRenderer != controlledRenderer) { // set the new set of controlled renderer m_ControlledRenderer = controlledRenderer; - m_OpenInAction->SetControlledRenderer(m_ControlledRenderer); + m_DataSetOpenInAction->SetControlledRenderer(m_ControlledRenderer); } } void QmitkSemanticRelationsContextMenu::OnContextMenuRequested(const QPoint& /*pos*/) { popup(QCursor::pos()); } void QmitkSemanticRelationsContextMenu::InitDefaultActions() { m_ControlPointAction = new QmitkDataNodeSetControlPointAction(m_Parent, m_WorkbenchPartSite.Lock()); addAction(m_ControlPointAction); m_InformationTypeAction = new QmitkDataNodeSetInformationTypeAction(m_Parent, m_WorkbenchPartSite.Lock()); addAction(m_InformationTypeAction); m_UnlinkFromLesionAction = new QmitkDataNodeUnlinkFromLesionAction(m_Parent, m_WorkbenchPartSite.Lock()); addAction(m_UnlinkFromLesionAction); m_RemoveFromSemanticRelationsAction = new QmitkDataNodeRemoveFromSemanticRelationsAction(m_Parent, m_WorkbenchPartSite.Lock()); addAction(m_RemoveFromSemanticRelationsAction); - m_OpenInAction = new QmitkDataNodeOpenInAction(m_Parent, m_WorkbenchPartSite.Lock()); - addAction(m_OpenInAction); + m_DataSetOpenInAction = new QmitkDataSetOpenInAction(m_Parent, m_WorkbenchPartSite.Lock()); + addAction(m_DataSetOpenInAction); } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsContextMenu.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsContextMenu.h index 99366a64b8..9189ea25e2 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsContextMenu.h +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsContextMenu.h @@ -1,75 +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 QMITKSEMANTICRELATIONSCONTEXTMENU_H #define QMITKSEMANTICRELATIONSCONTEXTMENU_H // semantic relations plugin #include "QmitkDataNodeSetControlPointAction.h" #include "QmitkDataNodeSetInformationTypeAction.h" #include "QmitkDataNodeUnlinkFromLesionAction.h" #include "QmitkDataNodeRemoveFromSemanticRelationsAction.h" +#include "QmitkDataSetOpenInAction.h" // mitk core #include #include // mitk render window manager module #include -// mitk gui qt application plugin -#include - // blueberry ui qt plugin #include //qt #include class QmitkSemanticRelationsContextMenu : public QMenu { Q_OBJECT public: QmitkSemanticRelationsContextMenu(berry::IWorkbenchPartSite::Pointer workbenchPartSite, QWidget* parent = nullptr); - virtual ~QmitkSemanticRelationsContextMenu() override; void SetDataStorage(mitk::DataStorage* dataStorage); void SetControlledRenderer(mitk::RenderWindowLayerUtilities::RendererVector controlledRenderer); public Q_SLOTS: void OnContextMenuRequested(const QPoint&); private: void InitDefaultActions(); QWidget* m_Parent; berry::IWorkbenchPartSite::WeakPtr m_WorkbenchPartSite; mitk::WeakPointer m_DataStorage; mitk::RenderWindowLayerUtilities::RendererVector m_ControlledRenderer; QmitkDataNodeSetControlPointAction* m_ControlPointAction; QmitkDataNodeSetInformationTypeAction* m_InformationTypeAction; QmitkDataNodeUnlinkFromLesionAction* m_UnlinkFromLesionAction; QmitkDataNodeRemoveFromSemanticRelationsAction* m_RemoveFromSemanticRelationsAction; - QmitkDataNodeOpenInAction* m_OpenInAction; - + QmitkDataSetOpenInAction* m_DataSetOpenInAction; }; #endif // QMITKSEMANTICRELATIONSCONTEXTMENU_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsStatisticsControls.ui b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsStatisticsControls.ui new file mode 100644 index 0000000000..330e1d12c1 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsStatisticsControls.ui @@ -0,0 +1,40 @@ + + + QmitkSemanticRelationsStatisticsControls + + + + 0 + 0 + 350 + 300 + + + + Semantic relations plugin + + + + + + + + + + 0 + 0 + + + + Currently selected patient: + + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsStatisticsView.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsStatisticsView.cpp new file mode 100644 index 0000000000..cf5a0f862e --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsStatisticsView.cpp @@ -0,0 +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 + +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 new file mode 100644 index 0000000000..64c6ec3c0d --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsStatisticsView.h @@ -0,0 +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" + +// semantic relations module +#include + +// semantic relations ui module +#include "QmitkStatisticsTreeModel.h" + +// mitk qt gui common plugin +#include + +/* +* @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 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 6a20449fda..19c4b7c8ba 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.cpp @@ -1,273 +1,285 @@ /*=================================================================== 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 "QmitkDataNodeAddToSemanticRelationsAction.h" #include "QmitkDataNodeRemoveFromSemanticRelationsAction.h" +#include "QmitkLabelSetJumpToAction.h" // semantic relations module #include #include +#include #include #include +// mitk core +#include + // mitk qt widgets module #include #include -// mitk multi label module -#include - // berry #include #include // qt #include +#include #include const std::string QmitkSemanticRelationsView::VIEW_ID = "org.mitk.views.semanticrelations"; void QmitkSemanticRelationsView::SetFocus() { // nothing here } +void QmitkSemanticRelationsView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) +{ + if (m_RenderWindowPart != renderWindowPart) + { + m_RenderWindowPart = renderWindowPart; + SetControlledRenderer(); + } +} + +void QmitkSemanticRelationsView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart) +{ + if (m_RenderWindowPart == renderWindowPart) + { + m_RenderWindowPart = nullptr; + SetControlledRenderer(); + } +} + +void QmitkSemanticRelationsView::RenderWindowPartInputChanged(mitk::IRenderWindowPart* renderWindowPart) +{ + if (m_RenderWindowPart == renderWindowPart) + { + SetControlledRenderer(); + } +} + void QmitkSemanticRelationsView::CreateQtPartControl(QWidget* parent) { // create GUI widgets m_Controls.setupUi(parent); - m_LesionInfoWidget = new QmitkLesionInfoWidget(GetDataStorage(), parent); + m_LesionInfoWidget = new QmitkLesionInfoWidget(GetDataStorage(), GetSite(), parent); m_Controls.gridLayout->addWidget(m_LesionInfoWidget); m_PatientTableInspector = new QmitkPatientTableInspector(parent); m_PatientTableInspector->SetDataStorage(GetDataStorage()); m_Controls.gridLayout->addWidget(m_PatientTableInspector); QGridLayout* dndDataNodeWidgetLayout = new QGridLayout; dndDataNodeWidgetLayout->addWidget(m_PatientTableInspector, 0, 0); dndDataNodeWidgetLayout->setContentsMargins(0, 0, 0, 0); m_DnDDataNodeWidget = new QmitkDnDDataNodeWidget(parent); m_DnDDataNodeWidget->setLayout(dndDataNodeWidgetLayout); m_Controls.gridLayout->addWidget(m_DnDDataNodeWidget); m_ContextMenu = new QmitkSemanticRelationsContextMenu(GetSite(), m_PatientTableInspector); m_ContextMenu->SetDataStorage(GetDataStorage()); mitk::IRenderWindowPart* renderWindowPart = GetRenderWindowPart(); if (nullptr != renderWindowPart) { RenderWindowPartActivated(renderWindowPart); } SetUpConnections(); const auto& allCaseIDs = mitk::RelationStorage::GetAllCaseIDs(); for (const auto& caseID : allCaseIDs) { AddToComboBox(caseID); } } -void QmitkSemanticRelationsView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) -{ - // connect QmitkRenderWindows - underlying vtkRenderWindow is the same as "mitk::RenderingManager::GetInstance()->GetAllRegisteredRenderWindows()" - QHash windowMap = renderWindowPart->GetQmitkRenderWindows(); - QHash::Iterator it; - - mitk::BaseRenderer* baseRenderer = nullptr; - mitk::RenderWindowLayerUtilities::RendererVector controlledRenderer; - for (it = windowMap.begin(); it != windowMap.end(); ++it) - { - baseRenderer = mitk::BaseRenderer::GetInstance(it.value()->GetVtkRenderWindow()); - if (nullptr != baseRenderer) - { - controlledRenderer.push_back(baseRenderer); - } - } - - m_ContextMenu->SetControlledRenderer(controlledRenderer); -} - void QmitkSemanticRelationsView::SetUpConnections() { connect(m_Controls.caseIDComboBox, static_cast(&QComboBox::currentIndexChanged), this, &QmitkSemanticRelationsView::OnCaseIDSelectionChanged); connect(m_LesionInfoWidget, &QmitkLesionInfoWidget::LesionSelectionChanged, this, &QmitkSemanticRelationsView::OnLesionSelectionChanged); connect(m_PatientTableInspector, &QmitkPatientTableInspector::CurrentSelectionChanged, this, &QmitkSemanticRelationsView::OnDataNodeSelectionChanged); connect(m_PatientTableInspector, &QmitkPatientTableInspector::DataNodeDoubleClicked, this, &QmitkSemanticRelationsView::OnDataNodeDoubleClicked); connect(m_DnDDataNodeWidget, &QmitkDnDDataNodeWidget::NodesDropped, this, &QmitkSemanticRelationsView::OnNodesAdded); connect(m_PatientTableInspector, &QmitkPatientTableInspector::OnContextMenuRequested, m_ContextMenu, &QmitkSemanticRelationsContextMenu::OnContextMenuRequested); connect(m_PatientTableInspector, &QmitkPatientTableInspector::OnNodeRemoved, this, &QmitkSemanticRelationsView::NodeRemoved); } QItemSelectionModel* QmitkSemanticRelationsView::GetDataNodeSelectionModel() const { return m_PatientTableInspector->GetSelectionModel(); } void QmitkSemanticRelationsView::NodeRemoved(const mitk::DataNode* dataNode) { if (nullptr == dataNode) { return; } if (mitk::SemanticRelationsInference::InstanceExists(dataNode)) { - // no observer needed for the integration; simply use a temporary instance for removing - mitk::SemanticRelationsIntegration semanticRelationsIntegration; - RemoveFromSemanticRelationsAction::Run(&semanticRelationsIntegration, dataNode); - mitk::SemanticTypes::CaseID caseID = mitk::GetCaseIDFromDataNode(dataNode); - RemoveFromComboBox(caseID); + try + { + RemoveFromSemanticRelationsAction::Run(GetDataStorage(), dataNode); + mitk::SemanticTypes::CaseID caseID = mitk::GetCaseIDFromDataNode(dataNode); + RemoveFromComboBox(caseID); + } + catch (const mitk::SemanticRelationException& e) + { + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox(QMessageBox::Warning, + "Could not remove the data node(s).", + "The program wasn't able to correctly remove the selected data node(s).\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); + msgBox.exec(); + } } } void QmitkSemanticRelationsView::OnLesionSelectionChanged(const mitk::SemanticTypes::Lesion& lesion) { m_PatientTableInspector->SetLesion(lesion); } void QmitkSemanticRelationsView::OnDataNodeSelectionChanged(const QList& dataNodeSelection) { m_LesionInfoWidget->SetDataNodeSelection(dataNodeSelection); } void QmitkSemanticRelationsView::OnDataNodeDoubleClicked(const mitk::DataNode* dataNode) { if (nullptr == dataNode) { return; } if (mitk::NodePredicates::GetImagePredicate()->CheckNode(dataNode)) { OpenInEditor(dataNode); } else if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { - JumpToPosition(dataNode); + LabelSetJumpToAction::Run(GetSite(), dataNode); } } void QmitkSemanticRelationsView::OnCaseIDSelectionChanged(const QString& caseID) { m_LesionInfoWidget->SetCaseID(caseID.toStdString()); m_PatientTableInspector->SetCaseID(caseID.toStdString()); } void QmitkSemanticRelationsView::OnNodesAdded(std::vector nodes) { mitk::SemanticTypes::CaseID caseID = ""; for (mitk::DataNode* dataNode : nodes) { - // no observer needed for the integration; simply use a temporary instance for adding - mitk::SemanticRelationsIntegration semanticRelationsIntegration; - AddToSemanticRelationsAction::Run(&semanticRelationsIntegration, GetDataStorage(), dataNode); - caseID = mitk::GetCaseIDFromDataNode(dataNode); - AddToComboBox(caseID); + try + { + AddToSemanticRelationsAction::Run(GetDataStorage(), dataNode); + caseID = mitk::GetCaseIDFromDataNode(dataNode); + AddToComboBox(caseID); + } + catch (const mitk::SemanticRelationException& e) + { + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox(QMessageBox::Warning, + "Could not add the data node(s).", + "The program wasn't able to correctly add the selected data node(s).\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); + msgBox.exec(); + } } } void QmitkSemanticRelationsView::OnNodeRemoved(const mitk::DataNode* dataNode) { NodeRemoved(dataNode); } 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 = mitk::RelationStorage::GetAllControlPointsOfCase(caseID); int foundIndex = m_Controls.caseIDComboBox->findText(QString::fromStdString(caseID)); if (allControlPoints.empty() && -1 != foundIndex) { // caseID does not contain any control points and therefore no data // remove the caseID, if it is still contained m_Controls.caseIDComboBox->removeItem(foundIndex); } } void QmitkSemanticRelationsView::OpenInEditor(const mitk::DataNode* dataNode) { auto renderWindowPart = GetRenderWindowPart(); if (nullptr == renderWindowPart) { renderWindowPart = GetRenderWindowPart(mitk::WorkbenchUtil::IRenderWindowPartStrategy::BRING_TO_FRONT | mitk::WorkbenchUtil::IRenderWindowPartStrategy::OPEN); if (nullptr == renderWindowPart) { // no render window available return; } } auto image = dynamic_cast(dataNode->GetData()); if (nullptr != image) { mitk::RenderingManager::GetInstance()->InitializeViews(image->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); } } -void QmitkSemanticRelationsView::JumpToPosition(const mitk::DataNode* dataNode) +void QmitkSemanticRelationsView::SetControlledRenderer() { - if (nullptr == dataNode) + QHash renderWindows; + if (m_RenderWindowPart != nullptr) { - return; + renderWindows = m_RenderWindowPart->GetQmitkRenderWindows(); } - mitk::LabelSetImage* labelSetImage = dynamic_cast(dataNode->GetData()); - if (nullptr == labelSetImage) - { - return; - } - - unsigned 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) + mitk::RenderWindowLayerUtilities::RendererVector controlledRenderer; + mitk::BaseRenderer* baseRenderer = nullptr; + for (const auto& renderWindow : renderWindows.values()) { - auto renderWindowPart = GetRenderWindowPart(); - if (nullptr == renderWindowPart) - { - renderWindowPart = GetRenderWindowPart(mitk::WorkbenchUtil::IRenderWindowPartStrategy::BRING_TO_FRONT | mitk::WorkbenchUtil::IRenderWindowPartStrategy::OPEN); - if (nullptr == renderWindowPart) - { - // no render window available - return; - } - } - - auto segmentation = dynamic_cast(dataNode->GetData()); - if (nullptr != segmentation) + baseRenderer = mitk::BaseRenderer::GetInstance(renderWindow->GetVtkRenderWindow()); + if (nullptr != baseRenderer) { - renderWindowPart->SetSelectedPosition(centerPosition); - mitk::RenderingManager::GetInstance()->InitializeViews(segmentation->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); + controlledRenderer.push_back(baseRenderer); } } + + m_ContextMenu->SetControlledRenderer(controlledRenderer); } 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 725696107d..3c41a3f2bf 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.h +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.h @@ -1,105 +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 QMITKSEMANTICRELATIONSVIEW_H #define QMITKSEMANTICRELATIONSVIEW_H // semantic relations plugin #include "ui_QmitkSemanticRelationsControls.h" #include "QmitkLesionInfoWidget.h" #include "QmitkSemanticRelationsContextMenu.h" // semantic relations module #include // semantic relations UI module #include // mitk gui common plugin #include // berry #include -// mitk qt +// mitk qt gui common plugin #include class QmitkDnDDataNodeWidget; class QMenu; /* * @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. * A combo box is used to select and show the current patient. */ class QmitkSemanticRelationsView : public QmitkAbstractView, public mitk::IRenderWindowPartListener { Q_OBJECT public: static const std::string VIEW_ID; + virtual void RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) override; + virtual void RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart) override; + virtual void RenderWindowPartInputChanged(mitk::IRenderWindowPart* renderWindowPart) override; + protected: virtual void SetFocus() override; virtual void CreateQtPartControl(QWidget* parent) override; - virtual void RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) override; - virtual void RenderWindowPartDeactivated(mitk::IRenderWindowPart*) override { } - private Q_SLOTS: void OnLesionSelectionChanged(const mitk::SemanticTypes::Lesion&); void OnDataNodeSelectionChanged(const QList&); void OnDataNodeDoubleClicked(const mitk::DataNode*); void OnCaseIDSelectionChanged(const QString&); void OnNodesAdded(std::vector); void OnNodeRemoved(const mitk::DataNode*); private: void SetUpConnections(); /** * @brief Provide a QItemSelectionModel, which supports the data role 'QmitkDataNodeRole' (\see QmitkRenderWindowDataModel). * * The provided QItemSelectionModel is used in the QmitkAbstractView-base class as the selection model of * the selection provider (\see QmitkAbstractView::SetSelectionProvider()). * The default selection provider is a QmitkDataNodeSelectionProvider. Each time a selection in the provided * QItemSeletionModel is changed, a selection changed event is fired. All plugins (views), that subclass the * QmitkAbstractView will be informed about the selection changed via the OnSelectionChanged-function. */ virtual QItemSelectionModel* GetDataNodeSelectionModel() const override; virtual void NodeRemoved(const mitk::DataNode* dataNode) override; void AddToComboBox(const mitk::SemanticTypes::CaseID& caseID); void RemoveFromComboBox(const mitk::SemanticTypes::CaseID& caseID); void OpenInEditor(const mitk::DataNode* dataNode); - void JumpToPosition(const mitk::DataNode* dataNode); + + void SetControlledRenderer(); Ui::QmitkSemanticRelationsControls m_Controls; + + mitk::IRenderWindowPart* m_RenderWindowPart; + QmitkLesionInfoWidget* m_LesionInfoWidget; QmitkPatientTableInspector* m_PatientTableInspector; QmitkDnDDataNodeWidget* m_DnDDataNodeWidget; QmitkSemanticRelationsContextMenu* m_ContextMenu; }; #endif // QMITKSEMANTICRELATIONSVIEW_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/mitkPluginActivator.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/mitkPluginActivator.cpp index 9a343753d6..d05c04421d 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/mitkPluginActivator.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/mitkPluginActivator.cpp @@ -1,31 +1,34 @@ /*=================================================================== 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 "mitkPluginActivator.h" +#include "QmitkSemanticRelationsStatisticsView.h" #include "QmitkSemanticRelationsView.h" + #include namespace mitk { void SemanticRelationsActivator::start(ctkPluginContext *context) { mitk::PersistenceService::LoadModule(); BERRY_REGISTER_EXTENSION_CLASS(QmitkSemanticRelationsView, context) + BERRY_REGISTER_EXTENSION_CLASS(QmitkSemanticRelationsStatisticsView, context) } void SemanticRelationsActivator::stop(ctkPluginContext *context) { Q_UNUSED(context) } }