diff --git a/Plugins/org.mitk.gui.qt.bonesegmentationrework/files.cmake b/Plugins/org.mitk.gui.qt.bonesegmentationrework/files.cmake index bb1698267d..7aa4318e50 100644 --- a/Plugins/org.mitk.gui.qt.bonesegmentationrework/files.cmake +++ b/Plugins/org.mitk.gui.qt.bonesegmentationrework/files.cmake @@ -1,44 +1,46 @@ set(SRC_CPP_FILES ) set(INTERNAL_CPP_FILES org_mitk_gui_qt_bonesegmentationrework_Activator.cpp BoneSegmentationRework.cpp + BoneSegmentationWorker.cpp ImageTransferPython.cpp ) set(UI_FILES src/internal/BoneSegmentationReworkControls.ui ) set(MOC_H_FILES src/internal/org_mitk_gui_qt_bonesegmentationrework_Activator.h src/internal/BoneSegmentationRework.h + src/internal/BoneSegmentationWorker.h src/internal/ImageTransferPython.h ) # list of resource files which can be used by the plug-in # system without loading the plug-ins shared library, # for example the icon used in the menu and tabs for the # plug-in views in the workbench set(CACHED_RESOURCE_FILES resources/icon.svg plugin.xml ) # list of Qt .qrc files which contain additional resources # specific to this plugin set(QRC_FILES resources/boneseg.qrc ) set(CPP_FILES ) foreach(file ${SRC_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/${file}) endforeach(file ${SRC_CPP_FILES}) foreach(file ${INTERNAL_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/internal/${file}) endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/BoneSegmentationRework.cpp b/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/BoneSegmentationRework.cpp index 0e6ad0ac8f..b91c9e300a 100644 --- a/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/BoneSegmentationRework.cpp +++ b/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/BoneSegmentationRework.cpp @@ -1,253 +1,247 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // Blueberry #include #include // Qmitk #include "BoneSegmentationRework.h" // Qt #include #include #include // mitk image #include #include #include "internal/org_mitk_gui_qt_bonesegmentationrework_Activator.h" #include #include #include #include #include #include -#include +#include -#include"ImageTransferPython.h" +#include "ImageTransferPython.h" const std::string BoneSegmentationRework::VIEW_ID = "org.mitk.views.bonesegmentationrework"; BoneSegmentationRework::~BoneSegmentationRework() { + if (m_ProcessBoneSegmentationPython.isRunning()) + { + m_ProcessBoneSegmentationPython.quit(); + } if (Py_IsInitialized()) { - Py_Finalize(); + if (PyGILState_Check() == 1) + { + PyGILState_Release(m_GState); + } + try + { + //PyErr_Clear(); + Py_FinalizeEx(); + } + catch (...) + { + MITK_ERROR << "Something went wrong in finalization"; + } } } void BoneSegmentationRework::SetFocus() { m_Controls.buttonPerformImageProcessing->setFocus(); } void BoneSegmentationRework::CreateQtPartControl(QWidget *parent) { m_Controls.setupUi(parent); m_Controls.buttonPerformImageProcessing->setText("Start Bone Segmentation"); m_Controls.buttonPerformImageProcessing->setEnabled(false); m_Controls.m_ImageSelector->SetDataStorage(GetDataStorage()); m_Controls.m_ImageSelector->SetPredicate(GetImagePredicate()); m_Controls.labelLoadTrainedNet->setText("Please load a trained network!"); + m_Controls.m_CancelButton->setEnabled(false); // Segmentation Thread - connect(&m_SegmentationWatcher, SIGNAL(finished()), this, SLOT(DoBoneSegmentationProcessFinished())); + m_Worker = new BoneSegmentationWorker; + m_Worker->moveToThread(&m_ProcessBoneSegmentationPython); + connect(&m_ProcessBoneSegmentationPython, &QThread::finished, m_Worker, &QObject::deleteLater); + connect(this, &BoneSegmentationRework::Operate, m_Worker, &BoneSegmentationWorker::doWork); + connect( + m_Worker, &BoneSegmentationWorker::resultReady, this, &BoneSegmentationRework::DoBoneSegmentationProcessFinished); + connect(m_Worker, + &BoneSegmentationWorker::segmentationProcessCancelled, + this, + &BoneSegmentationRework::DoBoneSegmentationProcessFailed); // Connects connect( m_Controls.buttonPerformImageProcessing, &QPushButton::clicked, this, &BoneSegmentationRework::DoImageProcessing); connect(m_Controls.m_ButtonLoadTrainedNet, &QPushButton::clicked, this, &BoneSegmentationRework::DoLoadTrainedNet); connect(this->m_Controls.m_ImageSelector, static_cast(&QComboBox::currentIndexChanged), this, &BoneSegmentationRework::OnImageSelectorChanged); + connect(m_Controls.m_CancelButton, &QPushButton::clicked, this, &BoneSegmentationRework::OnCancel); } void BoneSegmentationRework::OnImageSelectorChanged() { auto selectedImageNode = m_Controls.m_ImageSelector->GetSelectedNode(); if (selectedImageNode != m_selectedImageNode) { m_selectedImageNode = selectedImageNode; if (m_selectedImageNode.IsNotNull()) { m_Controls.labelWarning->setVisible(false); if (m_TrainedNet != "") { m_Controls.buttonPerformImageProcessing->setEnabled(true); } return; } m_Controls.labelWarning->setText("Please select an image!"); m_Controls.labelWarning->setVisible(true); m_Controls.buttonPerformImageProcessing->setEnabled(false); } } void BoneSegmentationRework::DoLoadTrainedNet() { QString tempPath = QString::fromStdString(mitk::IOUtil::GetTempPathA()); QString pretrainedNetResourcesPath = QFileDialog::getOpenFileName(nullptr, tr("Open File"), tempPath, tr("Images (*.pth.tar)")); - m_TrainedNet = pretrainedNetResourcesPath.toStdString(); - + if (pretrainedNetResourcesPath != "") + { + m_TrainedNet = pretrainedNetResourcesPath.toStdString(); + } if (m_TrainedNet != "") { m_Controls.labelLoadTrainedNet->setText("Network loaded!"); if (m_selectedImageNode.IsNotNull()) { m_Controls.buttonPerformImageProcessing->setEnabled(true); } } } void BoneSegmentationRework::DoImageProcessing() { + m_Controls.m_CancelButton->setEnabled(true); m_Controls.labelWarning->setVisible(true); m_Controls.labelWarning->setText("This might take a while.\nDepending ob your machine up to 20 minutes or more."); m_Controls.buttonPerformImageProcessing->setEnabled(false); m_Controls.m_ButtonLoadTrainedNet->setEnabled(false); m_Controls.m_ImageSelector->setEnabled(false); this->DoStartBoneSegmentationProcess(); } void BoneSegmentationRework::DoStartBoneSegmentationProcess() { MITK_INFO << "[Start] DoStartBoneSegmentationProcess()"; - m_SegmentationFuture = QtConcurrent::run(this, &BoneSegmentationRework::DoRunPythonScript); - m_SegmentationWatcher.setFuture(m_SegmentationFuture); + m_ProcessBoneSegmentationPython.start(); + emit Operate(QString::fromStdString(m_PythonFileName), + QString::fromStdString(m_TrainedNet), + dynamic_cast(m_selectedImageNode->GetData())); MITK_INFO << "[END] DoStartBoneSegmentationProcess()"; } -void BoneSegmentationRework::DoRunPythonScript() -{ - QString pythonFileName(":/" + m_PythonFileName); - std::string fileName = mitk::StandardFileLocations::GetInstance()->FindFile( - "segment.py", "Plugins/org.mitk.gui.qt.bonesegmentationrework/resources"); - std::replace(fileName.begin(), fileName.end(), '\\', '/'); - MITK_INFO << "Python file to execute: " << fileName; - QString data; - QFile file(QString::fromUtf8(fileName.c_str())); - if (!file.open(QIODevice::ReadOnly)) - { - qDebug() << "filenot opened" << endl; - } - else - { - qDebug() << "file opened" << endl; - data = file.readAll(); - } - - file.close(); - std::string dataString = data.toStdString(); - if (!Py_IsInitialized()) - { - Py_Initialize(); - } - try - { - std::string pythonEnv = ImageTransferPython::SetUpPythonEnvironment(); - if (PyRun_SimpleString(pythonEnv.c_str()) == -1) - { - mitkThrow() << "An error occured while setting up the Python environment"; - } - // Set path for the trained network in python - std::string networkPath = "seg_load_network_path=\"" + m_TrainedNet + "\""; - if (PyRun_SimpleString(networkPath.c_str()) == -1) - { - mitkThrow() << "An error occured while setting the network path in Python"; - } - // Copy image for segmentation in Python - std::string copyImage = ImageTransferPython::CopyToPythonAsSimpleItkImage( - dynamic_cast(m_selectedImageNode->GetData()), "nrrd_image"); - if (PyRun_SimpleString(copyImage.c_str()) == -1) - { - mitkThrow() << "An error occured while copying the input image path to Python"; - } - // Execute the python script - if (PyRun_SimpleString(dataString.c_str()) == -1) - { - mitkThrow() << "An error occured while running the Python script"; - } - //Get the result of the segmentation - m_OutputImage = ImageTransferPython::CopySimpleItkImageFromPython("image_to_write"); - - m_SuccessfulProcessing = true; - } - catch (const mitk::Exception &e) - { - MITK_ERROR << e.GetDescription(); - m_SuccessfulProcessing = false; - return; - } -} - -void BoneSegmentationRework::DoLoadOutputImage() +void BoneSegmentationRework::DoLoadOutputImage(mitk::Image *outputImage) { try { mitk::DataNode::Pointer outputNode = mitk::DataNode::New(); outputNode->SetName("Bone_seg_" + m_selectedImageNode->GetName()); - outputNode->SetData(m_OutputImage); + outputNode->SetData(outputImage); GetDataStorage().GetPointer()->Add(outputNode, m_selectedImageNode); } catch (...) { MITK_WARN << "Error loading output file. Maybe it does not exist."; } } -void BoneSegmentationRework::DoBoneSegmentationProcessFinished() +void BoneSegmentationRework::ResetUI() { - if (!m_SuccessfulProcessing) - { - MITK_INFO << "Failed during calculation thread."; - QMessageBox::warning(nullptr, - "Error in segmentation", - "There was an error in the segmentation process. No resulting segmentation can be loaded."); - } - else - { - MITK_INFO << "Ended calculation thread."; - this->DoLoadOutputImage(); - } + m_Controls.m_CancelButton->setEnabled(false); m_Controls.buttonPerformImageProcessing->setText("Start Bone Segmentation"); m_Controls.buttonPerformImageProcessing->setEnabled(true); m_Controls.m_ButtonLoadTrainedNet->setEnabled(true); m_Controls.labelWarning->setVisible(false); m_Controls.m_ImageSelector->setEnabled(true); } +void BoneSegmentationRework::DoBoneSegmentationProcessFinished(mitk::Image *outputImage) +{ + mitk::Image::Pointer image = outputImage; + + if (image.IsNotNull()) + image->UnRegister(); + + MITK_INFO << "Ended calculation thread."; + this->DoLoadOutputImage(image); + + this->ResetUI(); +} + +void BoneSegmentationRework::DoBoneSegmentationProcessFailed() +{ + MITK_INFO << "Failed during calculation thread."; + QMessageBox::warning(nullptr, + "Error in segmentation", + "There was an error in the segmentation process. No resulting segmentation can be loaded."); + this->ResetUI(); +} + +void BoneSegmentationRework::OnCancel() +{ + if (m_ProcessBoneSegmentationPython.isRunning()) + { + m_ProcessBoneSegmentationPython.terminate(); + m_Controls.m_CancelButton->setEnabled(false); + m_Controls.buttonPerformImageProcessing->setText("Start Bone Segmentation"); + m_Controls.buttonPerformImageProcessing->setEnabled(true); + m_Controls.m_ButtonLoadTrainedNet->setEnabled(true); + m_Controls.labelWarning->setVisible(false); + m_Controls.m_ImageSelector->setEnabled(true); + } +} + mitk::NodePredicateBase::Pointer BoneSegmentationRework::GetImagePredicate() { auto isImage = mitk::NodePredicateDataType::New("Image"); auto hasBinaryProperty = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); auto isNotBinary = mitk::NodePredicateNot::New(hasBinaryProperty); auto isNotBinaryImage = mitk::NodePredicateAnd::New(isImage, isNotBinary); auto hasHelperObjectProperty = mitk::NodePredicateProperty::New("helper object", nullptr); auto isNoHelperObject = mitk::NodePredicateNot::New(hasHelperObjectProperty); auto isNoHelperObjectPredicate = isNoHelperObject.GetPointer(); auto isImageForImageStatistics = mitk::NodePredicateAnd::New(isNotBinaryImage, isNoHelperObjectPredicate); return isImageForImageStatistics.GetPointer(); } diff --git a/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/BoneSegmentationRework.h b/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/BoneSegmentationRework.h index 9c4b2a1c30..f8964b8b61 100644 --- a/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/BoneSegmentationRework.h +++ b/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/BoneSegmentationRework.h @@ -1,85 +1,88 @@ /*============================================================================ 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 BoneSegmentationRework_h #define BoneSegmentationRework_h #include #include +#include "BoneSegmentationWorker.h" #include "ui_BoneSegmentationReworkControls.h" #include #include #include #include #include #include #include #include #include +#include #include /** \brief BoneSegmentationRework \warning The BoneSegmentationRework plugin provides an automatic bone segmentation for CT images based on deep learning. At this moment, only CT images can be segmented. The segmentation is done on 2D axial slices. The network was trained using data from the multiple myeloma project. It was trained on a very limited amount of training data and needs to be tested in "real world scenarios". \sa QmitkAbstractView \ingroup ${plugin_target}_internal */ class BoneSegmentationRework : 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; - void DoRunPythonScript(); + void ResetUI(); ~BoneSegmentationRework(); signals: - void Operate(QString); + void Operate(QString pythonFileName, QString trainedNet, mitk::Image *inputImage); protected slots: /// \brief Called when the user clicks the GUI button void OnImageSelectorChanged(); void DoImageProcessing(); void DoStartBoneSegmentationProcess(); - void DoBoneSegmentationProcessFinished(); + void DoBoneSegmentationProcessFinished(mitk::Image* outputImage); + void DoBoneSegmentationProcessFailed(); void DoLoadTrainedNet(); - void DoLoadOutputImage(); + void DoLoadOutputImage(mitk::Image* outputImage); + void OnCancel(); protected: virtual void CreateQtPartControl(QWidget *parent) override; virtual void SetFocus() override; // Predicate helper mitk::NodePredicateBase::Pointer GetImagePredicate(); Ui::BoneSegmentationReworkControls m_Controls; mitk::DataNode::Pointer m_selectedImageNode = nullptr; std::string m_TrainedNet; - const QString m_PythonFileName="segment.py"; - QFutureWatcher m_SegmentationWatcher; - QFuture m_SegmentationFuture; - bool m_SuccessfulProcessing; - mitk::Image::Pointer m_OutputImage; + const std::string m_PythonFileName="segment.py"; + QThread m_ProcessBoneSegmentationPython; + PyGILState_STATE m_GState; + BoneSegmentationWorker* m_Worker; }; #endif // BoneSegmentationRework_h diff --git a/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/BoneSegmentationReworkControls.ui b/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/BoneSegmentationReworkControls.ui index 423938e46a..f0562b201d 100644 --- a/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/BoneSegmentationReworkControls.ui +++ b/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/BoneSegmentationReworkControls.ui @@ -1,88 +1,95 @@ BoneSegmentationReworkControls 0 0 222 - 161 + 186 0 0 QmitkTemplate Please load a trained network! Load trained network QLabel { color: rgb(255, 0, 0) } Please select an image! Do image processing Do Something + + + + Cancel + + + Qt::Vertical QSizePolicy::Expanding 20 220 QmitkDataStorageComboBoxWithSelectNone QComboBox
QmitkDataStorageComboBoxWithSelectNone.h
diff --git a/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/BoneSegmentationWorker.cpp b/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/BoneSegmentationWorker.cpp new file mode 100644 index 0000000000..9895a89a5c --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/BoneSegmentationWorker.cpp @@ -0,0 +1,86 @@ +#include "BoneSegmentationWorker.h" +#include "ImageTransferPython.h" +#include "internal/org_mitk_gui_qt_bonesegmentationrework_Activator.h" +#include +#include +#include +#include +#include +#include +#include + +void BoneSegmentationWorker::doWork(QString pythonFileName, QString trainedNet, mitk::Image *pInputImage) +{ + MITK_INFO << pInputImage->GetReferenceCount(); + mitk::Image::Pointer inputImage = pInputImage; + MITK_INFO << pInputImage->GetReferenceCount(); + + std::string fileName = mitk::StandardFileLocations::GetInstance()->FindFile( + pythonFileName.toStdString().c_str(), "Plugins/org.mitk.gui.qt.bonesegmentationrework/resources"); + std::replace(fileName.begin(), fileName.end(), '\\', '/'); + MITK_INFO << "Python file to execute: " << fileName; + QString data; + QFile file(QString::fromUtf8(fileName.c_str())); + if (!file.open(QIODevice::ReadOnly)) + { + qDebug() << "filenot opened" << endl; + } + else + { + qDebug() << "file opened" << endl; + data = file.readAll(); + } + + file.close(); + std::string dataString = data.toStdString(); + if (!Py_IsInitialized()) + { + Py_Initialize(); + } + PyGILState_STATE gState = PyGILState_Ensure(); + try + { + std::string pythonEnv = ImageTransferPython::SetUpPythonEnvironment(); + if (PyRun_SimpleString(pythonEnv.c_str()) == -1) + { + mitkThrow() << "An error occured while setting up the Python environment"; + } + // Set path for the trained network in python + std::string networkPath = "seg_load_network_path=\"" + trainedNet.toStdString() + "\""; + if (PyRun_SimpleString(networkPath.c_str()) == -1) + { + mitkThrow() << "An error occured while setting the network path in Python"; + } + // Copy image for segmentation in Python + std::string copyImage = ImageTransferPython::CopyToPythonAsSimpleItkImage(inputImage, "nrrd_image"); + if (PyRun_SimpleString(copyImage.c_str()) == -1) + { + mitkThrow() << "An error occured while copying the input image path to Python"; + } + // Execute the python script + if (PyRun_SimpleString(dataString.c_str()) == -1) + { + mitkThrow() << "An error occured while running the Python script"; + } + // Get the result of the segmentation + auto outputImage = ImageTransferPython::CopySimpleItkImageFromPython("image_to_write"); + PyGILState_Release(gState); + + outputImage->Register(); + emit resultReady(outputImage); + } + catch (const mitk::Exception &e) + { + MITK_ERROR << e.GetDescription(); + PyGILState_Release(gState); + emit segmentationProcessCancelled(); + return; + } + catch (...) + { + MITK_ERROR << "other error"; + PyGILState_Release(gState); + emit segmentationProcessCancelled(); + return; + } +} \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/BoneSegmentationWorker.h b/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/BoneSegmentationWorker.h new file mode 100644 index 0000000000..60eb70aa77 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/BoneSegmentationWorker.h @@ -0,0 +1,30 @@ +/*============================================================================ + +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 BoneSegmentationWorker_h +#define BoneSegmentationWorker_h + +#include +#include + +class BoneSegmentationWorker : public QObject +{ + Q_OBJECT +public slots: + void doWork(QString pythonFileName, QString trainedNet, mitk::Image* inputImage); + +signals: + void resultReady(mitk::Image* outputImage); + void segmentationProcessCancelled(); +}; + +#endif \ No newline at end of file