diff --git a/Modules/ModuleList.cmake b/Modules/ModuleList.cmake index 592b712870..117970cb92 100644 --- a/Modules/ModuleList.cmake +++ b/Modules/ModuleList.cmake @@ -1,62 +1,62 @@ # The entries in the mitk_modules list must be # ordered according to their dependencies. set(MITK_MODULES Log Core CommandLine CoreCmdApps AppUtil LegacyIO DataTypesExt Annotation LegacyGL AlgorithmsExt MapperExt DICOM DICOMQI DICOMTesting SceneSerializationBase PlanarFigure ImageDenoising ImageExtraction SceneSerialization Gizmo GraphAlgorithms Multilabel Chart ImageStatistics ContourModel SurfaceInterpolation + BoundingShape Segmentation QtWidgets QtWidgetsExt ImageStatisticsUI SegmentationUI MatchPointRegistration MatchPointRegistrationUI Classification QtOverlays DICOMUI Remeshing Python QtPython Persistence RT RTUI IOExt XNAT - BoundingShape RenderWindowManagerUI CEST BasicImageProcessing ModelFit ModelFitUI Pharmacokinetics PharmacokineticsUI DICOMPM REST RESTService DICOMweb ROI ) diff --git a/Modules/Segmentation/CMakeLists.txt b/Modules/Segmentation/CMakeLists.txt index 709e882c01..1708ede8d0 100644 --- a/Modules/Segmentation/CMakeLists.txt +++ b/Modules/Segmentation/CMakeLists.txt @@ -1,11 +1,11 @@ mitk_create_module( INCLUDE_DIRS Algorithms Controllers DataManagement Interactions Rendering SegmentationUtilities/BooleanOperations SegmentationUtilities/MorphologicalOperations - DEPENDS MitkAlgorithmsExt MitkSurfaceInterpolation MitkGraphAlgorithms MitkContourModel MitkMultilabel + DEPENDS MitkAlgorithmsExt MitkSurfaceInterpolation MitkGraphAlgorithms MitkContourModel MitkMultilabel MitkBoundingShape PACKAGE_DEPENDS PUBLIC ITK|QuadEdgeMesh+RegionGrowing PRIVATE ITK|LabelMap+MathematicalMorphology VTK|ImagingGeneral TARGET_DEPENDS PRIVATE GrowCut ) add_subdirectory(cmdapps) add_subdirectory(Testing) diff --git a/Modules/Segmentation/Interactions/mitkMedSAMTool.cpp b/Modules/Segmentation/Interactions/mitkMedSAMTool.cpp index 44f02b41ef..68e7499b0f 100644 --- a/Modules/Segmentation/Interactions/mitkMedSAMTool.cpp +++ b/Modules/Segmentation/Interactions/mitkMedSAMTool.cpp @@ -1,62 +1,197 @@ /*============================================================================ 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 "mitkMedSAMTool.h" -#include "mitkImageAccessByItk.h" +//#include "mitkImageAccessByItk.h" #include "mitkToolManager.h" #include +#include "mitkGeometryData.h" +#include "mitkInteractionPositionEvent.h" + + // us #include #include #include #include using namespace std::chrono_literals; namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, MedSAMTool, "MedSAMTool"); + + void FlipAxis(mitk::BaseGeometry *geometry, int axis) + { + auto matrix = geometry->GetVtkMatrix(); + matrix->SetElement(axis, axis, -matrix->GetElement(axis, axis)); + matrix->SetElement(axis, 3, matrix->GetElement(axis, 3) - geometry->GetExtentInMM(axis)); + + geometry->SetIndexToWorldTransformByVtkMatrix(matrix); + + auto bounds = geometry->GetBounds(); + int minIndex = 2 * axis; + bounds[minIndex] *= -1; + bounds[minIndex + 1] += 2 * bounds[minIndex]; + + geometry->SetBounds(bounds); + } + + mitk::BaseGeometry::Pointer RectifyGeometry(const mitk::BaseGeometry *geometry) + { + auto rectifiedGeometry = geometry->Clone(); + auto matrix = rectifiedGeometry->GetVtkMatrix(); + + for (int axis = 0; axis < 3; ++axis) + { + if (matrix->GetElement(axis, axis) < 0.0) + FlipAxis(rectifiedGeometry, axis); + } + return rectifiedGeometry; + } } const char *mitk::MedSAMTool::GetName() const { return "MedSAM"; } -/* void mitk::MedSAMTool::Activated() +void mitk::MedSAMTool::Activated() { SegWithPreviewTool::Activated(); this->SetLabelTransferScope(LabelTransferScope::ActiveLabel); this->SetLabelTransferMode(LabelTransferMode::MapLabel); -}*/ +} + +void mitk::MedSAMTool::Deactivated() +{ + SegWithPreviewTool::Deactivated(); + GetDataStorage()->Remove(m_BoundingBoxNode); + m_BoundingBoxNode = nullptr; + m_PythonService.reset(); +} + +void mitk::MedSAMTool::ConnectActionsAndFunctions() +{ + CONNECT_FUNCTION("ShiftPrimaryButtonPressed", OnRenderWindowClicked); +} + +void mitk::MedSAMTool::OnRenderWindowClicked(StateMachineAction *, InteractionEvent *interactionEvent) +{ + /* if (!this->GetIsReady()) + { + return; + }*/ + if ((nullptr == this->GetWorkingPlaneGeometry()) || + !mitk::Equal(*(interactionEvent->GetSender()->GetCurrentWorldPlaneGeometry()), + *(this->GetWorkingPlaneGeometry()))) + { + this->GetDataStorage()->Remove(m_BoundingBoxNode); + m_BoundingBoxNode = nullptr; + this->SetWorkingPlaneGeometry(interactionEvent->GetSender()->GetCurrentWorldPlaneGeometry()->Clone()); + auto boundingBox = mitk::GeometryData::New(); + boundingBox->SetGeometry(static_cast( + this->InitializeWithImageGeometry(interactionEvent->GetSender()->GetCurrentWorldPlaneGeometry()->Clone()))); + m_BoundingBoxNode = mitk::DataNode::New(); + m_BoundingBoxNode->SetData(boundingBox); + m_BoundingBoxNode->SetVisibility(true); + m_BoundingBoxNode->SetName(std::string(this->GetName()) + "_Boundingbox"); + m_BoundingBoxNode->SetProperty("layer", mitk::IntProperty::New(99)); + m_BoundingBoxNode->AddProperty("Bounding Shape.Handle Size Factor", mitk::DoubleProperty::New(0.02)); + m_BoundingBoxNode->SetBoolProperty("pickable", true); + this->GetDataStorage()->Add(m_BoundingBoxNode, this->GetToolManager()->GetWorkingData(0)); + this->CreateBoundingShapeInteractor(false); + m_BoundingShapeInteractor->EnableInteraction(true); + m_BoundingShapeInteractor->SetDataNode(m_BoundingBoxNode); + mitk::RenderingManager::GetInstance()->InitializeViews(); + } +} -//void mitk::MedSAMTool::ConnectActionsAndFunctions() {} +void mitk::MedSAMTool::CreateBoundingShapeInteractor(bool rotationEnabled) +{ + if (m_BoundingShapeInteractor.IsNull()) + { + m_BoundingShapeInteractor = mitk::BoundingShapeInteractor::New(); + m_BoundingShapeInteractor->LoadStateMachine("BoundingShapeInteraction.xml", + us::ModuleRegistry::GetModule("MitkBoundingShape")); + m_BoundingShapeInteractor->SetEventConfig("BoundingShapeMouseConfig.xml", + us::ModuleRegistry::GetModule("MitkBoundingShape")); + } + m_BoundingShapeInteractor->SetRotationEnabled(rotationEnabled); +} -/* bool mitk::MedSAMTool::HasPicks() const +mitk::Geometry3D::Pointer mitk::MedSAMTool::InitializeWithImageGeometry(const mitk::BaseGeometry *geometry) const { - return true; // check if bounding box is ready -}*/ + // convert a BaseGeometry into a Geometry3D (otherwise IO is not working properly) + if (geometry == nullptr) + mitkThrow() << "Geometry is not valid."; + + auto boundingGeometry = mitk::Geometry3D::New(); + boundingGeometry->SetBounds(geometry->GetBounds()); + boundingGeometry->SetImageGeometry(geometry->GetImageGeometry()); + boundingGeometry->SetOrigin(geometry->GetOrigin()); + boundingGeometry->SetSpacing(geometry->GetSpacing()); + boundingGeometry->SetIndexToWorldTransform(geometry->GetIndexToWorldTransform()->Clone()); + boundingGeometry->Modified(); + return boundingGeometry; +} + +bool mitk::MedSAMTool::HasPicks() const +{ + return m_BoundingBoxNode.IsNotNull(); +} + +void mitk::MedSAMTool::ClearPicks() +{ + this->GetDataStorage()->Remove(m_BoundingBoxNode); + m_BoundingBoxNode = nullptr; +} std::stringstream mitk::MedSAMTool::GetPointsAsCSVString(const mitk::BaseGeometry * baseGeometry) { + //auto geometry = RectifyGeometry(m_BoundingBoxNode->GetData()->GetGeometry()); + //const auto origin = geometry->GetOrigin(); + // //- RectifyGeometry(this->GetToolManager()->GetWorkingData(0)->GetData()->GetGeometry())->GetOrigin(); + //const auto spacing = geometry->GetSpacing(); + //const auto bounds = geometry->GetBounds(); + // + //MITK_INFO << "origin " << origin[0]; + //MITK_INFO << "origin " << origin[1]; + //MITK_INFO << "origin " << origin[2]; + + //MITK_INFO << "bounds: " << bounds[0]; + //MITK_INFO << "bounds: " << bounds[1]; + //MITK_INFO << "bounds: " << bounds[2]; + //MITK_INFO << "bounds: " << bounds[3]; + //MITK_INFO << "bounds: " << bounds[4]; + //MITK_INFO << "bounds: " << bounds[5]; + + + //Point2D p2D_1 = this->Get2DIndicesfrom3DWorld(baseGeometry, origin); + //Point2D p2D_2; + //p2D_2[0] = p2D_1[0] - bounds[1]; + //p2D_2[1] = p2D_1[1] - bounds[3]; + + //MITK_INFO << p2D_2[0] << " " << p2D_2[1]; // remove + std::stringstream pointsAndLabels; pointsAndLabels << "Coordinates\n"; //pointsAndLabels << "20 20 450 450"; auto tmp = baseGeometry->GetBounds(); - int coordinate1 = static_cast(tmp[1]*0.25); int coordinate2 = static_cast(tmp[3]*0.75); const char SPACE = ' '; pointsAndLabels << coordinate1 << SPACE << coordinate1 << SPACE << coordinate2 << SPACE << coordinate2; + //pointsAndLabels << p2D_2[1] << SPACE << p2D_2[0] << SPACE << p2D_1[2] << SPACE << p2D_1[1]; return pointsAndLabels; } diff --git a/Modules/Segmentation/Interactions/mitkMedSAMTool.h b/Modules/Segmentation/Interactions/mitkMedSAMTool.h index 51ca2245bd..e4b5dd2567 100644 --- a/Modules/Segmentation/Interactions/mitkMedSAMTool.h +++ b/Modules/Segmentation/Interactions/mitkMedSAMTool.h @@ -1,60 +1,65 @@ /*============================================================================ 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. ============================================================================*/ #ifndef mitkMedSAMTool_h #define mitkMedSAMTool_h #include "mitkSegmentAnythingTool.h" -#include "mitkSegWithPreviewTool.h" -#include "mitkPointSet.h" -#include "mitkProcessExecutor.h" -#include "mitkSegmentAnythingPythonService.h" +#include "mitkBoundingShapeInteractor.h" #include -#include -#include +#include namespace us { class ModuleResource; } namespace mitk { /** \brief Medical Segment Anything Model interactive 2D tool class. \ingroup ToolManagerEtAl \sa mitk::Tool \sa QmitkInteractiveSegmentation */ class MITKSEGMENTATION_EXPORT MedSAMTool : public SegmentAnythingTool { public: mitkClassMacro(MedSAMTool, SegmentAnythingTool); itkFactorylessNewMacro(Self); itkCloneMacro(Self); const char *GetName() const override; - //void Activated() override; - //bool HasPicks() const; - //void ConnectActionsAndFunctions() override; + void Activated() override; + void Deactivated() override; + bool HasPicks() const override; + void ClearPicks() override; + void ConnectActionsAndFunctions() override; + void OnRenderWindowClicked(StateMachineAction *, InteractionEvent *); std::stringstream GetPointsAsCSVString(const mitk::BaseGeometry *); protected: MedSAMTool() = default; ~MedSAMTool() = default; + + private: + void CreateBoundingShapeInteractor(bool); + mitk::Geometry3D::Pointer InitializeWithImageGeometry(const mitk::BaseGeometry *) const; + DataNode::Pointer m_BoundingBoxNode; + BoundingShapeInteractor::Pointer m_BoundingShapeInteractor; }; } // namespace #endif diff --git a/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.cpp b/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.cpp index 9b8b7f478f..73d4a54ab4 100644 --- a/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.cpp +++ b/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.cpp @@ -1,425 +1,425 @@ /*============================================================================ 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 "mitkSegmentAnythingTool.h" #include #include #include #include "mitkInteractionPositionEvent.h" #include "mitkPointSetShapeProperty.h" #include "mitkProperties.h" #include "mitkToolManager.h" #include // us #include #include #include #include #include #include "mitkImageAccessByItk.h" using namespace std::chrono_literals; namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, SegmentAnythingTool, "SegmentAnythingTool"); } mitk::SegmentAnythingTool::SegmentAnythingTool() : SegWithPreviewTool(true, "PressMoveReleaseAndPointSetting") { this->ResetsToEmptyPreviewOn(); this->IsTimePointChangeAwareOff(); this->KeepActiveAfterAcceptOn(); } const char **mitk::SegmentAnythingTool::GetXPM() const { return nullptr; } const char *mitk::SegmentAnythingTool::GetName() const { return "Segment Anything"; } us::ModuleResource mitk::SegmentAnythingTool::GetIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("AI.svg"); return resource; } void mitk::SegmentAnythingTool::Activated() { Superclass::Activated(); m_PointSetPositive = mitk::PointSet::New(); m_PointSetNodePositive = mitk::DataNode::New(); m_PointSetNodePositive->SetData(m_PointSetPositive); m_PointSetNodePositive->SetName(std::string(this->GetName()) + "_PointSetPositive"); m_PointSetNodePositive->SetBoolProperty("helper object", true); m_PointSetNodePositive->SetColor(0.0, 1.0, 0.0); m_PointSetNodePositive->SetVisibility(true); m_PointSetNodePositive->SetProperty("Pointset.2D.shape", mitk::PointSetShapeProperty::New(mitk::PointSetShapeProperty::CIRCLE)); m_PointSetNodePositive->SetProperty("Pointset.2D.fill shape", mitk::BoolProperty::New(true)); this->GetDataStorage()->Add(m_PointSetNodePositive, this->GetToolManager()->GetWorkingData(0)); m_PointSetNegative = mitk::PointSet::New(); m_PointSetNodeNegative = mitk::DataNode::New(); m_PointSetNodeNegative->SetData(m_PointSetNegative); m_PointSetNodeNegative->SetName(std::string(this->GetName()) + "_PointSetNegative"); m_PointSetNodeNegative->SetBoolProperty("helper object", true); m_PointSetNodeNegative->SetColor(1.0, 0.0, 0.0); m_PointSetNodeNegative->SetVisibility(true); m_PointSetNodeNegative->SetProperty("Pointset.2D.shape", mitk::PointSetShapeProperty::New(mitk::PointSetShapeProperty::CIRCLE)); m_PointSetNodeNegative->SetProperty("Pointset.2D.fill shape", mitk::BoolProperty::New(true)); this->GetDataStorage()->Add(m_PointSetNodeNegative, this->GetToolManager()->GetWorkingData(0)); this->SetLabelTransferScope(LabelTransferScope::ActiveLabel); this->SetLabelTransferMode(LabelTransferMode::MapLabel); } void mitk::SegmentAnythingTool::Deactivated() { this->ClearSeeds(); GetDataStorage()->Remove(m_PointSetNodePositive); GetDataStorage()->Remove(m_PointSetNodeNegative); m_PointSetNodePositive = nullptr; m_PointSetNodeNegative = nullptr; m_PointSetPositive = nullptr; m_PointSetNegative = nullptr; m_PythonService.reset(); Superclass::Deactivated(); } void mitk::SegmentAnythingTool::ConnectActionsAndFunctions() { CONNECT_FUNCTION("ShiftSecondaryButtonPressed", OnAddNegativePoint); CONNECT_FUNCTION("ShiftPrimaryButtonPressed", OnAddPositivePoint); CONNECT_FUNCTION("DeletePoint", OnDelete); } void mitk::SegmentAnythingTool::InitSAMPythonProcess() { if (nullptr != m_PythonService) { m_PythonService.reset(); } this->ClearPicks(); m_PythonService = std::make_unique( this->GetPythonPath(), this->GetModelType(), this->GetCheckpointPath(), this->GetGpuId(), this->GetBackend()); m_PythonService->StartAsyncProcess(); } bool mitk::SegmentAnythingTool::IsPythonReady() const { return m_PythonService->CheckStatus(); } void mitk::SegmentAnythingTool::OnAddNegativePoint(StateMachineAction *, InteractionEvent *interactionEvent) { if (!this->GetIsReady() || m_PointSetPositive->GetSize() == 0 || nullptr == this->GetWorkingPlaneGeometry() || !mitk::Equal(*(interactionEvent->GetSender()->GetCurrentWorldPlaneGeometry()), *(this->GetWorkingPlaneGeometry()))) { return; } if (!this->IsUpdating() && m_PointSetNegative.IsNotNull()) { const auto positionEvent = dynamic_cast(interactionEvent); if (positionEvent != nullptr) { m_PointSetNegative->InsertPoint(m_PointSetCount, positionEvent->GetPositionInWorld()); m_PointSetCount++; this->UpdatePreview(); } } } void mitk::SegmentAnythingTool::OnAddPositivePoint(StateMachineAction *, InteractionEvent *interactionEvent) { if (!this->GetIsReady()) { return; } m_IsGenerateEmbeddings = false; if ((nullptr == this->GetWorkingPlaneGeometry()) || !mitk::Equal(*(interactionEvent->GetSender()->GetCurrentWorldPlaneGeometry()), *(this->GetWorkingPlaneGeometry()))) { m_IsGenerateEmbeddings = true; this->ClearSeeds(); this->SetWorkingPlaneGeometry(interactionEvent->GetSender()->GetCurrentWorldPlaneGeometry()->Clone()); } if (!this->IsUpdating() && m_PointSetPositive.IsNotNull()) { const auto positionEvent = dynamic_cast(interactionEvent); if (positionEvent != nullptr) { m_PointSetPositive->InsertPoint(m_PointSetCount, positionEvent->GetPositionInWorld()); ++m_PointSetCount; this->UpdatePreview(); } } } void mitk::SegmentAnythingTool::OnDelete(StateMachineAction *, InteractionEvent *) { if (!this->IsUpdating() && m_PointSetPositive.IsNotNull() && m_PointSetNegative.IsNotNull()) { PointSet::Pointer removeSet = m_PointSetPositive; decltype(m_PointSetPositive->GetMaxId().Index()) maxId = 0; if (m_PointSetPositive->GetSize() > 0) { maxId = m_PointSetPositive->GetMaxId().Index(); } if (m_PointSetNegative->GetSize() > 0 && (maxId < m_PointSetNegative->GetMaxId().Index())) { removeSet = m_PointSetNegative; } removeSet->RemovePointAtEnd(0); --m_PointSetCount; this->UpdatePreview(); } } void mitk::SegmentAnythingTool::ClearPicks() { this->ClearSeeds(); this->UpdatePreview(); } bool mitk::SegmentAnythingTool::HasPicks() const { return this->m_PointSetPositive.IsNotNull() && this->m_PointSetPositive->GetSize() > 0; } void mitk::SegmentAnythingTool::ClearSeeds() { if (this->m_PointSetPositive.IsNotNull()) { m_PointSetCount -= m_PointSetPositive->GetSize(); this->m_PointSetPositive = mitk::PointSet::New(); // renew pointset this->m_PointSetNodePositive->SetData(this->m_PointSetPositive); } if (this->m_PointSetNegative.IsNotNull()) { m_PointSetCount -= m_PointSetNegative->GetSize(); this->m_PointSetNegative = mitk::PointSet::New(); // renew pointset this->m_PointSetNodeNegative->SetData(this->m_PointSetNegative); } } void mitk::SegmentAnythingTool::ConfirmCleanUp() { auto previewImage = this->GetPreviewSegmentation(); for (unsigned int timeStep = 0; timeStep < previewImage->GetTimeSteps(); ++timeStep) { this->ResetPreviewContentAtTimeStep(timeStep); } this->ClearSeeds(); RenderingManager::GetInstance()->RequestUpdateAll(); } template void mitk::SegmentAnythingTool::ITKWindowing(const itk::Image *inputImage, Image *mitkImage, ScalarType min, ScalarType max) { typedef itk::Image ImageType; typedef itk::IntensityWindowingImageFilter IntensityFilterType; typename IntensityFilterType::Pointer filter = IntensityFilterType::New(); filter->SetInput(inputImage); filter->SetWindowMinimum(min); filter->SetWindowMaximum(max); filter->SetOutputMinimum(min); filter->SetOutputMaximum(max); filter->Update(); mitkImage->SetImportVolume((void *)(filter->GetOutput()->GetPixelContainer()->GetBufferPointer()), 0, 0, Image::ManageMemory); filter->GetOutput()->GetPixelContainer()->ContainerManageMemoryOff(); } namespace { // Checks if the image has valid size across each dimension. The check is // critical for 2D images since 2D image are not valid in Saggital and Coronal views. bool IsImageAtTimeStepValid(const mitk::Image *inputAtTimeStep) { int total = 0; total += (inputAtTimeStep->GetDimension(0) > 1); total += (inputAtTimeStep->GetDimension(1) > 1); total += (inputAtTimeStep->GetDimension(2) > 1); return (total > 1); } } void mitk::SegmentAnythingTool::DoUpdatePreview(const Image *inputAtTimeStep, const Image * /*oldSegAtTimeStep*/, LabelSetImage *previewImage, TimeStepType timeStep) { - if (nullptr != previewImage && m_PointSetPositive.IsNotNull() && ::IsImageAtTimeStepValid(inputAtTimeStep)) + if (nullptr != previewImage && ::IsImageAtTimeStepValid(inputAtTimeStep)) { if (this->HasPicks() && nullptr != m_PythonService) { mitk::LevelWindow levelWindow; this->GetToolManager()->GetReferenceData(0)->GetLevelWindow(levelWindow); std::string uniquePlaneID = GetHashForCurrentPlane(levelWindow); m_ProgressCommand->SetProgress(20); try { std::stringstream csvStream; this->EmitSAMStatusMessageEvent("Prompting Segment Anything Model..."); m_ProgressCommand->SetProgress(50); if (inputAtTimeStep->GetPixelType().GetNumberOfComponents() < 2) { auto filteredImage = mitk::Image::New(); filteredImage->Initialize(inputAtTimeStep); AccessByItk_n(inputAtTimeStep, ITKWindowing, // apply level window filter (filteredImage, levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound())); m_PythonService->TransferImageToProcess(filteredImage, uniquePlaneID); csvStream = this->GetPointsAsCSVString(filteredImage->GetGeometry()); } else { m_PythonService->TransferImageToProcess(inputAtTimeStep, uniquePlaneID); csvStream = this->GetPointsAsCSVString(inputAtTimeStep->GetGeometry()); } m_ProgressCommand->SetProgress(100); m_PythonService->TransferPointsToProcess(csvStream); m_ProgressCommand->SetProgress(150); std::this_thread::sleep_for(100ms); mitk::LabelSetImage::Pointer outputBuffer = m_PythonService->RetrieveImageFromProcess(this->GetTimeOutLimit()); m_ProgressCommand->SetProgress(180); mitk::SegTool2D::WriteSliceToVolume(previewImage, this->GetWorkingPlaneGeometry(), outputBuffer.GetPointer(), timeStep, false); this->SetSelectedLabels({MASK_VALUE}); this->EmitSAMStatusMessageEvent("Successfully generated segmentation."); } catch (const mitk::Exception &e) { this->ClearPicks(); this->EmitSAMStatusMessageEvent(e.GetDescription()); mitkThrow() << e.GetDescription(); } } else if (nullptr != this->GetWorkingPlaneGeometry()) { this->ResetPreviewContentAtTimeStep(timeStep); RenderingManager::GetInstance()->ForceImmediateUpdateAll(); } } } std::string mitk::SegmentAnythingTool::GetHashForCurrentPlane(const mitk::LevelWindow &levelWindow) { mitk::Vector3D normal = this->GetWorkingPlaneGeometry()->GetNormal(); mitk::Point3D center = this->GetWorkingPlaneGeometry()->GetCenter(); std::stringstream hashstream; hashstream << std::setprecision(3) << std::fixed << normal[0]; //Consider only 3 digits after decimal hashstream << std::setprecision(3) << std::fixed << normal[1]; hashstream << std::setprecision(3) << std::fixed << normal[2]; hashstream << std::setprecision(3) << std::fixed << center[0]; hashstream << std::setprecision(3) << std::fixed << center[1]; hashstream << std::setprecision(3) << std::fixed << center[2]; hashstream << levelWindow.GetLowerWindowBound(); hashstream << levelWindow.GetUpperWindowBound(); size_t hashVal = std::hash{}(hashstream.str()); return std::to_string(hashVal); } std::stringstream mitk::SegmentAnythingTool::GetPointsAsCSVString(const mitk::BaseGeometry *baseGeometry) { MITK_INFO << "No.of points: " << m_PointSetPositive->GetSize(); std::stringstream pointsAndLabels; pointsAndLabels << "Point,Label\n"; mitk::PointSet::PointsConstIterator pointSetItPos = m_PointSetPositive->Begin(); mitk::PointSet::PointsConstIterator pointSetItNeg = m_PointSetNegative->Begin(); const char SPACE = ' '; while (pointSetItPos != m_PointSetPositive->End() || pointSetItNeg != m_PointSetNegative->End()) { if (pointSetItPos != m_PointSetPositive->End()) { mitk::Point3D point = pointSetItPos.Value(); if (baseGeometry->IsInside(point)) { Point2D p2D = Get2DIndicesfrom3DWorld(baseGeometry, point); pointsAndLabels << static_cast(p2D[0]) << SPACE << static_cast(p2D[1]) << ",1" << std::endl; } ++pointSetItPos; } if (pointSetItNeg != m_PointSetNegative->End()) { mitk::Point3D point = pointSetItNeg.Value(); if (baseGeometry->IsInside(point)) { Point2D p2D = Get2DIndicesfrom3DWorld(baseGeometry, point); pointsAndLabels << static_cast(p2D[0]) << SPACE << static_cast(p2D[1]) << ",0" << std::endl; } ++pointSetItNeg; } } return pointsAndLabels; } std::vector> mitk::SegmentAnythingTool::GetPointsAsVector( const mitk::BaseGeometry *baseGeometry) { std::vector> clickVec; clickVec.reserve(m_PointSetPositive->GetSize() + m_PointSetNegative->GetSize()); mitk::PointSet::PointsConstIterator pointSetItPos = m_PointSetPositive->Begin(); mitk::PointSet::PointsConstIterator pointSetItNeg = m_PointSetNegative->Begin(); while (pointSetItPos != m_PointSetPositive->End() || pointSetItNeg != m_PointSetNegative->End()) { if (pointSetItPos != m_PointSetPositive->End()) { mitk::Point3D point = pointSetItPos.Value(); if (baseGeometry->IsInside(point)) { Point2D p2D = Get2DIndicesfrom3DWorld(baseGeometry, point); clickVec.push_back(std::pair(p2D, "1")); } ++pointSetItPos; } if (pointSetItNeg != m_PointSetNegative->End()) { mitk::Point3D point = pointSetItNeg.Value(); if (baseGeometry->IsInside(point)) { Point2D p2D = Get2DIndicesfrom3DWorld(baseGeometry, point); clickVec.push_back(std::pair(p2D, "0")); } ++pointSetItNeg; } } return clickVec; } mitk::Point2D mitk::SegmentAnythingTool::Get2DIndicesfrom3DWorld(const mitk::BaseGeometry *baseGeometry, const mitk::Point3D &point3d) { mitk::Point3D index3D; baseGeometry->WorldToIndex(point3d, index3D); MITK_INFO << index3D[0] << " " << index3D[1] << " " << index3D[2]; // remove Point2D point2D; point2D.SetElement(0, index3D[0]); point2D.SetElement(1, index3D[1]); return point2D; } void mitk::SegmentAnythingTool::EmitSAMStatusMessageEvent(const std::string& status) { SAMStatusMessageEvent.Send(status); } diff --git a/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.h b/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.h index 9878a59baa..99c2e20f65 100644 --- a/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.h +++ b/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.h @@ -1,220 +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. ============================================================================*/ #ifndef mitkSegmentAnythingTool_h #define mitkSegmentAnythingTool_h #include "mitkSegWithPreviewTool.h" #include "mitkPointSet.h" #include "mitkProcessExecutor.h" #include "mitkSegmentAnythingPythonService.h" #include #include #include namespace us { class ModuleResource; } namespace mitk { /** \brief Segment Anything Model interactive 2D tool class. \ingroup ToolManagerEtAl \sa mitk::Tool \sa QmitkInteractiveSegmentation */ class MITKSEGMENTATION_EXPORT SegmentAnythingTool : public SegWithPreviewTool { public: mitkClassMacro(SegmentAnythingTool, SegWithPreviewTool); itkFactorylessNewMacro(Self); itkCloneMacro(Self); const char **GetXPM() const override; const char *GetName() const override; us::ModuleResource GetIconResource() const override; void Activated() override; void Deactivated() override; /** * @brief Clears all picks and updates the preview. */ - void ClearPicks(); + virtual void ClearPicks(); /** * @brief Checks if any point exists in the either * of the pointsets * * @return bool */ - bool HasPicks() const; + virtual bool HasPicks() const; itkSetMacro(MitkTempDir, std::string); itkGetConstMacro(MitkTempDir, std::string); itkSetMacro(PythonPath, std::string); itkGetConstMacro(PythonPath, std::string); itkSetMacro(ModelType, std::string); itkGetConstMacro(ModelType, std::string); itkSetMacro(CheckpointPath, std::string); itkGetConstMacro(CheckpointPath, std::string); itkSetMacro(Backend, std::string); itkGetConstMacro(Backend, std::string); itkSetMacro(GpuId, int); itkGetConstMacro(GpuId, int); itkSetMacro(TimeOutLimit, long); itkGetConstMacro(TimeOutLimit, long); itkSetMacro(IsReady, bool); itkGetConstMacro(IsReady, bool); itkBooleanMacro(IsReady); /** * @brief Initializes python service and * starts async python daemon of SegmentAnything model. * */ void InitSAMPythonProcess(); /** * @brief Checks if Python daemon is ready to accept inputs. * * @return bool */ bool IsPythonReady() const; Message1 SAMStatusMessageEvent; protected: SegmentAnythingTool(); ~SegmentAnythingTool() = default; void ConnectActionsAndFunctions() override; /* * @brief Add positive seed point action of StateMachine pattern */ virtual void OnAddPositivePoint(StateMachineAction*, InteractionEvent *interactionEvent); /* * @brief Add negative seed point action of StateMachine pattern */ virtual void OnAddNegativePoint(StateMachineAction*, InteractionEvent *interactionEvent); /* * @brief Delete action of StateMachine pattern. The function deletes positive or negative points in the reverse order of creation. This is done by finding & deleting the Point having the highest PointIdentifier value from either of the PointSets m_PointSetPositive & m_PointSetNegative. */ virtual void OnDelete(StateMachineAction*, InteractionEvent*); /* * @brief Clear all seed points and call UpdatePreview to reset the segmentation Preview */ void ClearSeeds(); /** * @brief Overriden method from the tool manager to execute the segmentation * Implementation: * 1. Creates Hash for input image from current plane geometry. * 2. Transfers image pointer to python service along with the hash code. * 3. Creates seed points as CSV string & transfers to python service * 3. Retrieves resulting segmentation Image pointer from python service and sets to previewImage. * * @param inputAtTimeStep * @param oldSegAtTimeStep * @param previewImage * @param timeStep */ void DoUpdatePreview(const Image *inputAtTimeStep, const Image *oldSegAtTimeStep, LabelSetImage *previewImage, TimeStepType timeStep) override; /** * @brief Get the Points from positive and negative pointsets as std::vector. * * @return std::vector> */ std::vector> GetPointsAsVector(const mitk::BaseGeometry*); /** * @brief Get the Points from positive and negative pointsets as csv string. * * @param baseGeometry * @return std::stringstream */ virtual std::stringstream GetPointsAsCSVString(const mitk::BaseGeometry *baseGeometry); /** * @brief Get the Hash For Current Plane from current working plane geometry. * * @return std::string */ std::string GetHashForCurrentPlane(const mitk::LevelWindow&); /** * @brief Emits message to connected Listnerers. * */ void EmitSAMStatusMessageEvent(const std::string&); /** * @brief Cleans up segmentation preview and clears all seeds. * */ void ConfirmCleanUp() override; /** * @brief Applies ITK IntensityWindowing Filter to input image; * */ template void ITKWindowing(const itk::Image*, mitk::Image*, ScalarType, ScalarType); - private: /** * @brief Convert 3D world coordinates to 2D indices. * * @param baseGeometry * @param mitk::Point3D * @return mitk::Point2D */ static mitk::Point2D Get2DIndicesfrom3DWorld(const mitk::BaseGeometry*, const mitk::Point3D&); + std::unique_ptr m_PythonService; + + private: std::string m_MitkTempDir; std::string m_PythonPath; std::string m_ModelType; std::string m_CheckpointPath; std::string m_Backend; int m_GpuId = 0; PointSet::Pointer m_PointSetPositive; PointSet::Pointer m_PointSetNegative; DataNode::Pointer m_PointSetNodePositive; DataNode::Pointer m_PointSetNodeNegative; bool m_IsGenerateEmbeddings = true; bool m_IsReady = false; int m_PointSetCount = 0; long m_TimeOutLimit = -1; - std::unique_ptr m_PythonService; const Label::PixelType MASK_VALUE = 1; }; } // namespace #endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkMedSAMGUIControls.ui b/Modules/SegmentationUI/Qmitk/QmitkMedSAMGUIControls.ui index 17e3ada22b..af5d1b7f3f 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkMedSAMGUIControls.ui +++ b/Modules/SegmentationUI/Qmitk/QmitkMedSAMGUIControls.ui @@ -1,167 +1,173 @@ QmitkMedSAMGUIControls 0 0 699 490 0 0 100 0 100000 100000 QmitkMedSAMToolWidget 0 0 0 0 0 0 - <html><head/><body><p>Welcome to Medical Segment Anything Model (MedSAM) tool in MITK. [Experimental]</p><p>Please note that this is only an interface to MedSAM. MITK does not ship with SAM. Make sure to have a working internet connection to install MedSAM 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> + <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. +Press SHIFT+Left-click and drag for RoI on the render windows. 0 0 100000 16777215 Reset RoI 0 0 100000 16777215 Initialize MedSAM 0 0 true 1 0 false + + + + + 0 + 0 + + + + + 100000 + 16777215 + + + + Preview + + + - - - ctkComboBox - QComboBox -
ctkComboBox.h
- 1 -
- - ctkCollapsibleGroupBox - QWidget -
ctkCollapsibleGroupBox.h
-
-
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMedSAMToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkMedSAMToolGUI.cpp index 892ea24fa9..abd084f783 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkMedSAMToolGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkMedSAMToolGUI.cpp @@ -1,263 +1,280 @@ /*============================================================================ 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 #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"); } -} +} // namespace QmitkMedSAMToolGUI::QmitkMedSAMToolGUI() : QmitkSegWithPreviewToolGUIBase(true) { m_EnableConfirmSegBtnFnc = [this](bool enabled) { bool result = false; - auto tool = this->GetConnectedToolAs(); + 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() +QmitkMedSAMToolGUI::~QmitkMedSAMToolGUI() { - auto tool = this->GetConnectedToolAs(); + auto tool = this->GetConnectedToolAs(); // check -ashis if (nullptr != tool) { tool->SAMStatusMessageEvent -= - mitk::MessageDelegate1( - this, &QmitkMedSAMToolGUI::StatusMessageListener); + 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: " + 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 += " SAM is already found installed. Model type '" + modelType + "' selected in Preferences."; + 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."; + 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 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)); } } 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"); + 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); + 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 Segment Anything segmentation. Reason: " << e.what(); + 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 Segment Anything segmentation."; + 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 &) { 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/QmitkMedSAMToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitkMedSAMToolGUI.h index 5dc589fae7..57f910b099 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkMedSAMToolGUI.h +++ b/Modules/SegmentationUI/Qmitk/QmitkMedSAMToolGUI.h @@ -1,95 +1,105 @@ /*============================================================================ 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. ============================================================================*/ #ifndef QmitkMedSAMToolGUI_h #define QmitkMedSAMToolGUI_h #include "QmitkSegmentAnythingToolGUI.h" #include #include "ui_QmitkMedSAMGUIControls.h" #include #include "QmitknnUNetGPU.h" /** \ingroup org_mitk_gui_qt_interactivesegmentation_internal \brief GUI for mitk::MedSAMTool. */ class MITKSEGMENTATIONUI_EXPORT QmitkMedSAMToolGUI : public QmitkSegWithPreviewToolGUIBase { Q_OBJECT public: mitkClassMacro(QmitkMedSAMToolGUI, QmitkSegWithPreviewToolGUIBase); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** * @brief Enable (or Disable) GUI elements. Currently, on the activate button * is affected. */ void EnableAll(bool); /** * @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 Enable (or Disable) progressbar on GUI */ void ShowProgressBar(bool); bool ActivateSAMDaemon(); /** * @brief Function to listen to preference emitters. */ void OnPreferenceChangedEvent(const mitk::IPreferences::ChangeEvent &); /** * @brief Creates a QMessage object and shows on screen. */ void ShowErrorMessage(const std::string &, QMessageBox::Icon = QMessageBox::Critical); bool ValidatePrefences(); protected: QmitkMedSAMToolGUI(); ~QmitkMedSAMToolGUI(); void InitializeUI(QBoxLayout *mainLayout) override; /** * @brief Function to listen to tool class status emitters. */ void StatusMessageListener(const std::string&); protected slots: /** * @brief Qt Slot */ void OnActivateBtnClicked(); + /** + * @brief Qt Slot + */ + void OnPreviewBtnClicked(); + + /** + * @brief Qt Slot + */ + void OnResetPicksClicked(); + private: mitk::IPreferences *m_Preferences; QmitkGPULoader m_GpuLoader; Ui_QmitkMedSAMGUIControls m_Controls; bool m_FirstPreviewComputation = true; const std::string WARNING_SAM_NOT_FOUND = "MedSAM is not detected in the selected python environment. Please reinstall MedSAM."; }; #endif