diff --git a/Modules/Segmentation/Interactions/mitknnUnetTool.cpp b/Modules/Segmentation/Interactions/mitknnUnetTool.cpp index 97d9f9df2f..65de25e8cf 100644 --- a/Modules/Segmentation/Interactions/mitknnUnetTool.cpp +++ b/Modules/Segmentation/Interactions/mitknnUnetTool.cpp @@ -1,322 +1,334 @@ /*============================================================================ 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 "mitknnUnetTool.h" #include "mitkIOUtil.h" #include "mitkProcessExecutor.h" #include #include #include #include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, nnUNetTool, "nnUNet tool"); } mitk::nnUNetTool::nnUNetTool() { this->SetMitkTempDir(IOUtil::CreateTemporaryDirectory("mitk-XXXXXX")); } mitk::nnUNetTool::~nnUNetTool() { itksys::SystemTools::RemoveADirectory(this->GetMitkTempDir()); } void mitk::nnUNetTool::Activated() { Superclass::Activated(); - m_InputOutputPair = std::make_pair(nullptr, nullptr); } void mitk::nnUNetTool::UpdateCleanUp() { // This overriden method is intentionally left out for setting later upon demand // in the `RenderOutputBuffer` method. } void mitk::nnUNetTool::RenderOutputBuffer() { - if (this->m_OutputBuffer != nullptr) + if (m_OutputBuffer != nullptr) { - Superclass::SetNodeProperties(this->m_OutputBuffer); - this->ClearOutputBuffer(); + Superclass::SetNodeProperties(m_OutputBuffer); try { if (nullptr != this->GetPreviewSegmentationNode()) { this->GetPreviewSegmentationNode()->SetVisibility(!this->GetSelectedLabels().empty()); } if (this->GetSelectedLabels().empty()) { this->ResetPreviewNode(); } } catch (const mitk::Exception &e) { MITK_INFO << e.GetDescription(); } } } void mitk::nnUNetTool::SetNodeProperties(LabelSetImage::Pointer segmentation) { // This overriden method doesn't set node properties. Intentionally left out for setting later upon demand // in the `RenderOutputBuffer` method. - this->m_OutputBuffer = segmentation; + m_OutputBuffer = segmentation; } mitk::LabelSetImage::Pointer mitk::nnUNetTool::GetOutputBuffer() { - return this->m_OutputBuffer; + return m_OutputBuffer; } void mitk::nnUNetTool::ClearOutputBuffer() { - this->m_OutputBuffer = nullptr; + m_OutputBuffer = nullptr; } us::ModuleResource mitk::nnUNetTool::GetIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("AI_48x48.png"); return resource; } const char **mitk::nnUNetTool::GetXPM() const { return nullptr; } const char *mitk::nnUNetTool::GetName() const { return "nnUNet"; } mitk::DataStorage *mitk::nnUNetTool::GetDataStorage() { return this->GetToolManager()->GetDataStorage(); } mitk::DataNode *mitk::nnUNetTool::GetRefNode() { return this->GetToolManager()->GetReferenceData(0); } namespace { void onPythonProcessEvent(itk::Object * /*pCaller*/, const itk::EventObject &e, void *) { std::string testCOUT; std::string testCERR; const auto *pEvent = dynamic_cast(&e); if (pEvent) { testCOUT = testCOUT + pEvent->GetOutput(); MITK_INFO << testCOUT; } const auto *pErrEvent = dynamic_cast(&e); if (pErrEvent) { testCERR = testCERR + pErrEvent->GetOutput(); MITK_ERROR << testCERR; } } } // namespace mitk::LabelSetImage::Pointer mitk::nnUNetTool::ComputeMLPreview(const Image *inputAtTimeStep, TimeStepType /*timeStep*/) { - if (m_InputOutputPair.first == inputAtTimeStep) + if (m_InputBuffer == inputAtTimeStep) { - return m_InputOutputPair.second; + return m_OutputBuffer; } std::string inDir, outDir, inputImagePath, outputImagePath, scriptPath; - std::string templateFilename = "XXXXXX_000_0000.nii.gz"; ProcessExecutor::Pointer spExec = ProcessExecutor::New(); itk::CStyleCommand::Pointer spCommand = itk::CStyleCommand::New(); spCommand->SetCallback(&onPythonProcessEvent); spExec->AddObserver(ExternalProcessOutputEvent(), spCommand); ProcessExecutor::ArgumentListType args; inDir = IOUtil::CreateTemporaryDirectory("nnunet-in-XXXXXX", this->GetMitkTempDir()); std::ofstream tmpStream; - inputImagePath = IOUtil::CreateTemporaryFile(tmpStream, templateFilename, inDir + IOUtil::GetDirectorySeparator()); + inputImagePath = IOUtil::CreateTemporaryFile(tmpStream, m_TEMPLATE_FILENAME, inDir + IOUtil::GetDirectorySeparator()); tmpStream.close(); std::size_t found = inputImagePath.find_last_of(IOUtil::GetDirectorySeparator()); std::string fileName = inputImagePath.substr(found + 1); std::string token = fileName.substr(0, fileName.find("_")); if (this->GetNoPip()) { scriptPath = this->GetnnUNetDirectory() + IOUtil::GetDirectorySeparator() + "nnunet" + IOUtil::GetDirectorySeparator() + "inference" + IOUtil::GetDirectorySeparator() + "predict_simple.py"; } try { - IOUtil::Save(inputAtTimeStep, inputImagePath); - if (this->GetMultiModal()) { - for (size_t i = 0; i < this->m_OtherModalPaths.size(); ++i) + const std::string fileFormat(".nii.gz"); + const std::string fileNamePart("_000_000"); + std::string outModalFile; + size_t len = inDir.length() + 1 + token.length() + fileNamePart.length() + 1 + fileFormat.length(); + outModalFile.reserve(len); // The 1(s) indicates a directory separator char and an underscore. + for (size_t i = 0; i < m_OtherModalPaths.size(); ++i) { - mitk::Image::ConstPointer modalImage = this->m_OtherModalPaths[i]; - std::string outModalFile = - inDir + IOUtil::GetDirectorySeparator() + token + "_000_000" + std::to_string(i + 1) + ".nii.gz"; + mitk::Image::ConstPointer modalImage = m_OtherModalPaths[i]; + outModalFile.append(inDir); + outModalFile.push_back(IOUtil::GetDirectorySeparator()); + outModalFile.append(token); + outModalFile.append(fileNamePart); + outModalFile.append(std::to_string(i)); + outModalFile.append(fileFormat); IOUtil::Save(modalImage.GetPointer(), outModalFile); + outModalFile.clear(); } } + else + { + IOUtil::Save(inputAtTimeStep, inputImagePath); + } } catch (const mitk::Exception &e) { /* Can't throw mitk exception to the caller. Refer: T28691 */ MITK_ERROR << e.GetDescription(); return nullptr; } // Code calls external process std::string command = "nnUNet_predict"; if (this->GetNoPip()) { #ifdef _WIN32 command = "python"; #else command = "python3"; #endif } for (ModelParams &modelparam : m_ParamQ) { outDir = IOUtil::CreateTemporaryDirectory("nnunet-out-XXXXXX", this->GetMitkTempDir()); outputImagePath = outDir + IOUtil::GetDirectorySeparator() + token + "_000.nii.gz"; modelparam.outputDir = outDir; args.clear(); if (this->GetNoPip()) { args.push_back(scriptPath); } args.push_back("-i"); args.push_back(inDir); args.push_back("-o"); args.push_back(outDir); args.push_back("-t"); args.push_back(modelparam.task); if (modelparam.model.find("cascade") != std::string::npos) { args.push_back("-ctr"); } else { args.push_back("-tr"); } args.push_back(modelparam.trainer); args.push_back("-m"); args.push_back(modelparam.model); args.push_back("-p"); args.push_back(modelparam.planId); if (!modelparam.folds.empty()) { args.push_back("-f"); for (auto fold : modelparam.folds) { args.push_back(fold); } } args.push_back("--num_threads_nifti_save"); args.push_back("1"); // fixing to 1 if (!this->GetMirror()) { args.push_back("--disable_tta"); } if (!this->GetMixedPrecision()) { args.push_back("--disable_mixed_precision"); } - if (this->GetEnsemble() && !this->GetPostProcessingJsonDirectory().empty()) + if (this->GetEnsemble()) { args.push_back("--save_npz"); } try { std::string resultsFolderEnv = "RESULTS_FOLDER=" + this->GetModelDirectory(); itksys::SystemTools::PutEnv(resultsFolderEnv.c_str()); std::string cudaEnv = "CUDA_VISIBLE_DEVICES=" + std::to_string(this->GetGpuId()); itksys::SystemTools::PutEnv(cudaEnv.c_str()); spExec->Execute(this->GetPythonPath(), command, args); } catch (const mitk::Exception &e) { /* Can't throw mitk exception to the caller. Refer: T28691 */ MITK_ERROR << e.GetDescription(); return nullptr; } } if (this->GetEnsemble() && !this->GetPostProcessingJsonDirectory().empty()) { args.clear(); command = "nnUNet_ensemble"; outDir = IOUtil::CreateTemporaryDirectory("nnunet-ensemble-out-XXXXXX", this->GetMitkTempDir()); outputImagePath = outDir + IOUtil::GetDirectorySeparator() + token + "_000.nii.gz"; args.push_back("-f"); for (ModelParams &modelparam : m_ParamQ) { args.push_back(modelparam.outputDir); } args.push_back("-o"); args.push_back(outDir); - args.push_back("-pp"); - args.push_back(this->GetPostProcessingJsonDirectory()); + if (!this->GetPostProcessingJsonDirectory().empty()) + { + args.push_back("-pp"); + args.push_back(this->GetPostProcessingJsonDirectory()); + } spExec->Execute(this->GetPythonPath(), command, args); } try { LabelSetImage::Pointer resultImage = LabelSetImage::New(); Image::Pointer outputImage = IOUtil::Load(outputImagePath); resultImage->InitializeByLabeledImage(outputImage); resultImage->SetGeometry(inputAtTimeStep->GetGeometry()); - m_InputOutputPair = std::make_pair(inputAtTimeStep, resultImage); + m_InputBuffer = inputAtTimeStep; return resultImage; } catch (const mitk::Exception &e) { /* Can't throw mitk exception to the caller. Refer: T28691 */ MITK_ERROR << e.GetDescription(); return nullptr; } } diff --git a/Modules/Segmentation/Interactions/mitknnUnetTool.h b/Modules/Segmentation/Interactions/mitknnUnetTool.h index 9e5a31cac3..9185b0f0a7 100644 --- a/Modules/Segmentation/Interactions/mitknnUnetTool.h +++ b/Modules/Segmentation/Interactions/mitknnUnetTool.h @@ -1,196 +1,214 @@ /*============================================================================ 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 mitknnUnetTool_h_Included #define mitknnUnetTool_h_Included #include "mitkAutoMLSegmentationWithPreviewTool.h" #include "mitkCommon.h" #include "mitkToolManager.h" #include #include #include +#include namespace us { class ModuleResource; } namespace mitk { /** * @brief nnUNet parameter request object holding all model parameters for input. * Also holds output temporary directory path. */ struct ModelParams { std::string task; std::vector folds; std::string model; std::string trainer; std::string planId; std::string outputDir; + std::string inputName; + std::string timeStamp; + + size_t generateHash() const + { + std::string toHash; + std::string foldsConcatenated = std::accumulate(folds.begin(), folds.end(), std::string("")); + toHash += this->task; + toHash += this->model; + toHash += this->inputName; + toHash += foldsConcatenated; + toHash += this->timeStamp; + size_t hashVal = std::hash{}(toHash); + return hashVal; + } }; /** \brief nnUNet segmentation tool. \ingroup Interaction \ingroup ToolManagerEtAl \warning Only to be instantiated by mitk::ToolManager. */ class MITKSEGMENTATION_EXPORT nnUNetTool : public AutoMLSegmentationWithPreviewTool { public: mitkClassMacro(nnUNetTool, AutoMLSegmentationWithPreviewTool); itkFactorylessNewMacro(Self); itkCloneMacro(Self); const char **GetXPM() const override; const char *GetName() const override; us::ModuleResource GetIconResource() const override; void Activated() override; itkSetMacro(nnUNetDirectory, std::string); itkGetConstMacro(nnUNetDirectory, std::string); itkSetMacro(ModelDirectory, std::string); itkGetConstMacro(ModelDirectory, std::string); itkSetMacro(PythonPath, std::string); itkGetConstMacro(PythonPath, std::string); itkSetMacro(MitkTempDir, std::string); itkGetConstMacro(MitkTempDir, std::string); itkSetMacro(PostProcessingJsonDirectory, std::string); itkGetConstMacro(PostProcessingJsonDirectory, std::string); itkSetMacro(MixedPrecision, bool); itkGetConstMacro(MixedPrecision, bool); itkBooleanMacro(MixedPrecision); itkSetMacro(Mirror, bool); itkGetConstMacro(Mirror, bool); itkBooleanMacro(Mirror); itkSetMacro(MultiModal, bool); itkGetConstMacro(MultiModal, bool); itkBooleanMacro(MultiModal); itkSetMacro(NoPip, bool); itkGetConstMacro(NoPip, bool); itkBooleanMacro(NoPip); itkSetMacro(Ensemble, bool); itkGetConstMacro(Ensemble, bool); itkBooleanMacro(Ensemble); itkSetMacro(Predict, bool); itkGetConstMacro(Predict, bool); itkBooleanMacro(Predict); itkSetMacro(GpuId, unsigned int); itkGetConstMacro(GpuId, unsigned int); /** * @brief vector of ModelParams. * Size > 1 only for ensemble prediction. */ std::vector m_ParamQ; /** * @brief Holds paths to other input image modalities. * */ std::vector m_OtherModalPaths; - std::pair m_InputOutputPair; + mitk::Image::ConstPointer m_InputBuffer; /** * @brief Renders the output LabelSetImage. * To called in the main thread. */ void RenderOutputBuffer(); /** * @brief Get the Output Buffer object * * @return LabelSetImage::Pointer */ LabelSetImage::Pointer GetOutputBuffer(); /** * @brief Sets the outputBuffer to nullptr * */ void ClearOutputBuffer(); /** * @brief Returns the DataStorage from the ToolManager */ mitk::DataStorage *GetDataStorage(); mitk::DataNode *GetRefNode(); + void SetNodeProperties(LabelSetImage::Pointer) override; + protected: /** * @brief Construct a new nnUNet Tool object and temp directory. * */ nnUNetTool(); /** * @brief Destroy the nnUNet Tool object and deletes the temp directory. * */ ~nnUNetTool(); /** * @brief Overriden method from the tool manager to execute the segmentation * Implementation: * 1. Saves the inputAtTimeStep in a temporary directory. * 2. Copies other modalities, renames and saves in the temporary directory, if required. * 3. Sets RESULTS_FOLDER and CUDA_VISIBLE_DEVICES variables in the environment. * 3. Iterates through the parameter queue (m_ParamQ) and executes "nnUNet_predict" command with the parameters * 4. Expects an output image to be saved in the temporary directory by the python proces. Loads it as * LabelSetImage and returns. * * @param inputAtTimeStep * @param timeStep * @return LabelSetImage::Pointer */ LabelSetImage::Pointer ComputeMLPreview(const Image *inputAtTimeStep, TimeStepType timeStep) override; void UpdateCleanUp() override; - void SetNodeProperties(LabelSetImage::Pointer) override; private: std::string m_MitkTempDir; std::string m_nnUNetDirectory; std::string m_ModelDirectory; std::string m_PythonPath; std::string m_PostProcessingJsonDirectory; // bool m_UseGPU; kept for future // bool m_AllInGPU; bool m_MixedPrecision; bool m_Mirror; bool m_NoPip; bool m_MultiModal; bool m_Ensemble = false; bool m_Predict; LabelSetImage::Pointer m_OutputBuffer; unsigned int m_GpuId; + const std::string m_TEMPLATE_FILENAME = "XXXXXX_000_0000.nii.gz"; }; } // namespace mitk #endif diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetEnsembleLayout.h b/Modules/SegmentationUI/Qmitk/QmitknnUNetEnsembleLayout.h index 881cb52567..9a5f009830 100644 --- a/Modules/SegmentationUI/Qmitk/QmitknnUNetEnsembleLayout.h +++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetEnsembleLayout.h @@ -1,86 +1,91 @@ /*============================================================================ 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.s ============================================================================*/ #ifndef QmitknnUNetEnsembleLayout_h_Included #define QmitknnUNetEnsembleLayout_h_Included #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE class MITKSEGMENTATIONUI_EXPORT QmitknnUNetTaskParamsUITemplate { public: QLabel* trainerLabel; ctkComboBox* trainerBox; QLabel* plannerLabel; ctkComboBox* plannerBox; QLabel* foldLabel; ctkCheckableComboBox* foldBox; QLabel* modelLabel; ctkComboBox* modelBox; QWidget* parent; QmitknnUNetTaskParamsUITemplate(QWidget* inputGroupBox_1) { this->parent = inputGroupBox_1; QVBoxLayout* verticalLayout_x = new QVBoxLayout(inputGroupBox_1); verticalLayout_x->setObjectName(QString::fromUtf8("verticalLayout_x")); QGridLayout* g_x = new QGridLayout(); #ifndef Q_OS_MAC g_x->setSpacing(6); #endif #ifndef Q_OS_MAC g_x->setContentsMargins(0, 0, 0, 0); #endif g_x->setObjectName(QString::fromUtf8("g_2")); modelLabel = new QLabel("Configuration", inputGroupBox_1); g_x->addWidget(modelLabel, 0, 0, 1, 1); trainerLabel = new QLabel("Trainer", inputGroupBox_1); g_x->addWidget(trainerLabel, 0, 1, 1, 1); modelBox = new ctkComboBox(inputGroupBox_1); modelBox->setObjectName(QString::fromUtf8("modelBox_1")); g_x->addWidget(modelBox, 1, 0, 1, 1); trainerBox = new ctkComboBox(inputGroupBox_1); trainerBox->setObjectName(QString::fromUtf8("trainerBox_1")); g_x->addWidget(trainerBox, 1, 1, 1, 1); plannerLabel = new QLabel("Planner", inputGroupBox_1); g_x->addWidget(plannerLabel, 2, 0, 1, 1); foldLabel = new QLabel("Fold", inputGroupBox_1); g_x->addWidget(foldLabel, 2, 1, 1, 1); plannerBox = new ctkComboBox(inputGroupBox_1); plannerBox->setObjectName(QString::fromUtf8("plannerBox_1")); g_x->addWidget(plannerBox, 3, 0, 1, 1); foldBox = new ctkCheckableComboBox(inputGroupBox_1); foldBox->setObjectName(QString::fromUtf8("foldBox_1")); g_x->addWidget(foldBox, 3, 1, 1, 1); verticalLayout_x->addLayout(g_x); } + + void setVisible(bool visiblity) + { + this->parent->setVisible(visiblity); + } }; QT_END_NAMESPACE #endif diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetGPU.h b/Modules/SegmentationUI/Qmitk/QmitknnUNetGPU.h index 6cd9532f46..cf92c9e028 100644 --- a/Modules/SegmentationUI/Qmitk/QmitknnUNetGPU.h +++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetGPU.h @@ -1,83 +1,92 @@ /*============================================================================ 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.s ============================================================================*/ #ifndef QmitknnUNetToolGPU_h_Included #define QmitknnUNetToolGPU_h_Included #include #include #include #include /** * @brief Struct to store GPU info. * */ struct QmitkGPUSpec { QString name; - int memoryFree; + QString memory; unsigned int id; }; /** * @brief Class to load and save GPU information * for further validation */ class QmitkGPULoader : public QObject { Q_OBJECT private: std::vector m_Gpus; public: /** * @brief Construct a new Qmitk GPU Loader object. * Parses GPU info using `nvidia-smi` command and saves it as QmitkGPUSpec objects. */ QmitkGPULoader() { QProcess process; - process.start("nvidia-smi --query-gpu=name,memory.free --format=csv"); + process.start("nvidia-smi --query-gpu=name,memory.total --format=csv"); process.waitForFinished(-1); QStringList infoStringList; while (process.canReadLine()) { QString line = process.readLine(); if (!line.startsWith("name")) { infoStringList << line; } } + unsigned int count = 0; foreach (QString infoString, infoStringList) { QmitkGPUSpec spec; QStringList gpuDetails; gpuDetails = infoString.split(","); spec.name = gpuDetails.at(0); - // spec.id = id; - spec.memoryFree = gpuDetails.at(1).split(" ")[0].toInt(); + spec.id = count; + spec.memory = gpuDetails.at(1); this->m_Gpus.push_back(spec); + ++count; } } ~QmitkGPULoader() = default; /** * @brief Returns the number of GPUs parsed and saved as QmitkGPUSpec objects. * * @return int */ int GetGPUCount() { return static_cast(m_Gpus.size()); } + + /** + * @brief Returns all the parsed GPU information + * + * @return std::vector + */ + std::vector GetAllGPUSpecs() { return m_Gpus; } }; #endif diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.cpp index ea0b15c38b..f7ec71bac2 100644 --- a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.cpp @@ -1,328 +1,495 @@ /*============================================================================ 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 "QmitknnUNetToolGUI.h" #include "mitknnUnetTool.h" #include #include #include +#include #include +#include MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitknnUNetToolGUI, "") QmitknnUNetToolGUI::QmitknnUNetToolGUI() : QmitkAutoMLSegmentationToolGUIBase() { - // Nvidia-smi command returning zero doesn't alway mean lack of GPUs. + // Nvidia-smi command returning zero doesn't always imply lack of GPUs. // Pytorch uses its own libraries to communicate to the GPUs. Hence, only a warning can be given. if (m_GpuLoader.GetGPUCount() == 0) { std::string warning = "WARNING: No GPUs were detected on your machine. The nnUNet tool might not work."; ShowErrorMessage(warning); } // define predicates for multi modal data selection combobox auto imageType = mitk::TNodePredicateDataType::New(); auto labelSetImageType = mitk::NodePredicateNot::New(mitk::TNodePredicateDataType::New()); - this->m_MultiModalPredicate = mitk::NodePredicateAnd::New(imageType, labelSetImageType).GetPointer(); + m_MultiModalPredicate = mitk::NodePredicateAnd::New(imageType, labelSetImageType).GetPointer(); } void QmitknnUNetToolGUI::ConnectNewTool(mitk::AutoSegmentationWithPreviewTool *newTool) { Superclass::ConnectNewTool(newTool); newTool->IsTimePointChangeAwareOff(); } void QmitknnUNetToolGUI::InitializeUI(QBoxLayout *mainLayout) { m_Controls.setupUi(this); #ifndef _WIN32 m_Controls.pythonEnvComboBox->addItem("/usr/bin"); #endif m_Controls.pythonEnvComboBox->addItem("Select"); AutoParsePythonPaths(); + SetGPUInfo(); connect(m_Controls.previewButton, SIGNAL(clicked()), this, SLOT(OnPreviewRequested())); connect(m_Controls.modeldirectoryBox, SIGNAL(directoryChanged(const QString &)), this, SLOT(OnDirectoryChanged(const QString &))); connect( m_Controls.modelBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnModelChanged(const QString &))); connect(m_Controls.taskBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnTaskChanged(const QString &))); connect( m_Controls.plannerBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnTrainerChanged(const QString &))); connect(m_Controls.nopipBox, SIGNAL(stateChanged(int)), this, SLOT(OnCheckBoxChanged(int))); connect(m_Controls.multiModalBox, SIGNAL(stateChanged(int)), this, SLOT(OnCheckBoxChanged(int))); connect(m_Controls.multiModalSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnModalitiesNumberChanged(int))); - connect(m_Controls.posSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnModalPositionChanged(int))); connect(m_Controls.pythonEnvComboBox, #if QT_VERSION >= 0x050F00 // 5.15 SIGNAL(textActivated(const QString &)), #elif QT_VERSION >= 0x050C00 // 5.12 SIGNAL(activated(const QString &)), #endif this, SLOT(OnPythonPathChanged(const QString &))); connect(m_Controls.refreshdirectoryBox, SIGNAL(clicked()), this, SLOT(OnRefreshPresssed())); + connect(m_Controls.clearCacheButton, SIGNAL(clicked()), this, SLOT(OnClearCachePressed())); m_Controls.codedirectoryBox->setVisible(false); m_Controls.nnUnetdirLabel->setVisible(false); m_Controls.multiModalSpinBox->setVisible(false); m_Controls.multiModalSpinLabel->setVisible(false); - m_Controls.posSpinBoxLabel->setVisible(false); - m_Controls.posSpinBox->setVisible(false); m_Controls.previewButton->setEnabled(false); - QSize sz(16, 16); - m_Controls.stopButton->setVisible(false); + m_Controls.stopButton->setVisible(false); // Hidden for future - QIcon refreshIcon; - refreshIcon.addPixmap(style()->standardIcon(QStyle::SP_BrowserReload).pixmap(sz), QIcon::Normal, QIcon::Off); + QIcon refreshIcon = + QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/view-refresh.svg")); m_Controls.refreshdirectoryBox->setIcon(refreshIcon); + QIcon dirIcon = + QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/document-open.svg")); + m_Controls.modeldirectoryBox->setIcon(dirIcon); m_Controls.refreshdirectoryBox->setEnabled(true); m_Controls.statusLabel->setTextFormat(Qt::RichText); - m_Controls.statusLabel->setText("STATUS: Welcome to nnUNet. " + QString::number(m_GpuLoader.GetGPUCount()) + - " GPUs were detected."); - if (m_GpuLoader.GetGPUCount() != 0) { - m_Controls.gpuSpinBox->setMaximum(m_GpuLoader.GetGPUCount() - 1); + WriteStatusMessage(QString("STATUS: Welcome to nnUNet. " + QString::number(m_GpuLoader.GetGPUCount()) + + " GPUs were detected.")); + } + else + { + WriteErrorMessage(QString("STATUS: Welcome to nnUNet. " + QString::number(m_GpuLoader.GetGPUCount()) + + " GPUs were detected.")); } mainLayout->addLayout(m_Controls.verticalLayout); Superclass::InitializeUI(mainLayout); m_UI_ROWS = m_Controls.advancedSettingsLayout->rowCount(); // Must do. Row count is correct only here. + + if (nullptr != m_Controls.modeldirectoryBox) + { + QString setVal = m_Settings.value("nnUNet/LastRESULTS_FOLDERPath").toString(); + m_Controls.modeldirectoryBox->setDirectory(setVal); + } } void QmitknnUNetToolGUI::OnPreviewRequested() { mitk::nnUNetTool::Pointer tool = this->GetConnectedToolAs(); if (nullptr != tool) { try { + size_t hashKey(0); m_Controls.previewButton->setEnabled(false); // To prevent misclicked back2back prediction. qApp->processEvents(); tool->PredictOn(); // purposefully placed to make tool->GetMTime different than before. QString modelName = m_Controls.modelBox->currentText(); if (modelName.startsWith("ensemble", Qt::CaseInsensitive)) { ProcessEnsembleModelsParams(tool); } else { ProcessModelParams(tool); } QString pythonPathTextItem = m_Controls.pythonEnvComboBox->currentText(); QString pythonPath = pythonPathTextItem.mid(pythonPathTextItem.indexOf(" ") + 1); bool isNoPip = m_Controls.nopipBox->isChecked(); #ifdef _WIN32 if (!isNoPip && !(pythonPath.endsWith("Scripts", Qt::CaseInsensitive) || pythonPath.endsWith("Scripts/", Qt::CaseInsensitive))) { pythonPath += QDir::separator() + QString("Scripts"); } #else if (!(pythonPath.endsWith("bin", Qt::CaseInsensitive) || pythonPath.endsWith("bin/", Qt::CaseInsensitive))) { pythonPath += QDir::separator() + QString("bin"); } #endif std::string nnUNetDirectory; if (isNoPip) { nnUNetDirectory = m_Controls.codedirectoryBox->directory().toStdString(); } else if (!IsNNUNetInstalled(pythonPath)) { throw std::runtime_error("nnUNet is not detected in the selected python environment. Please select a valid " "python environment or install nnUNet."); } tool->SetnnUNetDirectory(nnUNetDirectory); tool->SetPythonPath(pythonPath.toStdString()); tool->SetModelDirectory(m_ParentFolder->getResultsFolder().toStdString()); // checkboxes tool->SetMirror(m_Controls.mirrorBox->isChecked()); tool->SetMixedPrecision(m_Controls.mixedPrecisionBox->isChecked()); tool->SetNoPip(isNoPip); - tool->SetMultiModal(m_Controls.multiModalBox->isChecked()); + bool doCache = m_Controls.enableCachingCheckBox->isChecked(); // Spinboxes - tool->SetGpuId(static_cast(m_Controls.gpuSpinBox->value())); + tool->SetGpuId(FetchSelectedGPUFromUI()); // Multi-Modal tool->MultiModalOff(); - if (m_Controls.multiModalBox->isChecked()) + if (m_Controls.multiModalBox->isChecked() && m_Controls.multiModalSpinBox->value() > 0) { tool->m_OtherModalPaths.clear(); tool->m_OtherModalPaths = FetchMultiModalImagesFromUI(); tool->MultiModalOn(); } - tool->m_InputOutputPair = std::make_pair(nullptr, nullptr); - m_Controls.statusLabel->setText("STATUS: Starting Segmentation task... This might take a while."); - tool->UpdatePreview(); - if (tool->GetOutputBuffer() == nullptr) + else { - SegmentationProcessFailed(); + throw std::runtime_error("Please select more than one modalities for a multi-modal task. If you " + "would like to use only one modality then uncheck the Multi-Modal option."); + } + if (doCache) + { + hashKey = nnUNetCache::GetUniqueHash(tool->m_ParamQ); + if (m_Cache.contains(hashKey)) + { + tool->PredictOff(); // purposefully placed to make tool->GetMTime different than before. + } + } + if (tool->GetPredict()) + { + tool->m_InputBuffer = nullptr; + WriteStatusMessage(QString("STATUS: Starting Segmentation task... This might take a while.")); + tool->UpdatePreview(); + if (nullptr == tool->GetOutputBuffer()) + { + SegmentationProcessFailed(); + } + else + { + SegmentationResultHandler(tool); + if (doCache) + { + AddToCache(hashKey, tool->GetMLPreview()); + } + } + tool->PredictOff(); // purposefully placed to make tool->GetMTime different than before. + } + else + { + MITK_INFO << "won't do segmentation. Key found: " << QString::number(hashKey).toStdString(); + if (m_Cache.contains(hashKey)) + { + nnUNetCache *cacheObject = m_Cache[hashKey]; + MITK_INFO << "fetched pointer " << cacheObject->m_SegCache.GetPointer(); + tool->SetNodeProperties(const_cast(cacheObject->m_SegCache.GetPointer())); + SegmentationResultHandler(tool); + } } - SegmentationResultHandler(tool); - tool->PredictOff(); // purposefully placed to make tool->GetMTime different than before. m_Controls.previewButton->setEnabled(true); } catch (const std::exception &e) { std::stringstream errorMsg; - errorMsg << "Error while processing parameters for nnUNet segmentation. Reason: " << e.what(); + errorMsg << "STATUS: Error while processing parameters for nnUNet segmentation. Reason: " << e.what(); ShowErrorMessage(errorMsg.str()); + WriteErrorMessage(QString::fromStdString(errorMsg.str())); m_Controls.previewButton->setEnabled(true); tool->PredictOff(); return; } catch (...) { std::string errorMsg = "Unkown error occured while generation nnUNet segmentation."; ShowErrorMessage(errorMsg); m_Controls.previewButton->setEnabled(true); tool->PredictOff(); return; } } } std::vector QmitknnUNetToolGUI::FetchMultiModalImagesFromUI() { - std::vector paths; + std::vector modals; if (m_Controls.multiModalBox->isChecked() && !m_Modalities.empty()) { + std::set nodeNames; // set container for keeping names of all nodes to check if they are added twice. for (QmitkDataStorageComboBox *modality : m_Modalities) { - if (modality->objectName() != "multiModal_0") + if (nodeNames.find(modality->GetSelectedNode()->GetName()) == nodeNames.end()) + { + modals.push_back(dynamic_cast(modality->GetSelectedNode()->GetData())); + nodeNames.insert(modality->GetSelectedNode()->GetName()); + } + else { - paths.push_back(dynamic_cast(modality->GetSelectedNode()->GetData())); + throw std::runtime_error("Same modality is selected more than once. Please change your selection."); + break; } } } - return paths; + return modals; } bool QmitknnUNetToolGUI::IsNNUNetInstalled(const QString &pythonPath) { QString fullPath = pythonPath; #ifdef _WIN32 if (!(fullPath.endsWith("Scripts", Qt::CaseInsensitive) || fullPath.endsWith("Scripts/", Qt::CaseInsensitive))) { fullPath += QDir::separator() + QString("Scripts"); } #else if (!(fullPath.endsWith("bin", Qt::CaseInsensitive) || fullPath.endsWith("bin/", Qt::CaseInsensitive))) { fullPath += QDir::separator() + QString("bin"); } #endif fullPath = fullPath.mid(fullPath.indexOf(" ") + 1); return QFile::exists(fullPath + QDir::separator() + QString("nnUNet_predict")); } void QmitknnUNetToolGUI::ShowErrorMessage(const std::string &message, QMessageBox::Icon icon) { this->setCursor(Qt::ArrowCursor); QMessageBox *messageBox = new QMessageBox(icon, nullptr, message.c_str()); messageBox->exec(); delete messageBox; MITK_WARN << message; } +void QmitknnUNetToolGUI::WriteStatusMessage(const QString &message) +{ + m_Controls.statusLabel->setText(message); + m_Controls.statusLabel->setStyleSheet("font-weight: bold; color: white"); +} + +void QmitknnUNetToolGUI::WriteErrorMessage(const QString &message) +{ + m_Controls.statusLabel->setText(message); + m_Controls.statusLabel->setStyleSheet("font-weight: bold; color: red"); +} + void QmitknnUNetToolGUI::ProcessEnsembleModelsParams(mitk::nnUNetTool::Pointer tool) { if (m_EnsembleParams[0]->modelBox->currentText() == m_EnsembleParams[1]->modelBox->currentText()) { throw std::runtime_error("Both models you have selected for ensembling are the same."); } QString taskName = m_Controls.taskBox->currentText(); + bool isPPJson = m_Controls.postProcessingCheckBox->isChecked(); std::vector requestQ; QString ppDirFolderNamePart1 = "ensemble_"; QStringList ppDirFolderNameParts; - for (std::unique_ptr &layout : m_EnsembleParams) + for (auto &layout : m_EnsembleParams) { QStringList ppDirFolderName; QString modelName = layout->modelBox->currentText(); ppDirFolderName << modelName; ppDirFolderName << "__"; QString trainer = layout->trainerBox->currentText(); ppDirFolderName << trainer; ppDirFolderName << "__"; QString planId = layout->plannerBox->currentText(); ppDirFolderName << planId; if (!IsModelExists(modelName, taskName, QString(trainer + "__" + planId))) { std::string errorMsg = "The configuration " + modelName.toStdString() + " you have selected doesn't exist. Check your Results Folder again."; throw std::runtime_error(errorMsg); } - std::vector testfold; // empty vector to consider all folds for inferencing. + std::vector testfold = FetchSelectedFoldsFromUI(layout->foldBox); mitk::ModelParams modelObject = MapToRequest(modelName, taskName, trainer, planId, testfold); requestQ.push_back(modelObject); ppDirFolderNameParts << ppDirFolderName.join(QString("")); } tool->EnsembleOn(); - - QString ppJsonFilePossibility1 = QDir::cleanPath( - m_ParentFolder->getResultsFolder() + QDir::separator() + "nnUNet" + QDir::separator() + "ensembles" + - QDir::separator() + taskName + QDir::separator() + ppDirFolderNamePart1 + ppDirFolderNameParts.first() + "--" + - ppDirFolderNameParts.last() + QDir::separator() + "postprocessing.json"); - QString ppJsonFilePossibility2 = QDir::cleanPath( - m_ParentFolder->getResultsFolder() + QDir::separator() + "nnUNet" + QDir::separator() + "ensembles" + - QDir::separator() + taskName + QDir::separator() + ppDirFolderNamePart1 + ppDirFolderNameParts.last() + "--" + - ppDirFolderNameParts.first() + QDir::separator() + "postprocessing.json"); - - if (QFile(ppJsonFilePossibility1).exists()) - { - tool->SetPostProcessingJsonDirectory(ppJsonFilePossibility1.toStdString()); - } - else if (QFile(ppJsonFilePossibility2).exists()) - { - tool->SetPostProcessingJsonDirectory(ppJsonFilePossibility2.toStdString()); - } - else + if (isPPJson) { - // warning message + QString ppJsonFilePossibility1 = QDir::cleanPath( + m_ParentFolder->getResultsFolder() + QDir::separator() + "nnUNet" + QDir::separator() + "ensembles" + + QDir::separator() + taskName + QDir::separator() + ppDirFolderNamePart1 + ppDirFolderNameParts.first() + "--" + + ppDirFolderNameParts.last() + QDir::separator() + "postprocessing.json"); + QString ppJsonFilePossibility2 = QDir::cleanPath( + m_ParentFolder->getResultsFolder() + QDir::separator() + "nnUNet" + QDir::separator() + "ensembles" + + QDir::separator() + taskName + QDir::separator() + ppDirFolderNamePart1 + ppDirFolderNameParts.last() + "--" + + ppDirFolderNameParts.first() + QDir::separator() + "postprocessing.json"); + + if (QFile(ppJsonFilePossibility1).exists()) + { + tool->SetPostProcessingJsonDirectory(ppJsonFilePossibility1.toStdString()); + const QString statusMsg = "Post Processing JSON file found: " + ppJsonFilePossibility1; + WriteStatusMessage(statusMsg); + } + else if (QFile(ppJsonFilePossibility2).exists()) + { + tool->SetPostProcessingJsonDirectory(ppJsonFilePossibility2.toStdString()); + const QString statusMsg = "Post Processing JSON file found:" + ppJsonFilePossibility2; + WriteStatusMessage(statusMsg); + } + else + { + std::string errorMsg = + "No post processing file was found for the selected ensemble combination. Continuing anyway..."; + ShowErrorMessage(errorMsg); + } } tool->m_ParamQ.clear(); tool->m_ParamQ = requestQ; } void QmitknnUNetToolGUI::ProcessModelParams(mitk::nnUNetTool::Pointer tool) { tool->EnsembleOff(); std::vector requestQ; QString modelName = m_Controls.modelBox->currentText(); QString taskName = m_Controls.taskBox->currentText(); QString trainer = m_Controls.trainerBox->currentText(); QString planId = m_Controls.plannerBox->currentText(); - std::vector fetchedFolds = FetchSelectedFoldsFromUI(); + std::vector fetchedFolds = FetchSelectedFoldsFromUI(m_Controls.foldBox); mitk::ModelParams modelObject = MapToRequest(modelName, taskName, trainer, planId, fetchedFolds); requestQ.push_back(modelObject); tool->m_ParamQ.clear(); tool->m_ParamQ = requestQ; } bool QmitknnUNetToolGUI::IsModelExists(const QString &modelName, const QString &taskName, const QString &trainerPlanner) { QString modelSearchPath = QDir::cleanPath(m_ParentFolder->getResultsFolder() + QDir::separator() + "nnUNet" + QDir::separator() + modelName + QDir::separator() + taskName + QDir::separator() + trainerPlanner); if (QDir(modelSearchPath).exists()) { return true; } return false; } + +void QmitknnUNetToolGUI::CheckAllInCheckableComboBox(ctkCheckableComboBox *foldBox) +{ + // Recalling all added items to check-mark it. + const QAbstractItemModel *qaim = foldBox->checkableModel(); + auto rows = qaim->rowCount(); + for (std::remove_const_t i = 0; i < rows; ++i) + { + const QModelIndex mi = qaim->index(i, 0); + foldBox->setCheckState(mi, Qt::Checked); + } +} + +std::pair QmitknnUNetToolGUI::ExtractTrainerPlannerFromString(QStringList trainerPlanners) +{ + QString splitterString = "__"; + QStringList trainers, planners; + for (const auto &trainerPlanner : trainerPlanners) + { + trainers << trainerPlanner.split(splitterString, QString::SplitBehavior::SkipEmptyParts).first(); + planners << trainerPlanner.split(splitterString, QString::SplitBehavior::SkipEmptyParts).last(); + } + trainers.removeDuplicates(); + planners.removeDuplicates(); + return std::make_pair(trainers, planners); +} + +std::vector QmitknnUNetToolGUI::FetchSelectedFoldsFromUI(ctkCheckableComboBox *foldBox) +{ + std::vector folds; + if (!(foldBox->allChecked() || foldBox->noneChecked())) + { + QModelIndexList foldList = foldBox->checkedIndexes(); + for (const auto &index : foldList) + { + QString foldQString = foldBox->itemText(index.row()).split("_", QString::SplitBehavior::SkipEmptyParts).last(); + folds.push_back(foldQString.toStdString()); + } + } + return folds; +} + +void QmitknnUNetToolGUI::OnClearCachePressed() +{ + m_Cache.clear(); + UpdateCacheCountOnUI(); +} + +void QmitknnUNetToolGUI::UpdateCacheCountOnUI() +{ + QString cacheText = m_CACHE_COUNT_BASE_LABEL + QString::number(m_Cache.size()); + m_Controls.cacheCountLabel->setText(cacheText); +} + +void QmitknnUNetToolGUI::AddToCache(size_t &hashKey, mitk::LabelSetImage::ConstPointer mlPreview) +{ + nnUNetCache *newCacheObj = new nnUNetCache; + newCacheObj->m_SegCache = mlPreview; + m_Cache.insert(hashKey, newCacheObj); + MITK_INFO << "New hash: " << hashKey << " " << newCacheObj->m_SegCache.GetPointer(); + UpdateCacheCountOnUI(); +} + +void QmitknnUNetToolGUI::SetGPUInfo() +{ + std::vector specs = m_GpuLoader.GetAllGPUSpecs(); + for (const QmitkGPUSpec &gpuSpec : specs) + { + m_Controls.gpuComboBox->addItem(QString::number(gpuSpec.id) + ": " + gpuSpec.name + " (" + gpuSpec.memory + ")"); + } + if (specs.empty()) + { + m_Controls.gpuComboBox->setEditable(true); + m_Controls.gpuComboBox->addItem(QString::number(0)); + m_Controls.gpuComboBox->setValidator(new QIntValidator(0, 999, this)); + } +} + +unsigned int QmitknnUNetToolGUI::FetchSelectedGPUFromUI() +{ + QString gpuInfo = m_Controls.gpuComboBox->currentText(); + if (m_GpuLoader.GetGPUCount() == 0) + { + return static_cast(gpuInfo.toInt()); + } + else + { + QString gpuId = gpuInfo.split(":", QString::SplitBehavior::SkipEmptyParts).first(); + return static_cast(gpuId.toInt()); + } +} diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.h index 583151c47a..b5a6106778 100644 --- a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.h +++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.h @@ -1,222 +1,298 @@ /*============================================================================ 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.s ============================================================================*/ #ifndef QmitknnUNetToolGUI_h_Included #define QmitknnUNetToolGUI_h_Included #include "QmitkAutoMLSegmentationToolGUIBase.h" #include "QmitknnUNetFolderParser.h" #include "QmitknnUNetGPU.h" #include "mitknnUnetTool.h" #include "ui_QmitknnUNetToolGUIControls.h" #include +#include #include +#include #include #include +#include + +class nnUNetCache +{ +public: + mitk::LabelSetImage::ConstPointer m_SegCache; + static size_t GetUniqueHash(std::vector &requestQ) + { + size_t hashCode = 0; + for (mitk::ModelParams &request : requestQ) + { + boost::hash_combine(hashCode, request.generateHash()); + } + return hashCode; + } +}; class MITKSEGMENTATIONUI_EXPORT QmitknnUNetToolGUI : public QmitkAutoMLSegmentationToolGUIBase { Q_OBJECT public: mitkClassMacro(QmitknnUNetToolGUI, QmitkAutoMLSegmentationToolGUIBase); itkFactorylessNewMacro(Self); itkCloneMacro(Self); + QCache m_Cache; + protected slots: /** * @brief Qt slot * */ void OnPreviewRequested(); /** * @brief Qt slot * */ void OnDirectoryChanged(const QString &); /** * @brief Qt slot * */ void OnModelChanged(const QString &); /** * @brief Qt slot * */ void OnTaskChanged(const QString &); /** * @brief Qt slot * */ void OnTrainerChanged(const QString &); /** * @brief Qt slot * */ void OnCheckBoxChanged(int); /** * @brief Qthread slot to capture failures from thread worker and * shows error message * */ void SegmentationProcessFailed(); /** * @brief Qthread to capture sucessfull nnUNet segmentation. * Further, renders the LabelSet image */ void SegmentationResultHandler(mitk::nnUNetTool *); /** * @brief Qt Slot * */ void OnModalitiesNumberChanged(int); /** * @brief Qt Slot * */ void OnPythonPathChanged(const QString &); /** - * @brief Qt Slot + * @brief Qt slot * */ - void OnModalPositionChanged(int); + void OnRefreshPresssed(); /** * @brief Qt slot * */ - void OnRefreshPresssed(); + void OnClearCachePressed(); protected: QmitknnUNetToolGUI(); ~QmitknnUNetToolGUI() = default; void ConnectNewTool(mitk::AutoSegmentationWithPreviewTool *newTool) override; void InitializeUI(QBoxLayout *mainLayout) override; void EnableWidgets(bool enabled) override; private: + + /** + * @brief Returns GPU id of the selected GPU from the Combo box. + * + * @return unsigned int + */ + unsigned int FetchSelectedGPUFromUI(); + + /** + * @brief Adds GPU information to the gpu combo box. + * In case, there aren't any GPUs avaialble, the combo box will be + * rendered editable. + */ + void SetGPUInfo(); + + /** + * @brief Inserts the hash and segmentation into cache and + * updates count on UI. + */ + void AddToCache(size_t&, mitk::LabelSetImage::ConstPointer); + + /** + * @brief Checks all the entries of the ctkCheckableComboBox ui widget. + * This feature is not present in ctkCheckableComboBox API. + */ + void CheckAllInCheckableComboBox(ctkCheckableComboBox *); + + /** + * @brief Parses the folder names containing trainer and planner together and, + * returns it as separate lists. + * @return std::pair + */ + + std::pair ExtractTrainerPlannerFromString(QStringList); /** * @brief Parses the ensemble UI elements and sets to nnUNetTool object pointer. * */ void ProcessEnsembleModelsParams(mitk::nnUNetTool::Pointer); /** * @brief Parses the UI elements and sets to nnUNetTool object pointer. * */ void ProcessModelParams(mitk::nnUNetTool::Pointer); /** * @brief Creates and renders QmitknnUNetTaskParamsUITemplate layout for ensemble input. */ void ShowEnsembleLayout(bool visible = true); /** * @brief Creates a QMessage object and shows on screen. */ void ShowErrorMessage(const std::string &, QMessageBox::Icon = QMessageBox::Critical); + /** + * @brief Writes any message in white on the tool pane. + */ + void WriteStatusMessage(const QString &); + + /** + * @brief Writes any message in red on the tool pane. + */ + void WriteErrorMessage(const QString &); + /** * @brief Searches and parses paths of python virtual enviroments * from predefined lookout locations */ void AutoParsePythonPaths(); /** * @brief Check if pretrained model sub folder inside RESULTS FOLDER exist. - * */ - bool IsModelExists(const QString&, const QString&, const QString&); + bool IsModelExists(const QString &, const QString &, const QString &); /** * @brief Clears all combo boxes * Any new combo box added in the future can be featured here for clearance. * */ void ClearAllComboBoxes(); /** * @brief Checks if nnUNet_predict command is valid in the selected python virtual environment. * * @return bool */ bool IsNNUNetInstalled(const QString &); /** * @brief Mapper function to map QString entries from UI to ModelParam attributes. * * @return mitk::ModelParams */ mitk::ModelParams MapToRequest( const QString &, const QString &, const QString &, const QString &, const std::vector &); /** * @brief Returns checked fold names from the ctk-Checkable-ComboBox. * * @return std::vector */ - std::vector FetchSelectedFoldsFromUI(); + std::vector FetchSelectedFoldsFromUI(ctkCheckableComboBox *); /** * @brief Returns all paths from the dynamically generated ctk-path-line-edit boxes. * * @return std::vector */ std::vector FetchMultiModalImagesFromUI(); + /** + * @brief Updates cache count on UI. + * + */ + void UpdateCacheCountOnUI(); + Ui_QmitknnUNetToolGUIControls m_Controls; QmitkGPULoader m_GpuLoader; /** * @brief Stores all dynamically added ctk-path-line-edit UI elements. * */ std::vector m_Modalities; std::vector> m_EnsembleParams; mitk::NodePredicateBase::Pointer m_MultiModalPredicate; /** * @brief Stores row count of the "advancedSettingsLayout" layout element. This value helps dynamically add * ctk-path-line-edit UI elements at the right place. Forced to initialize in the InitializeUI method since there is * no guarantee of retrieving exact row count anywhere else. * */ int m_UI_ROWS; /** * @brief Stores path of the model director (RESULTS_FOLDER appended by "nnUNet"). * */ std::shared_ptr m_ParentFolder = nullptr; /** * @brief Valid list of models supported by nnUNet * */ const QStringList m_VALID_MODELS = {"2d", "3d_lowres", "3d_fullres", "3d_cascade_fullres", "ensembles"}; + + const QString m_CACHE_COUNT_BASE_LABEL = "Cached Items: "; + + /** + * @brief For storing values across sessions. Currently, RESULTS_FOLDER value is cached using this. + */ + QSettings m_Settings; }; #endif diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUIControls.ui b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUIControls.ui index 534b398ddf..82e12d2b63 100644 --- a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUIControls.ui +++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUIControls.ui @@ -1,440 +1,478 @@ - QmitknnUNetToolGUIControls - - - - 0 - 0 - 192 - 352 - - - - - 0 - 0 - - - - - 100 - 0 - - - + QmitknnUNetToolGUIControls + + + + 0 + 0 + 877 + 711 + + + + + 0 + 0 + + + + + 100 + 0 + + + + + 100000 + 100000 + + + + QmitknnUNetToolWidget + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + Refresh Results Folder + + + + + + + + 0 + 0 + + + + Python Path: + + + + + + + + 0 + 0 + + + + Plan: + + + + + + + + 0 + 0 + + + + Trainer: + + + + + + + + 0 + 0 + + + + nnUNet Results Folder: + + + + + + + + + + Stop + + + + + + + + + + + 0 + 0 + + + + Multi-Modal: + + + + + + + + 0 + 0 + + + + <html><head/><body><p>Welcome to nnUNet in MITK. [Experimental]</p><p>Please note that this is only an interface to nnUNet. MITK does not ship with nnUNet. Make sure to have a working Python environment with nnUNet set up beforehand. Choose that environment in the Python Path before inferencing.</p><p>Refer to <a href="https://github.com/MIC-DKFZ/nnUNet"><span style=" text-decoration: underline; color:#0000ff;">https://github.com/MIC-DKFZ/nnUNet</span></a> to learn everything about the nnUNet.</p></body></html> + + + Qt::RichText + + + true + + + + + + + + + + Configuration: + + + + + + + + + + + 0 + 0 + + + + Task: + + + + + + + + + + + 0 + 0 + + + + No. of Extra Modalities: + + + + + + + Fold: + + + + + + + + + + + + + + - 100000 - 100000 + 0 + 0 - - - QmitknnUNetToolWidget - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - 0 - 0 - - - - nnUNet Results Folder: - - - - - - - - - - - - Refresh Results Folder - - - - - - - - - Configuration: - - - - - - - - - - - 0 - 0 - - - - Task: - - - - - - - - - - - - - 0 - 0 - - - - Trainer: - - - - - - - - - - - - 0 - 0 - - - - Plan: - - - - - - - - - - - Fold: - - - - - - - - - - Stop - - - - - - - - - 0 - 0 - - - - Multi-Modal: - - - - - - - - - - - 0 - 0 - - - - No. of Extra Modalities: - - - - - - - - - - - - - 0 - 0 - - - - Reference Image Position - - - - - - - - - - - + + + Advanced + + + Qt::AlignRight + + + true + + + 5 + + + true + + - - - - 0 - 0 - + + + 0 + + + 0 + + + 0 + + + 0 + + + 6 + + + + + + 0 + 0 + - - 5 + + No Pip: + + + + + + + + + + + 0 + 0 + - - Advanced + + Mixed Precision: - - true + + + + + + true - - true + + + + + + + 0 + 0 + - - Qt::AlignRight + + GPU Id: - - - - - - 6 - - - 0 - - - - - - - 0 - 0 - - - - No Pip - - - - - - - - - - - 0 - 0 - - - - Mixed Precision - - - - - - - true - - - - - - - - - 0 - 0 - - - - GPU Id: - - - - - - - - - - - - 0 - 0 - - - - Enable Mirroring - - - - - - - true - - - - - - - - - 0 - 0 - - - - Python Path: - - - - - - - - - - - - 0 - 0 - - - - nnUNet Path: - - - - - - - - - - - - - - - - - + + + + + + + - - 0 - 0 - + + 0 + 0 + - - - 100000 - 16777215 - + + Enable Mirroring: + + + + + + + true + + + + + + + + 0 + 0 + - Preview + nnUNet Path: - - - - + + + + + + + + + + 0 + 0 + + + + Use Postprocessing JSON: + + + + + + + true + + + + + + + + 0 + 0 + + + + Enable Caching: + + + + + + + true + + + + + + + Clear Cache + + + + + - - 0 - 0 - + + 0 + 0 + + + + Cached Items: 0 - + + + - - - - - - - ctkDirectoryButton - QWidget -
ctkDirectoryButton.h
- 1 -
- - ctkComboBox - QComboBox -
ctkComboBox.h
- 1 -
- - ctkCheckableComboBox - QComboBox -
ctkCheckableComboBox.h
- 1 -
- - ctkCheckBox - QCheckBox -
ctkCheckBox.h
- 1 -
- - ctkCollapsibleGroupBox - QGroupBox -
ctkCollapsibleGroupBox.h
- 1 -
-
- - + + + + + + + + 0 + 0 + + + + + 100000 + 16777215 + + + + Preview + + + + + + + + 0 + 0 + + + + true + + + + + + + + ctkDirectoryButton + QWidget +
ctkDirectoryButton.h
+ 1 +
+ + ctkComboBox + QComboBox +
ctkComboBox.h
+ 1 +
+ + ctkCheckableComboBox + QComboBox +
ctkCheckableComboBox.h
+ 1 +
+ + ctkCheckBox + QCheckBox +
ctkCheckBox.h
+ 1 +
+ + ctkCollapsibleGroupBox + QGroupBox +
ctkCollapsibleGroupBox.h
+ 1 +
+
+ +
diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolSlots.cpp b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolSlots.cpp index b04034d042..39a386248c 100644 --- a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolSlots.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolSlots.cpp @@ -1,434 +1,409 @@ #include "QmitknnUNetToolGUI.h" #include #include #include #include #include void QmitknnUNetToolGUI::EnableWidgets(bool enabled) { Superclass::EnableWidgets(enabled); } void QmitknnUNetToolGUI::ClearAllComboBoxes() { m_Controls.modelBox->clear(); m_Controls.taskBox->clear(); m_Controls.foldBox->clear(); m_Controls.trainerBox->clear(); - for (std::unique_ptr &layout : m_EnsembleParams) + for (auto &layout : m_EnsembleParams) { layout->modelBox->clear(); layout->trainerBox->clear(); layout->plannerBox->clear(); + layout->foldBox->clear(); } } void QmitknnUNetToolGUI::OnRefreshPresssed() { const QString resultsFolder = m_Controls.modeldirectoryBox->directory(); OnDirectoryChanged(resultsFolder); } void QmitknnUNetToolGUI::OnDirectoryChanged(const QString &resultsFolder) { m_Controls.previewButton->setEnabled(false); this->ClearAllComboBoxes(); m_ParentFolder = std::make_shared(resultsFolder); auto models = m_ParentFolder->getModelNames(); std::for_each(models.begin(), models.end(), [this](QString model) { if (m_VALID_MODELS.contains(model, Qt::CaseInsensitive)) m_Controls.modelBox->addItem(model); }); + m_Settings.setValue("nnUNet/LastRESULTS_FOLDERPath",resultsFolder); } void QmitknnUNetToolGUI::OnModelChanged(const QString &model) { if (model.isEmpty()) { return; } - m_Controls.taskBox->clear(); - auto tasks = m_ParentFolder->getTasksForModel(model); - std::for_each(tasks.begin(), tasks.end(), [this](QString task) { m_Controls.taskBox->addItem(task); }); + ctkComboBox *box = qobject_cast(sender()); + if (box == m_Controls.modelBox) + { + m_Controls.taskBox->clear(); + auto tasks = m_ParentFolder->getTasksForModel(model); + std::for_each(tasks.begin(), tasks.end(), [this](QString task) { m_Controls.taskBox->addItem(task); }); + } + else if (!m_EnsembleParams.empty()) + { + for (auto &layout : m_EnsembleParams) + { + if (box == layout->modelBox) + { + layout->trainerBox->clear(); + layout->plannerBox->clear(); + auto trainerPlanners = + m_ParentFolder->getTrainerPlannersForTask(m_Controls.taskBox->currentText(), model); + QStringList trainers, planners; + std::tie(trainers, planners) = ExtractTrainerPlannerFromString(trainerPlanners); + + std::for_each( + trainers.begin(), trainers.end(), [&layout](const QString& trainer) { layout->trainerBox->addItem(trainer); }); + std::for_each( + planners.begin(), planners.end(), [&layout](const QString& planner) { layout->plannerBox->addItem(planner); }); + break; + } + } + } } void QmitknnUNetToolGUI::OnTaskChanged(const QString &task) { if (task.isEmpty()) { return; } m_Controls.trainerBox->clear(); m_Controls.plannerBox->clear(); - QStringList trainerPlanners = - m_ParentFolder->getTrainerPlannersForTask(task, m_Controls.modelBox->currentText()); - if (m_Controls.modelBox->currentText() == "ensembles") + if (m_Controls.modelBox->currentText() == m_VALID_MODELS.last()) { m_Controls.trainerBox->setVisible(false); m_Controls.trainerLabel->setVisible(false); m_Controls.plannerBox->setVisible(false); m_Controls.plannerLabel->setVisible(false); m_Controls.foldBox->setVisible(false); m_Controls.foldLabel->setVisible(false); ShowEnsembleLayout(true); - QString splitterString = "--"; - QStringList models, trainers, planners; - foreach (QString trainerPlanner, trainerPlanners) - { - QStringList trainerSplitParts = trainerPlanner.split(splitterString, QString::SplitBehavior::SkipEmptyParts); - foreach (QString modelSet, trainerSplitParts) - { - modelSet.remove("ensemble_", Qt::CaseInsensitive); - QStringList splitParts = modelSet.split("__", QString::SplitBehavior::SkipEmptyParts); - QString modelName = splitParts.first(); - QString trainer = splitParts.at(1); - QString planId = splitParts.at(2); - models << modelName; - trainers << trainer; - planners << planId; - } - } - trainers.removeDuplicates(); - planners.removeDuplicates(); + auto models = m_ParentFolder->getModelsForTask(m_Controls.taskBox->currentText()); models.removeDuplicates(); + models.removeOne(m_VALID_MODELS.last()); - for (std::unique_ptr &layout : m_EnsembleParams) + for (auto &layout : m_EnsembleParams) { layout->modelBox->clear(); layout->trainerBox->clear(); layout->plannerBox->clear(); std::for_each(models.begin(), models.end(), [&layout, this](QString model) { if (m_VALID_MODELS.contains(model, Qt::CaseInsensitive)) layout->modelBox->addItem(model); }); - std::for_each( - trainers.begin(), trainers.end(), [&layout](QString trainer) { layout->trainerBox->addItem(trainer); }); - std::for_each( - planners.begin(), planners.end(), [&layout](QString planner) { layout->plannerBox->addItem(planner); }); } m_Controls.previewButton->setEnabled(true); } else { m_Controls.trainerBox->setVisible(true); m_Controls.trainerLabel->setVisible(true); m_Controls.plannerBox->setVisible(true); m_Controls.plannerLabel->setVisible(true); m_Controls.foldBox->setVisible(true); m_Controls.foldLabel->setVisible(true); m_Controls.previewButton->setEnabled(false); ShowEnsembleLayout(false); - QString splitterString = "__"; + auto trainerPlanners = + m_ParentFolder->getTrainerPlannersForTask(task, m_Controls.modelBox->currentText()); QStringList trainers, planners; - foreach (QString trainerPlanner, trainerPlanners) - { - trainers << trainerPlanner.split(splitterString, QString::SplitBehavior::SkipEmptyParts).first(); - planners << trainerPlanner.split(splitterString, QString::SplitBehavior::SkipEmptyParts).last(); - } - trainers.removeDuplicates(); - planners.removeDuplicates(); + std::tie(trainers, planners) = ExtractTrainerPlannerFromString(trainerPlanners); std::for_each( trainers.begin(), trainers.end(), [this](QString trainer) { m_Controls.trainerBox->addItem(trainer); }); std::for_each( planners.begin(), planners.end(), [this](QString planner) { m_Controls.plannerBox->addItem(planner); }); } } void QmitknnUNetToolGUI::OnTrainerChanged(const QString &plannerSelected) { if (plannerSelected.isEmpty()) { return; } - m_Controls.foldBox->clear(); - if (m_Controls.modelBox->currentText() != "ensembles") + auto *box = qobject_cast(sender()); + if (box == m_Controls.plannerBox) { + m_Controls.foldBox->clear(); auto selectedTrainer = m_Controls.trainerBox->currentText(); auto selectedTask = m_Controls.taskBox->currentText(); auto selectedModel = m_Controls.modelBox->currentText(); auto folds = m_ParentFolder->getFoldsForTrainerPlanner( selectedTrainer, plannerSelected, selectedTask, selectedModel); std::for_each(folds.begin(), folds.end(), [this](QString fold) { if (fold.startsWith("fold_", Qt::CaseInsensitive)) // imposed by nnUNet m_Controls.foldBox->addItem(fold); }); if (m_Controls.foldBox->count() != 0) { - // Now recalling all added items to check-mark it. - const QAbstractItemModel *qaim = m_Controls.foldBox->checkableModel(); - for (int i = 0; i < folds.size(); ++i) - { - const QModelIndex mi = qaim->index(i, 0); - m_Controls.foldBox->setCheckState(mi, Qt::Checked); - } + CheckAllInCheckableComboBox(m_Controls.foldBox); m_Controls.previewButton->setEnabled(true); } } - else + else if (!m_EnsembleParams.empty()) { - m_Controls.previewButton->setEnabled(true); + for (auto &layout : m_EnsembleParams) + { + if (box == layout->plannerBox) + { + layout->foldBox->clear(); + auto selectedTrainer = layout->trainerBox->currentText(); + auto selectedTask = m_Controls.taskBox->currentText(); + auto selectedModel = layout->modelBox->currentText(); + auto folds = m_ParentFolder->getFoldsForTrainerPlanner( + selectedTrainer, plannerSelected, selectedTask, selectedModel); + std::for_each(folds.begin(), + folds.end(), + [&layout](const QString& fold) + { + if (fold.startsWith("fold_", Qt::CaseInsensitive)) // imposed by nnUNet + layout->foldBox->addItem(fold); + }); + if (layout->foldBox->count() != 0) + { + CheckAllInCheckableComboBox(layout->foldBox); + m_Controls.previewButton->setEnabled(true); + } + break; + } + } } } void QmitknnUNetToolGUI::OnPythonPathChanged(const QString &pyEnv) { if (pyEnv == QString("Select")) { QString path = QFileDialog::getExistingDirectory(m_Controls.pythonEnvComboBox->parentWidget(), "Python Path", "dir"); if (!path.isEmpty()) { m_Controls.pythonEnvComboBox->insertItem(0, path); m_Controls.pythonEnvComboBox->setCurrentIndex(0); } } else if (!IsNNUNetInstalled(pyEnv)) { std::string warning = "WARNING: nnUNet is not detected on the Python environment you selected. Please select another " "environment or create one. For more info refer https://github.com/MIC-DKFZ/nnUNet"; ShowErrorMessage(warning); } } void QmitknnUNetToolGUI::OnCheckBoxChanged(int state) { bool visibility = false; if (state == Qt::Checked) { visibility = true; } ctkCheckBox *box = qobject_cast(sender()); if (box != nullptr) { if (box->objectName() == QString("nopipBox")) { m_Controls.codedirectoryBox->setVisible(visibility); m_Controls.nnUnetdirLabel->setVisible(visibility); } else if (box->objectName() == QString("multiModalBox")) { m_Controls.multiModalSpinLabel->setVisible(visibility); m_Controls.multiModalSpinBox->setVisible(visibility); - m_Controls.posSpinBoxLabel->setVisible(visibility); - m_Controls.posSpinBox->setVisible(visibility); if (visibility) { QmitkDataStorageComboBox *defaultImage = new QmitkDataStorageComboBox(this, true); defaultImage->setObjectName(QString("multiModal_" + QString::number(0))); - defaultImage->SetPredicate(this->m_MultiModalPredicate); - defaultImage->setDisabled(true); + defaultImage->SetPredicate(m_MultiModalPredicate); mitk::nnUNetTool::Pointer tool = this->GetConnectedToolAs(); if (tool != nullptr) { defaultImage->SetDataStorage(tool->GetDataStorage()); defaultImage->SetSelectedNode(tool->GetRefNode()); } - m_Controls.advancedSettingsLayout->addWidget(defaultImage, this->m_UI_ROWS + m_Modalities.size() + 1, 1, 1, 3); + m_Controls.advancedSettingsLayout->addWidget(defaultImage, m_UI_ROWS + m_Modalities.size() + 1, 1, 1, 3); m_Modalities.push_back(defaultImage); - m_Controls.posSpinBox->setMaximum(this->m_Modalities.size() - 1); m_UI_ROWS++; } else { OnModalitiesNumberChanged(0); m_Controls.multiModalSpinBox->setValue(0); - m_Controls.posSpinBox->setMaximum(0); - delete this->m_Modalities[0]; + delete m_Modalities[0]; m_Modalities.pop_back(); } } } } void QmitknnUNetToolGUI::OnModalitiesNumberChanged(int num) { - while (num > static_cast(this->m_Modalities.size() - 1)) + while (num > static_cast(m_Modalities.size() - 1)) { QmitkDataStorageComboBox *multiModalBox = new QmitkDataStorageComboBox(this, true); mitk::nnUNetTool::Pointer tool = this->GetConnectedToolAs(); multiModalBox->SetDataStorage(tool->GetDataStorage()); - multiModalBox->SetPredicate(this->m_MultiModalPredicate); + multiModalBox->SetPredicate(m_MultiModalPredicate); multiModalBox->setObjectName(QString("multiModal_" + QString::number(m_Modalities.size() + 1))); - m_Controls.advancedSettingsLayout->addWidget(multiModalBox, this->m_UI_ROWS + m_Modalities.size() + 1, 1, 1, 3); + m_Controls.advancedSettingsLayout->addWidget(multiModalBox, m_UI_ROWS + m_Modalities.size() + 1, 1, 1, 3); m_Modalities.push_back(multiModalBox); } - while (num < static_cast(this->m_Modalities.size() - 1) && !m_Modalities.empty()) + while (num < static_cast(m_Modalities.size() - 1) && !m_Modalities.empty()) { QmitkDataStorageComboBox *child = m_Modalities.back(); if (child->objectName() == "multiModal_0") { - std::iter_swap(this->m_Modalities.end() - 2, this->m_Modalities.end() - 1); + std::iter_swap(m_Modalities.end() - 2, m_Modalities.end() - 1); child = m_Modalities.back(); } delete child; // delete the layout item m_Modalities.pop_back(); } - m_Controls.posSpinBox->setMaximum(this->m_Modalities.size() - 1); m_Controls.advancedSettingsLayout->update(); } -void QmitknnUNetToolGUI::OnModalPositionChanged(int posIdx) -{ - if (posIdx < static_cast(m_Modalities.size())) - { - int currPos = 0; - bool stopCheck = false; - // for-loop clears all widgets from the QGridLayout and also, finds the position of loaded-image widget. - for (QmitkDataStorageComboBox *multiModalBox : m_Modalities) - { - m_Controls.advancedSettingsLayout->removeWidget(multiModalBox); - multiModalBox->setParent(nullptr); - if (multiModalBox->objectName() != "multiModal_0" && !stopCheck) - { - currPos++; - } - else - { - stopCheck = true; - } - } - // moving the loaded-image widget to the required position - std::iter_swap(this->m_Modalities.begin() + currPos, m_Modalities.begin() + posIdx); - // re-adding all widgets in the order - for (int i = 0; i < static_cast(m_Modalities.size()); ++i) - { - QmitkDataStorageComboBox *multiModalBox = m_Modalities[i]; - m_Controls.advancedSettingsLayout->addWidget(multiModalBox, m_UI_ROWS + i + 1, 1, 1, 3); - } - m_Controls.advancedSettingsLayout->update(); - } -} - void QmitknnUNetToolGUI::AutoParsePythonPaths() { QString homeDir = QDir::homePath(); std::vector searchDirs; #ifdef _WIN32 searchDirs.push_back(QString("C:") + QDir::separator() + QString("ProgramData") + QDir::separator() + QString("anaconda3")); #else // Add search locations for possible standard python paths here searchDirs.push_back(homeDir + QDir::separator() + "environments"); searchDirs.push_back(homeDir + QDir::separator() + "anaconda3"); searchDirs.push_back(homeDir + QDir::separator() + "miniconda3"); searchDirs.push_back(homeDir + QDir::separator() + "opt" + QDir::separator() + "miniconda3"); searchDirs.push_back(homeDir + QDir::separator() + "opt" + QDir::separator() + "anaconda3"); #endif for (QString searchDir : searchDirs) { if (searchDir.endsWith("anaconda3", Qt::CaseInsensitive)) { if (QDir(searchDir).exists()) { m_Controls.pythonEnvComboBox->insertItem(0, "(base): " + searchDir); searchDir.append((QDir::separator() + QString("envs"))); } } for (QDirIterator subIt(searchDir, QDir::AllDirs, QDirIterator::NoIteratorFlags); subIt.hasNext();) { subIt.next(); QString envName = subIt.fileName(); if (!envName.startsWith('.')) // Filter out irrelevent hidden folders, if any. { m_Controls.pythonEnvComboBox->insertItem(0, "(" + envName + "): " + subIt.filePath()); } } } m_Controls.pythonEnvComboBox->setCurrentIndex(-1); } -std::vector QmitknnUNetToolGUI::FetchSelectedFoldsFromUI() -{ - std::vector folds; - if (!(m_Controls.foldBox->allChecked() || m_Controls.foldBox->noneChecked())) - { - QModelIndexList foldList = m_Controls.foldBox->checkedIndexes(); - foreach (QModelIndex index, foldList) - { - QString foldQString = - m_Controls.foldBox->itemText(index.row()).split("_", QString::SplitBehavior::SkipEmptyParts).last(); - folds.push_back(foldQString.toStdString()); - } - } - return folds; -} - mitk::ModelParams QmitknnUNetToolGUI::MapToRequest(const QString &modelName, const QString &taskName, const QString &trainer, const QString &planId, const std::vector &folds) { mitk::ModelParams requestObject; requestObject.model = modelName.toStdString(); requestObject.trainer = trainer.toStdString(); requestObject.planId = planId.toStdString(); requestObject.task = taskName.toStdString(); requestObject.folds = folds; + mitk::nnUNetTool::Pointer tool = this->GetConnectedToolAs(); + requestObject.inputName = tool->GetRefNode()->GetName(); + requestObject.timeStamp = std::to_string(mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint()); return requestObject; } void QmitknnUNetToolGUI::SegmentationProcessFailed() { - m_Controls.statusLabel->setText( - "STATUS: Error in the segmentation process. No resulting segmentation can be loaded."); + WriteErrorMessage( + "STATUS: Error in the segmentation process.
No resulting segmentation can be loaded.
"); this->setCursor(Qt::ArrowCursor); std::stringstream stream; stream << "Error in the segmentation process. No resulting segmentation can be loaded."; ShowErrorMessage(stream.str()); m_Controls.stopButton->setEnabled(false); } void QmitknnUNetToolGUI::SegmentationResultHandler(mitk::nnUNetTool *tool) { tool->RenderOutputBuffer(); this->SetLabelSetPreview(tool->GetMLPreview()); - m_Controls.statusLabel->setText("STATUS: Segmentation task finished successfully. Please Confirm the " - "segmentation else will result in data loss"); + WriteStatusMessage("STATUS: Segmentation task finished successfully.
Please Confirm the " + "segmentation else, could result in data loss
"); m_Controls.stopButton->setEnabled(false); } void QmitknnUNetToolGUI::ShowEnsembleLayout(bool visible) { if (m_EnsembleParams.empty()) { ctkCollapsibleGroupBox *groupBoxModel1 = new ctkCollapsibleGroupBox(this); auto lay1 = std::make_unique(groupBoxModel1); - m_EnsembleParams.push_back(std::move(lay1)); groupBoxModel1->setObjectName(QString::fromUtf8("model_1_Box")); groupBoxModel1->setTitle(QString::fromUtf8("Model 1")); groupBoxModel1->setMinimumSize(QSize(0, 0)); groupBoxModel1->setCollapsedHeight(5); groupBoxModel1->setCollapsed(false); groupBoxModel1->setFlat(true); groupBoxModel1->setAlignment(Qt::AlignRight); - m_Controls.advancedSettingsLayout->addWidget(groupBoxModel1, 3, 0, 1, 2); + m_Controls.advancedSettingsLayout->addWidget(groupBoxModel1, 4, 0, 1, 2); + connect(lay1->modelBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnModelChanged(const QString &))); + connect( + lay1->plannerBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnTrainerChanged(const QString &))); + m_EnsembleParams.push_back(std::move(lay1)); + ctkCollapsibleGroupBox *groupBoxModel2 = new ctkCollapsibleGroupBox(this); auto lay2 = std::make_unique(groupBoxModel2); - m_EnsembleParams.push_back(std::move(lay2)); groupBoxModel2->setObjectName(QString::fromUtf8("model_2_Box")); groupBoxModel2->setTitle(QString::fromUtf8("Model 2")); groupBoxModel2->setMinimumSize(QSize(0, 0)); groupBoxModel2->setCollapsedHeight(5); groupBoxModel2->setCollapsed(false); groupBoxModel2->setFlat(true); groupBoxModel2->setAlignment(Qt::AlignLeft); - m_Controls.advancedSettingsLayout->addWidget(groupBoxModel2, 3, 2, 1, 2); + m_Controls.advancedSettingsLayout->addWidget(groupBoxModel2, 4, 2, 1, 2); + + connect(lay2->modelBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnModelChanged(const QString &))); + connect( + lay2->plannerBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnTrainerChanged(const QString &))); + m_EnsembleParams.push_back(std::move(lay2)); } - for (std::unique_ptr &layout : m_EnsembleParams) + for (auto &layout : m_EnsembleParams) { - layout->parent->setVisible(visible); + layout->setVisible(visible); } }