diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.cpp index a3d04dbc17..741e1c3f9d 100644 --- a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.cpp @@ -1,271 +1,269 @@ /*============================================================================ 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. // Pytorch uses its own libraries to communicate to the GPUs. Hence, only a warning can be given. if (m_GpuLoader.GetGPUCount() == 0) { - ShowErrorMessage(std::string("WARNING: No GPUs were detected on your machine. The nnUNet tool might not work.")); + std::string warning= "WARNING: No GPUs were detected on your machine. The nnUNet tool might not work."; + ShowErrorMessage(warning); } m_SegmentationThread = new QThread(this); m_Worker = new nnUNetSegmentationWorker; m_Worker->moveToThread(m_SegmentationThread); } QmitknnUNetToolGUI::~QmitknnUNetToolGUI() { this->m_SegmentationThread->quit(); this->m_SegmentationThread->wait(); } 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(); connect(m_Controls.previewButton, SIGNAL(clicked()), this, SLOT(OnSettingsAccepted())); 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.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(this, &QmitknnUNetToolGUI::Operate, m_Worker, &nnUNetSegmentationWorker::DoWork); connect(m_Worker, &nnUNetSegmentationWorker::Finished, this, &QmitknnUNetToolGUI::SegmentationResultHandler); connect(m_Worker, &nnUNetSegmentationWorker::Failed, this, &QmitknnUNetToolGUI::SegmentationProcessFailed); connect(m_SegmentationThread, &QThread::finished, m_Worker, &QObject::deleteLater); m_Controls.codedirectoryBox->setVisible(false); m_Controls.nnUnetdirLabel->setVisible(false); m_Controls.multiModalSpinBox->setVisible(false); m_Controls.multiModalSpinLabel->setVisible(false); 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); } mainLayout->addLayout(m_Controls.verticalLayout); Superclass::InitializeUI(mainLayout); m_UI_ROWS = m_Controls.advancedSettingsLayout->rowCount(); // Must do. Row count is correct only here. } void QmitknnUNetToolGUI::OnSettingsAccepted() { auto tool = this->GetConnectedToolAs(); if (nullptr != tool) { try { QString modelName = m_Controls.modelBox->currentText(); QString taskName = m_Controls.taskBox->currentText(); bool isNoPip = m_Controls.nopipBox->isChecked(); QString pythonPathTextItem = m_Controls.pythonEnvComboBox->currentText(); QString pythonPath = pythonPathTextItem.mid(pythonPathTextItem.indexOf(" ") + 1); #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."); } QString trainerPlanner = m_Controls.trainerBox->currentText(); QString splitterString = "__"; tool->EnsembleOff(); if (modelName.startsWith("ensemble", Qt::CaseInsensitive)) { QString ppJsonFile = QDir::cleanPath(m_ModelDirectory + QDir::separator() + modelName + QDir::separator() + taskName + QDir::separator() + trainerPlanner + QDir::separator() + "postprocessing.json"); if (QFile(ppJsonFile).exists()) { tool->EnsembleOn(); tool->SetPostProcessingJsonDirectory(ppJsonFile.toStdString()); splitterString = "--"; } } QStringList trainerSplitParts = trainerPlanner.split(splitterString, QString::SplitBehavior::SkipEmptyParts); std::vector requestQ; if (tool->GetEnsemble()) { 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); auto testfold = std::vector(1, "1"); mitk::ModelParams modelObject = MapToRequest(modelName, taskName, trainer, planId, testfold); requestQ.push_back(modelObject); } } else { QString trainer = trainerSplitParts.first(); QString planId = trainerSplitParts.last(); std::vector fetchedFolds = FetchSelectedFoldsFromUI(); mitk::ModelParams modelObject = MapToRequest(modelName, taskName, trainer, planId, fetchedFolds); requestQ.push_back(modelObject); } tool->m_ParamQ.clear(); tool->m_ParamQ = requestQ; tool->SetnnUNetDirectory(nnUNetDirectory); tool->SetPythonPath(pythonPath.toStdString()); tool->SetModelDirectory(m_ModelDirectory.left(m_ModelDirectory.lastIndexOf(QDir::separator())).toStdString()); // checkboxes tool->SetMirror(m_Controls.mirrorBox->isChecked()); tool->SetMixedPrecision(m_Controls.mixedPrecisionBox->isChecked()); tool->SetNoPip(isNoPip); tool->SetMultiModal(m_Controls.multiModalBox->isChecked()); // Spinboxes tool->SetGpuId(static_cast(m_Controls.gpuSpinBox->value())); // Multi-Modal tool->MultiModalOff(); if (m_Controls.multiModalBox->isChecked()) { tool->m_OtherModalPaths.clear(); tool->m_OtherModalPaths = FetchMultiModalPathsFromUI(); tool->MultiModalOn(); } if (!m_SegmentationThread->isRunning()) { MITK_DEBUG << "Starting thread..."; m_SegmentationThread->start(); } m_Controls.statusLabel->setText("STATUS: Starting Segmentation task... This might take a while."); emit Operate(tool); } catch (const std::exception &e) { this->setCursor(Qt::ArrowCursor); std::stringstream stream; stream << "Error while processing parameters for nnUNet segmentation. Reason: " << e.what(); QMessageBox *messageBox = new QMessageBox(QMessageBox::Critical, nullptr, stream.str().c_str()); messageBox->exec(); delete messageBox; MITK_ERROR << stream.str(); return; } catch (...) { this->setCursor(Qt::ArrowCursor); std::stringstream stream; stream << "Unkown error occured while generation nnUNet segmentation."; QMessageBox *messageBox = new QMessageBox(QMessageBox::Critical, nullptr, stream.str().c_str()); messageBox->exec(); delete messageBox; MITK_ERROR << stream.str(); return; } } } std::vector QmitknnUNetToolGUI::FetchMultiModalPathsFromUI() { std::vector paths; if (m_Controls.multiModalBox->isChecked() && !m_ModalPaths.empty()) { for (auto modality : m_ModalPaths) { paths.push_back(modality->currentPath().toStdString()); } } return paths; } 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 return QFile::exists(fullPath + QDir::separator() + QString("nnUNet_predict")); } void QmitknnUNetToolGUI::ShowErrorMessage(std::string &message) { this->setCursor(Qt::ArrowCursor); QMessageBox *messageBox = new QMessageBox(QMessageBox::Critical, nullptr, message.c_str()); messageBox->exec(); delete messageBox; MITK_WARN << message; } diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolSlots.cpp b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolSlots.cpp index fb4a42053d..e69a15b970 100644 --- a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolSlots.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolSlots.cpp @@ -1,318 +1,286 @@ #include "QmitknnUNetToolGUI.h" #include #include #include -#include -#include -#include #include -#include -#include -#include void QmitknnUNetToolGUI::EnableWidgets(bool enabled) { Superclass::EnableWidgets(enabled); m_Controls.previewButton->setEnabled(false); } void QmitknnUNetToolGUI::ClearAllComboBoxes() { m_Controls.modelBox->clear(); m_Controls.taskBox->clear(); m_Controls.foldBox->clear(); m_Controls.trainerBox->clear(); } template T QmitknnUNetToolGUI::FetchFoldersFromDir(const QString &path) { T folders; for (QDirIterator it(path, QDir::AllDirs, QDirIterator::NoIteratorFlags); it.hasNext();) { it.next(); if (!it.fileName().startsWith('.')) { folders.push_back(it.fileName()); } } return folders; } void QmitknnUNetToolGUI::OnDirectoryChanged(const QString &resultsFolder) { m_Controls.previewButton->setEnabled(false); this->ClearAllComboBoxes(); m_ModelDirectory = resultsFolder + QDir::separator() + "nnUNet"; auto models = FetchFoldersFromDir(m_ModelDirectory); QStringList validlist; // valid list of models supported by nnUNet validlist << "2d" << "3d_lowres" << "3d_fullres" << "3d_cascade_fullres" << "ensembles"; std::for_each(models.begin(), models.end(), [this, validlist](QString model) { if (validlist.contains(model, Qt::CaseInsensitive)) m_Controls.modelBox->addItem(model); }); } void QmitknnUNetToolGUI::OnModelChanged(const QString &text) { QString updatedPath(QDir::cleanPath(m_ModelDirectory + QDir::separator() + text)); m_Controls.taskBox->clear(); auto datasets = FetchFoldersFromDir(updatedPath); std::for_each(datasets.begin(), datasets.end(), [this](QString dataset) { m_Controls.taskBox->addItem(dataset); }); } void QmitknnUNetToolGUI::OnTaskChanged(const QString &text) { QString updatedPath = QDir::cleanPath(m_ModelDirectory + QDir::separator() + m_Controls.modelBox->currentText() + QDir::separator() + text); m_Controls.trainerBox->clear(); auto trainerPlanners = FetchFoldersFromDir(updatedPath); QStringList trainers, planners; foreach (QString trainerPlanner, trainerPlanners) { trainers << trainerPlanner.split("__", QString::SplitBehavior::SkipEmptyParts).first(); planners << trainerPlanner.split("__", QString::SplitBehavior::SkipEmptyParts).last(); } trainers.removeDuplicates(); planners.removeDuplicates(); 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 &trainerSelected) { m_Controls.foldBox->clear(); if (m_Controls.modelBox->currentText() != "ensembles") { QString updatedPath(QDir::cleanPath(m_ModelDirectory + QDir::separator() + m_Controls.modelBox->currentText() + QDir::separator() + m_Controls.taskBox->currentText() + QDir::separator() + m_Controls.trainerBox->currentText() + "__" + trainerSelected)); auto folds = FetchFoldersFromDir(updatedPath); 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) { m_Controls.previewButton->setEnabled(true); } } else { m_Controls.previewButton->setEnabled(true); } } 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 + else if (!IsNNUNetInstalled(pyEnv)) { - if (!IsNNUNetInstalled(pyEnv)) - { - //QString nnUNetPath = - // "C://DKFZ//nnUNet_work//nnUNet//nnunet//inference//pretrained_models//download_pretrained_model.py"; - //QFile file(nnUNetPath); - //if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) - // return; - //QTextStream in(&file); - //QString lines = in.readAll(); - //int startPos = lines.indexOf("available_models =") + 18; - //int endPos = lines.indexOf("return available_models"); - //int length = endPos - startPos; - //QString pyFn = lines.mid(startPos,length); - //MITK_INFO << pyFn.toStdString(); - //QStringList pyFnLines = pyFn.split(QRegExp("[\r\n]"), QString::SkipEmptyParts); - //for (i = pyFnLines.begin(); i != pyFnLines.end(); ++i) - //{ - - //} - // - //std::for_each(keys.begin(), keys.end(), [this](QString planner) { m_Controls.taskComboBox->addItem(planner); }); - - // - //} - //else - //{ - ShowErrorMessage( - std::string("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")); - } + 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); if (!visibility) { OnModalitiesNumberChanged(0); m_Controls.multiModalSpinBox->setValue(0); } else { ctkPathLineEdit *multiModalPath = new ctkPathLineEdit(this); QSpinBox *multiModalOrderBox = new QSpinBox(this); multiModalPath->setObjectName(QString("multiModalPath" + QString::number(0))); multiModalPath->setCurrentPath("default_loaded_image"); multiModalPath->setDisabled(true); m_Controls.advancedSettingsLayout->addWidget( multiModalPath, this->m_UI_ROWS + m_ModalPaths.size() + 1, 1, 1, 3); m_Controls.advancedSettingsLayout->addWidget(multiModalOrderBox, this->m_UI_ROWS + m_ModalPaths.size() + 1, 0); m_ModalPaths.push_back(multiModalPath); m_ModalOrder.push_back(multiModalOrderBox); m_UI_ROWS += 1; } } } } void QmitknnUNetToolGUI::OnModalitiesNumberChanged(int num) { while (num > static_cast(this->m_ModalPaths.size())) { ctkPathLineEdit *multiModalPath = new ctkPathLineEdit(this); QSpinBox *multiModalOrderBox = new QSpinBox(this); multiModalPath->setObjectName(QString("multiModalPath" + QString::number(m_ModalPaths.size() + 1))); m_Controls.advancedSettingsLayout->addWidget(multiModalPath, this->m_UI_ROWS + m_ModalPaths.size() + 1, 1, 1, 3); m_Controls.advancedSettingsLayout->addWidget(multiModalOrderBox, this->m_UI_ROWS + m_ModalPaths.size() + 1, 0); m_ModalPaths.push_back(multiModalPath); m_ModalOrder.push_back(multiModalOrderBox); } while (num < static_cast(this->m_ModalPaths.size() - 1) && !m_ModalPaths.empty()) { ctkPathLineEdit *child = m_ModalPaths.back(); delete child; // delete the layout item m_ModalPaths.pop_back(); auto *_child = m_ModalOrder.back(); delete _child; // delete the layout item m_ModalOrder.pop_back(); } 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; return requestObject; } void QmitknnUNetToolGUI::SegmentationProcessFailed() { m_Controls.statusLabel->setText( "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."; QMessageBox *messageBox = new QMessageBox(QMessageBox::Critical, nullptr, stream.str().c_str()); messageBox->exec(); delete messageBox; MITK_ERROR << stream.str(); } void QmitknnUNetToolGUI::SegmentationResultHandler(mitk::nnUNetTool *tool) { MITK_INFO << "Finished slot"; tool->RenderOutputBuffer(); this->SetLabelSetPreview(tool->GetMLPreview()); tool->IsTimePointChangeAwareOn(); m_Controls.statusLabel->setText("STATUS: Segmentation task finished successfully."); }