diff --git a/Modules/QtWidgetsExt/files.cmake b/Modules/QtWidgetsExt/files.cmake index e8bd9dd1f6..d5b2255af4 100644 --- a/Modules/QtWidgetsExt/files.cmake +++ b/Modules/QtWidgetsExt/files.cmake @@ -1,106 +1,104 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES qclickablelabel.cpp QmitkAboutDialog.cpp QmitkBasePropertyView.cpp QmitkBoolPropertyWidget.cpp QmitkBoundingObjectWidget.cpp QmitkCallbackFromGUIThread.cpp QmitkColorPropertyEditor.cpp QmitkColorPropertyView.cpp QmitkColorTransferFunctionCanvas.cpp QmitkCrossWidget.cpp QmitkEditPointDialog.cpp QmitkEnumerationPropertyWidget.cpp QmitkFFmpegWriter.cpp QmitkFileChooser.cpp QmitkGnuplotWidget.cpp QmitkHistogram.cpp QmitkHotkeyLineEdit.cpp QmitkModulesDialog.cpp QmitkModuleTableModel.cpp QmitkNumberPropertyEditor.cpp QmitkNumberPropertySlider.cpp QmitkNumberPropertyView.cpp QmitkPiecewiseFunctionCanvas.cpp QmitkPlotDialog.cpp QmitkPlotWidget.cpp QmitkPointListModel.cpp QmitkPointListView.cpp - QmitkPointListViewWidget.cpp QmitkPointListWidget.cpp QmitkPrimitiveMovieNavigatorWidget.cpp QmitkPropertyViewFactory.cpp QmitkSelectableGLWidget.cpp QmitkSliceWidget.cpp QmitkSliderNavigatorWidget.cpp QmitkStandardViews.cpp QmitkStepperAdapter.cpp QmitkStringPropertyEditor.cpp QmitkStringPropertyOnDemandEdit.cpp QmitkStringPropertyView.cpp QmitkTransferFunctionCanvas.cpp QmitkTransferFunctionGeneratorWidget.cpp QmitkTransferFunctionWidget.cpp QmitkUGCombinedRepresentationPropertyWidget.cpp QmitkVideoBackground.cpp QtWidgetsExtRegisterClasses.cpp ) set(MOC_H_FILES include/qclickablelabel.h include/QmitkAboutDialog.h include/QmitkBasePropertyView.h include/QmitkBoolPropertyWidget.h include/QmitkBoundingObjectWidget.h include/QmitkCallbackFromGUIThread.h include/QmitkColorPropertyEditor.h include/QmitkColorPropertyView.h include/QmitkColorTransferFunctionCanvas.h include/QmitkCrossWidget.h include/QmitkEditPointDialog.h include/QmitkEnumerationPropertyWidget.h include/QmitkFFmpegWriter.h include/QmitkFileChooser.h include/QmitkGnuplotWidget.h include/QmitkHotkeyLineEdit.h include/QmitkNumberPropertyEditor.h include/QmitkNumberPropertySlider.h include/QmitkNumberPropertyView.h include/QmitkPiecewiseFunctionCanvas.h include/QmitkPlotWidget.h include/QmitkPointListModel.h include/QmitkPointListView.h - include/QmitkPointListViewWidget.h include/QmitkPointListWidget.h include/QmitkPrimitiveMovieNavigatorWidget.h include/QmitkSelectableGLWidget.h include/QmitkSliceWidget.h include/QmitkSliderNavigatorWidget.h include/QmitkStandardViews.h include/QmitkStepperAdapter.h include/QmitkStringPropertyEditor.h include/QmitkStringPropertyOnDemandEdit.h include/QmitkStringPropertyView.h include/QmitkTransferFunctionCanvas.h include/QmitkTransferFunctionGeneratorWidget.h include/QmitkTransferFunctionWidget.h include/QmitkUGCombinedRepresentationPropertyWidget.h include/QmitkVideoBackground.h ) set(UI_FILES src/QmitkAboutDialogGUI.ui src/QmitkGnuplotWidget.ui src/QmitkPrimitiveMovieNavigatorWidget.ui src/QmitkSelectableGLWidget.ui src/QmitkSliceWidget.ui src/QmitkSliderNavigator.ui src/QmitkTransferFunctionGeneratorWidget.ui src/QmitkTransferFunctionWidget.ui ) set(QRC_FILES resource/QtWidgetsExt.qrc ) diff --git a/Modules/QtWidgetsExt/include/QmitkPointListView.h b/Modules/QtWidgetsExt/include/QmitkPointListView.h index fccb80b0b4..ba348168a3 100644 --- a/Modules/QtWidgetsExt/include/QmitkPointListView.h +++ b/Modules/QtWidgetsExt/include/QmitkPointListView.h @@ -1,120 +1,122 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QMITK_POINTLIST_VIEW_H_INCLUDED #define QMITK_POINTLIST_VIEW_H_INCLUDED #include "MitkQtWidgetsExtExports.h" -#include "QmitkPointListModel.h" + +#include + #include #include #include class QmitkStdMultiWidget; /*! * \brief GUI widget for handling mitk::PointSet * * Displays all the points in a mitk::PointSet graphically. * Reacts automatically to changes in the PointSet's selection status. * Updates PointSet's selection status when this list's selection changes. * * If a QmitkStdMultiWidget is assigned via SetMultiWidget(), the * crosshair of the QmitkStdMultiWidget is moved to the currently selected * point. * */ class MITKQTWIDGETSEXT_EXPORT QmitkPointListView : public QListView { Q_OBJECT public: QmitkPointListView(QWidget *parent = nullptr); ~QmitkPointListView() override; /// assign a point set for observation void SetPointSetNode(mitk::DataNode *pointSetNode); /// which point set to work on const mitk::PointSet *GetPointSet() const; /** * \brief If Multiwidget is set, the crosshair is automatically centering to the selected point * As an alternative, if you dont have a multiwidget, you can call SetSnc1, SetSnc2, SetSnc3 to set the * SliceNavigationControllers directly to enable the focussing feature. */ void SetMultiWidget(QmitkStdMultiWidget *multiWidget); QmitkStdMultiWidget *GetMultiWidget() const; ///< return the QmitkStdMultiWidget that is used for updating render window crosshair /** * @brief Add a mitk::SliceNavigationController instance. * @param snc The mitk::SliceNavigationController instance. * * This method adds \c snc to the set of slice navigation controllers which are * used to navigate to the selected point. */ void AddSliceNavigationController(mitk::SliceNavigationController *snc); /** * @brief Remove a mitk::SliceNavigationController instance. * @param snc The mitk::SliceNavigationController instance. * * This method removes \c snc from the set of slice navigation controllers which are * used to navigate to the selected point. */ void RemoveSliceNavigationController(mitk::SliceNavigationController *snc); signals: void SignalPointSelectionChanged(); ///< this signal is emmitted, if the selection of a point in the pointset is changed void SignalTimeStepChanged(int); protected slots: /// Filtering double click event for editing point coordinates via a dialog void OnPointDoubleClicked(const QModelIndex &index); /// called when the point set data structure changes void OnPointSetSelectionChanged(); /// called when the selection of the view widget changes void OnListViewSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); /// open ContextMenu void ctxMenu(const QPoint &pos); /// Turn TimeStep Fading On/Off void SetFading(bool onOff); /// Delete all points in the list void ClearPointList(); /// delete all points in the list in the current timestep void ClearPointListTS(); protected: void keyPressEvent(QKeyEvent *e) override; ///< react to F2, F3 and DEL keys void wheelEvent(QWheelEvent *event) override; ///< change timestep of the current pointset by mouse wheel std::set m_Sncs; QmitkPointListModel *m_PointListModel; bool m_SelfCall; bool m_showFading; /// used to position the planes on a selected point QmitkStdMultiWidget *m_MultiWidget; }; #endif diff --git a/Modules/QtWidgetsExt/include/QmitkPointListViewWidget.h b/Modules/QtWidgetsExt/include/QmitkPointListViewWidget.h deleted file mode 100644 index 51420445c1..0000000000 --- a/Modules/QtWidgetsExt/include/QmitkPointListViewWidget.h +++ /dev/null @@ -1,100 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#ifndef QmitkPointListViewWidget_h -#define QmitkPointListViewWidget_h - -#include "MitkQtWidgetsExtExports.h" -#include - -#include -#include - -class QmitkStdMultiWidget; - -/*! - * \brief GUI widget for handling mitk::PointSet - * - * Displays all the points in a mitk::PointSet graphically. - * Reacts automatically to changes in the PointSet's selection status. - * Updates PointSet's selection status when this list's selection changes. - * - * If a QmitkStdMultiWidget is assigned via SetMultiWidget(), the - * crosshair of the QmitkStdMultiWidget is moved to the currently selected - * point. - * - */ -class MITKQTWIDGETSEXT_EXPORT QmitkPointListViewWidget : public QListWidget -{ - Q_OBJECT - -signals: - void PointSelectionChanged(); ///< this signal is emmitted, if the selection of a point in the pointset is changed -public: - QmitkPointListViewWidget(QWidget *parent = nullptr); - ~QmitkPointListViewWidget() override; - - /// assign a point set for observation - void SetPointSet(mitk::PointSet *pointSet); - - /// which point set to work on - const mitk::PointSet *GetPointSet() const; - - void SetMultiWidget( - QmitkStdMultiWidget *multiWidget); ///< assign a QmitkStdMultiWidget for updating render window crosshair - - QmitkStdMultiWidget *GetMultiWidget() - const; ///< return the QmitkStdMultiWidget that is used for updating render window crosshair - - /// which time step to display/model - void SetTimeStep(int t); - - /// which time step to display/model - int GetTimeStep() const; - - /// observer for point set "modified" events - void OnPointSetChanged(); - - /// observer for point set "delete" events - void OnPointSetDeleted(); - -protected slots: - /// - /// Filtering double click event for editing point coordinates via a dialog - /// - void OnItemDoubleClicked(QListWidgetItem *item); - - /// called when the selection of the view widget changes - void OnCurrentRowChanged(int /*currentRow*/); - -protected: - void keyPressEvent(QKeyEvent *e) override; ///< react to F2, F3 and DEL keys - void MoveSelectedPointUp(); - void MoveSelectedPointDown(); - void RemoveSelectedPoint(); - void Update(bool currentRowChanged = false); - -protected: - mitk::WeakPointer m_PointSet; - - unsigned long m_PointSetDeletedTag; - unsigned long m_PointSetModifiedTag; - - int m_TimeStep; - - bool m_SelfCall; - - /// used to position the planes on a selected point - QmitkStdMultiWidget *m_MultiWidget; -}; - -#endif diff --git a/Modules/QtWidgetsExt/include/QmitkPointListWidget.h b/Modules/QtWidgetsExt/include/QmitkPointListWidget.h index b886dea24a..dd6811ec91 100644 --- a/Modules/QtWidgetsExt/include/QmitkPointListWidget.h +++ b/Modules/QtWidgetsExt/include/QmitkPointListWidget.h @@ -1,152 +1,153 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QmitkPointListWidget_H #define QmitkPointListWidget_H #include "MitkQtWidgetsExtExports.h" #include #include -#include #include #include #include #include #include +class QmitkStdMultiWidget; + /*! * \brief Widget for regular operations on point sets * * Displays a list of point coordinates and a couple of * buttons which * * \li enable point set interaction * \li clear all points from a set * \li load points from file * \li save points to file * * The user/application module of this widget needs to * assign a mitk::PointSet object to this widget. The user * also has to decide whether it wants to put the point set * into (a) DataStorage. This widget will not add/remove * point sets to DataStorage. * * If the render window crosshair should be moved to the * currently selected point, the widget user has to provide * a QmitkStdMultiWidget object. */ class MITKQTWIDGETSEXT_EXPORT QmitkPointListWidget : public QWidget { Q_OBJECT public: QmitkPointListWidget(QWidget *parent = nullptr, int orientation = 0); ~QmitkPointListWidget() override; void SetupConnections(); /** * @brief Add a mitk::SliceNavigationController instance. * @param snc The mitk::SliceNavigationController instance. * * This method adds \c snc to the set of slice navigation controllers which are * used to navigate to the selected point. */ void AddSliceNavigationController(mitk::SliceNavigationController *snc); /** * @brief Remove a mitk::SliceNavigationController instance. * @param snc The mitk::SliceNavigationController instance. * * This method removes \c snc from the set of slice navigation controllers which are * used to navigate to the selected point. */ void RemoveSliceNavigationController(mitk::SliceNavigationController *snc); /** @brief assign a point set (contained in a node of DataStorage) for observation */ void SetPointSet(mitk::PointSet *newPs); mitk::PointSet *GetPointSet(); /** @brief assign a point set (contained in a node of DataStorage) for observation */ void SetPointSetNode(mitk::DataNode *newNode); mitk::DataNode *GetPointSetNode(); /** @brief assign a QmitkStdMultiWidget for updating render window crosshair */ void SetMultiWidget(QmitkStdMultiWidget *multiWidget); /** @brief itk observer for node "delete" events */ void OnNodeDeleted(const itk::EventObject &e); /** @brief Unselects the edit button if it is selected. */ void UnselectEditButton(); public slots: void DeactivateInteractor(bool deactivate); void EnableEditButton(bool enabled); signals: /** @brief signal to inform about the state of the EditPointSetButton, whether an interactor for setting points is * active or not */ void EditPointSets(bool active); /// signal to inform that the selection of a point in the pointset has changed void PointSelectionChanged(); /// signal to inform about cleared or loaded point sets void PointListChanged(); protected slots: void OnBtnSavePoints(); void OnBtnLoadPoints(); void RemoveSelectedPoint(); void MoveSelectedPointDown(); void MoveSelectedPointUp(); void OnBtnAddPoint(bool checked); void OnBtnAddPointManually(); void OnTimeStepChanged(int timeStep); /*! \brief pass through signal from PointListView that point selection has changed */ void OnPointSelectionChanged(); void OnListDoubleClick(); protected: void SetupUi(); void ObserveNewNode(mitk::DataNode *node); QmitkPointListView *m_PointListView; mitk::DataNode::Pointer m_PointSetNode; int m_Orientation; QPushButton *m_MovePointUpBtn; QPushButton *m_MovePointDownBtn; QPushButton *m_RemovePointBtn; QPushButton *m_SavePointsBtn; QPushButton *m_LoadPointsBtn; QPushButton *m_ToggleAddPoint; QPushButton *m_AddPoint; QLabel *m_TimeStepDisplay; QLabel *m_TimeStepLabel; mitk::DataInteractor::Pointer m_DataInteractor; int m_TimeStep; bool m_EditAllowed; unsigned long m_NodeObserverTag; QmitkPointListModel *m_PointListModel; }; #endif diff --git a/Modules/QtWidgetsExt/src/QmitkPointListView.cpp b/Modules/QtWidgetsExt/src/QmitkPointListView.cpp index 9ecf11704a..474a5827b8 100644 --- a/Modules/QtWidgetsExt/src/QmitkPointListView.cpp +++ b/Modules/QtWidgetsExt/src/QmitkPointListView.cpp @@ -1,335 +1,335 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkPointListView.h" -#include "QmitkEditPointDialog.h" -#include "QmitkPointListModel.h" -#include "QmitkRenderWindow.h" -#include "QmitkStdMultiWidget.h" +#include +#include +#include +#include -#include "mitkRenderingManager.h" +#include #include #include #include #include #include QmitkPointListView::QmitkPointListView(QWidget *parent) : QListView(parent), m_PointListModel(new QmitkPointListModel()), m_SelfCall(false), m_showFading(false), m_MultiWidget(nullptr) { QListView::setAlternatingRowColors(true); QListView::setSelectionBehavior(QAbstractItemView::SelectItems); QListView::setSelectionMode(QAbstractItemView::SingleSelection); QListView::setModel(m_PointListModel); QString tooltip = QString("Use the F2/F3 keys to move a point up/down, the Del key to remove a point\nand the mouse " "wheel to change the timestep.\n\nTimeStep:\t%1") .arg(0); QListView::setToolTip(tooltip); this->setContextMenuPolicy(Qt::CustomContextMenu); this->setMinimumHeight(40); this->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); connect(m_PointListModel, SIGNAL(SignalUpdateSelection()), this, SLOT(OnPointSetSelectionChanged())); connect(this, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(OnPointDoubleClicked(const QModelIndex &))); connect(QListView::selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), this, SLOT(OnListViewSelectionChanged(const QItemSelection &, const QItemSelection &))); // T28213: Every single action in the context menu is either not implemented or working. Implement/fix or remove in future. // connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(ctxMenu(const QPoint &))); } QmitkPointListView::~QmitkPointListView() { delete m_PointListModel; } void QmitkPointListView::SetPointSetNode(mitk::DataNode *pointSetNode) { m_PointListModel->SetPointSetNode(pointSetNode); } const mitk::PointSet *QmitkPointListView::GetPointSet() const { return m_PointListModel->GetPointSet(); } void QmitkPointListView::SetMultiWidget(QmitkStdMultiWidget *multiWidget) { m_MultiWidget = multiWidget; if (nullptr != m_MultiWidget) { AddSliceNavigationController(m_MultiWidget->GetRenderWindow1()->GetSliceNavigationController()); AddSliceNavigationController(m_MultiWidget->GetRenderWindow2()->GetSliceNavigationController()); AddSliceNavigationController(m_MultiWidget->GetRenderWindow3()->GetSliceNavigationController()); } } QmitkStdMultiWidget *QmitkPointListView::GetMultiWidget() const { return m_MultiWidget; } void QmitkPointListView::OnPointDoubleClicked(const QModelIndex &index) { mitk::PointSet::PointType p; mitk::PointSet::PointIdentifier id; m_PointListModel->GetPointForModelIndex(index, p, id); QmitkEditPointDialog _EditPointDialog(this); _EditPointDialog.SetPoint(m_PointListModel->GetPointSet(), id, m_PointListModel->GetTimeStep()); _EditPointDialog.exec(); } void QmitkPointListView::OnPointSetSelectionChanged() { const mitk::PointSet *pointSet = m_PointListModel->GetPointSet(); if (pointSet == nullptr) return; // update this view's selection status as a result to changes in the point set data structure m_SelfCall = true; int timeStep = m_PointListModel->GetTimeStep(); if (pointSet->GetNumberOfSelected(timeStep) > 1) { MITK_ERROR << "Point set has multiple selected points. This view is not designed for more than one selected point."; } int selectedIndex = pointSet->SearchSelectedPoint(timeStep); if (selectedIndex == -1) // no selected point is found { m_SelfCall = false; return; } QModelIndex index; bool modelIndexOkay = m_PointListModel->GetModelIndexForPointID(selectedIndex, index); if (modelIndexOkay == true) QListView::selectionModel()->select(index, QItemSelectionModel::ClearAndSelect); emit SignalPointSelectionChanged(); m_SelfCall = false; } void QmitkPointListView::OnListViewSelectionChanged(const QItemSelection &selected, const QItemSelection & /*deselected*/) { if (m_SelfCall) return; mitk::PointSet *pointSet = const_cast(m_PointListModel->GetPointSet()); if (pointSet == nullptr) return; // (take care that this widget doesn't react to self-induced changes by setting m_SelfCall) m_SelfCall = true; // update selection of all points in pointset: select the one(s) that are selected in the view, deselect all others QModelIndexList selectedIndexes = selected.indexes(); // only call setSelectInfo on a point set with 'selected = true' if you deselcted the other entries int indexToSelect = -1; for (mitk::PointSet::PointsContainer::Iterator it = pointSet->GetPointSet(m_PointListModel->GetTimeStep())->GetPoints()->Begin(); it != pointSet->GetPointSet(m_PointListModel->GetTimeStep())->GetPoints()->End(); ++it) { QModelIndex index; if (m_PointListModel->GetModelIndexForPointID(it->Index(), index)) { if (selectedIndexes.indexOf(index) != -1) // index is found in the selected indices list { indexToSelect = it->Index(); } else { pointSet->SetSelectInfo(it->Index(), false, m_PointListModel->GetTimeStep()); } } } // force selection of only one index after deselecting the others if (indexToSelect > -1) { pointSet->SetSelectInfo(indexToSelect, true, m_PointListModel->GetTimeStep()); mitk::Point3D p = pointSet->GetPoint(indexToSelect, m_PointListModel->GetTimeStep()); for (auto snc : m_Sncs) snc->SelectSliceByPoint(p); } m_SelfCall = false; emit SignalPointSelectionChanged(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkPointListView::keyPressEvent(QKeyEvent *e) { if (m_PointListModel == nullptr) return; int key = e->key(); switch (key) { case Qt::Key_F2: m_PointListModel->MoveSelectedPointUp(); break; case Qt::Key_F3: m_PointListModel->MoveSelectedPointDown(); break; case Qt::Key_Delete: m_PointListModel->RemoveSelectedPoint(); break; default: break; } } void QmitkPointListView::wheelEvent(QWheelEvent *event) { if (!m_PointListModel || !m_PointListModel->GetPointSet() || (int)(m_PointListModel->GetPointSet()->GetTimeSteps()) == 1) return; int whe = event->delta(); mitk::PointSet::Pointer ps = dynamic_cast(m_PointListModel->GetPointSet()); unsigned int numberOfTS = ps->GetTimeSteps(); if (numberOfTS == 1) return; int currentTS = this->m_PointListModel->GetTimeStep(); if (whe > 0) { if ((currentTS + 1 >= (int)numberOfTS)) return; this->m_PointListModel->SetTimeStep(++currentTS); } else { if ((currentTS <= 0)) return; this->m_PointListModel->SetTimeStep(--currentTS); } QString tooltip = QString("Use the F2/F3 keys to move a point up/down, the Del key to remove a point\nand the mouse " "wheel to change the timestep.\n\nTimeStep:\t%1") .arg(currentTS); this->setToolTip(tooltip); emit SignalTimeStepChanged(currentTS); } void QmitkPointListView::ctxMenu(const QPoint &pos) { QMenu *menu = new QMenu; // add Fading check QAction *showFading = new QAction(this); showFading->setCheckable(false); // TODO: reset when fading is working showFading->setEnabled(false); // TODO: reset when fading is working showFading->setText("Fade TimeStep"); connect(showFading, SIGNAL(triggered(bool)), this, SLOT(SetFading(bool))); menu->addAction(showFading); // add Clear action QAction *clearList = new QAction(this); clearList->setText("Clear List"); connect(clearList, SIGNAL(triggered()), this, SLOT(ClearPointList())); menu->addAction(clearList); // add Clear TimeStep action QAction *clearTS = new QAction(this); clearTS->setText("Clear current time step"); connect(clearTS, SIGNAL(triggered()), this, SLOT(ClearPointListTS())); menu->addAction(clearTS); menu->exec(this->mapToGlobal(pos)); } void QmitkPointListView::SetFading(bool onOff) { m_showFading = onOff; } void QmitkPointListView::ClearPointList() { if (!m_PointListModel->GetPointSet()) return; mitk::PointSet::Pointer curPS = m_PointListModel->GetPointSet(); if (curPS->GetSize() == 0) return; switch (QMessageBox::question(this, tr("Clear Points"), tr("Remove all points from the displayed list?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No)) { case QMessageBox::Yes: { mitk::PointSet::PointsIterator it; mitk::PointSet::PointsContainer *curPsPoints; while (!curPS->IsEmptyTimeStep(0)) { curPsPoints = curPS->GetPointSet()->GetPoints(); it = curPsPoints->Begin(); curPS->SetSelectInfo(it->Index(), true); m_PointListModel->RemoveSelectedPoint(); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); break; } case QMessageBox::No: default: break; } } void QmitkPointListView::ClearPointListTS() { } void QmitkPointListView::AddSliceNavigationController(mitk::SliceNavigationController *snc) { if (snc == nullptr) return; m_Sncs.insert(snc); } void QmitkPointListView::RemoveSliceNavigationController(mitk::SliceNavigationController *snc) { if (snc == nullptr) return; m_Sncs.erase(snc); } diff --git a/Modules/QtWidgetsExt/src/QmitkPointListViewWidget.cpp b/Modules/QtWidgetsExt/src/QmitkPointListViewWidget.cpp deleted file mode 100644 index 13a7321abe..0000000000 --- a/Modules/QtWidgetsExt/src/QmitkPointListViewWidget.cpp +++ /dev/null @@ -1,255 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#include "QmitkPointListViewWidget.h" - -#include "QmitkEditPointDialog.h" -#include "QmitkPointListModel.h" -#include "QmitkStdMultiWidget.h" - -#include "mitkInteractionConst.h" -#include "mitkPointOperation.h" -#include "mitkRenderingManager.h" - -#include - -QmitkPointListViewWidget::QmitkPointListViewWidget(QWidget *parent) - : QListWidget(parent), m_TimeStep(0), m_SelfCall(false), m_MultiWidget(nullptr) -{ - QListWidget::setAlternatingRowColors(true); - // logic - - QListWidget::setSelectionBehavior(QAbstractItemView::SelectRows); - QListWidget::setSelectionMode(QAbstractItemView::SingleSelection); - - connect(this, SIGNAL(itemDoubleClicked(QListWidgetItem *)), this, SLOT(OnItemDoubleClicked(QListWidgetItem *))); - - connect(this, SIGNAL(currentRowChanged(int)), this, SLOT(OnCurrentRowChanged(int))); -} - -QmitkPointListViewWidget::~QmitkPointListViewWidget() -{ - this->SetPointSet(nullptr); // remove listener -} - -void QmitkPointListViewWidget::SetPointSet(mitk::PointSet *pointSet) -{ - auto lockedPointSet = m_PointSet.Lock(); - - if (lockedPointSet.IsNotNull()) - { - lockedPointSet->RemoveObserver(m_PointSetModifiedTag); - lockedPointSet->RemoveObserver(m_PointSetDeletedTag); - } - - m_PointSet = pointSet; - lockedPointSet = m_PointSet.Lock(); - - if (lockedPointSet.IsNotNull()) - { - auto onPointSetDeleted = itk::SimpleMemberCommand::New(); - onPointSetDeleted->SetCallbackFunction(this, &QmitkPointListViewWidget::OnPointSetDeleted); - m_PointSetDeletedTag = lockedPointSet->AddObserver(itk::DeleteEvent(), onPointSetDeleted); - - auto onPointSetModified = itk::SimpleMemberCommand::New(); - onPointSetModified->SetCallbackFunction(this, &QmitkPointListViewWidget::OnPointSetChanged); - m_PointSetModifiedTag = lockedPointSet->AddObserver(itk::DeleteEvent(), onPointSetModified); - } - - this->Update(); -} - -const mitk::PointSet *QmitkPointListViewWidget::GetPointSet() const -{ - return m_PointSet.Lock(); -} - -void QmitkPointListViewWidget::SetTimeStep(int t) -{ - m_TimeStep = t; - this->Update(); -} - -int QmitkPointListViewWidget::GetTimeStep() const -{ - return m_TimeStep; -} - -void QmitkPointListViewWidget::SetMultiWidget(QmitkStdMultiWidget *multiWidget) -{ - m_MultiWidget = multiWidget; -} - -QmitkStdMultiWidget *QmitkPointListViewWidget::GetMultiWidget() const -{ - return m_MultiWidget; -} - -void QmitkPointListViewWidget::OnPointSetChanged() -{ - if (!m_SelfCall) - this->Update(); -} - -void QmitkPointListViewWidget::OnPointSetDeleted() -{ - this->SetPointSet(nullptr); - this->Update(); -} - -void QmitkPointListViewWidget::OnItemDoubleClicked(QListWidgetItem *item) -{ - QmitkEditPointDialog _EditPointDialog(this); - _EditPointDialog.SetPoint(m_PointSet.Lock(), this->row(item), m_TimeStep); - _EditPointDialog.exec(); -} - -void QmitkPointListViewWidget::OnCurrentRowChanged(int) -{ - this->Update(true); -} - -void QmitkPointListViewWidget::keyPressEvent(QKeyEvent *e) -{ - if (m_PointSet.IsExpired()) - return; - - int key = e->key(); - switch (key) - { - case Qt::Key_F2: - this->MoveSelectedPointUp(); - break; - case Qt::Key_F3: - this->MoveSelectedPointDown(); - break; - case Qt::Key_Delete: - this->RemoveSelectedPoint(); - break; - default: - break; - } -} - -void QmitkPointListViewWidget::MoveSelectedPointUp() -{ - auto pointSet = m_PointSet.Lock(); - - if (pointSet.IsNull()) - return; - - mitk::PointSet::PointIdentifier selectedID; - selectedID = pointSet->SearchSelectedPoint(m_TimeStep); - mitk::PointOperation *doOp = - new mitk::PointOperation(mitk::OpMOVEPOINTUP, pointSet->GetPoint(selectedID, m_TimeStep), selectedID, true); - pointSet->ExecuteOperation(doOp); - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); // Workaround for update problem in Pointset/Mapper -} - -void QmitkPointListViewWidget::MoveSelectedPointDown() -{ - auto pointSet = m_PointSet.Lock(); - - if (pointSet.IsNull()) - return; - - mitk::PointSet::PointIdentifier selectedID; - selectedID = pointSet->SearchSelectedPoint(m_TimeStep); - mitk::PointOperation *doOp = - new mitk::PointOperation(mitk::OpMOVEPOINTDOWN, pointSet->GetPoint(selectedID, m_TimeStep), selectedID, true); - pointSet->ExecuteOperation(doOp); - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); // Workaround for update problem in Pointset/Mapper -} - -void QmitkPointListViewWidget::RemoveSelectedPoint() -{ - auto pointSet = m_PointSet.Lock(); - - if (pointSet.IsNull()) - return; - - mitk::PointSet::PointIdentifier selectedID; - selectedID = pointSet->SearchSelectedPoint(m_TimeStep); - mitk::PointOperation *doOp = - new mitk::PointOperation(mitk::OpREMOVE, pointSet->GetPoint(selectedID, m_TimeStep), selectedID, true); - pointSet->ExecuteOperation(doOp); - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); // Workaround for update problem in Pointset/Mapper -} - -void QmitkPointListViewWidget::Update(bool currentRowChanged) -{ - if (m_SelfCall) - return; - - auto pointSet = m_PointSet.Lock(); - - if (pointSet.IsNull()) - { - this->clear(); - return; - } - - m_SelfCall = true; - QString text; - int i = 0; - - mitk::PointSet::DataType::Pointer pointset = pointSet->GetPointSet(m_TimeStep); - for (mitk::PointSet::PointsContainer::Iterator it = pointset->GetPoints()->Begin(); - it != pointset->GetPoints()->End(); - ++it) - { - text = QString("%0: (%1, %2, %3)") - .arg(i, 3) - .arg(it.Value().GetElement(0), 0, 'f', 3) - .arg(it.Value().GetElement(1), 0, 'f', 3) - .arg(it.Value().GetElement(2), 0, 'f', 3); - - if (i == this->count()) - this->addItem(text); // insert text - else - this->item(i)->setText(text); // update text - - if (currentRowChanged) - { - if (i == this->currentRow()) - pointSet->SetSelectInfo(this->currentRow(), true, m_TimeStep); - else - pointSet->SetSelectInfo(it->Index(), false, m_TimeStep); // select nothing now - } - ++i; - } - - // remove unnecessary listwidgetitems - while (pointSet->GetPointSet(m_TimeStep)->GetPoints()->Size() < (unsigned int)this->count()) - { - QListWidgetItem *item = this->takeItem(this->count() - 1); - delete item; - } - - // update selection in pointset or in the list widget - if (!currentRowChanged) - { - if (pointSet->GetNumberOfSelected(m_TimeStep) > 1) - { - /// @TODO use logging as soon as available - std::cerr << "Point set has multiple selected points. This view is not designed for more than one selected point." - << std::endl; - } - - int selectedIndex = pointSet->SearchSelectedPoint(m_TimeStep); - if (selectedIndex != -1) // no selected point is found - { - this->setCurrentRow(selectedIndex); - } - } - m_SelfCall = false; -} diff --git a/Modules/QtWidgetsExt/src/QmitkPointListWidget.cpp b/Modules/QtWidgetsExt/src/QmitkPointListWidget.cpp index b2c9dcd2a8..083e294dba 100644 --- a/Modules/QtWidgetsExt/src/QmitkPointListWidget.cpp +++ b/Modules/QtWidgetsExt/src/QmitkPointListWidget.cpp @@ -1,484 +1,481 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ + #include "QmitkPointListWidget.h" -#include "QmitkPointListView.h" -#include "QmitkPointListModel.h" #include #include #include #include #include - #include #include +#include #include -#include - QmitkPointListWidget::QmitkPointListWidget(QWidget *parent, int orientation) : QWidget(parent), m_PointListView(nullptr), m_PointSetNode(nullptr), m_Orientation(0), m_MovePointUpBtn(nullptr), m_MovePointDownBtn(nullptr), m_RemovePointBtn(nullptr), m_SavePointsBtn(nullptr), m_LoadPointsBtn(nullptr), m_ToggleAddPoint(nullptr), m_AddPoint(nullptr), m_TimeStepDisplay(nullptr), m_DataInteractor(nullptr), m_TimeStep(0), m_EditAllowed(true), m_NodeObserverTag(0) { m_PointListView = new QmitkPointListView(); if (orientation != 0) m_Orientation = orientation; SetupUi(); SetupConnections(); ObserveNewNode(nullptr); } QmitkPointListWidget::~QmitkPointListWidget() { m_DataInteractor = nullptr; if (m_PointSetNode && m_NodeObserverTag) { m_PointSetNode->RemoveObserver(m_NodeObserverTag); m_NodeObserverTag = 0; } delete m_PointListView; } void QmitkPointListWidget::SetupConnections() { connect(this->m_LoadPointsBtn, SIGNAL(clicked()), this, SLOT(OnBtnLoadPoints())); connect(this->m_SavePointsBtn, SIGNAL(clicked()), this, SLOT(OnBtnSavePoints())); connect(this->m_MovePointUpBtn, SIGNAL(clicked()), this, SLOT(MoveSelectedPointUp())); connect(this->m_MovePointDownBtn, SIGNAL(clicked()), this, SLOT(MoveSelectedPointDown())); connect(this->m_RemovePointBtn, SIGNAL(clicked()), this, SLOT(RemoveSelectedPoint())); connect(this->m_ToggleAddPoint, SIGNAL(toggled(bool)), this, SLOT(OnBtnAddPoint(bool))); connect(this->m_AddPoint, SIGNAL(clicked()), this, SLOT(OnBtnAddPointManually())); connect(this->m_PointListView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(OnListDoubleClick())); connect(this->m_PointListView, SIGNAL(SignalPointSelectionChanged()), this, SLOT(OnPointSelectionChanged())); connect(this->m_PointListView, SIGNAL(SignalTimeStepChanged(int)), this, SLOT(OnTimeStepChanged(int))); } void QmitkPointListWidget::OnTimeStepChanged(int timeStep) { m_TimeStepLabel->setText(QString("%1").arg(timeStep)); } void QmitkPointListWidget::SetupUi() { // Setup the buttons m_ToggleAddPoint = new QPushButton(); m_ToggleAddPoint->setMaximumSize(25, 25); m_ToggleAddPoint->setCheckable(true); m_ToggleAddPoint->setToolTip("Toggle point editing (use SHIFT + Left Mouse Button to add Points)"); m_ToggleAddPoint->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/QtWidgetsExt/plus.svg"))); m_AddPoint = new QPushButton(); m_AddPoint->setMaximumSize(25, 25); m_AddPoint->setToolTip("Manually add point"); m_AddPoint->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/QtWidgetsExt/plus-xyz.svg"))); m_RemovePointBtn = new QPushButton(); m_RemovePointBtn->setMaximumSize(25, 25); m_RemovePointBtn->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/QtWidgetsExt/eraser.svg"))); m_RemovePointBtn->setToolTip("Erase one point from list (Hotkey: DEL)"); m_MovePointUpBtn = new QPushButton(); m_MovePointUpBtn->setMaximumSize(25, 25); m_MovePointUpBtn->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/QtWidgetsExt/arrow-up.svg"))); m_MovePointUpBtn->setToolTip("Swap selected point upwards (Hotkey: F2)"); m_MovePointDownBtn = new QPushButton(); m_MovePointDownBtn->setMaximumSize(25, 25); m_MovePointDownBtn->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/QtWidgetsExt/arrow-down.svg"))); m_MovePointDownBtn->setToolTip("Swap selected point downwards (Hotkey: F3)"); m_SavePointsBtn = new QPushButton(); m_SavePointsBtn->setMaximumSize(25, 25); m_SavePointsBtn->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/QtWidgetsExt/save.svg"))); m_SavePointsBtn->setToolTip("Save points to file"); m_LoadPointsBtn = new QPushButton(); m_LoadPointsBtn->setMaximumSize(25, 25); m_LoadPointsBtn->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/QtWidgetsExt/folder-open.svg"))); m_LoadPointsBtn->setToolTip("Load list of points from file (REPLACES current content)"); int i; QBoxLayout *lay1; QBoxLayout *lay2; QBoxLayout *lay3; switch (m_Orientation) { case 0: lay1 = new QVBoxLayout(this); lay2 = new QHBoxLayout(); i = 0; break; case 1: lay1 = new QHBoxLayout(this); lay2 = new QVBoxLayout(); i = -1; break; case 2: lay1 = new QHBoxLayout(this); lay2 = new QVBoxLayout(); i = 0; break; default: lay1 = new QVBoxLayout(this); lay2 = new QHBoxLayout(); i = -1; break; } // setup Layouts this->setLayout(lay1); lay2->stretch(true); lay2->addWidget(m_ToggleAddPoint); lay2->addWidget(m_AddPoint); lay2->addWidget(m_RemovePointBtn); lay2->addWidget(m_MovePointUpBtn); lay2->addWidget(m_MovePointDownBtn); lay2->addWidget(m_SavePointsBtn); lay2->addWidget(m_LoadPointsBtn); // setup Labels m_TimeStepDisplay = new QLabel; m_TimeStepLabel = new QLabel; lay3 = new QHBoxLayout; m_TimeStepDisplay->setMaximumSize(200, 15); lay3->stretch(true); lay3->setAlignment(Qt::AlignLeft); lay3->addWidget(m_TimeStepDisplay); lay3->addWidget(m_TimeStepLabel); m_TimeStepDisplay->setText("Time Step: "); m_TimeStepLabel->setMaximumSize(10, 15); this->OnTimeStepChanged(0); //Add Layouts lay1->insertWidget(i, m_PointListView); this->setLayout(lay1); lay1->addLayout(lay2); lay1->addLayout(lay3); } void QmitkPointListWidget::SetPointSet(mitk::PointSet *newPs) { if (newPs == nullptr) return; this->m_PointSetNode->SetData(newPs); dynamic_cast(this->m_PointListView->model())->SetPointSetNode(m_PointSetNode); ObserveNewNode(m_PointSetNode); } void QmitkPointListWidget::SetPointSetNode(mitk::DataNode *newNode) { if (m_DataInteractor.IsNotNull()) m_DataInteractor->SetDataNode(newNode); ObserveNewNode(newNode); dynamic_cast(this->m_PointListView->model())->SetPointSetNode(newNode); } void QmitkPointListWidget::OnBtnSavePoints() { if ((dynamic_cast(m_PointSetNode->GetData())) == nullptr) return; // don't write empty point sets. If application logic requires something else then do something else. if ((dynamic_cast(m_PointSetNode->GetData()))->GetSize() == 0) return; // take the previously defined name of node as proposal for filename std::string nodeName = m_PointSetNode->GetName(); nodeName = "/" + nodeName + ".mps"; QString fileNameProposal = QString(); fileNameProposal.append(nodeName.c_str()); QString aFilename = QFileDialog::getSaveFileName( nullptr, "Save point set", QDir::currentPath() + fileNameProposal, "MITK Pointset (*.mps)"); if (aFilename.isEmpty()) return; try { mitk::IOUtil::Save(m_PointSetNode->GetData(), aFilename.toStdString()); } catch (...) { QMessageBox::warning(this, "Save point set", QString("File writer reported problems writing %1\n\n" "PLEASE CHECK output file!") .arg(aFilename)); } } void QmitkPointListWidget::OnBtnLoadPoints() { // get the name of the file to load QString filename = QFileDialog::getOpenFileName(nullptr, "Open MITK Pointset", "", "MITK Point Sets (*.mps)"); if (filename.isEmpty()) return; // attempt to load file try { mitk::PointSet::Pointer pointSet = mitk::IOUtil::Load(filename.toStdString()); if (pointSet.IsNull()) { QMessageBox::warning(this, "Load point set", QString("File reader could not read %1").arg(filename)); return; } // loading successful this->SetPointSet(pointSet); } catch (...) { QMessageBox::warning(this, "Load point set", QString("File reader collapsed while reading %1").arg(filename)); } emit PointListChanged(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } mitk::PointSet *QmitkPointListWidget::GetPointSet() { return dynamic_cast(m_PointSetNode->GetData()); } mitk::DataNode *QmitkPointListWidget::GetPointSetNode() { return m_PointSetNode; } void QmitkPointListWidget::SetMultiWidget(QmitkStdMultiWidget *multiWidget) { m_PointListView->SetMultiWidget(multiWidget); } void QmitkPointListWidget::RemoveSelectedPoint() { if (!m_PointSetNode) return; mitk::PointSet *pointSet = dynamic_cast(m_PointSetNode->GetData()); if (!pointSet) return; if (pointSet->GetSize() == 0) return; QmitkPointListModel *pointListModel = dynamic_cast(m_PointListView->model()); pointListModel->RemoveSelectedPoint(); emit PointListChanged(); } void QmitkPointListWidget::MoveSelectedPointDown() { if (!m_PointSetNode) return; mitk::PointSet *pointSet = dynamic_cast(m_PointSetNode->GetData()); if (!pointSet) return; if (pointSet->GetSize() == 0) return; QmitkPointListModel *pointListModel = dynamic_cast(m_PointListView->model()); pointListModel->MoveSelectedPointDown(); emit PointListChanged(); } void QmitkPointListWidget::MoveSelectedPointUp() { if (!m_PointSetNode) return; mitk::PointSet *pointSet = dynamic_cast(m_PointSetNode->GetData()); if (!pointSet) return; if (pointSet->GetSize() == 0) return; QmitkPointListModel *pointListModel = dynamic_cast(m_PointListView->model()); pointListModel->MoveSelectedPointUp(); emit PointListChanged(); } void QmitkPointListWidget::OnBtnAddPoint(bool checked) { if (m_PointSetNode.IsNotNull()) { if (checked) { m_DataInteractor = m_PointSetNode->GetDataInteractor(); // If no data Interactor is present create a new one if (m_DataInteractor.IsNull()) { // Create PointSetData Interactor m_DataInteractor = mitk::PointSetDataInteractor::New(); // Load the according state machine for regular point set interaction m_DataInteractor->LoadStateMachine("PointSet.xml"); // Set the configuration file that defines the triggers for the transitions m_DataInteractor->SetEventConfig("PointSetConfig.xml"); // set the DataNode (which already is added to the DataStorage m_DataInteractor->SetDataNode(m_PointSetNode); } } else { m_PointSetNode->SetDataInteractor(nullptr); m_DataInteractor = nullptr; } emit EditPointSets(checked); } } void QmitkPointListWidget::OnBtnAddPointManually() { mitk::PointSet *pointSet = this->GetPointSet(); QmitkEditPointDialog editPointDialog(this); if (this->GetPointSet()->IsEmpty()) { editPointDialog.SetPoint(pointSet, 0, m_TimeStep); } else { mitk::PointSet::PointsIterator maxIt = pointSet->GetMaxId(); mitk::PointSet::PointIdentifier maxId = maxIt->Index(); editPointDialog.SetPoint(pointSet, maxId + 1, m_TimeStep); } editPointDialog.exec(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkPointListWidget::OnListDoubleClick() { } void QmitkPointListWidget::OnPointSelectionChanged() { emit this->PointSelectionChanged(); } void QmitkPointListWidget::DeactivateInteractor(bool) { } void QmitkPointListWidget::EnableEditButton(bool enabled) { m_EditAllowed = enabled; if (enabled == false) m_ToggleAddPoint->setEnabled(false); else m_ToggleAddPoint->setEnabled(true); OnBtnAddPoint(enabled); } void QmitkPointListWidget::ObserveNewNode(mitk::DataNode *node) { if (m_DataInteractor.IsNotNull()) m_DataInteractor->SetDataNode(node); // remove old observer if (m_PointSetNode) { if (m_DataInteractor) { m_DataInteractor = nullptr; m_ToggleAddPoint->setChecked(false); } m_PointSetNode->RemoveObserver(m_NodeObserverTag); m_NodeObserverTag = 0; } m_PointSetNode = node; // add new observer if necessary if (m_PointSetNode) { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &QmitkPointListWidget::OnNodeDeleted); m_NodeObserverTag = m_PointSetNode->AddObserver(itk::DeleteEvent(), command); } else { m_NodeObserverTag = 0; } if (m_EditAllowed == true) m_ToggleAddPoint->setEnabled(m_PointSetNode); else m_ToggleAddPoint->setEnabled(false); m_RemovePointBtn->setEnabled(m_PointSetNode); m_LoadPointsBtn->setEnabled(m_PointSetNode); m_SavePointsBtn->setEnabled(m_PointSetNode); m_AddPoint->setEnabled(m_PointSetNode); } void QmitkPointListWidget::OnNodeDeleted(const itk::EventObject &) { if (m_PointSetNode.IsNotNull() && !m_NodeObserverTag) m_PointSetNode->RemoveObserver(m_NodeObserverTag); m_NodeObserverTag = 0; m_PointSetNode = nullptr; m_PointListView->SetPointSetNode(nullptr); m_ToggleAddPoint->setEnabled(false); m_RemovePointBtn->setEnabled(false); m_LoadPointsBtn->setEnabled(false); m_SavePointsBtn->setEnabled(false); m_AddPoint->setEnabled(false); } void QmitkPointListWidget::AddSliceNavigationController(mitk::SliceNavigationController *snc) { m_PointListView->AddSliceNavigationController(snc); } void QmitkPointListWidget::RemoveSliceNavigationController(mitk::SliceNavigationController *snc) { m_PointListView->RemoveSliceNavigationController(snc); } void QmitkPointListWidget::UnselectEditButton() { m_ToggleAddPoint->setChecked(false); } diff --git a/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/ClassificationRegionGrow.cpp b/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/ClassificationRegionGrow.cpp index a64a782ba3..01a7afcf61 100644 --- a/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/ClassificationRegionGrow.cpp +++ b/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/ClassificationRegionGrow.cpp @@ -1,624 +1,619 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // STD #include // Blueberry #include #include // Qmitk #include "ClassificationRegionGrow.h" // Qt #include #include #include //mitk image #include #include -//#include -//#include #include #include #include "mitkVigraRandomForestClassifier.h" #include "mitkCLUtil.h" #include "qboxlayout.h" #include #include "Eigen/Dense" #include #include #include #include #include #include #include #include #include #include #include #include -//#include #include "mitkLabelSetImage.h" -//#include #include #include #include #include #include -//#include #include const std::string ClassificationRegionGrow::VIEW_ID = "org.mitk.views.ClassificationRegionGrow"; void ClassificationRegionGrow::SetFocus() { // m_Controls.buttonPerformImageProcessing->setFocus(); } void ClassificationRegionGrow::CreateQtPartControl( QWidget *parent ) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi( parent ); m_CalculateFeatures = true; m_BlockManualSegmentation = false; m_BlockPostProcessing = false; m_Controls.groupLearningParameter->setVisible(false); m_Controls.groupFeatureSelection->setVisible(false); QmitkDataStorageComboBox * cb_inputimage = new QmitkDataStorageComboBox(this->GetDataStorage(), mitk::TNodePredicateDataType::New()); QmitkDataStorageComboBox * cb_maskimage= new QmitkDataStorageComboBox(this->GetDataStorage(),mitk::TNodePredicateDataType::New()); QmitkDataStorageComboBox * cb_baseimage = new QmitkDataStorageComboBox(this->GetDataStorage(), mitk::TNodePredicateDataType::New()); m_Controls.m_InputImageLayout->addWidget(cb_inputimage); m_Controls.m_MaskImageLayout->addWidget(cb_maskimage); m_Controls.StartingPointLayout->addWidget(cb_baseimage); m_Controls.addInputButton->setIcon(QIcon::fromTheme("list-add")); m_Controls.removeInputButton->setIcon(QIcon::fromTheme("edit-delete")); connect( cb_inputimage, SIGNAL(OnSelectionChanged(const mitk::DataNode*)), this, SLOT(OnInitializeSession(const mitk::DataNode*))); connect( cb_maskimage, SIGNAL(OnSelectionChanged(const mitk::DataNode*)), this, SLOT(OnInitializeSession(const mitk::DataNode*))); connect(m_Controls.SelectAdvancedParameter, SIGNAL(toggled(bool)), m_Controls.groupLearningParameter, SLOT(setVisible(bool))); connect(m_Controls.SelectAdvancedParameter, SIGNAL(toggled(bool)), m_Controls.groupFeatureSelection, SLOT(setVisible(bool))); connect(m_Controls.SelectSimpleParameters, SIGNAL(toggled(bool)), m_Controls.parameterWidget, SLOT(setVisible(bool))); connect(m_Controls.m_DoAutomaticSecmentation, SIGNAL( clicked()), this, SLOT(DoAutomSegmentation())); connect(m_Controls.removeInputButton, SIGNAL(clicked()), this, SLOT(RemoveItemFromLabelList())); connect(m_Controls.addInputButton, SIGNAL(clicked()), this, SLOT(AddInputField())); connect(m_Controls.UseIntensity, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); connect(m_Controls.Gauss1, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); connect(m_Controls.Gauss2, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); connect(m_Controls.Gauss3, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); connect(m_Controls.Gauss4, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); connect(m_Controls.Gauss5, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); connect(m_Controls.DoG1, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); connect(m_Controls.DoG2, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); connect(m_Controls.DoG3, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); connect(m_Controls.DoG4, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); connect(m_Controls.DoG5, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); connect(m_Controls.LoG1, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); connect(m_Controls.LoG2, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); connect(m_Controls.LoG3, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); connect(m_Controls.LoG4, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); connect(m_Controls.LoG5, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); connect(m_Controls.HoG1, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); connect(m_Controls.HoG2, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); connect(m_Controls.HoG3, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); connect(m_Controls.HoG4, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); connect(m_Controls.HoG5, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); connect(m_Controls.LH1, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); connect(m_Controls.LH2, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); connect(m_Controls.LH3, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); connect(m_Controls.LH4, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); } void ClassificationRegionGrow::AddInputField() { QmitkDataStorageComboBox * cb_inputimage = new QmitkDataStorageComboBox(this->GetDataStorage(), mitk::TNodePredicateDataType::New()); //QPushButton * lockButton = new QPushButton(); //lockButton->setText(""); //lockButton->setMinimumWidth(40); //lockButton->setCheckable(true); //lockButton->setIcon(QApplication::style()->standardIcon(QStyle::SP_MediaStop)); QHBoxLayout *layout = new QHBoxLayout; layout->addWidget(cb_inputimage,100); //layout->addWidget(lockButton,1); m_Controls.m_InputImageLayout->addLayout(layout); connect(cb_inputimage, SIGNAL(OnSelectionChanged(const mitk::DataNode*)), this, SLOT(OnInitializeSession(const mitk::DataNode*))); } void ClassificationRegionGrow::RemoveItemFromLabelList() { auto numberOfElements = m_Controls.m_InputImageLayout->count(); auto lastItem = m_Controls.m_InputImageLayout->itemAt(numberOfElements-1); QHBoxLayout *layout = dynamic_cast(lastItem); while (QLayoutItem* item = layout->takeAt(0)) { if (QWidget* widget = item->widget()) widget->deleteLater(); delete item; } m_Controls.m_InputImageLayout->removeItem(lastItem); delete lastItem; } void ClassificationRegionGrow::OnSelectionChanged( berry::IWorkbenchPart::Pointer /*source*/, const QList& nodes ) { // iterate all selected objects, adjust warning visibility foreach( mitk::DataNode::Pointer node, nodes ) { if( node.IsNotNull() && dynamic_cast(node->GetData()) ) { return; } } } void ClassificationRegionGrow::OnInitializeSession(const mitk::DataNode *) { OnFeatureSettingsChanged(); } void ClassificationRegionGrow::ProcessFeatureImages(const mitk::Image::Pointer & raw_image) { // RAW if (m_Controls.UseIntensity->isChecked()) { m_FeatureImageVector.push_back(raw_image); } // GAUSS if (m_Controls.Gauss1->isChecked()) { mitk::Image::Pointer smoothed; mitk::CLUtil::GaussianFilter(raw_image, smoothed, 1); m_FeatureImageVector.push_back(smoothed); } if (m_Controls.Gauss2->isChecked()) { mitk::Image::Pointer smoothed; mitk::CLUtil::GaussianFilter(raw_image, smoothed, 2); m_FeatureImageVector.push_back(smoothed); } if (m_Controls.Gauss3->isChecked()) { mitk::Image::Pointer smoothed; mitk::CLUtil::GaussianFilter(raw_image, smoothed, 3); m_FeatureImageVector.push_back(smoothed); } if (m_Controls.Gauss4->isChecked()) { mitk::Image::Pointer smoothed; mitk::CLUtil::GaussianFilter(raw_image, smoothed, 4); m_FeatureImageVector.push_back(smoothed); } if (m_Controls.Gauss5->isChecked()) { mitk::Image::Pointer smoothed; mitk::CLUtil::GaussianFilter(raw_image, smoothed, 5); m_FeatureImageVector.push_back(smoothed); } // Difference of Gaussian if (m_Controls.DoG1->isChecked()) { mitk::Image::Pointer smoothed; mitk::CLUtil::DifferenceOfGaussianFilter(raw_image, smoothed, 1,0.8); m_FeatureImageVector.push_back(smoothed); } if (m_Controls.DoG2->isChecked()) { mitk::Image::Pointer smoothed; mitk::CLUtil::DifferenceOfGaussianFilter(raw_image, smoothed, 2, 1.8); m_FeatureImageVector.push_back(smoothed); } if (m_Controls.DoG3->isChecked()) { mitk::Image::Pointer smoothed; mitk::CLUtil::DifferenceOfGaussianFilter(raw_image, smoothed, 3, 2.6); m_FeatureImageVector.push_back(smoothed); } if (m_Controls.DoG4->isChecked()) { mitk::Image::Pointer smoothed; mitk::CLUtil::DifferenceOfGaussianFilter(raw_image, smoothed, 4, 3.4); m_FeatureImageVector.push_back(smoothed); } if (m_Controls.DoG5->isChecked()) { mitk::Image::Pointer smoothed; mitk::CLUtil::DifferenceOfGaussianFilter(raw_image, smoothed, 5, 4.3); m_FeatureImageVector.push_back(smoothed); } // Laplacian of Gaussian if (m_Controls.LoG1->isChecked()) { mitk::Image::Pointer smoothed; mitk::CLUtil::LaplacianOfGaussianFilter(raw_image, smoothed, 1); m_FeatureImageVector.push_back(smoothed); } if (m_Controls.LoG2->isChecked()) { mitk::Image::Pointer smoothed; mitk::CLUtil::LaplacianOfGaussianFilter(raw_image, smoothed, 2); m_FeatureImageVector.push_back(smoothed); } if (m_Controls.LoG3->isChecked()) { mitk::Image::Pointer smoothed; mitk::CLUtil::LaplacianOfGaussianFilter(raw_image, smoothed, 3); m_FeatureImageVector.push_back(smoothed); } if (m_Controls.LoG4->isChecked()) { mitk::Image::Pointer smoothed; mitk::CLUtil::LaplacianOfGaussianFilter(raw_image, smoothed, 4); m_FeatureImageVector.push_back(smoothed); } if (m_Controls.LoG5->isChecked()) { mitk::Image::Pointer smoothed; mitk::CLUtil::LaplacianOfGaussianFilter(raw_image, smoothed, 5); m_FeatureImageVector.push_back(smoothed); } // Hessian of Gaussian if (m_Controls.HoG1->isChecked()) { mitk::CLUtil::HessianOfGaussianFilter(raw_image, m_FeatureImageVector, 1); } if (m_Controls.HoG2->isChecked()) { mitk::CLUtil::HessianOfGaussianFilter(raw_image, m_FeatureImageVector, 2); } if (m_Controls.HoG3->isChecked()) { mitk::CLUtil::HessianOfGaussianFilter(raw_image, m_FeatureImageVector, 3); } if (m_Controls.HoG4->isChecked()) { mitk::CLUtil::HessianOfGaussianFilter(raw_image, m_FeatureImageVector, 4); } if (m_Controls.HoG5->isChecked()) { mitk::CLUtil::HessianOfGaussianFilter(raw_image, m_FeatureImageVector, 5); } // LocalHistogram if (m_Controls.LH1->isChecked()) { mitk::CLUtil::LocalHistogram(raw_image, m_FeatureImageVector, 5,3); } if (m_Controls.LH2->isChecked()) { mitk::CLUtil::LocalHistogram(raw_image, m_FeatureImageVector, 5, 5); } if (m_Controls.LH3->isChecked()) { mitk::CLUtil::LocalHistogram(raw_image, m_FeatureImageVector, 10, 3); } if (m_Controls.LH4->isChecked()) { mitk::CLUtil::LocalHistogram(raw_image, m_FeatureImageVector, 10, 5); } } void ClassificationRegionGrow::OnFeatureSettingsChanged() { MITK_INFO << "FeatureSettingsChanged"; m_CalculateFeatures = true; } void ClassificationRegionGrow::DoAutomSegmentation() { MITK_INFO << "Start Automatic Segmentation ..."; // Load Images from registration process QmitkDataStorageComboBox * cb_image = dynamic_cast(m_Controls.m_InputImageLayout->itemAt(1)->widget()); QmitkDataStorageComboBox * cb_maskimage = dynamic_cast(m_Controls.m_MaskImageLayout->itemAt(1)->widget()); mitk::Image::Pointer raw_image; mitk::Image::Pointer mask_image; if ((cb_image != nullptr) || (cb_maskimage != nullptr)) { raw_image = dynamic_cast(cb_image->GetSelectedNode()->GetData()); mask_image = dynamic_cast(cb_maskimage->GetSelectedNode()->GetData()); } else { QMessageBox msgBox; msgBox.setText("Please specify the images that shlould be used."); msgBox.exec(); return; } if (raw_image.IsNull() || mask_image.IsNull()) { QMessageBox msgBox; msgBox.setText("Error during processing the specified images."); msgBox.exec(); return; } std::vector imageList; imageList.push_back(raw_image); for (int i = 2; i < m_Controls.m_InputImageLayout->count(); ++i) { QLayout* layout = dynamic_cast(m_Controls.m_InputImageLayout->itemAt(i)); MITK_INFO << layout; QmitkDataStorageComboBox * tmp_cb_image = dynamic_cast(layout->itemAt(0)->widget()); MITK_INFO << tmp_cb_image; if (tmp_cb_image) { mitk::Image::Pointer tmp_image = dynamic_cast(tmp_cb_image); if (tmp_image.IsNotNull()) { MITK_INFO << "Adding Image..."; imageList.push_back(tmp_image); } } } MITK_INFO << "Start Feature Calculation ..."; if(m_CalculateFeatures) { m_FeatureImageVector.clear(); for (auto img : imageList) { ProcessFeatureImages(img); } m_CalculateFeatures = false; if (m_Controls.checkAddFeaturesToDataManager->isChecked()) { for (std::size_t i = 0; i < m_FeatureImageVector.size(); ++i) { auto newName = "Feature_" + std::to_string(i); AddAsDataNode(m_FeatureImageVector[i].GetPointer(), newName); } } } MITK_INFO << "Start Classifier Training ..."; TrainClassifier(raw_image, mask_image); MITK_INFO << "Predict extended Segmentation ..."; PredictSegmentation(raw_image, mask_image); } void ClassificationRegionGrow::TrainClassifier(const mitk::Image::Pointer & raw_image, const mitk::Image::Pointer & mask_image) { typedef itk::Image DoubleImageType; typedef itk::Image ShortImageType; DoubleImageType::Pointer input; ShortImageType::Pointer mask; mitk::CastToItkImage(raw_image, input); mitk::CastToItkImage(mask_image, mask); int numberOfSegmentedVoxel = 0; int numberOfFeatures = m_FeatureImageVector.size(); auto maskIter = itk::ImageRegionConstIteratorWithIndex(mask, mask->GetLargestPossibleRegion()); m_SegmentedLocations.clear(); m_SegmentedOrganLocations.clear(); MITK_INFO << "Count Segmentation Size ..."; while ( ! maskIter.IsAtEnd()) { if (maskIter.Value() > 0) { m_SegmentedLocations.push_back(maskIter.GetIndex()); numberOfSegmentedVoxel++; if (maskIter.Value() > 1) { m_SegmentedOrganLocations.push_back(maskIter.GetIndex()); } } ++maskIter; } MITK_INFO << "Sizes: " << numberOfSegmentedVoxel << " " << m_SegmentedOrganLocations.size(); Eigen::MatrixXi Y_train = mitk::CLUtil::Transform(mask_image, mask_image); Eigen::MatrixXd X_train = Eigen::MatrixXd(numberOfSegmentedVoxel, numberOfFeatures); unsigned int index = 0; MITK_INFO << "Convert Training Data to Eigen Matrix ..."; for (const auto & image : m_FeatureImageVector) { X_train.col(index) = mitk::CLUtil::Transform(image, mask_image); ++index; } MITK_INFO << "Classifier Training ..."; m_Classifier = mitk::VigraRandomForestClassifier::New(); //this->m_Controls.Maximum m_Classifier->SetTreeCount(m_Controls.NumberOfTrees->value()); m_Classifier->SetSamplesPerTree(m_Controls.SamplesPerTree->value()); m_Classifier->SetMinimumSplitNodeSize(m_Controls.MinimumSamplesPerNode->value()); m_Classifier->SetMaximumTreeDepth(m_Controls.MaximumTreeDepth->value()); m_Classifier->Train(X_train, Y_train); } static void addNeighbours(std::stack > &stack, itk::Index<3> idx) { idx[0] -= 1; stack.push(idx); idx[0] += 2; stack.push(idx); idx[0] -= 1; idx[1] -= 1; stack.push(idx); idx[1] += 2; stack.push(idx); idx[1] -= 1; idx[2] -= 1; stack.push(idx); idx[2] += 2; stack.push(idx); } void ClassificationRegionGrow::PredictSegmentation(const mitk::Image::Pointer & raw_image, const mitk::Image::Pointer & mask_image) { typedef itk::Image DoubleImageType; typedef itk::Image ShortImageType; DoubleImageType::Pointer input; ShortImageType::Pointer mask; mitk::CastToItkImage(raw_image, input); mitk::CastToItkImage(mask_image, mask); std::vector featureImages; for (auto fimage : m_FeatureImageVector) { DoubleImageType::Pointer feature; mitk::CastToItkImage(fimage, feature); featureImages.push_back(feature); } ShortImageType::Pointer usedLocation = ShortImageType::New(); usedLocation->SetRegions(mask->GetLargestPossibleRegion()); usedLocation->Allocate(); usedLocation->FillBuffer(0); ShortImageType::Pointer resultSegmentation = ShortImageType::New(); if (m_Controls.UpdateImage->isChecked()) { QmitkDataStorageComboBox * cb_maskimage = dynamic_cast(m_Controls.StartingPointLayout->itemAt(2)->widget()); mitk::Image::Pointer base_image = dynamic_cast(cb_maskimage->GetSelectedNode()->GetData()); mitk::CastToItkImage(base_image, resultSegmentation); } else { resultSegmentation->SetRegions(mask->GetLargestPossibleRegion()); resultSegmentation->Allocate(); if (m_Controls.SegmentBackground->isChecked()) { resultSegmentation->FillBuffer(1); } else { resultSegmentation->FillBuffer(0); } } // Fill list of Stacks std::vector > > listOfStacks; while (m_SegmentedOrganLocations.size() > 0) { auto currentLocation = m_SegmentedOrganLocations.back(); m_SegmentedOrganLocations.pop_back(); std::size_t cValue = mask->GetPixel(currentLocation); resultSegmentation->SetPixel(currentLocation, cValue); usedLocation->SetPixel(currentLocation, 1000); while (listOfStacks.size() < cValue+1) { listOfStacks.push_back(std::stack >()); } addNeighbours(listOfStacks[cValue],currentLocation); } int countPredicted = 0; bool connectAllLabels = m_Controls.localGrowing->isChecked(); //m_SegmentedOrganLocations.reserve(10000); Eigen::MatrixXd currentX = Eigen::MatrixXd(1, featureImages.size()); vigra::MultiArrayView<2, double> X(vigra::Shape2(currentX.rows(), currentX.cols()), currentX.data()); auto outLabel = Eigen::MatrixXi(currentX.rows(), 1); vigra::MultiArrayView<2, int> Y(vigra::Shape2(currentX.rows(), 1), outLabel.data()); for (std::size_t i = 2; i < listOfStacks.size(); ++i) { while (listOfStacks[i].size() > 0) { auto currentLocation = listOfStacks[i].top(); listOfStacks[i].pop(); if (!mask->GetLargestPossibleRegion().IsInside(currentLocation)) { continue; } if (usedLocation->GetPixel(currentLocation) > i) { continue; } usedLocation->SetPixel(currentLocation, i+1); for (std::size_t f = 0; f < featureImages.size(); ++f) { currentX(0, f) = featureImages[f]->GetPixel(currentLocation); } m_Classifier->GetRandomForest().predictLabels(X, Y); ++countPredicted; if ((static_cast(Y(0, 0)) == i ) || ((Y(0, 0) > 1) && (connectAllLabels))) { resultSegmentation->SetPixel(currentLocation, std::abs(Y(0, 0))); addNeighbours(listOfStacks[i], currentLocation); } } } MITK_INFO << "Number of Predictions: " << countPredicted; MITK_INFO << "Finished Segmentation..."; mitk::Image::Pointer result; mitk::CastToMitkImage(resultSegmentation, result); result->SetOrigin(raw_image->GetGeometry()->GetOrigin()); result->SetSpacing(raw_image->GetGeometry()->GetSpacing()); mitk::LabelSetImage::Pointer labelResult = mitk::LabelSetImage::New(); labelResult->InitializeByLabeledImage(result); mitk::LabelSetImage::Pointer oldLabelSet = dynamic_cast(mask_image.GetPointer()); labelResult->AddLabelSetToLayer(labelResult->GetActiveLayer(),oldLabelSet->GetLabelSet()); MITK_INFO << "Passing Back..."; AddAsDataNode(labelResult.GetPointer(), "ResultSegmentation"); } mitk::DataNode::Pointer ClassificationRegionGrow::AddAsDataNode(const mitk::BaseData::Pointer & data_, const std::string & name ) { mitk::DataNode::Pointer node = nullptr; node = this->GetDataStorage()->GetNamedNode(name); if(node.IsNull()) { node = mitk::DataNode::New(); node->SetData(data_); node->SetName(name); this->GetDataStorage()->Add(node); }else{ if(dynamic_cast(node->GetData()) && dynamic_cast(data_.GetPointer())) { mitk::Image::Pointer target_image = dynamic_cast(node->GetData()); mitk::Image::Pointer source_image = dynamic_cast(data_.GetPointer()); mitk::ImageReadAccessor ra(source_image); target_image->SetImportVolume(const_cast(ra.GetData())); this->RequestRenderWindowUpdate(); } if(dynamic_cast(node->GetData()) && dynamic_cast(data_.GetPointer())) { node->SetData(data_); node->Modified(); } } return node; } diff --git a/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/ClassificationRegionGrow.h b/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/ClassificationRegionGrow.h index bca9dcd73e..3de437f3f4 100644 --- a/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/ClassificationRegionGrow.h +++ b/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/ClassificationRegionGrow.h @@ -1,110 +1,102 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef ClassificationRegionGrow_h #define ClassificationRegionGrow_h #include #include #include "ui_ClassificationRegionGrowControls.h" #include #include #include #include #include -//#include -//#include -//#include -#include #include #include -#include "QmitkPointListViewWidget.h" -#include #include /** \brief ClassificationRegionGrow \warning This class is not yet documented. Use "git blame" and ask the author to provide basic documentation. \sa QmitkAbstractView \ingroup ${plugin_target}_internal */ - -//class QmitkPointListWidget; class ctkSliderWidget; class ClassificationRegionGrow : public QmitkAbstractView { // this is needed for all Qt objects that should have a Qt meta-object // (everything that derives from QObject and wants to have signal/slots) Q_OBJECT public: static const std::string VIEW_ID; bool m_CalculateFeatures; std::vector m_FeatureImageVector; std::vector m_ResultImageVector; bool m_BlockManualSegmentation; QFutureWatcher> m_ManualSegmentationFutureWatcher; bool m_BlockPostProcessing; QFutureWatcher> m_PostProcessingFutureWatcher; public slots: /// \brief Called when the user clicks the GUI button void DoAutomSegmentation(); void AddInputField(); void RemoveItemFromLabelList(); void OnFeatureSettingsChanged(); void OnInitializeSession(const mitk::DataNode*); protected: std::vector > m_SegmentedLocations; std::vector > m_SegmentedOrganLocations; typedef float MeasurementType; typedef itk::Statistics::Histogram< MeasurementType, itk::Statistics::DenseFrequencyContainer2 > HistogramType; void CreateQtPartControl(QWidget *parent) override; void SetFocus() override; mitk::DataNode::Pointer AddAsDataNode(const mitk::BaseData::Pointer & data_, const std::string & name ); void ProcessFeatureImages(const mitk::Image::Pointer & raw_image); void TrainClassifier(const mitk::Image::Pointer & raw_image, const mitk::Image::Pointer & mask_image); void PredictSegmentation(const mitk::Image::Pointer & raw_image, const mitk::Image::Pointer & mask_image); /// \brief called by QmitkFunctionality when DataManager's selection has changed void OnSelectionChanged( berry::IWorkbenchPart::Pointer source, const QList& nodes ) override; Ui::ClassificationRegionGrowControls m_Controls; mitk::VigraRandomForestClassifier::Pointer m_Classifier; }; #endif // ClassificationRegionGrow_h diff --git a/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/ClassificationSegmentation.cpp b/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/ClassificationSegmentation.cpp index 1b466f9f58..3c593e5c3c 100644 --- a/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/ClassificationSegmentation.cpp +++ b/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/ClassificationSegmentation.cpp @@ -1,975 +1,969 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // Blueberry #include #include // Qmitk #include "ClassificationSegmentation.h" // Qt #include //mitk image #include #include #include #include #include #include #include "mitkVigraRandomForestClassifier.h" #include "mitkCLUtil.h" #include "qboxlayout.h" #include #include "Eigen/Dense" #include #include #include #include #include #include #include #include #include #include #include #include -//#include - #include #include #include #include #include -#include #include const std::string ClassificationSegmentation::VIEW_ID = "org.mitk.views.classificationsegmentation"; void ClassificationSegmentation::SetFocus() { // m_Controls.buttonPerformImageProcessing->setFocus(); } void ClassificationSegmentation::OnButtonCSFToggle(bool checked) { - // m_PointListWidget->SetPointSet(dynamic_cast(m_PointSetList[0]->GetData())); if(checked) m_PointSetDataInteractor->SetDataNode(m_PointSetList[0]); else m_PointSetDataInteractor->SetDataNode(nullptr); } void ClassificationSegmentation::OnButtonLESToggle(bool checked) { - // m_PointListWidget->SetPointSet(dynamic_cast(m_PointSetList[1]->GetData())); if(checked) m_PointSetDataInteractor->SetDataNode(m_PointSetList[1]); else m_PointSetDataInteractor->SetDataNode(nullptr); } void ClassificationSegmentation::OnButtonBRAToggle(bool checked) { - // m_PointListWidget->SetPointSet(dynamic_cast(m_PointSetList[2]->GetData())); if(checked) m_PointSetDataInteractor->SetDataNode(m_PointSetList[2]); else m_PointSetDataInteractor->SetDataNode(nullptr); } void ClassificationSegmentation::OnButtonNoInteractionToggle(bool checked) { if(checked) m_PointSetDataInteractor->SetDataNode(nullptr); } void ClassificationSegmentation::CreateQtPartControl( QWidget *parent ) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi( parent ); m_CalculateFeatures = true; m_BlockManualSegmentation = false; m_BlockPostProcessing = false; QmitkDataStorageComboBox * cb_inputimage = new QmitkDataStorageComboBox(this->GetDataStorage(), mitk::TNodePredicateDataType::New()); QmitkDataStorageComboBox * cb_maskimage= new QmitkDataStorageComboBox(this->GetDataStorage(),mitk::TNodePredicateDataType::New()); QmitkDataStorageComboBox * cb_classifer= new QmitkDataStorageComboBox(this->GetDataStorage(),mitk::TNodePredicateDataType::New()); m_Controls.m_InputImageLayout->addWidget(cb_inputimage); m_Controls.m_MaskImageLayout->addWidget(cb_maskimage); m_Controls.m_ClassifierLayout->addWidget(cb_classifer); m_Controls.m_ParameterLayout->layout()->addWidget(new QLabel("Gauss Sigma")); m_GaussSlider = new ctkSliderWidget(); m_GaussSlider->setMinimum(0); m_GaussSlider->setMaximum(10); m_GaussSlider->setValue(1); m_Controls.m_ParameterLayout->layout()->addWidget(m_GaussSlider); m_Controls.m_ParameterLayout->layout()->addWidget(new QLabel("Hessian Sigma")); m_HessianSlider = new ctkSliderWidget(); m_HessianSlider->setMinimum(0); m_HessianSlider->setMaximum(10); m_HessianSlider->setValue(3); m_Controls.m_ParameterLayout->layout()->addWidget(m_HessianSlider); m_Controls.m_ParameterLayout->layout()->addWidget(new QLabel("Structure Tensor Inner and Outer Scale")); m_STInnerSlider = new ctkSliderWidget(); m_STInnerSlider->setMinimum(0); m_STInnerSlider->setMaximum(10); m_STInnerSlider->setValue(1.5); m_Controls.m_ParameterLayout->layout()->addWidget(m_STInnerSlider); m_STOuterSlider = new ctkSliderWidget(); m_STOuterSlider->setMinimum(0); m_STOuterSlider->setMaximum(10); m_STOuterSlider->setValue(3); m_Controls.m_ParameterLayout->layout()->addWidget(m_STOuterSlider); m_Controls.m_PostProcessingLayout->layout()->addWidget(new QLabel("Probability map smoothing")); m_GaussCSFSlider = new ctkSliderWidget; m_GaussCSFSlider->setMinimum(0); m_GaussCSFSlider->setMaximum(10); m_GaussCSFSlider->setValue(1.5); m_Controls.m_PostProcessingLayout->layout()->addWidget(m_GaussCSFSlider); m_GaussLESSlider = new ctkSliderWidget; m_GaussLESSlider->setMinimum(0); m_GaussLESSlider->setMaximum(10); m_GaussLESSlider->setValue(3); m_Controls.m_PostProcessingLayout->layout()->addWidget(m_GaussLESSlider); m_GaussBRASlider = new ctkSliderWidget; m_GaussBRASlider->setMinimum(0); m_GaussBRASlider->setMaximum(10); m_GaussBRASlider->setValue(0.5); m_Controls.m_PostProcessingLayout->layout()->addWidget(m_GaussBRASlider); m_Controls.m_PostProcessingLayout->layout()->addWidget(new QLabel("Probability map weighting")); m_WeightCSFSlider = new ctkSliderWidget; m_WeightCSFSlider->setMinimum(0.0); m_WeightCSFSlider->setMaximum(2.0); m_WeightCSFSlider->setValue(1.0); m_WeightCSFSlider->setSingleStep(0.1); m_Controls.m_PostProcessingLayout->layout()->addWidget(m_WeightCSFSlider); m_WeightLESSlider = new ctkSliderWidget; m_WeightLESSlider->setMinimum(0.0); m_WeightLESSlider->setMaximum(2.0); m_WeightLESSlider->setValue(1.0); m_WeightLESSlider->setSingleStep(0.1); m_Controls.m_PostProcessingLayout->layout()->addWidget(m_WeightLESSlider); m_WeightBRASlider = new ctkSliderWidget; m_WeightBRASlider->setMinimum(0.0); m_WeightBRASlider->setMaximum(2.0); m_WeightBRASlider->setValue(1.0); m_WeightBRASlider->setSingleStep(0.1); m_Controls.m_PostProcessingLayout->layout()->addWidget(m_WeightBRASlider); m_PointSetDataInteractor = mitk::PointSetDataInteractor::New(); m_PointSetDataInteractor->LoadStateMachine("PointSet.xml"); m_PointSetDataInteractor->SetEventConfig("PointSetConfig.xml"); connect( cb_inputimage, SIGNAL(OnSelectionChanged(const mitk::DataNode*)), this, SLOT(OnInitializeSession(const mitk::DataNode*))); connect( cb_maskimage, SIGNAL(OnSelectionChanged(const mitk::DataNode*)), this, SLOT(OnInitializeSession(const mitk::DataNode*))); connect( m_Controls.m_SavePointsButton, SIGNAL(clicked(bool)), this, SLOT(DoSavePointsAsMask())); connect( m_Controls.m_ButtonCSFToggle, SIGNAL(toggled(bool)), this, SLOT(OnButtonCSFToggle(bool))); connect( m_Controls.m_ButtonLESToggle, SIGNAL(toggled(bool)), this, SLOT(OnButtonLESToggle(bool))); connect( m_Controls.m_ButtonBRAToggle, SIGNAL(toggled(bool)), this, SLOT(OnButtonBRAToggle(bool))); connect( m_Controls.m_ButtonNoInteraction, SIGNAL(toggled(bool)), this, SLOT(OnButtonNoInteractionToggle(bool))); connect( &m_ManualSegmentationFutureWatcher, SIGNAL(finished()), this, SLOT(ManualSegmentationFinished())); connect( &m_PostProcessingFutureWatcher, SIGNAL(finished()), this, SLOT(PostProcessingFinished())); //connect( m_Controls.m_AddPointSets, SIGNAL(clicked()),this, SLOT(OnAddPointSets()) ); connect( m_GaussSlider, SIGNAL(valueChanged(double)), this, SLOT(OnFeatureSettingsChanged())); connect( m_HessianSlider, SIGNAL(valueChanged(double)), this, SLOT(OnFeatureSettingsChanged())); connect( m_STInnerSlider, SIGNAL(valueChanged(double)), this, SLOT(OnFeatureSettingsChanged())); connect( m_STOuterSlider, SIGNAL(valueChanged(double)), this, SLOT(OnFeatureSettingsChanged())); connect( m_GaussCSFSlider, SIGNAL(valueChanged(double)), this, SLOT(OnPostProcessingSettingsChanged())); connect( m_GaussLESSlider, SIGNAL(valueChanged(double)), this, SLOT(OnPostProcessingSettingsChanged())); connect( m_GaussBRASlider, SIGNAL(valueChanged(double)), this, SLOT(OnPostProcessingSettingsChanged())); connect( m_WeightCSFSlider, SIGNAL(valueChanged(double)), this, SLOT(OnPostProcessingSettingsChanged())); connect( m_WeightLESSlider, SIGNAL(valueChanged(double)), this, SLOT(OnPostProcessingSettingsChanged())); connect( m_WeightBRASlider, SIGNAL(valueChanged(double)), this, SLOT(OnPostProcessingSettingsChanged())); connect(m_Controls.m_DoAutomaticSecmentation, SIGNAL(clicked(bool)), this, SLOT(DoAutomSegmentation())); connect(m_Controls.m_AddForestToDataManager, SIGNAL(clicked(bool)), this, SLOT(OnAddForestToDataManager())); mitk::DataNode::Pointer pointSetNode = mitk::DataNode::New(); pointSetNode->SetName("CSF_Points."); pointSetNode->SetProperty("helper object", mitk::BoolProperty::New(true)); pointSetNode->SetProperty("layer", mitk::IntProperty::New(1024)); pointSetNode->SetColor(0,1.0,0); pointSetNode->SetDataInteractor(m_PointSetDataInteractor.GetPointer()); m_PointSetList.push_back(pointSetNode); GetDataStorage()->Add(pointSetNode); pointSetNode = mitk::DataNode::New(); pointSetNode->SetName("LES_Points."); pointSetNode->SetProperty("helper object", mitk::BoolProperty::New(true)); pointSetNode->SetProperty("layer", mitk::IntProperty::New(1024)); pointSetNode->SetColor(1.0,0,0); pointSetNode->SetDataInteractor(m_PointSetDataInteractor.GetPointer()); m_PointSetList.push_back(pointSetNode); GetDataStorage()->Add(pointSetNode); pointSetNode = mitk::DataNode::New(); pointSetNode->SetName("BRA_Points."); pointSetNode->SetProperty("helper object", mitk::BoolProperty::New(true)); pointSetNode->SetProperty("layer", mitk::IntProperty::New(1024)); pointSetNode->SetColor(0,0,1.0); pointSetNode->SetDataInteractor(m_PointSetDataInteractor.GetPointer()); m_PointSetList.push_back(pointSetNode); GetDataStorage()->Add(pointSetNode); m_Controls.m_PostProcessingLayout->hide(); m_Controls.m_ParameterLayout->hide(); } void ClassificationSegmentation::OnSelectionChanged( berry::IWorkbenchPart::Pointer /*source*/, const QList& nodes ) { // iterate all selected objects, adjust warning visibility foreach( mitk::DataNode::Pointer node, nodes ) { if( node.IsNotNull() && dynamic_cast(node->GetData()) ) { // m_Controls.labelWarning->setVisible( false ); // m_Controls.buttonPerformImageProcessing->setEnabled( true ); return; } } // m_Controls.labelWarning->setVisible( true ); // m_Controls.buttonPerformImageProcessing->setEnabled( false ); } void ClassificationSegmentation::OnAddForestToDataManager() { AddAsDataNode(m_TempClassifier.GetPointer(),"ManualSegmentation_Classifier"); } void ClassificationSegmentation::OnInitializeSession(const mitk::DataNode *) { MITK_WARN << "Data selection changed! New session initialized."; m_PointSetDataInteractor->SetDataNode(nullptr); /// CSF POINTSET mitk::PointSet::Pointer pntset = mitk::PointSet::New(); m_PointSetList[0]->SetData(pntset); itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &ClassificationSegmentation::ManualSegmentationTrigger); pntset->AddObserver( mitk::PointSetAddEvent(), command ); pntset->AddObserver( mitk::PointSetRemoveEvent(), command ); pntset->AddObserver( mitk::PointSetMoveEvent(), command ); /// LES POINTSET pntset = mitk::PointSet::New(); m_PointSetList[1]->SetData(pntset); command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &ClassificationSegmentation::ManualSegmentationTrigger); pntset->AddObserver( mitk::PointSetAddEvent(), command ); pntset->AddObserver( mitk::PointSetRemoveEvent(), command ); pntset->AddObserver( mitk::PointSetMoveEvent(), command ); /// BRA POINTSET pntset = mitk::PointSet::New(); m_PointSetList[2]->SetData(pntset); command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &ClassificationSegmentation::ManualSegmentationTrigger); pntset->AddObserver( mitk::PointSetAddEvent(), command ); pntset->AddObserver( mitk::PointSetRemoveEvent(), command ); pntset->AddObserver( mitk::PointSetMoveEvent(), command ); } void ClassificationSegmentation::DoSavePointsAsMask() { mitk::Image::Pointer sampled_image = nullptr; QmitkDataStorageComboBox * cb_image = dynamic_cast(m_Controls.m_InputImageLayout->itemAt(1)->widget()); mitk::Image::Pointer raw_image = dynamic_cast(cb_image->GetSelectedNode()->GetData()); int label = 1; for( auto datanode : m_PointSetList) { mitk::PointSet::Pointer point_set = dynamic_cast(datanode->GetData()); mitk::Image::Pointer temp_img; SampleClassMaskByPointSet(raw_image, point_set,temp_img); mitk::CLUtil::InsertLabel(temp_img,sampled_image,label++); QmitkIOUtil::Save(datanode->GetData(),QString("PointSet.mps")); } QmitkIOUtil::Save(sampled_image,QString("PointSetMask.nrrd")); } void ClassificationSegmentation::ProcessFeatureImages(const mitk::Image::Pointer & raw_image, const mitk::Image::Pointer & brain_mask) { typedef itk::Image DoubleImageType; typedef itk::Image ShortImageType; typedef itk::ConstNeighborhoodIterator NeighborhoodType; // Neighborhood iterator to access image typedef itk::Functor::NeighborhoodFirstOrderStatistics FunctorType; typedef itk::NeighborhoodFunctorImageFilter FOSFilerType; m_FeatureImageVector.clear(); // RAW m_FeatureImageVector.push_back(raw_image); // GAUSS mitk::Image::Pointer smoothed; mitk::CLUtil::GaussianFilter(raw_image,smoothed,m_GaussSlider->value()); m_FeatureImageVector.push_back(smoothed); // Calculate Probability maps (parameters used from literatur) // CSF mitk::Image::Pointer csf_prob = mitk::Image::New(); mitk::CLUtil::ProbabilityMap(smoothed,13.9, 8.3,csf_prob); m_FeatureImageVector.push_back(csf_prob); // Lesion mitk::Image::Pointer les_prob = mitk::Image::New(); mitk::CLUtil::ProbabilityMap(smoothed,59, 11.6,les_prob); m_FeatureImageVector.push_back(les_prob); // Barin (GM/WM) mitk::Image::Pointer brain_prob = mitk::Image::New(); mitk::CLUtil::ProbabilityMap(smoothed,32, 5.6,brain_prob); m_FeatureImageVector.push_back(brain_prob); std::vector FOS_sizes; FOS_sizes.push_back(1); DoubleImageType::Pointer input; ShortImageType::Pointer mask; mitk::CastToItkImage(smoothed, input); mitk::CastToItkImage(brain_mask, mask); for(unsigned int i = 0 ; i < FOS_sizes.size(); i++) { FOSFilerType::Pointer filter = FOSFilerType::New(); filter->SetNeighborhoodSize(FOS_sizes[i]); filter->SetInput(input); filter->SetMask(mask); filter->Update(); FOSFilerType::DataObjectPointerArray array = filter->GetOutputs(); for( unsigned int i = 0; i < FunctorType::OutputCount; i++) { mitk::Image::Pointer featureimage; mitk::CastToMitkImage(dynamic_cast(array[i].GetPointer()),featureimage); m_FeatureImageVector.push_back(featureimage); // AddImageAsDataNode(featureimage,FunctorType::GetFeatureName(i))->SetVisibility(show_nodes); } } { itk::HessianMatrixEigenvalueImageFilter< DoubleImageType >::Pointer filter = itk::HessianMatrixEigenvalueImageFilter< DoubleImageType >::New(); filter->SetInput(input); filter->SetImageMask(mask); filter->SetSigma(m_HessianSlider->value()); filter->Update(); mitk::Image::Pointer o1,o2,o3; mitk::CastToMitkImage(filter->GetOutput(0),o1); mitk::CastToMitkImage(filter->GetOutput(1),o2); mitk::CastToMitkImage(filter->GetOutput(2),o3); m_FeatureImageVector.push_back(o1); m_FeatureImageVector.push_back(o2); m_FeatureImageVector.push_back(o3); // AddImageAsDataNode(o1,"HE_1")->SetVisibility(show_nodes); // AddImageAsDataNode(o2,"HE_2")->SetVisibility(show_nodes); // AddImageAsDataNode(o3,"HE_3")->SetVisibility(show_nodes); } { itk::StructureTensorEigenvalueImageFilter< DoubleImageType >::Pointer filter = itk::StructureTensorEigenvalueImageFilter< DoubleImageType >::New(); filter->SetInput(input); filter->SetImageMask(mask); filter->SetInnerScale(m_STInnerSlider->value()); filter->SetOuterScale(m_STOuterSlider->value()); filter->Update(); mitk::Image::Pointer o1,o2,o3; mitk::CastToMitkImage(filter->GetOutput(0),o1); mitk::CastToMitkImage(filter->GetOutput(1),o2); mitk::CastToMitkImage(filter->GetOutput(2),o3); m_FeatureImageVector.push_back(o1); m_FeatureImageVector.push_back(o2); m_FeatureImageVector.push_back(o3); // AddImageAsDataNode(o1,"ST_1")->SetVisibility(show_nodes); // AddImageAsDataNode(o2,"ST_2")->SetVisibility(show_nodes); // AddImageAsDataNode(o3,"ST_3")->SetVisibility(show_nodes); } { itk::LineHistogramBasedMassImageFilter< DoubleImageType >::Pointer filter = itk::LineHistogramBasedMassImageFilter< DoubleImageType >::New(); filter->SetInput(input); filter->SetImageMask(mask); filter->Update(); mitk::Image::Pointer o1; mitk::CastToMitkImage(filter->GetOutput(0),o1); m_FeatureImageVector.push_back(o1); } } void ClassificationSegmentation::OnFeatureSettingsChanged() { MITK_INFO << "FeatureSettingsChanged"; m_CalculateFeatures = true; ManualSegmentationTrigger(); } void ClassificationSegmentation::OnPostProcessingSettingsChanged() { MITK_INFO << "PostProcessingSettigsChanged"; PostProcessingTrigger(); } void ClassificationSegmentation::DoAutomSegmentation() { QmitkDataStorageComboBox * cb_image = dynamic_cast(m_Controls.m_InputImageLayout->itemAt(1)->widget()); mitk::Image::Pointer raw_image = dynamic_cast(cb_image->GetSelectedNode()->GetData()); QmitkDataStorageComboBox * cb_maskimage = dynamic_cast(m_Controls.m_MaskImageLayout->itemAt(1)->widget()); QmitkDataStorageComboBox * cb_rf = dynamic_cast(m_Controls.m_ClassifierLayout->itemAt(1)->widget()); mitk::Image::Pointer mask_image = dynamic_cast(cb_maskimage->GetSelectedNode()->GetData()); mitk::VigraRandomForestClassifier::Pointer classifier = dynamic_cast(cb_rf->GetSelectedNode()->GetData()); if(m_CalculateFeatures) { ProcessFeatureImages(raw_image,mask_image); m_CalculateFeatures = false; } Eigen::MatrixXd X_test; unsigned int count_test = 0; mitk::CLUtil::CountVoxel(mask_image, count_test); X_test = Eigen::MatrixXd(count_test, m_FeatureImageVector.size()); unsigned int index = 0; for( const auto & image : m_FeatureImageVector) { X_test.col(index) = mitk::CLUtil::Transform(image,mask_image); ++index; } Eigen::MatrixXi Y_test = classifier->Predict(X_test); mitk::Image::Pointer result_mask = mitk::CLUtil::Transform(Y_test,mask_image); auto node = AddAsDataNode(result_mask.GetPointer(),"Autom-ResultMask"); auto lut = mitk::LookupTable::New(); lut->SetType(mitk::LookupTable::PET_20); auto * lut_prop = dynamic_cast(node->GetProperty("LookupTable")); lut_prop->SetLookupTable(lut); mitk::LevelWindow lw(1,3); node->SetLevelWindow(lw); node->SetOpacity(0.3); std::map perlabelvoxelcount; mitk::CLUtil::CountVoxel(result_mask, perlabelvoxelcount); double voxel_volume = result_mask->GetGeometry()->GetSpacing().GetVnlVector().inf_norm(); QString newtext; newtext += "Name\tVolume\tUnit\n"; for(const auto & pair: perlabelvoxelcount) { newtext += "Label" + QString::number(pair.first) + "\t" + QString::number(pair.second*voxel_volume* 0.001) + "\tml\n"; } m_Controls.m_ResultTextEdit->setText(newtext); } std::vector ClassificationSegmentation::ManualSegmentationCallback() { QmitkDataStorageComboBox * cb_image = dynamic_cast(m_Controls.m_InputImageLayout->itemAt(1)->widget()); QmitkDataStorageComboBox * cb_maskimage = dynamic_cast(m_Controls.m_MaskImageLayout->itemAt(1)->widget()); mitk::Image::Pointer raw_image = dynamic_cast(cb_image->GetSelectedNode()->GetData()); mitk::Image::Pointer mask_image = dynamic_cast(cb_maskimage->GetSelectedNode()->GetData()); if(raw_image.IsNull() || mask_image.IsNull()) { MITK_WARN << "Please provide input iamge and mask image"; //return nullptr; } if(m_CalculateFeatures) { ProcessFeatureImages(raw_image,mask_image); m_CalculateFeatures = false; } mitk::Image::Pointer sampled_image = nullptr; int label = 1; for( auto datanode : m_PointSetList) { mitk::PointSet::Pointer point_set = dynamic_cast(datanode->GetData()); mitk::Image::Pointer temp_img; SampleClassMaskByPointSet(raw_image, point_set,temp_img); mitk::CLUtil::InsertLabel(temp_img,sampled_image,label++); } m_TempClassifier = mitk::VigraRandomForestClassifier::New(); m_TempClassifier->SetTreeCount(50); m_TempClassifier->SetSamplesPerTree(0.66); Eigen::MatrixXd X_train; Eigen::MatrixXd X_test; unsigned int count_train = 0; unsigned int count_test = 0; mitk::CLUtil::CountVoxel(sampled_image, count_train); mitk::CLUtil::CountVoxel(mask_image, count_test); X_train = Eigen::MatrixXd(count_train, m_FeatureImageVector.size() ); X_test = Eigen::MatrixXd(count_test, m_FeatureImageVector.size()); unsigned int index = 0; for( const auto & image : m_FeatureImageVector) { X_train.col(index) = mitk::CLUtil::Transform(image,sampled_image); X_test.col(index) = mitk::CLUtil::Transform(image,mask_image); ++index; } Eigen::MatrixXi Y = mitk::CLUtil::Transform(sampled_image,sampled_image); m_TempClassifier->Train(X_train,Y); Eigen::MatrixXi Y_test = m_TempClassifier->Predict(X_test); Eigen::MatrixXd Yp_test = m_TempClassifier->GetPointWiseProbabilities(); // mitk::Image::Pointer result_mask = mitk::CLUtil::Transform(Y_test,mask_image); std::vector resultvector; resultvector.push_back( mitk::CLUtil::Transform(Y_test,mask_image) ); resultvector.push_back( mitk::CLUtil::Transform(Yp_test.col(0),mask_image) ); resultvector.push_back( mitk::CLUtil::Transform(Yp_test.col(1),mask_image) ); resultvector.push_back( mitk::CLUtil::Transform(Yp_test.col(2),mask_image) ); return resultvector; } void ClassificationSegmentation::ManualSegmentationFinished() { // Receive Future result m_ResultImageVector = m_ManualSegmentationFutureWatcher.result(); // Add result to Datastorage mitk::DataNode::Pointer node = AddAsDataNode(m_ResultImageVector[0].GetPointer(),"Manual-ResultMask"); mitk::LookupTable::Pointer lut = mitk::LookupTable::New(); lut->SetType(mitk::LookupTable::PET_20); mitk::LookupTableProperty * lut_prop = dynamic_cast(node->GetProperty("LookupTable")); lut_prop->SetLookupTable(lut); mitk::LevelWindow lw(1,3); node->SetLevelWindow(lw); node->SetOpacity(0.3); m_BlockManualSegmentation = false; this->PostProcessingTrigger(); // Update Volume data std::map perlabelvoxelcount; mitk::CLUtil::CountVoxel(m_ResultImageVector[0], perlabelvoxelcount); double voxel_volume = m_ResultImageVector[0]->GetGeometry()->GetSpacing().GetVnlVector().inf_norm(); QString newtext; newtext += "Name\tVolume\tUnit\n"; for(const auto & pair: perlabelvoxelcount) newtext += "Label" + QString::number(pair.first) + "\t" + QString::number(pair.second*voxel_volume* 0.001) + "\tml\n"; m_Controls.m_ResultTextEdit->setText(newtext); } void ClassificationSegmentation::ManualSegmentationTrigger() { unsigned int samplecounter = 0; for( auto datanode : m_PointSetList) { mitk::PointSet::Pointer point_set = dynamic_cast(datanode->GetData()); samplecounter += point_set->GetSize(); } if(samplecounter < 10) return; if(!m_BlockManualSegmentation){ // Start GUI Thread m_ManualSegmentationFutureWatcher.setFuture( QtConcurrent::run(this, &ClassificationSegmentation::ManualSegmentationCallback)); // on finish call OnManualSegmentationFinished(); m_BlockManualSegmentation = true; } } void ClassificationSegmentation::SampleClassMaskByPointSet(const mitk::Image::Pointer & ref_img, mitk::PointSet::Pointer & pointset, mitk::Image::Pointer & outimage) { outimage = ref_img->Clone(); itk::Image::Pointer itk_out; mitk::CastToItkImage(outimage,itk_out); itk_out->FillBuffer(0); typedef itk::ImageRegionIteratorWithIndex > IteratorType; IteratorType oit(itk_out, itk_out->GetLargestPossibleRegion()); for(int i = 0 ; i < pointset->GetSize(); ++i) { IteratorType::IndexType index; ref_img->GetGeometry()->WorldToIndex(pointset->GetPoint(i), index); oit.SetIndex(index); oit.Set(1); } mitk::CastToMitkImage(itk_out,outimage); } // old version //void ClassificationSegmentation::DoImageProcessing() //{ // QmitkDataStorageComboBox * cb_image = dynamic_cast(m_Controls.m_InputImageLayout->itemAt(1)->widget()); // QmitkDataStorageComboBox * cb_maskimage = dynamic_cast(m_Controls.m_MaskImageLayout->itemAt(1)->widget()); // QmitkDataStorageComboBox * cb_classifier = dynamic_cast(m_Controls.m_RandomForestLayout->itemAt(1)->widget()); // if (cb_image == nullptr || cb_classifier == nullptr || cb_maskimage == nullptr) // { // QMessageBox::information( nullptr, "Template", "Please load and select an image before starting image processing."); // return; // } // mitk::VigraRandomForestClassifier::Pointer rf = dynamic_cast(cb_classifier->GetSelectedNode()->GetData()); // mitk::Image::Pointer image = dynamic_cast(cb_image->GetSelectedNode()->GetData()); // mitk::Image::Pointer mask = dynamic_cast(cb_maskimage->GetSelectedNode()->GetData()); // unsigned int n_samples = 0; // mitk::CLUtil::CountVoxel(mask, n_samples); // unsigned int n_features = 13; // InputImage, PROBA_CSF, PROBA_LES, PROBA_BRA, FOS_MEAN,FOS_VARIANCE,FOS_SKEWNESS, FOS_KURTOSIS, FOS_MIN, FOS_MAX, HE_1, HE_2, HE_3 // Eigen::MatrixXd feature_matrix = Eigen::MatrixXd(n_samples, n_features); // MITK_INFO << "Remove voxels outside the mask"; // // mitk::CLUtil::LogicalAndImages(image,brain_mask,image); // feature_matrix.block(0,0,n_samples,1) = mitk::CLUtil::Transform(image,mask); // AddImageAsDataNode(image, "UI_InputImage"); // mitk::Image::Pointer csf_prob = mitk::Image::New(); // mitk::CLUtil::ProbabilityMap(image,13.9, 8.3,csf_prob); // feature_matrix.block(0,1,n_samples,1) = mitk::CLUtil::Transform(csf_prob,mask); // AddImageAsDataNode(csf_prob, "UI_CSFProb"); // mitk::Image::Pointer les_prob = mitk::Image::New(); // mitk::CLUtil::ProbabilityMap(image,59, 11.6,les_prob); // feature_matrix.block(0,2,n_samples,1) = mitk::CLUtil::Transform(les_prob,mask); // AddImageAsDataNode(les_prob, "UI_LESProb"); // mitk::Image::Pointer brain_prob = mitk::Image::New(); // mitk::CLUtil::ProbabilityMap(image,32, 5.6,brain_prob); // feature_matrix.block(0,3,n_samples,1) = mitk::CLUtil::Transform(brain_prob,mask); // AddImageAsDataNode(brain_prob, "UI_BRAProb"); // int n = 0; // std::vector res; // DoFirstOrderFeatureCalculation(image, mask, 1, res); // for( const auto & img: res) // { // feature_matrix.block(0,3+(++n),n_samples,1) = mitk::CLUtil::Transform(img,mask); // std::string name; // img->GetPropertyList()->GetStringProperty("name", name); // AddImageAsDataNode(img.GetPointer(), name ); // } // res.clear(); // DoHessianEigenvaluesFeatureCalculation(image, mask, 3.0, res); // for( const auto & img: res) // { // feature_matrix.block(0,3+(++n),n_samples,1) = mitk::CLUtil::Transform(img,mask); // std::string name; // img->GetPropertyList()->GetStringProperty("name", name); // AddImageAsDataNode(img.GetPointer(), name ); // } // MITK_INFO << "Start Prediction"; // rf->Predict(feature_matrix); // MITK_INFO << "End Prediction"; // mitk::Image::Pointer result_image = mitk::CLUtil::Transform(rf->GetLabels(),mask); // mitk::Image::Pointer proba1_image = mitk::CLUtil::Transform(rf->GetPointWiseProbabilities().col(0),mask); // mitk::Image::Pointer proba2_image = mitk::CLUtil::Transform(rf->GetPointWiseProbabilities().col(1),mask); // mitk::Image::Pointer proba3_image = mitk::CLUtil::Transform(rf->GetPointWiseProbabilities().col(2),mask); // AddImageAsDataNode(result_image, "ClassificationResult"); // AddImageAsDataNode(proba1_image, "CSF_Proba_CL"); // AddImageAsDataNode(proba2_image, "LES_Proba_CL"); // AddImageAsDataNode(proba3_image, "BRA_Proba_CL"); // PostProcessing(rf,mask); //} mitk::DataNode::Pointer ClassificationSegmentation::AddAsDataNode(const mitk::BaseData::Pointer & data_, const std::string & name ) { mitk::DataNode::Pointer node = nullptr; node = this->GetDataStorage()->GetNamedNode(name); if(node.IsNull()) { node = mitk::DataNode::New(); node->SetData(data_); node->SetName(name); this->GetDataStorage()->Add(node); }else{ if(dynamic_cast(node->GetData()) && dynamic_cast(data_.GetPointer())) { mitk::Image::Pointer target_image = dynamic_cast(node->GetData()); mitk::Image::Pointer source_image = dynamic_cast(data_.GetPointer()); mitk::ImageReadAccessor ra(source_image); target_image->SetImportVolume(const_cast(ra.GetData())); this->RequestRenderWindowUpdate(); } if(dynamic_cast(node->GetData()) && dynamic_cast(data_.GetPointer())) { node->SetData(data_); node->Modified(); } } return node; } void ClassificationSegmentation::PostProcessingTrigger() { if(m_ResultImageVector.empty() || m_ResultImageVector.size() < 4) { MITK_ERROR("PostProcessingCallback") << "Result image vector not initialized!"; return; } if(!m_BlockPostProcessing){ m_PostProcessingFutureWatcher.setFuture( QtConcurrent::run(this, &ClassificationSegmentation::PostProcessingCallback)); // on finish call OnManualSegmentationFinished(); m_BlockPostProcessing = true; } } void ClassificationSegmentation::PostProcessingFinished() { // Receive Future result m_PostProcessingImageVector = m_PostProcessingFutureWatcher.result(); // Add result to Datastorage mitk::DataNode::Pointer node = AddAsDataNode(m_PostProcessingImageVector[0].GetPointer(),"Manual-ResultMask-PostProcessing"); mitk::LookupTable::Pointer lut = mitk::LookupTable::New(); lut->SetType(mitk::LookupTable::PET_20); mitk::LookupTableProperty * lut_prop = dynamic_cast(node->GetProperty("LookupTable")); lut_prop->SetLookupTable(lut); mitk::LevelWindow lw(1,3); node->SetLevelWindow(lw); node->SetOpacity(0.3); m_BlockPostProcessing = false; } std::vector ClassificationSegmentation::PostProcessingCallback() { std::vector resultvector; mitk::Image::Pointer CSF_PMap = m_ResultImageVector[1]->Clone(); mitk::Image::Pointer LES_PMap = m_ResultImageVector[2]->Clone(); mitk::Image::Pointer BRA_PMap = m_ResultImageVector[3]->Clone(); mitk::Image::Pointer mask = m_ResultImageVector[0]->Clone(); MITK_INFO("PostProcessingCallback") << "ProbabilityMap merg strat ..."; { mitk::Image::Pointer resultmask = mask->Clone(); mitk::CLUtil::GaussianFilter(CSF_PMap, CSF_PMap, m_GaussCSFSlider->value()); mitk::CLUtil::GaussianFilter(LES_PMap, LES_PMap, m_GaussLESSlider->value()); mitk::CLUtil::GaussianFilter(BRA_PMap, BRA_PMap, m_GaussBRASlider->value()); itk::Image::Pointer itk_csf, itk_les, itk_bra; itk::Image::Pointer itk_result; mitk::CastToItkImage(CSF_PMap, itk_csf); mitk::CastToItkImage(LES_PMap, itk_les); mitk::CastToItkImage(BRA_PMap, itk_bra); mitk::CastToItkImage(resultmask, itk_result); itk::ImageRegionIterator > it_csf(itk_csf,itk_csf->GetLargestPossibleRegion()); itk::ImageRegionIterator > it_les(itk_les,itk_les->GetLargestPossibleRegion()); itk::ImageRegionIterator > it_bra(itk_bra,itk_bra->GetLargestPossibleRegion()); itk::ImageRegionIterator > it_res(itk_result,itk_result->GetLargestPossibleRegion()); while (!it_csf.IsAtEnd()) { double csf = it_csf.Value() * m_WeightCSFSlider->value(); double les = it_les.Value() * m_WeightLESSlider->value(); double bra = it_bra.Value() * m_WeightBRASlider->value(); if(csf > les && csf > bra) it_res.Set(1); if(les > csf && les > bra) it_res.Set(2); if(bra > les && bra > csf) it_res.Set(3); ++it_csf; ++it_les; ++it_bra; ++it_res; } mitk::CastToMitkImage(itk_result, resultmask); { std::map mergeinstruction ; mergeinstruction[1] = 1; mergeinstruction[2] = 1; mergeinstruction[3] = 1; mergeinstruction[4] = 1; mergeinstruction[5] = 1; mergeinstruction[6] = 1; // {{1,1},{2,1},{3,1},{4,1},{5,1},{6,1}}; mitk::Image::Pointer brain_mask = mask->Clone(); mitk::CLUtil::MergeLabels(brain_mask, mergeinstruction); mitk::CLUtil::ClosingBinary(brain_mask,brain_mask,2,mitk::CLUtil::All); mitk::CLUtil::LogicalAndImages(resultmask, brain_mask, resultmask); } MITK_INFO("PostProcessingCallback") << "ProbabilityMap merg end!"; resultvector.push_back(resultmask); } return resultvector; // { // MITK_INFO << "Morphological processing strat ..."; // mitk::Image::Pointer resultmask = mask->Clone(); // mitk::Image::Pointer csf_mask; // mitk::CLUtil::GrabLabel(resultmask, csf_mask, 1); // mitk::CLUtil::ClosingBinary(csf_mask,csf_mask,1,mitk::CLUtil::Axial); // mitk::CLUtil::ErodeBinary(csf_mask, csf_mask, 2, mitk::CLUtil::Axial); // mitk::CLUtil::DilateBinary(csf_mask, csf_mask, 1, mitk::CLUtil::Axial); // std::map merge_instruction = {{0,0},{1,3},{2,2},{3,3}}; // mitk::CLUtil::MergeLabels(resultmask, merge_instruction); // mitk::CLUtil::InsertLabel(resultmask, csf_mask, 1/*as csf mask*/); // add morpological manipulated csf_mask // // ------------ // mitk::Image::Pointer les_mask; // mitk::CLUtil::GrabLabel(resultmask, les_mask, 2); // mitk::CLUtil::ClosingBinary(les_mask,les_mask,1,mitk::CLUtil::Axial); // mitk::Image::Pointer les_cc_mask; unsigned int num = 0; // mitk::CLUtil::ConnectedComponentsImage(les_mask, mask, les_cc_mask, num); // std::map map; // mitk::CLUtil::CountVoxel(les_cc_mask,map); // unsigned int counter = 0; // while(map.size() > 2) // { // mitk::CLUtil::ErodeBinary(les_mask, les_mask, 1, mitk::CLUtil::Axial); // mitk::CLUtil::LogicalAndImages(les_cc_mask,les_mask,les_cc_mask); // map.clear(); // mitk::CLUtil::CountVoxel(les_cc_mask,map); // MITK_INFO("PostProcessing") << map.size(); // counter++; // } // while(counter != 0) // { // mitk::CLUtil::DilateBinary(les_mask, les_mask, 1, mitk::CLUtil::Axial); // counter--; // } // merge_instruction = {{0,0},{1,1},{2,3},{3,3}}; // mitk::CLUtil::MergeLabels(resultmask, merge_instruction); // mitk::CLUtil::InsertLabel(resultmask, les_mask, 2/*as les mask*/); // MITK_INFO << "Morphological processing end"; // // ------------ // mitk::CLUtil::LogicalAndImages(resultmask,mask,resultmask); // AddImageAsDataNode(resultmask,"SmoothedMaskMorphed"); // } } // mitk::Image::Pointer r= mitk::Image::New(),g= mitk::Image::New(),b = mitk::Image::New(); // unsigned int* m_ImageDimensions; // m_ImageDimensions = new unsigned int[3]; // m_ImageDimensions[0] = raw_image->GetDimensions()[0]; // m_ImageDimensions[1] = raw_image->GetDimensions()[1]; // m_ImageDimensions[2] = 1; // r->Initialize( mitk::MakePixelType() ,3,m_ImageDimensions); // g->Initialize( mitk::MakePixelType() ,3,m_ImageDimensions); // b->Initialize( mitk::MakePixelType() ,3,m_ImageDimensions); // mitk::ImageReadAccessor inputAccForRGB(raw_image, raw_image->GetSliceData(0,0,0)); // unsigned char* rbgSlice = (unsigned char*)inputAccForRGB.GetData(); // mitk::ImageReadAccessor inputAcc(r, r->GetSliceData(0,0,0)); // unsigned char* rData = (unsigned char*)inputAcc.GetData(); // mitk::ImageReadAccessor inputAcc2(g, g->GetSliceData(0,0,0)); // unsigned char* gData = (unsigned char*)inputAcc2.GetData(); // mitk::ImageReadAccessor inputAcc3(b, b->GetSliceData(0,0,0)); // unsigned char* // bData = (unsigned char*)inputAcc3.GetData(); // for(int i = 0; i < m_ImageDimensions[0] * m_ImageDimensions[1]*3; i+=3) // { // rData[i/3] = rbgSlice[i]; // gData[i/3] = rbgSlice[i+1]; // bData[i/3] = rbgSlice[i+2]; // } // m_FeatureImageVector.push_back(r); // m_FeatureImageVector.push_back(g); // m_FeatureImageVector.push_back(b); // AddImageAsDataNode(r,"r"); // AddImageAsDataNode(g,"g"); // AddImageAsDataNode(b,"b"); diff --git a/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/ClassificationSegmentation.h b/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/ClassificationSegmentation.h index 3fce603c7d..f13eb8f9ef 100644 --- a/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/ClassificationSegmentation.h +++ b/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/ClassificationSegmentation.h @@ -1,141 +1,137 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef ClassificationSegmentation_h #define ClassificationSegmentation_h #include #include #include "ui_ClassificationSegmentationControls.h" #include #include #include #include #include #include #include #include #include #include #include -#include "QmitkPointListViewWidget.h" #include #include /** \brief ClassificationSegmentation \warning This class is not yet documented. Use "git blame" and ask the author to provide basic documentation. \sa QmitkAbstractView \ingroup ${plugin_target}_internal */ - -//class QmitkPointListWidget; class ctkSliderWidget; class ClassificationSegmentation : public QmitkAbstractView { // this is needed for all Qt objects that should have a Qt meta-object // (everything that derives from QObject and wants to have signal/slots) Q_OBJECT public: static const std::string VIEW_ID; - QmitkPointListViewWidget * m_PointListWidget; std::vector m_PointSetList; bool m_CalculateFeatures; std::vector m_FeatureImageVector; std::vector m_ResultImageVector; std::vector m_PostProcessingImageVector; bool m_BlockManualSegmentation; QFutureWatcher> m_ManualSegmentationFutureWatcher; bool m_BlockPostProcessing; QFutureWatcher> m_PostProcessingFutureWatcher; protected slots: /// \brief Called when the user clicks the GUI button void DoAutomSegmentation(); void DoSavePointsAsMask(); void OnButtonCSFToggle(bool); void OnButtonLESToggle(bool); void OnButtonBRAToggle(bool); void OnButtonNoInteractionToggle(bool); void OnAddForestToDataManager(); void ManualSegmentationTrigger(); std::vector ManualSegmentationCallback(); void ManualSegmentationFinished(); void PostProcessingTrigger(); std::vector PostProcessingCallback(); void PostProcessingFinished(); void OnFeatureSettingsChanged(); void OnPostProcessingSettingsChanged(); void OnInitializeSession(const mitk::DataNode*); protected: typedef float MeasurementType; typedef itk::Statistics::Histogram< MeasurementType, itk::Statistics::DenseFrequencyContainer2 > HistogramType; // void RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart); // void RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart); void CreateQtPartControl(QWidget *parent) override; void SetFocus() override; mitk::DataNode::Pointer AddAsDataNode(const mitk::BaseData::Pointer & data_, const std::string & name ); void SampleClassMaskByPointSet(const mitk::Image::Pointer & ref_img, mitk::PointSet::Pointer & pointset, mitk::Image::Pointer & outimage); void ProcessFeatureImages(const mitk::Image::Pointer & raw_image, const mitk::Image::Pointer & mask_image); /// \brief called by QmitkFunctionality when DataManager's selection has changed void OnSelectionChanged( berry::IWorkbenchPart::Pointer source, const QList& nodes ) override; mitk::Image::Pointer CreateClassMaskByPointsetList(std::map a_args); Ui::ClassificationSegmentationControls m_Controls; // Feature settings ctkSliderWidget * m_GaussSlider; ctkSliderWidget * m_HessianSlider; ctkSliderWidget * m_STInnerSlider; ctkSliderWidget * m_STOuterSlider; ctkSliderWidget * m_GaussCSFSlider; ctkSliderWidget * m_GaussLESSlider; ctkSliderWidget * m_GaussBRASlider; ctkSliderWidget * m_WeightCSFSlider; ctkSliderWidget * m_WeightLESSlider; ctkSliderWidget * m_WeightBRASlider; mitk::PointSetDataInteractor::Pointer m_PointSetDataInteractor; mitk::VigraRandomForestClassifier::Pointer m_TempClassifier; }; #endif // ClassificationSegmentation_h