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
-
- 1
-
-
- ctkCollapsibleGroupBox
- QWidget
-
-
-
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();
}
}