diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetGPU.cpp b/Modules/SegmentationUI/Qmitk/QmitknnUNetGPU.cpp index 55710d9ef2..ecf51e1a6e 100644 --- a/Modules/SegmentationUI/Qmitk/QmitknnUNetGPU.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetGPU.cpp @@ -1,75 +1,48 @@ /*============================================================================ 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 ============================================================================*/ #include "QmitknnUNetGPU.h" #include #include #include #include QmitkGPULoader::QmitkGPULoader() { QProcess process; process.start("nvidia-smi --query-gpu=name,memory.free --format=csv"); process.waitForFinished(-1); QStringList infoStringList; while (process.canReadLine()) { QString line = process.readLine(); if (!line.startsWith("name")) { infoStringList << line; } } 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(); this->gpus.push_back(spec); } } int QmitkGPULoader::GetGPUCount() { return static_cast(gpus.size()); } - -int QmitkGPULoader::GetTotalGPUs() -{ -#if defined(__APPLE__) || defined(MACOSX) || defined(linux) || defined(__linux__) - QProcess process1, process2; - process1.setStandardOutputProcess(&process2); - process1.start("nvidia-smi -L"); - process2.start("wc -l"); - process1.waitForFinished(-1); - process2.waitForFinished(-1); - QString nGpus = process2.readAll(); - return nGpus.toInt(); -#elif defined(_WIN32) - QProcess process; - QStringList nGpus; - process.setReadChannel(QProcess::StandardOutput); - process.start("cmd", - QStringList() << "/c" - << "nvidia-smi -L"); - process.waitForFinished(-1); - while (process.canReadLine()) - { - nGpus << QString(process.readLine()); - } - return nGpus.size(); -#endif -} diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetGPU.h b/Modules/SegmentationUI/Qmitk/QmitknnUNetGPU.h index ec1cc97ad6..52cac71e5b 100644 --- a/Modules/SegmentationUI/Qmitk/QmitknnUNetGPU.h +++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetGPU.h @@ -1,41 +1,58 @@ /*============================================================================ 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 +/** + * @brief Struct to store GPU info. + * + */ struct QmitkGPUSpec { QString name; int memoryFree; unsigned int id; }; +/** + * @brief Class to load and save GPU information + * for further validation + */ class QmitkGPULoader : public QObject { Q_OBJECT private: std::vector gpus; - int GetTotalGPUs(); public: + /** + * @brief Construct a new Qmitk GPU Loader object. + * Parses GPU info using `nvidia-smi` command and saves it as QmitkGPUSpec objects. + */ QmitkGPULoader(); ~QmitkGPULoader() = default; + + /** + * @brief Returns the number of GPUs parsed and saved as QmitkGPUSpec objects. + * + * @return int + */ int GetGPUCount(); }; #endif diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.cpp index 91e8e95020..c9e15afdcd 100644 --- a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.cpp @@ -1,256 +1,256 @@ /*============================================================================ 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 (gpuLoader.GetGPUCount() == 0) + if (m_GpuLoader.GetGPUCount() == 0) { std::stringstream stream; stream << "WARNING: No GPUs were detected on your machine. The nnUNet plugin might not work."; QMessageBox *messageBox = new QMessageBox(QMessageBox::Critical, nullptr, stream.str().c_str()); messageBox->exec(); delete messageBox; MITK_WARN << stream.str(); } 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); #if defined(__APPLE__) || defined(MACOSX) || defined(linux) || defined(__linux__) m_Controls.pythonEnvComboBox->addItem("/usr/bin"); #endif m_Controls.pythonEnvComboBox->addItem("Select"); AutoParsePythonPaths(); connect(m_Controls.previewButton, SIGNAL(clicked()), this, SLOT(OnSettingsAccept())); 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.trainerBox, 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(OnPythonChanged(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: No Tasks Running. " + QString::number(gpuLoader.GetGPUCount()) + + m_Controls.statusLabel->setText("STATUS: No Tasks Running. " + QString::number(m_GpuLoader.GetGPUCount()) + " GPUs were detected."); - if (gpuLoader.GetGPUCount() != 0) + if (m_GpuLoader.GetGPUCount() != 0) { - m_Controls.gpuSpinBox->setMaximum(gpuLoader.GetGPUCount() - 1); + 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::OnSettingsAccept() { 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); #if defined(__APPLE__) || defined(MACOSX) || defined(linux) || defined(__linux__) if (!(pythonPath.endsWith("bin", Qt::CaseInsensitive) || pythonPath.endsWith("bin/", Qt::CaseInsensitive))) { pythonPath += QDir::separator() + QString("bin"); } #elif defined(_WIN32) if (!isNoPip && !(pythonPath.endsWith("Scripts", Qt::CaseInsensitive) || pythonPath.endsWith("Scripts/", Qt::CaseInsensitive))) { pythonPath += QDir::separator() + QString("Scripts"); } #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->otherModalPaths.clear(); tool->otherModalPaths = FetchMultiModalPathsFromUI(); tool->MultiModalOn(); } if (!m_SegmentationThread->isRunning()) { MITK_INFO << "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 &text) { return QFile::exists(text + QDir::separator() + QString("nnUNet_predict")); } \ No newline at end of file diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.h index d8c5e45a5d..0ddca92d47 100644 --- a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.h +++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.h @@ -1,199 +1,188 @@ /*============================================================================ 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 "QmitknnUNetWorker.h" #include "QmitknnUNetGPU.h" +#include "QmitknnUNetWorker.h" #include "mitknnUnetTool.h" #include "ui_QmitknnUNetToolGUIControls.h" #include #include #include #include class MITKSEGMENTATIONUI_EXPORT QmitknnUNetToolGUI : public QmitkAutoMLSegmentationToolGUIBase { Q_OBJECT public: mitkClassMacro(QmitknnUNetToolGUI, QmitkAutoMLSegmentationToolGUIBase); itkFactorylessNewMacro(Self); itkCloneMacro(Self); protected slots: /** * @brief Qt slot - * + * */ void OnSettingsAccept(); /** * @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 OnPythonChanged(const QString &); /** * @brief Qt slot - * + * */ void OnCheckBoxChanged(int); /** * @brief Qthread slot to captured 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); - signals: /** * @brief signal for starting the segmentation which is caught by a worker thread. */ void Operate(mitk::nnUNetTool *); protected: QmitknnUNetToolGUI(); ~QmitknnUNetToolGUI(); void ConnectNewTool(mitk::AutoSegmentationWithPreviewTool *newTool) override; void InitializeUI(QBoxLayout *mainLayout) override; void EnableWidgets(bool enabled) override; private: /** * @brief Searches and parses paths of python virtual enviroments * from predefined lookout locations */ void AutoParsePythonPaths(); - + /** * @brief Clears all combo boxes * Any new combo box added in the future can be featured here for clearance. - * + * */ void ClearAllComboBoxes(); - /** - * @brief Returns number of Nvidia GPUS available in the machine. - * - * The method essentially executes (Linux) `nvidia-smi -L | wc -l` or - * (Windows) `nvidia-smi -L` and counts the output and returns an integer. - * @return int - */ - //int GetGPUCount(); - /** * @brief Checks if nnUNet_predict command is valid in the selected python virtual environment. - * - * @return bool + * + * @return bool */ - bool IsNNUNetInstalled(const QString&); - + bool IsNNUNetInstalled(const QString &); /** * @brief Mapper function to map QString entries from UI to ModelParam attributes. - * - * @return mitk::ModelParams + * + * @return mitk::ModelParams */ mitk::ModelParams MapToRequest(QString &, QString &, QString &, QString &, std::vector &); /** * @brief Returns checked fold names from the ctk-Checkable-ComboBox. - * - * @return std::vector + * + * @return std::vector */ std::vector FetchSelectedFoldsFromUI(); /** * @brief Returns all paths from the dynamically generated ctk-path-line-edit boxes. - * - * @return std::vector + * + * @return std::vector */ std::vector FetchMultiModalPathsFromUI(); /** * @brief Template function to fetch all folders inside a given path. * The type can be any of stl or Qt containers which supports push_back call. - * - * @tparam T - * @return T + * + * @tparam T + * @return T */ template static T FetchFoldersFromDir(const QString &); /** * @brief Stores path of the model director (RESULTS_FOLDER appended by "nnUNet"). - * + * */ QString m_ModelDirectory; Ui_QmitknnUNetToolGUIControls m_Controls; QThread *m_SegmentationThread; nnUNetSegmentationWorker *m_Worker; - QmitkGPULoader gpuLoader; + QmitkGPULoader m_GpuLoader; /** * @brief Stores all dynamically added ctk-path-line-edit UI elements. - * + * */ std::vector m_ModalPaths; /** - * @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. - * + * @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; }; #endif