diff --git a/Modules/Segmentation/Interactions/mitkTotalSegmentatorTool.cpp b/Modules/Segmentation/Interactions/mitkTotalSegmentatorTool.cpp index c26e3457b1..27d659f45f 100644 --- a/Modules/Segmentation/Interactions/mitkTotalSegmentatorTool.cpp +++ b/Modules/Segmentation/Interactions/mitkTotalSegmentatorTool.cpp @@ -1,262 +1,270 @@ /*============================================================================ 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. ============================================================================*/ // MITK #include "mitkTotalSegmentatorTool.h" #include "mitkIOUtil.h" #include #include // us #include #include #include #include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, TotalSegmentatorTool, "Total Segmentator"); } mitk::TotalSegmentatorTool::~TotalSegmentatorTool() { itksys::SystemTools::RemoveADirectory(this->GetMitkTempDir()); } void mitk::TotalSegmentatorTool::Activated() { Superclass::Activated(); this->SetLabelTransferMode(LabelTransferMode::AllLabels); } const char **mitk::TotalSegmentatorTool::GetXPM() const { return nullptr; } us::ModuleResource mitk::TotalSegmentatorTool::GetIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("AI.svg"); return resource; } const char *mitk::TotalSegmentatorTool::GetName() const { return "Total Segmentator"; } void mitk::TotalSegmentatorTool::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; } } void mitk::TotalSegmentatorTool::DoUpdatePreview(const Image *inputAtTimeStep, const Image * /*oldSegAtTimeStep*/, LabelSetImage *previewImage, TimeStepType timeStep) { if (!m_IsMitkTempDirAvailable) // create temp directory if not done already { this->SetMitkTempDir(IOUtil::CreateTemporaryDirectory("mitk-XXXXXX")); m_IsMitkTempDirAvailable = true; } this->ParseLabelNames(this->GetLabelMapPath()); ProcessExecutor::Pointer spExec = ProcessExecutor::New(); itk::CStyleCommand::Pointer spCommand = itk::CStyleCommand::New(); spCommand->SetCallback(&onPythonProcessEvent); spExec->AddObserver(ExternalProcessOutputEvent(), spCommand); std::string inDir, outDir, inputImagePath, outputImagePath, scriptPath; inDir = IOUtil::CreateTemporaryDirectory("totalseg-in-XXXXXX", this->GetMitkTempDir()); std::ofstream tmpStream; 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("_")); outDir = IOUtil::CreateTemporaryDirectory("totalseg-out-XXXXXX", this->GetMitkTempDir()); outputImagePath = outDir + IOUtil::GetDirectorySeparator() + token + "_000.nii.gz"; ProcessExecutor::ArgumentListType args; IOUtil::Save(inputAtTimeStep, inputImagePath); std::string *outArg = &outputImagePath; bool isSubTask = false; if (this->GetSubTask() != m_DEFAULT_TOTAL_TASK) { isSubTask = true; outputImagePath = outDir + IOUtil::GetDirectorySeparator() + this->GetSubTask() + ".nii.gz"; outArg = &outDir; } this->run_totalsegmentator( spExec, inputImagePath, *outArg, this->GetFast(), !isSubTask, this->GetGpuId(), m_DEFAULT_TOTAL_TASK); if (isSubTask) { this->run_totalsegmentator( spExec, inputImagePath, *outArg, !isSubTask, !isSubTask, this->GetGpuId(), this->GetSubTask()); } Image::Pointer outputImage = IOUtil::Load(outputImagePath); auto outputBuffer = mitk::LabelSetImage::New(); outputBuffer->InitializeByLabeledImage(outputImage); outputBuffer->SetGeometry(inputAtTimeStep->GetGeometry()); this->MapLabelsToSegmentation(outputBuffer, m_LabelMapTotal); TransferLabelSetImageContent(outputBuffer, previewImage, timeStep); } void mitk::TotalSegmentatorTool::run_totalsegmentator(ProcessExecutor::Pointer spExec, const std::string &inputImagePath, const std::string &outputImagePath, bool isFast, bool isMultiLabel, unsigned int gpuId, const std::string &subTask) { ProcessExecutor::ArgumentListType args; std::string command = "TotalSegmentator"; #ifdef _WIN32 command = "python"; #else command = "TotalSegmentator"; #endif args.clear(); #ifdef _WIN32 - args.push_back("TotalSegmentator"); + std::string ending = "Scripts"; + if (0 == this->GetPythonPath().compare(this->GetPythonPath().length() - ending.length(), ending.length(), ending)) + { + args.push_back("TotalSegmentator"); + } + else + { + args.push_back("Scripts/TotalSegmentator"); + } #endif args.push_back("-i"); args.push_back(inputImagePath); args.push_back("-o"); args.push_back(outputImagePath); if (subTask != m_DEFAULT_TOTAL_TASK) { args.push_back("-ta"); args.push_back(subTask); } if (isMultiLabel) { args.push_back("--ml"); } if (isFast) { args.push_back("--fast"); } try { std::string cudaEnv = "CUDA_VISIBLE_DEVICES=" + std::to_string(gpuId); itksys::SystemTools::PutEnv(cudaEnv.c_str()); for (auto &arg : args) MITK_INFO << arg; MITK_INFO << this->GetPythonPath(); spExec->Execute(this->GetPythonPath(), command, args); } catch (const mitk::Exception &e) { MITK_ERROR << e.GetDescription(); return; } } void mitk::TotalSegmentatorTool::ParseLabelNames(const std::string &fileName) { std::fstream newfile; newfile.open(fileName, ios::in); std::stringstream buffer; if (newfile.is_open()) { int line = 0; std::string temp; while (std::getline(newfile, temp)) { if (line > 1 && line < 106) { buffer << temp; } ++line; } } std::string key, val; while (std::getline(std::getline(buffer, key, ':'), val, ',')) { m_LabelMapTotal[std::stoi(key)] = val; } } void mitk::TotalSegmentatorTool::MapLabelsToSegmentation(mitk::LabelSetImage::Pointer outputBuffer, std::map &labelMap) { for (auto const &[key, val] : labelMap) { mitk::Label *labelptr = outputBuffer->GetLabel(key, 0); if (nullptr != labelptr) { labelptr->SetName(val); } } } std::string mitk::TotalSegmentatorTool::GetLabelMapPath() { std::string pythonFileName; std::filesystem::path pathToLabelMap(this->GetPythonPath()); pathToLabelMap = pathToLabelMap.parent_path(); #ifdef _WIN32 pythonFileName = pathToLabelMap.string() + "/Lib/site-packages/totalsegmentator/map_to_binary.py"; #else pathToLabelMap.append("lib"); for (auto const &dir_entry : std::filesystem::directory_iterator{pathToLabelMap}) { if (dir_entry.is_directory()) { auto dirName = dir_entry.path().filename().string(); if (dirName.rfind("python", 0) == 0) { pathToLabelMap.append(dir_entry.path().filename().string()); break; } } } pythonFileName = pathToLabelMap.string() + "/site-packages/totalsegmentator/map_to_binary.py"; #endif return pythonFileName; } diff --git a/Modules/SegmentationUI/Qmitk/QmitkSetupVirtualEnvUtil.cpp b/Modules/SegmentationUI/Qmitk/QmitkSetupVirtualEnvUtil.cpp index ac2972cab2..5bd21ddb6c 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkSetupVirtualEnvUtil.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkSetupVirtualEnvUtil.cpp @@ -1,223 +1,221 @@ /*============================================================================ 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 "QmitkSetupVirtualEnvUtil.h" #include "mitkLogMacros.h" #include #include #include #include QmitkSetupVirtualEnvUtil::QmitkSetupVirtualEnvUtil() { m_BaseDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QDir::separator() + qApp->organizationName() + QDir::separator(); } QmitkSetupVirtualEnvUtil::QmitkSetupVirtualEnvUtil(const QString baseDir) { m_BaseDir = baseDir; } QString QmitkSetupVirtualEnvUtil::GetBaseDir() { return m_BaseDir; } QString QmitkSetupVirtualEnvUtil::GetVirtualEnvPath() { return m_venvPath; } -void QmitkSetupVirtualEnvUtil::SetSystemPythonPath(QString& path) +void QmitkSetupVirtualEnvUtil::SetVirtualEnvPath(const QString &path) { - if (IsPythonPath(path)) + m_venvPath = path; +} + +void QmitkSetupVirtualEnvUtil::SetSystemPythonPath(const QString& path) +{ + if (this->IsPythonPath(path)) { m_SysPythonPath = path; - MITK_ERROR << "Python was detected in " + path.toStdString(); } else { - MITK_ERROR << "Python was not detected in "+path.toStdString(); + MITK_INFO << "Python was not detected in "+path.toStdString(); } } std::map QmitkSetupVirtualEnvUtil::GetInstallParameters( QmitkSetupVirtualEnvUtil::Tool packageName) { std::map parameters; switch (packageName) { case QmitkSetupVirtualEnvUtil::Tool::TOTALSEGMENTATOR: { parameters["venv_name"] = ".totalsegmentator"; - parameters["pkg_version"] = "1.5.2"; - parameters["pkg_names"] = "Totalsegmentator==1.5.2,scipy==1.9.1"; // comma separated string + parameters["pkg_version"] = "1.5.3"; + parameters["pkg_names"] = "Totalsegmentator==1.5.3,scipy==1.9.1"; // comma separated string /*Add other parameters here...*/ break; } /*Add more tools here...*/ default: { std::runtime_error("Unsupported Tool type"); break; } } return parameters; } void QmitkSetupVirtualEnvUtil::PrintProcessEvent(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; } } bool QmitkSetupVirtualEnvUtil::SetupVirtualEnv(QmitkSetupVirtualEnvUtil::Tool packageName) { + MITK_INFO << m_SysPythonPath.toStdString(); + if (m_SysPythonPath.isEmpty()) + { + return false; + } std::vector packages; std::map params = GetInstallParameters(packageName); std::stringstream pkgs(params["pkg_names"]); while (pkgs.good()) { std::string pkg; std::getline(pkgs, pkg, ','); packages.push_back(pkg); } QString venvName = QString::fromStdString(params["venv_name"]); QDir folderPath(m_BaseDir); folderPath.mkdir(venvName); if (!folderPath.cd(venvName)) { return false; // Check if directory creation was successful. } mitk::ProcessExecutor::ArgumentListType args; auto spExec = mitk::ProcessExecutor::New(); auto spCommand = itk::CStyleCommand::New(); spCommand->SetCallback(&PrintProcessEvent); spExec->AddObserver(mitk::ExternalProcessOutputEvent(), spCommand); args.push_back("-m"); args.push_back("venv"); args.push_back(venvName.toStdString()); #ifdef _WIN32 - MITK_INFO << m_SysPythonPath.toStdString(); - spExec->Execute( - m_BaseDir.toStdString(), "C:/ProgramData/Anaconda3/python.exe", args); // Setup local virtual environment + QString pythonFile = m_SysPythonPath + QDir::separator() + "python.exe"; + spExec->Execute(m_BaseDir.toStdString(), pythonFile.toStdString(), args); // Setup local virtual environment QString pythonExeFolder = "Scripts"; #else + QString pythonFile = m_SysPythonPath + QDir::separator() + "python3"; spExec->Execute(m_BaseDir.toStdString(), "/usr/bin/python3", args); // Setup local virtual environment QString pythonExeFolder = "bin"; #endif if (folderPath.cd(pythonExeFolder)) { m_venvPath = folderPath.absolutePath(); std::string workingDir = m_venvPath.toStdString(); InstallPytorch(workingDir, &PrintProcessEvent); for (auto &package : packages) { PipInstall(package, workingDir, &PrintProcessEvent); } std::string pythonCode; // python syntax to check if torch is installed with CUDA. pythonCode.append("import torch;"); pythonCode.append("print('Pytorch was installed with CUDA') if torch.cuda.is_available() else print('PyTorch was " "installed WITHOUT CUDA');"); ExecutePython(pythonCode, workingDir, &PrintProcessEvent); return true; } return false; } void QmitkSetupVirtualEnvUtil::InstallPytorch(const std::string &workingDir, void (*callback)(itk::Object *, const itk::EventObject &, void *)) { mitk::ProcessExecutor::ArgumentListType args; auto spExec = mitk::ProcessExecutor::New(); auto spCommand = itk::CStyleCommand::New(); spCommand->SetCallback(callback); spExec->AddObserver(mitk::ExternalProcessOutputEvent(), spCommand); args.push_back("-m"); args.push_back("pip"); args.push_back("install"); args.push_back("light-the-torch"); spExec->Execute(workingDir, "python", args); PipInstall("torch", workingDir, callback, "ltt"); } void QmitkSetupVirtualEnvUtil::PipInstall(const std::string &library, const std::string &workingDir, void (*callback)(itk::Object *, const itk::EventObject &, void *), const std::string &command) { mitk::ProcessExecutor::ArgumentListType args; auto spExec = mitk::ProcessExecutor::New(); auto spCommand = itk::CStyleCommand::New(); spCommand->SetCallback(callback); spExec->AddObserver(mitk::ExternalProcessOutputEvent(), spCommand); args.push_back("install"); args.push_back(library); spExec->Execute(workingDir, command, args); } void QmitkSetupVirtualEnvUtil::ExecutePython(const std::string &pythonCode, const std::string &workingDir, void (*callback)(itk::Object *, const itk::EventObject &, void *), const std::string &command) { mitk::ProcessExecutor::ArgumentListType args; auto spExec = mitk::ProcessExecutor::New(); auto spCommand = itk::CStyleCommand::New(); spCommand->SetCallback(callback); spExec->AddObserver(mitk::ExternalProcessOutputEvent(), spCommand); args.push_back("-c"); args.push_back(pythonCode); spExec->Execute(workingDir, command, args); } bool QmitkSetupVirtualEnvUtil::IsPythonPath(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); bool isExists = #ifdef _WIN32 QFile::exists(fullPath + QDir::separator() + QString("python.exe")); #else QFile::exists(fullPath + QDir::separator() + QString("python3")); #endif return isExists; } \ No newline at end of file diff --git a/Modules/SegmentationUI/Qmitk/QmitkSetupVirtualEnvUtil.h b/Modules/SegmentationUI/Qmitk/QmitkSetupVirtualEnvUtil.h index 5d6af1af84..808b060ead 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkSetupVirtualEnvUtil.h +++ b/Modules/SegmentationUI/Qmitk/QmitkSetupVirtualEnvUtil.h @@ -1,60 +1,63 @@ /*============================================================================ 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 QmitkSetupVirtualEnvUtil_h_Included #define QmitkSetupVirtualEnvUtil_h_Included #include "mitkLogMacros.h" #include "mitkProcessExecutor.h" #include #include /** * @brief Class to ... */ class MITKSEGMENTATIONUI_EXPORT QmitkSetupVirtualEnvUtil { public: enum Tool { TOTALSEGMENTATOR }; QmitkSetupVirtualEnvUtil(const QString baseDir); QmitkSetupVirtualEnvUtil(); bool SetupVirtualEnv(Tool packageName = Tool::TOTALSEGMENTATOR); void PipInstall(const std::string &library, const std::string &workingDir, void (*callback)(itk::Object *, const itk::EventObject &, void *), const std::string &command = "pip3"); void ExecutePython(const std::string &args, const std::string &pythonPath, void (*callback)(itk::Object *, const itk::EventObject &, void *), const std::string &command = "python"); void InstallPytorch(const std::string &workingDir, void (*callback)(itk::Object *, const itk::EventObject &, void *)); std::map GetInstallParameters(QmitkSetupVirtualEnvUtil::Tool); QString GetBaseDir(); QString GetVirtualEnvPath(); - void SetSystemPythonPath(QString& path); + void SetSystemPythonPath(const QString& path); + void SetVirtualEnvPath(const QString &path); bool IsPythonPath(const QString &pythonPath); static void PrintProcessEvent(itk::Object * /*pCaller*/, const itk::EventObject &e, void *); private: + QString m_PythonPath; + QString m_PipPath; QString m_BaseDir; QString m_venvPath; QString m_SysPythonPath; }; #endif \ No newline at end of file diff --git a/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorGUIControls.ui b/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorGUIControls.ui index d055d68ed2..46bf777f20 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorGUIControls.ui +++ b/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorGUIControls.ui @@ -1,257 +1,293 @@ QmitkTotalSegmentatorToolGUIControls 0 0 699 352 0 0 100 0 100000 100000 QmitkTotalSegmentatorToolWidget 0 0 0 0 0 0 <html><head/><body><p>Welcome to Total Segmentator tool in MITK. [Experimental]</p><p>Please note that this is only an interface to Total Segmentator. MITK does not ship with Total Segmentator. Make sure to have a working Python environment with TotalSegmentator set up beforehand. Choose that environment in the Python Path before inferencing. </p><p>Refer to <a href="https://github.com/wasserth/TotalSegmentator"><span style=" text-decoration: underline; color:#0000ff;">https://github.com/wasserth/TotalSegmentator</span></a> to learn everything about the Total Segmentator.</p><p><br/></p></body></html> Qt::RichText true 0 0 100000 16777215 Install Total Segmentator - - + 0 0 Task: 0 0 Advanced true 5 true 0 0 0 0 6 - + + + + + 0 + 0 + + + + Fast + + + + + + + true + + + + + + + + 0 + 0 + + + + Override install + + + + + + + false + + + + 0 0 GPU Id: - + - - - - false - - - - - - - - 0 - 0 - - - - Fast - - - - + + 0 0 - System Python: + Override Python: - + + + + + + 0 + 0 + + + + System Python: + + + + + + 0 0 100000 16777215 Run Total Segmentator 0 0 true ctkComboBox QComboBox
ctkComboBox.h
1
ctkCollapsibleGroupBox QWidget
ctkCollapsibleGroupBox.h
ctkCheckBox QWidget
ctkCheckBox.h
diff --git a/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.cpp index 3a8264cf4a..7e4fcf7b78 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.cpp @@ -1,326 +1,395 @@ #include "QmitkTotalSegmentatorToolGUI.h" #include "mitkProcessExecutor.h" #include "mitkTotalSegmentatorTool.h" #include #include #include #include #include #include #include #include #include MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitkTotalSegmentatorToolGUI, "") QmitkTotalSegmentatorToolGUI::QmitkTotalSegmentatorToolGUI() : QmitkMultiLabelSegWithPreviewToolGUIBase(), m_SuperclassEnableConfirmSegBtnFnc(m_EnableConfirmSegBtnFnc) { // 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 TotalSegmentator tool can be very slow."; this->ShowErrorMessage(warning); } m_EnableConfirmSegBtnFnc = [this](bool enabled) { return !m_FirstPreviewComputation ? m_SuperclassEnableConfirmSegBtnFnc(enabled) : false; }; } void QmitkTotalSegmentatorToolGUI::ConnectNewTool(mitk::SegWithPreviewTool *newTool) { Superclass::ConnectNewTool(newTool); newTool->IsTimePointChangeAwareOff(); m_FirstPreviewComputation = true; } void QmitkTotalSegmentatorToolGUI::InitializeUI(QBoxLayout *mainLayout) { m_Controls.setupUi(this); +#ifndef _WIN32 + m_Controls.sysPythonComboBox->addItem("/usr/bin"); +#endif + m_Controls.sysPythonComboBox->addItem("Select"); + this->AutoParsePythonPaths(); + + m_Controls.pythonEnvComboBox->addItem("Default"); m_Controls.pythonEnvComboBox->addItem("Select"); m_Controls.pythonEnvComboBox->setDuplicatesEnabled(false); + m_Controls.pythonEnvComboBox->setDisabled(true); m_Controls.previewButton->setDisabled(true); m_Controls.statusLabel->setTextFormat(Qt::RichText); m_Controls.subtaskComboBox->addItems(m_VALID_TASKS); - this->AutoParsePythonPaths(); + QString welcomeText; this->SetGPUInfo(); if (m_GpuLoader.GetGPUCount() != 0) { - WriteStatusMessage(QString("STATUS: Welcome to Total Segmentator tool. You're in luck: " + - QString::number(m_GpuLoader.GetGPUCount()) + " GPU(s) were detected.")); + welcomeText = "STATUS: Welcome to Total Segmentator tool. You're in luck: " + + QString::number(m_GpuLoader.GetGPUCount()) + " GPU(s) were detected."; } else { - WriteErrorMessage(QString("STATUS: Welcome to Total Segmentator tool. Sorry, " + - QString::number(m_GpuLoader.GetGPUCount()) + " GPUs were detected.")); + welcomeText = "STATUS: Welcome to Total Segmentator tool. Sorry, " + + QString::number(m_GpuLoader.GetGPUCount()) + " GPUs were detected."; } mainLayout->addLayout(m_Controls.verticalLayout); connect(m_Controls.previewButton, SIGNAL(clicked()), this, SLOT(OnPreviewBtnClicked())); connect(m_Controls.installButton, SIGNAL(clicked()), this, SLOT(OnInstallBtnClicked())); - connect(m_Controls.pythonEnvComboBox, - SIGNAL(currentTextChanged(const QString &)), - this, - SLOT(OnPythonPathChanged(const QString &))); + connect(m_Controls.overrideBox, SIGNAL(stateChanged(int)), this, SLOT(OnOverrideChecked(int))); Superclass::InitializeUI(mainLayout); - - QString lastSelectedPyEnv = m_Settings.value("TotalSeg/LastPythonPath").toString(); - m_Controls.pythonEnvComboBox->insertItem(0, lastSelectedPyEnv); - + //QString lastSelectedPyEnv = m_Settings.value("TotalSeg/LastPythonPath").toString(); + //m_Controls.pythonEnvComboBox->insertItem(0, lastSelectedPyEnv); const QString storageDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QDir::separator() + - qApp->organizationName() + QDir::separator() + m_VENV_NAME; + qApp->organizationName() + QDir::separator() + m_VENV_NAME; m_IsInstalled = this->IsTotalSegmentatorInstalled(storageDir); if (m_IsInstalled) { m_PythonPath = GetPythonPathFromUI(storageDir); + m_Installer.SetVirtualEnvPath(m_PythonPath); this->EnableAll(m_IsInstalled); + welcomeText += " Totalsegmentator is already found installed"; } + else + { + welcomeText += " Totalsegmentator not installed. Please click on \"Install Totalsegmentator\" above."; + } + this->WriteStatusMessage(welcomeText); } void QmitkTotalSegmentatorToolGUI::EnableWidgets(bool enabled) { Superclass::EnableWidgets(enabled); } void QmitkTotalSegmentatorToolGUI::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 QmitkTotalSegmentatorToolGUI::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()); } } void QmitkTotalSegmentatorToolGUI::EnableAll(bool isEnable) { m_Controls.previewButton->setEnabled(isEnable); m_Controls.subtaskComboBox->setEnabled(isEnable); m_Controls.installButton->setEnabled((!isEnable)); - m_Controls.pythonEnvComboBox->setEnabled((!isEnable)); } void QmitkTotalSegmentatorToolGUI::OnInstallBtnClicked() { bool isInstalled = false; - QString pythonPathTextItem = m_Controls.pythonEnvComboBox->currentText(); - - + MITK_INFO << m_Controls.sysPythonComboBox->currentText().toStdString(); + QString systemPython = GetPythonPathFromUI(m_Controls.sysPythonComboBox->currentText()); + MITK_INFO << systemPython.toStdString(); + if (systemPython.isEmpty()) + { + this->WriteErrorMessage("Couldn't find Python"); + return; + } + else + { + m_Installer.SetSystemPythonPath(systemPython); + } + isInstalled = m_Installer.SetupVirtualEnv(); if (isInstalled) { const QString pythonPath = m_Installer.GetVirtualEnvPath(); m_PythonPath = GetPythonPathFromUI(m_Installer.GetVirtualEnvPath()); this->WriteStatusMessage("Successfully installed TotalSegmentator"); } else { this->WriteErrorMessage("Couldn't find TotalSegmentator"); } this->EnableAll(isInstalled); } void QmitkTotalSegmentatorToolGUI::OnPreviewBtnClicked() { auto tool = this->GetConnectedToolAs(); if (nullptr == tool) { return; } // QString pythonPathTextItem = ""; try { m_Controls.previewButton->setEnabled(false); qApp->processEvents(); if (!this->IsTotalSegmentatorInstalled(m_PythonPath)) { throw std::runtime_error(m_WARNING_TOTALSEG_NOT_FOUND); } bool isFast = m_Controls.fastBox->isChecked(); QString subTask = m_Controls.subtaskComboBox->currentText(); if (subTask != m_VALID_TASKS[0]) { isFast = true; } tool->SetPythonPath(m_PythonPath.toStdString()); tool->SetGpuId(FetchSelectedGPUFromUI()); tool->SetFast(isFast); tool->SetSubTask(subTask.toStdString()); this->WriteStatusMessage(QString("STATUS: Starting Segmentation task... This might take a while.")); tool->UpdatePreview(); m_Controls.previewButton->setEnabled(true); } catch (const std::exception &e) { std::stringstream errorMsg; errorMsg << "STATUS: Error while processing parameters for TotalSegmentator segmentation. Reason: " << e.what(); this->ShowErrorMessage(errorMsg.str()); this->WriteErrorMessage(QString::fromStdString(errorMsg.str())); m_Controls.previewButton->setEnabled(true); return; } catch (...) { std::string errorMsg = "Unkown error occured while generation TotalSegmentator segmentation."; this->ShowErrorMessage(errorMsg); m_Controls.previewButton->setEnabled(true); return; } this->SetLabelSetPreview(tool->GetPreviewSegmentation()); tool->IsTimePointChangeAwareOn(); this->ActualizePreviewLabelVisibility(); this->WriteStatusMessage("STATUS: Segmentation task finished successfully."); /* if (!pythonPathTextItem.isEmpty()) // only cache if the prediction ended without errors. { QString lastSelectedPyEnv = m_Settings.value("TotalSeg/LastPythonPath").toString(); if (lastSelectedPyEnv != pythonPathTextItem) { m_Settings.setValue("TotalSeg/LastPythonPath", pythonPathTextItem); } }*/ } void QmitkTotalSegmentatorToolGUI::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 QmitkTotalSegmentatorToolGUI::WriteStatusMessage(const QString &message) { m_Controls.statusLabel->setText(message); m_Controls.statusLabel->setStyleSheet("font-weight: bold; color: white"); } void QmitkTotalSegmentatorToolGUI::WriteErrorMessage(const QString &message) { m_Controls.statusLabel->setText(message); m_Controls.statusLabel->setStyleSheet("font-weight: bold; color: red"); } bool QmitkTotalSegmentatorToolGUI::IsTotalSegmentatorInstalled(const QString &pythonPath) { QString fullPath = pythonPath; + fullPath = fullPath.mid(fullPath.indexOf(" ") + 1); + bool isPythonExists, isTotalSegExists = false; #ifdef _WIN32 + isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python.exe")); if (!(fullPath.endsWith("Scripts", Qt::CaseInsensitive) || fullPath.endsWith("Scripts/", Qt::CaseInsensitive))) { fullPath += QDir::separator() + QString("Scripts"); + isPythonExists = + (!isPythonExists) ? QFile::exists(fullPath + QDir::separator() + QString("python.exe")) : isPythonExists; } #else + isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python3")); if (!(fullPath.endsWith("bin", Qt::CaseInsensitive) || fullPath.endsWith("bin/", Qt::CaseInsensitive))) { fullPath += QDir::separator() + QString("bin"); + isPythonExists = + (!isPythonExists) ? QFile::exists(fullPath + QDir::separator() + QString("python3")) : isPythonExists; } #endif - fullPath = fullPath.mid(fullPath.indexOf(" ") + 1); - bool isExists = QFile::exists(fullPath + QDir::separator() + QString("TotalSegmentator")) && -#ifdef _WIN32 - QFile::exists(fullPath + QDir::separator() + QString("python.exe")); -#else - QFile::exists(fullPath + QDir::separator() + QString("python3")); -#endif + bool isExists = QFile::exists(fullPath + QDir::separator() + QString("TotalSegmentator")) && isPythonExists; return isExists; } void QmitkTotalSegmentatorToolGUI::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); + m_Controls.sysPythonComboBox->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.sysPythonComboBox->insertItem(0, "(" + envName + "): " + subIt.filePath()); } } } - m_Controls.pythonEnvComboBox->setCurrentIndex(-1); + m_Controls.sysPythonComboBox->setCurrentIndex(-1); } void QmitkTotalSegmentatorToolGUI::OnPythonPathChanged(const QString &pyEnv) { if (pyEnv == QString("Select")) { QString path = QFileDialog::getExistingDirectory(m_Controls.pythonEnvComboBox->parentWidget(), "Python Path", "dir"); if (!path.isEmpty()) { this->OnPythonPathChanged(path); // recall same function for new path validation bool oldState = m_Controls.pythonEnvComboBox->blockSignals(true); // block signal firing while inserting item m_Controls.pythonEnvComboBox->insertItem(0, path); m_Controls.pythonEnvComboBox->setCurrentIndex(0); m_Controls.pythonEnvComboBox->blockSignals( oldState); // unblock signal firing after inserting item. Remove this after Qt6 migration } } - else + else if (!this->IsTotalSegmentatorInstalled(pyEnv)) { - QString pythonPath = GetPythonPathFromUI(pyEnv); - m_Installer.SetSystemPythonPath(pythonPath); + this->ShowErrorMessage(m_WARNING_TOTALSEG_NOT_FOUND); + m_Controls.previewButton->setDisabled(true); + } + else + {// Show positive status meeage + m_Controls.previewButton->setDisabled(false); + m_PythonPath = GetPythonPathFromUI(pyEnv); } } QString QmitkTotalSegmentatorToolGUI::GetPythonPathFromUI(const QString &pyEnv) { - QString pythonPath = pyEnv.mid(pyEnv.indexOf(" ") + 1); + QString pythonPath; + QString fullPath = pyEnv.mid(pyEnv.indexOf(" ") + 1); + bool isPythonExists = false; #ifdef _WIN32 - if (!(pythonPath.endsWith("Scripts", Qt::CaseInsensitive) || pythonPath.endsWith("Scripts/", Qt::CaseInsensitive))) + isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python.exe")); + if (isPythonExists) + { + pythonPath = fullPath; + } + else if (!(fullPath.endsWith("Scripts", Qt::CaseInsensitive) || fullPath.endsWith("Scripts/", Qt::CaseInsensitive))) { - pythonPath += QDir::separator() + QString("Scripts"); + fullPath += QDir::separator() + QString("Scripts"); + pythonPath = fullPath; } #else - if (!(pythonPath.endsWith("bin", Qt::CaseInsensitive) || pythonPath.endsWith("bin/", Qt::CaseInsensitive))) + isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python3")); + if (isPythonExists) { - pythonPath += QDir::separator() + QString("bin"); + pythonPath = fullPath; + } + else if (!(fullPath.endsWith("bin", Qt::CaseInsensitive) || fullPath.endsWith("bin/", Qt::CaseInsensitive))) + { + fullPath += QDir::separator() + QString("bin"); + pythonPath = fullPath; } #endif return pythonPath; } + +void QmitkTotalSegmentatorToolGUI::OnOverrideChecked(int state) +{ + bool isEnabled = false; + if (state == Qt::Checked) + { + isEnabled = true; + m_Controls.previewButton->setDisabled(true); + m_PythonPath.clear(); + connect(m_Controls.pythonEnvComboBox, + SIGNAL(currentTextChanged(const QString &)), + this, + SLOT(OnPythonPathChanged(const QString &))); + } + else + { + m_PythonPath.clear(); + disconnect(m_Controls.pythonEnvComboBox, + SIGNAL(currentTextChanged(const QString &)), + this, + SLOT(OnPythonPathChanged(const QString &))); + if (m_IsInstalled) + { + m_PythonPath = m_Installer.GetVirtualEnvPath(); + this->EnableAll(m_IsInstalled); + } + } + m_Controls.pythonEnvComboBox->setEnabled(isEnabled); +} diff --git a/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.h index 27bd4db60c..9e0dbcd558 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.h +++ b/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.h @@ -1,121 +1,126 @@ #ifndef QmitkTotalSegmentatorToolGUI_h_Included #define QmitkTotalSegmentatorToolGUI_h_Included #include "QmitkMultiLabelSegWithPreviewToolGUIBase.h" #include "ui_QmitkTotalSegmentatorGUIControls.h" #include #include #include "QmitknnUNetGPU.h" #include #include "QmitkSetupVirtualEnvUtil.h" class MITKSEGMENTATIONUI_EXPORT QmitkTotalSegmentatorToolGUI : public QmitkMultiLabelSegWithPreviewToolGUIBase { Q_OBJECT public: mitkClassMacro(QmitkTotalSegmentatorToolGUI, QmitkMultiLabelSegWithPreviewToolGUIBase); itkFactorylessNewMacro(Self); itkCloneMacro(Self); protected slots : /** * @brief Qt Slot */ void OnPreviewBtnClicked(); /** * @brief Qt Slot */ void OnPythonPathChanged(const QString &); /** - @brief Qt Slot + * @brief Qt Slot */ void OnInstallBtnClicked(); + /** + * @brief Qt Slot + */ + void OnOverrideChecked(int); + protected: QmitkTotalSegmentatorToolGUI(); ~QmitkTotalSegmentatorToolGUI() = default; void ConnectNewTool(mitk::SegWithPreviewTool* newTool) override; void InitializeUI(QBoxLayout* mainLayout) override; void EnableWidgets(bool enabled) override; /** - @brief Enable (or Disable) GUI elements. + * @brief Enable (or Disable) GUI elements. */ void EnableAll(bool); /** * @brief Searches and parses paths of python virtual enviroments * from predefined lookout locations */ void AutoParsePythonPaths(); /** * @brief Checks if TotalSegmentator command is valid in the selected python virtual environment. * * @return bool */ bool IsTotalSegmentatorInstalled(const QString &); /** * @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 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 Returns GPU id of the selected GPU from the Combo box. * * @return unsigned int */ unsigned int FetchSelectedGPUFromUI(); bool SetUpTotalSegmentator(const QString &); bool SetUpTotalSegmentatorWIN(const QString &); QString GetPythonPathFromUI(const QString &pyEnv); /** * @brief For storing values like Python path across sessions. */ QSettings m_Settings; QString m_PythonPath; QmitkSetupVirtualEnvUtil m_Installer; QmitkGPULoader m_GpuLoader; Ui_QmitkTotalSegmentatorToolGUIControls m_Controls; bool m_FirstPreviewComputation = true; bool m_IsInstalled = false; EnableConfirmSegBtnFunctionType m_SuperclassEnableConfirmSegBtnFnc; const std::string m_WARNING_TOTALSEG_NOT_FOUND = "TotalSegmentator is not detected in the selected python environment.Please select a valid " "python environment or install TotalSegmentator."; const QStringList m_VALID_TASKS = {"total", "cerebral_bleed", "hip_implant", "coronary_arteries"}; const QString m_VENV_NAME = ".totalsegmentator"; const std::string m_TOTALSEGMENTATOR_VERSION = "1.5.2"; }; #endif