diff --git a/Modules/SegmentationUI/Qmitk/QmitkMedSAMGUIControls.ui b/Modules/SegmentationUI/Qmitk/QmitkMedSAMGUIControls.ui index af5d1b7f3f..13b4eee81a 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkMedSAMGUIControls.ui +++ b/Modules/SegmentationUI/Qmitk/QmitkMedSAMGUIControls.ui @@ -1,173 +1,174 @@ QmitkMedSAMGUIControls 0 0 699 490 0 0 100 0 100000 100000 QmitkMedSAMToolWidget 0 0 0 0 0 0 <html><head/><body><p>Welcome to Segment anything in medical images (MedSAM) tool in MITK. [Experimental]</p><p>Please note that this is only an interface to MedSAM. MITK does not ship with MedSAM. Make sure to have a working internet connection to install MedSAM via MITK. </p><p>Refer to <a href="https://www.nature.com/articles/s41467-024-44824-z"><span style=" text-decoration: underline; color:#0000ff;">https://www.nature.com/articles/s41467-024-44824-z</span></a> to learn everything about the Segment anything in medical images.</p></body></html> Qt::RichText true 0 0 Press SHIFT+Left-click and drag for RoI on the render windows. - +Use level window slider to adjust image contrast. + 0 0 100000 16777215 Reset RoI 0 0 100000 16777215 Initialize MedSAM 0 0 true 1 0 false 0 0 100000 16777215 Preview diff --git a/Modules/SegmentationUI/Qmitk/QmitkMedSAMToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkMedSAMToolGUI.cpp index ad8a1f4e9a..6e36812f17 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkMedSAMToolGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkMedSAMToolGUI.cpp @@ -1,284 +1,276 @@ /*============================================================================ 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 "QmitkMedSAMToolGUI.h" #include #include #include #include #include MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitkMedSAMToolGUI, "") namespace { mitk::IPreferences *GetPreferences() { auto *preferencesService = mitk::CoreServices::GetPreferencesService(); return preferencesService->GetSystemPreferences()->Node("org.mitk.views.segmentation"); } } QmitkMedSAMToolGUI::QmitkMedSAMToolGUI() : QmitkSegWithPreviewToolGUIBase(true) { m_EnableConfirmSegBtnFnc = [this](bool enabled) { bool result = false; auto tool = this->GetConnectedToolAs(); if (nullptr != tool) { result = enabled && tool->HasPicks(); } return result; }; m_Preferences = GetPreferences(); m_Preferences->OnPropertyChanged += mitk::MessageDelegate1( this, &QmitkMedSAMToolGUI::OnPreferenceChangedEvent); } QmitkMedSAMToolGUI::~QmitkMedSAMToolGUI() { auto tool = this->GetConnectedToolAs(); if (nullptr != tool) { tool->SAMStatusMessageEvent -= mitk::MessageDelegate1(this, &QmitkMedSAMToolGUI::StatusMessageListener); } } void QmitkMedSAMToolGUI::EnableAll(bool isEnable) { m_Controls.activateButton->setEnabled(isEnable); } void QmitkMedSAMToolGUI::WriteStatusMessage(const QString &message) { m_Controls.statusLabel->setText(message); m_Controls.statusLabel->setStyleSheet("font-weight: bold; color: white"); qApp->processEvents(); } void QmitkMedSAMToolGUI::WriteErrorMessage(const QString &message) { m_Controls.statusLabel->setText(message); m_Controls.statusLabel->setStyleSheet("font-weight: bold; color: red"); qApp->processEvents(); } void QmitkMedSAMToolGUI::ShowProgressBar(bool enabled) { m_Controls.samProgressBar->setEnabled(enabled); m_Controls.samProgressBar->setVisible(enabled); } void QmitkMedSAMToolGUI::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 QmitkMedSAMToolGUI::InitializeUI(QBoxLayout *mainLayout) { m_Controls.setupUi(this); m_Controls.statusLabel->setTextFormat(Qt::RichText); QString welcomeText; - if (m_GpuLoader.GetGPUCount() != 0) - { - welcomeText = "STATUS: Welcome to MedSAM Anything tool. You're in luck: " + + welcomeText = "STATUS: Welcome to MedSAM tool. You're in luck: " + QString::number(m_GpuLoader.GetGPUCount()) + " GPU(s) were detected."; - } - else - { - welcomeText = "STATUS: Welcome to MedSAM Anything tool. Sorry, " + - QString::number(m_GpuLoader.GetGPUCount()) + " GPUs were detected."; - } connect(m_Controls.previewButton, SIGNAL(clicked()), this, SLOT(OnPreviewBtnClicked())); connect(m_Controls.activateButton, SIGNAL(clicked()), this, SLOT(OnActivateBtnClicked())); connect(m_Controls.resetButton, SIGNAL(clicked()), this, SLOT(OnResetPicksClicked())); QIcon arrowIcon = QmitkStyleManager::ThemeIcon( QStringLiteral(":/org_mitk_icons/icons/tango/scalable/actions/media-playback-start.svg")); m_Controls.activateButton->setIcon(arrowIcon); bool isInstalled = this->ValidatePrefences(); if (isInstalled) { QString modelType = QString::fromStdString(m_Preferences->Get("sam modeltype", "")); welcomeText += " MedSAM is already found installed."; } else { welcomeText += " MedSAM tool is not configured correctly. Please go to Preferences (Cntl+P) > Segment Anything to " "configure and/or install SAM & MedSAM."; } this->EnableAll(isInstalled); this->WriteStatusMessage(welcomeText); this->ShowProgressBar(false); m_Controls.samProgressBar->setMaximum(0); mainLayout->addLayout(m_Controls.verticalLayout); Superclass::InitializeUI(mainLayout); } bool QmitkMedSAMToolGUI::ValidatePrefences() { const QString storageDir = QString::fromStdString(m_Preferences->Get("sam python path", "")); bool isInstalled = QmitkSegmentAnythingToolGUI::IsSAMInstalled(storageDir); std::string modelType = m_Preferences->Get("sam modeltype", ""); std::string path = m_Preferences->Get("sam parent path", ""); return (isInstalled && !modelType.empty() && !path.empty()); } void QmitkMedSAMToolGUI::StatusMessageListener(const std::string &message) { if (message.rfind("Error", 0) == 0) { this->EnableAll(true); this->WriteErrorMessage(QString::fromStdString(message)); } else if (message == "TimeOut") { // trying to re init the daemon this->WriteErrorMessage(QString("STATUS: Sorry, operation timed out. Reactivating MedSAM tool...")); if (this->ActivateSAMDaemon()) { this->WriteStatusMessage(QString("STATUS: MedSAM tool re-initialized.")); } else { this->WriteErrorMessage(QString("STATUS: Couldn't init tool backend.")); this->EnableAll(true); } } else { this->WriteStatusMessage(QString::fromStdString(message)); } } bool QmitkMedSAMToolGUI::ActivateSAMDaemon() { auto tool = this->GetConnectedToolAs(); if (nullptr == tool) { return false; } this->ShowProgressBar(true); qApp->processEvents(); try { tool->InitSAMPythonProcess(); while (!tool->IsPythonReady()) { qApp->processEvents(); } tool->IsReadyOn(); } catch (...) { tool->IsReadyOff(); } this->ShowProgressBar(false); return tool->GetIsReady(); } void QmitkMedSAMToolGUI::OnActivateBtnClicked() { auto tool = this->GetConnectedToolAs(); if (nullptr == tool) { return; } try { this->EnableAll(false); qApp->processEvents(); QString pythonPath = QString::fromStdString(m_Preferences->Get("sam python path", "")); if (!QmitkSegmentAnythingToolGUI::IsSAMInstalled(pythonPath)) { throw std::runtime_error(WARNING_SAM_NOT_FOUND); } tool->SetPythonPath(pythonPath.toStdString()); tool->SetGpuId(m_Preferences->GetInt("sam gpuid", -1)); tool->SetModelType("vit_b"); // MedSAM only works with vit_b tool->SetTimeOutLimit(m_Preferences->GetInt("sam timeout", 300)); tool->SetCheckpointPath(m_Preferences->Get("sam parent path", "")); tool->SetBackend("MedSAM"); this->WriteStatusMessage(QString("STATUS: Initializing MedSAM...")); tool->SAMStatusMessageEvent += mitk::MessageDelegate1(this, &QmitkMedSAMToolGUI::StatusMessageListener); if (this->ActivateSAMDaemon()) { this->WriteStatusMessage(QString("STATUS: MedSAM tool initialized.")); } else { this->WriteErrorMessage(QString("STATUS: Couldn't init tool backend.")); this->EnableAll(true); } } catch (const std::exception &e) { std::stringstream errorMsg; errorMsg << "STATUS: Error while processing parameters for MedSAM segmentation. Reason: " << e.what(); this->ShowErrorMessage(errorMsg.str()); this->WriteErrorMessage(QString::fromStdString(errorMsg.str())); this->EnableAll(true); return; } catch (...) { std::string errorMsg = "Unkown error occured while generation MedSAM segmentation."; this->ShowErrorMessage(errorMsg); this->EnableAll(true); return; } } void QmitkMedSAMToolGUI::OnPreviewBtnClicked() { auto tool = this->GetConnectedToolAs(); if (nullptr != tool) { tool->UpdatePreview(); } } void QmitkMedSAMToolGUI::OnResetPicksClicked() { auto tool = this->GetConnectedToolAs(); if (nullptr != tool) { tool->ClearPicks(); } } void QmitkMedSAMToolGUI::OnPreferenceChangedEvent(const mitk::IPreferences::ChangeEvent &event) { const std::string property = event.GetProperty(); const std::string modelType = "modeltype"; if (property.compare(property.size() - modelType.size(), modelType.size(), modelType) == 0) return; // Model type change ignored. this->EnableAll(true); this->WriteStatusMessage("A Preference change was detected. Please initialize the tool again."); auto tool = this->GetConnectedToolAs(); if (nullptr != tool) { tool->IsReadyOff(); } } diff --git a/Modules/SegmentationUI/Qmitk/QmitkSegmentAnythingGUIControls.ui b/Modules/SegmentationUI/Qmitk/QmitkSegmentAnythingGUIControls.ui index 6f1f495a00..348723789d 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkSegmentAnythingGUIControls.ui +++ b/Modules/SegmentationUI/Qmitk/QmitkSegmentAnythingGUIControls.ui @@ -1,169 +1,158 @@ QmitkSegmentAnythingGUIControls 0 0 699 490 0 0 100 0 100000 100000 QmitkSegmentAnythingToolWidget 0 0 0 0 0 0 <html><head/><body><p>Welcome to Segment Anything Model (SAM) tool in MITK. [Experimental]</p><p>Please note that this is only an interface to SAM. MITK does not ship with SAM. Make sure to have a working internet connection to install Segment Anything Model via MITK. </p><p>Refer to <a href="https://segment-anything.com/"><span style=" text-decoration: underline; color:#0000ff;">https://segment-anything.com</span></a> to learn everything about the Segment Anything Model.</p></body></html> Qt::RichText true 0 0 Press SHIFT+Left-click for positive seeds. Press SHIFT+Right-click for negative seeds. Press DEL to remove last seed. + +Use level window slider to adjust image contrast. 0 0 100000 16777215 Reset Picks 0 0 100000 16777215 Initialize Segment Anything 0 0 true 1 0 false - - - ctkComboBox - QComboBox -
ctkComboBox.h
- 1 -
- - ctkCollapsibleGroupBox - QWidget -
ctkCollapsibleGroupBox.h
-
-
diff --git a/Modules/SegmentationUI/Qmitk/QmitkSegmentAnythingToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkSegmentAnythingToolGUI.cpp index f3fb059ddf..032c160dce 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkSegmentAnythingToolGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkSegmentAnythingToolGUI.cpp @@ -1,303 +1,296 @@ /*============================================================================ 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 "QmitkSegmentAnythingToolGUI.h" #include #include #include #include #include #include #include #include #include MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitkSegmentAnythingToolGUI, "") namespace { mitk::IPreferences *GetPreferences() { auto *preferencesService = mitk::CoreServices::GetPreferencesService(); return preferencesService->GetSystemPreferences()->Node("org.mitk.views.segmentation"); } } QmitkSegmentAnythingToolGUI::QmitkSegmentAnythingToolGUI() : QmitkSegWithPreviewToolGUIBase(true) { m_EnableConfirmSegBtnFnc = [this](bool enabled) { bool result = false; auto tool = this->GetConnectedToolAs(); if (nullptr != tool) { result = enabled && tool->HasPicks(); } return result; }; m_Preferences = GetPreferences(); m_Preferences->OnPropertyChanged += mitk::MessageDelegate1( this, &QmitkSegmentAnythingToolGUI::OnPreferenceChangedEvent); } QmitkSegmentAnythingToolGUI::~QmitkSegmentAnythingToolGUI() { auto tool = this->GetConnectedToolAs(); if (nullptr != tool) { tool->SAMStatusMessageEvent -= mitk::MessageDelegate1( this, &QmitkSegmentAnythingToolGUI::StatusMessageListener); } } void QmitkSegmentAnythingToolGUI::InitializeUI(QBoxLayout *mainLayout) { m_Controls.setupUi(this); m_Controls.statusLabel->setTextFormat(Qt::RichText); QString welcomeText; - if (m_GpuLoader.GetGPUCount() != 0) - { - welcomeText = "STATUS: Welcome to Segment Anything tool. You're in luck: " + + welcomeText = "STATUS: Welcome to Segment Anything tool. You're in luck: " + QString::number(m_GpuLoader.GetGPUCount()) + " GPU(s) were detected."; - } - else - { - welcomeText = "STATUS: Welcome to Segment Anything tool. Sorry, " + - QString::number(m_GpuLoader.GetGPUCount()) + " GPUs were detected."; - } + connect(m_Controls.activateButton, SIGNAL(clicked()), this, SLOT(OnActivateBtnClicked())); connect(m_Controls.resetButton, SIGNAL(clicked()), this, SLOT(OnResetPicksClicked())); QIcon arrowIcon = QmitkStyleManager::ThemeIcon( QStringLiteral(":/org_mitk_icons/icons/tango/scalable/actions/media-playback-start.svg")); m_Controls.activateButton->setIcon(arrowIcon); bool isInstalled = this->ValidatePrefences(); if (isInstalled) { QString modelType = QString::fromStdString(m_Preferences->Get("sam modeltype", "")); welcomeText += " SAM is already found installed. Model type '" + modelType + "' selected in Preferences."; } else { welcomeText += " SAM tool is not configured correctly. Please go to Preferences (Cntl+P) > Segment Anything to configure and/or install SAM."; } this->EnableAll(isInstalled); this->WriteStatusMessage(welcomeText); this->ShowProgressBar(false); m_Controls.samProgressBar->setMaximum(0); mainLayout->addLayout(m_Controls.verticalLayout); Superclass::InitializeUI(mainLayout); } bool QmitkSegmentAnythingToolGUI::ValidatePrefences() { const QString storageDir = QString::fromStdString(m_Preferences->Get("sam python path", "")); bool isInstalled = QmitkSegmentAnythingToolGUI::IsSAMInstalled(storageDir); std::string modelType = m_Preferences->Get("sam modeltype", ""); std::string path = m_Preferences->Get("sam parent path", ""); return (isInstalled && !modelType.empty() && !path.empty()); } void QmitkSegmentAnythingToolGUI::EnableAll(bool isEnable) { m_Controls.activateButton->setEnabled(isEnable); } void QmitkSegmentAnythingToolGUI::WriteStatusMessage(const QString &message) { m_Controls.statusLabel->setText(message); m_Controls.statusLabel->setStyleSheet("font-weight: bold; color: white"); qApp->processEvents(); } void QmitkSegmentAnythingToolGUI::WriteErrorMessage(const QString &message) { m_Controls.statusLabel->setText(message); m_Controls.statusLabel->setStyleSheet("font-weight: bold; color: red"); qApp->processEvents(); } void QmitkSegmentAnythingToolGUI::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 QmitkSegmentAnythingToolGUI::StatusMessageListener(const std::string &message) { if (message.rfind("Error", 0) == 0) { this->EnableAll(true); this->WriteErrorMessage(QString::fromStdString(message)); } else if (message == "TimeOut") { // trying to re init the daemon this->WriteErrorMessage(QString("STATUS: Sorry, operation timed out. Reactivating SAM tool...")); if (this->ActivateSAMDaemon()) { this->WriteStatusMessage(QString("STATUS: Segment Anything tool re-initialized.")); } else { this->WriteErrorMessage(QString("STATUS: Couldn't init tool backend.")); this->EnableAll(true); } } else { this->WriteStatusMessage(QString::fromStdString(message)); } } void QmitkSegmentAnythingToolGUI::OnActivateBtnClicked() { auto tool = this->GetConnectedToolAs(); if (nullptr == tool) { return; } try { this->EnableAll(false); qApp->processEvents(); QString pythonPath = QString::fromStdString(m_Preferences->Get("sam python path", "")); if (!QmitkSegmentAnythingToolGUI::IsSAMInstalled(pythonPath)) { throw std::runtime_error(WARNING_SAM_NOT_FOUND); } tool->SetPythonPath(pythonPath.toStdString()); tool->SetGpuId(m_Preferences->GetInt("sam gpuid", -1)); const QString modelType = QString::fromStdString(m_Preferences->Get("sam modeltype", "")); tool->SetModelType(modelType.toStdString()); tool->SetTimeOutLimit(m_Preferences->GetInt("sam timeout", 300)); tool->SetCheckpointPath(m_Preferences->Get("sam parent path", "")); tool->SetBackend("SAM"); this->WriteStatusMessage( QString("STATUS: Initializing Segment Anything Model...")); tool->SAMStatusMessageEvent += mitk::MessageDelegate1( this, &QmitkSegmentAnythingToolGUI::StatusMessageListener); if (this->ActivateSAMDaemon()) { this->WriteStatusMessage(QString("STATUS: Segment Anything tool initialized.")); } else { this->WriteErrorMessage(QString("STATUS: Couldn't init tool backend.")); this->EnableAll(true); } } catch (const std::exception &e) { std::stringstream errorMsg; errorMsg << "STATUS: Error while processing parameters for Segment Anything segmentation. Reason: " << e.what(); this->ShowErrorMessage(errorMsg.str()); this->WriteErrorMessage(QString::fromStdString(errorMsg.str())); this->EnableAll(true); return; } catch (...) { std::string errorMsg = "Unkown error occured while generation Segment Anything segmentation."; this->ShowErrorMessage(errorMsg); this->EnableAll(true); return; } } bool QmitkSegmentAnythingToolGUI::ActivateSAMDaemon() { auto tool = this->GetConnectedToolAs(); if (nullptr == tool) { return false; } this->ShowProgressBar(true); qApp->processEvents(); try { tool->InitSAMPythonProcess(); while (!tool->IsPythonReady()) { qApp->processEvents(); } tool->IsReadyOn(); } catch (...) { tool->IsReadyOff(); } this->ShowProgressBar(false); return tool->GetIsReady(); } void QmitkSegmentAnythingToolGUI::ShowProgressBar(bool enabled) { m_Controls.samProgressBar->setEnabled(enabled); m_Controls.samProgressBar->setVisible(enabled); } bool QmitkSegmentAnythingToolGUI::IsSAMInstalled(const QString &pythonPath) { QString fullPath = pythonPath; bool isPythonExists = false; bool isSamExists = 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 isSamExists = QFile::exists(fullPath + QDir::separator() + QString("run_inference_daemon.py")) && QFile::exists(fullPath + QDir::separator() + QString("sam_runner.py")) && QFile::exists(fullPath + QDir::separator() + QString("medsam_runner.py")); bool isExists = isSamExists && isPythonExists; return isExists; } void QmitkSegmentAnythingToolGUI::OnResetPicksClicked() { auto tool = this->GetConnectedToolAs(); if (nullptr != tool) { tool->ClearPicks(); } } void QmitkSegmentAnythingToolGUI::OnPreferenceChangedEvent(const mitk::IPreferences::ChangeEvent&) { this->EnableAll(true); this->WriteStatusMessage("A Preference change was detected. Please initialize the tool again."); auto tool = this->GetConnectedToolAs(); if (nullptr != tool) { tool->IsReadyOff(); } }