diff --git a/Modules/Segmentation/Interactions/mitkMonaiLabel2DTool.cpp b/Modules/Segmentation/Interactions/mitkMonaiLabel2DTool.cpp index 857a51befd..8469212b32 100644 --- a/Modules/Segmentation/Interactions/mitkMonaiLabel2DTool.cpp +++ b/Modules/Segmentation/Interactions/mitkMonaiLabel2DTool.cpp @@ -1,148 +1,72 @@ /*============================================================================ 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 "mitkMonaiLabel2DTool.h" #include #include #include // us #include #include #include #include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, MonaiLabel2DTool, "MonaiLabel2D"); } void mitk::MonaiLabel2DTool::Activated() { Superclass::Activated(); this->SetLabelTransferScope(LabelTransferScope::AllLabels); } const char **mitk::MonaiLabel2DTool::GetXPM() const { return nullptr; } us::ModuleResource mitk::MonaiLabel2DTool::GetIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("AI.svg"); return resource; } const char *mitk::MonaiLabel2DTool::GetName() const { return "MONAI Label 2D"; } -void mitk::MonaiLabel2DTool::OnAddPositivePoint(StateMachineAction *, InteractionEvent *interactionEvent) -{ - if (m_RequestParameters->model.IsInteractive()) - { - if ((nullptr == this->GetWorkingPlaneGeometry()) || - !mitk::Equal(*(interactionEvent->GetSender()->GetCurrentWorldPlaneGeometry()), - *(this->GetWorkingPlaneGeometry()))) - { - this->ClearSeeds(); - this->SetWorkingPlaneGeometry(interactionEvent->GetSender()->GetCurrentWorldPlaneGeometry()->Clone()); - this->ResetPreviewContent(); - } - 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::MonaiLabel2DTool::OnAddNegativePoint(StateMachineAction *, InteractionEvent *interactionEvent) -{ - if ("deepgrow" != m_RequestParameters->model.type) - { - return; - } - if (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::MonaiLabel2DTool::WriteImage(const Image *inputAtTimeStep, const std::string &inputImagePath) const { auto extendedImage = Image::New(); std::array dim = {inputAtTimeStep->GetDimension(0), inputAtTimeStep->GetDimension(1), 1}; mitk::PixelType pt = inputAtTimeStep->GetPixelType(); extendedImage->Initialize(pt, 3, dim.data()); ImageReadAccessor readAccessor(inputAtTimeStep); extendedImage->SetVolume(readAccessor.GetData()); IOUtil::Save(extendedImage.GetPointer(), inputImagePath); } -std::stringstream mitk::MonaiLabel2DTool::GetPointsAsListString(const mitk::BaseGeometry *baseGeometry, - const PointSet::Pointer pointSet) const -{ - std::stringstream pointsAndLabels; - pointsAndLabels << "["; - auto pointSetIter = pointSet->Begin(); - const char COMMA = ','; - while (pointSetIter != pointSet->End()) - { - auto point3d = pointSetIter.Value(); - if (baseGeometry->IsInside(point3d)) - { - Point3D index3D; - baseGeometry->WorldToIndex(point3d, index3D); - pointsAndLabels << "["; - pointsAndLabels << static_cast(index3D[0]) << COMMA << static_cast(index3D[1]) << COMMA << 0 << "],"; - } - ++pointSetIter; - } - if (pointsAndLabels.tellp() > 1) - { - pointsAndLabels.seekp(-1, pointsAndLabels.end); // remove last added comma character - } - pointsAndLabels << "]"; - return pointsAndLabels; -} - void mitk::MonaiLabel2DTool::WriteBackResults(LabelSetImage *previewImage, LabelSetImage *segResults, TimeStepType timeStep) const { mitk::SegTool2D::WriteSliceToVolume(previewImage, this->GetWorkingPlaneGeometry(), segResults, timeStep, false); } diff --git a/Modules/Segmentation/Interactions/mitkMonaiLabel2DTool.h b/Modules/Segmentation/Interactions/mitkMonaiLabel2DTool.h index 17217d2b60..51ca9e40ed 100644 --- a/Modules/Segmentation/Interactions/mitkMonaiLabel2DTool.h +++ b/Modules/Segmentation/Interactions/mitkMonaiLabel2DTool.h @@ -1,57 +1,53 @@ /*============================================================================ 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 mitkMonaiLabel2DTool_h #define mitkMonaiLabel2DTool_h #include "mitkMonaiLabelTool.h" #include namespace us { class ModuleResource; } namespace mitk { /** \brief MonaiLabel segmentation 2D tool. \ingroup ToolManagerEtAl \sa mitk::Tool \sa QmitkInteractiveSegmentation */ class MITKSEGMENTATION_EXPORT MonaiLabel2DTool : public MonaiLabelTool { public: mitkClassMacro(MonaiLabel2DTool, MonaiLabelTool); itkFactorylessNewMacro(Self); itkCloneMacro(Self); const char *GetName() const override; const char **GetXPM() const override; us::ModuleResource GetIconResource() const override; void Activated() override; void WriteImage(const Image *inputAtTimeStep, const std::string &inputImagePath) const override; - std::stringstream GetPointsAsListString(const mitk::BaseGeometry *baseGeometry, - const PointSet::Pointer pointSet) const override; - void OnAddPositivePoint(StateMachineAction *, InteractionEvent *interactionEvent) override; - void OnAddNegativePoint(StateMachineAction *, InteractionEvent *interactionEvent) override; void WriteBackResults(LabelSetImage *previewImage, LabelSetImage *segResults, TimeStepType timeStep) const override; protected: MonaiLabel2DTool() = default; ~MonaiLabel2DTool() = default; }; } #endif diff --git a/Modules/Segmentation/Interactions/mitkMonaiLabel3DTool.cpp b/Modules/Segmentation/Interactions/mitkMonaiLabel3DTool.cpp index 3f36174ee1..bc3f2d0b91 100644 --- a/Modules/Segmentation/Interactions/mitkMonaiLabel3DTool.cpp +++ b/Modules/Segmentation/Interactions/mitkMonaiLabel3DTool.cpp @@ -1,127 +1,62 @@ /*============================================================================ 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 "mitkMonaiLabel3DTool.h" #include #include #include #include #include #include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, MonaiLabel3DTool, "MonaiLabel3D"); } void mitk::MonaiLabel3DTool::Activated() { Superclass::Activated(); this->SetLabelTransferScope(LabelTransferScope::AllLabels); } const char **mitk::MonaiLabel3DTool::GetXPM() const { return nullptr; } us::ModuleResource mitk::MonaiLabel3DTool::GetIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("AI.svg"); return resource; } const char *mitk::MonaiLabel3DTool::GetName() const { return "MONAI Label 3D"; } void mitk::MonaiLabel3DTool::WriteImage(const Image *inputAtTimeStep, const std::string &inputImagePath) const { IOUtil::Save(inputAtTimeStep, inputImagePath); } void mitk::MonaiLabel3DTool::WriteBackResults(LabelSetImage *previewImage, LabelSetImage *segResults, TimeStepType timeStep) const { mitk::ImageReadAccessor newMitkImgAcc(segResults); previewImage->SetVolume(newMitkImgAcc.GetData(), timeStep); } - -void mitk::MonaiLabel3DTool::OnAddPositivePoint(StateMachineAction *, InteractionEvent *interactionEvent) -{ - if ("deepgrow" == m_RequestParameters->model.type || "deepedit" == m_RequestParameters->model.type) - { - 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::MonaiLabel3DTool::OnAddNegativePoint(StateMachineAction *, InteractionEvent *interactionEvent) -{ - if ("deepgrow" != m_RequestParameters->model.type) - { - 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(); - } - } -} - -std::stringstream mitk::MonaiLabel3DTool::GetPointsAsListString(const mitk::BaseGeometry *baseGeometry, - const PointSet::Pointer pointSet) const -{ - MITK_INFO << "No.of points: " << pointSet->GetSize(); - std::stringstream pointsAndLabels; - pointsAndLabels << "["; - mitk::PointSet::PointsConstIterator pointSetIter = pointSet->Begin(); - const char COMMA = ','; - while (pointSetIter != pointSet->End()) - { - mitk::Point3D point3d = pointSetIter.Value(); - if (baseGeometry->IsInside(point3d)) - { - mitk::Point3D index3D; - baseGeometry->WorldToIndex(point3d, index3D); - pointsAndLabels << "["; - pointsAndLabels << static_cast(index3D[0]) << COMMA << static_cast(index3D[1]) << COMMA - << static_cast(index3D[2]) << "],"; - } - ++pointSetIter; - } - if (pointsAndLabels.tellp() > 1) - { - pointsAndLabels.seekp(-1, pointsAndLabels.end); // remove last added comma character - } - pointsAndLabels << "]"; - return pointsAndLabels; -} - diff --git a/Modules/Segmentation/Interactions/mitkMonaiLabel3DTool.h b/Modules/Segmentation/Interactions/mitkMonaiLabel3DTool.h index 3c9fd24ebe..31452cf166 100644 --- a/Modules/Segmentation/Interactions/mitkMonaiLabel3DTool.h +++ b/Modules/Segmentation/Interactions/mitkMonaiLabel3DTool.h @@ -1,55 +1,52 @@ /*============================================================================ 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 mitkMonaiLabel3DTool_h #define mitkMonaiLabel3DTool_h #include "mitkMonaiLabelTool.h" #include namespace us { class ModuleResource; } namespace mitk { /** \brief MonaiLabel segmentation 3D tool. \ingroup Interaction \ingroup ToolManagerEtAl \warning Only to be instantiated by mitk::ToolManager. */ class MITKSEGMENTATION_EXPORT MonaiLabel3DTool : public MonaiLabelTool { public: mitkClassMacro(MonaiLabel3DTool, MonaiLabelTool); itkFactorylessNewMacro(Self); itkCloneMacro(Self); const char *GetName() const override; const char **GetXPM() const override; us::ModuleResource GetIconResource() const override; void Activated() override; - void OnAddPositivePoint(StateMachineAction *, InteractionEvent *interactionEvent) override; - void OnAddNegativePoint(StateMachineAction *, InteractionEvent *interactionEvent) override; - std::stringstream GetPointsAsListString(const mitk::BaseGeometry *baseGeometry, const PointSet::Pointer pointSet) const override; void WriteImage(const Image *inputAtTimeStep, const std::string &inputImagePath) const override; void WriteBackResults(LabelSetImage *previewImage, LabelSetImage *segResults, TimeStepType timeStep) const override; protected: MonaiLabel3DTool() = default; ~MonaiLabel3DTool() = default; }; } // namespace mitk #endif diff --git a/Modules/Segmentation/Interactions/mitkMonaiLabelTool.cpp b/Modules/Segmentation/Interactions/mitkMonaiLabelTool.cpp index 09eb256a8f..7b0acb92cf 100644 --- a/Modules/Segmentation/Interactions/mitkMonaiLabelTool.cpp +++ b/Modules/Segmentation/Interactions/mitkMonaiLabelTool.cpp @@ -1,568 +1,660 @@ /*============================================================================ 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 "mitkMonaiLabelTool.h" #ifndef CPPHTTPLIB_OPENSSL_SUPPORT #define CPPHTTPLIB_OPENSSL_SUPPORT #endif #include #include #include #include #include #include #include #include #include "mitkImageAccessByItk.h" mitk::MonaiLabelTool::MonaiLabelTool() : SegWithPreviewTool(true, "PressMoveReleaseAndPointSetting") { this->ResetsToEmptyPreviewOn(); this->IsTimePointChangeAwareOff(); } mitk::MonaiLabelTool::~MonaiLabelTool() { std::filesystem::remove_all(this->GetTempDir()); } void mitk::MonaiLabelTool::ConnectActionsAndFunctions() { CONNECT_FUNCTION("ShiftSecondaryButtonPressed", OnAddNegativePoint); CONNECT_FUNCTION("ShiftPrimaryButtonPressed", OnAddPositivePoint); CONNECT_FUNCTION("DeletePoint", OnDelete); } void mitk::MonaiLabelTool::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)); } void mitk::MonaiLabelTool::Deactivated() { this->ClearSeeds(); this->GetDataStorage()->Remove(m_PointSetNodePositive); this->GetDataStorage()->Remove(m_PointSetNodeNegative); m_PointSetNodePositive = nullptr; m_PointSetNodeNegative = nullptr; m_PointSetPositive = nullptr; m_PointSetNegative = nullptr; Superclass::Deactivated(); } void mitk::MonaiLabelTool::UpdatePrepare() { Superclass::UpdatePrepare(); auto preview = this->GetPreviewSegmentation(); preview->RemoveLabels(preview->GetAllLabelValues()); } +void mitk::MonaiLabelTool::OnAddPositivePoint(StateMachineAction *, InteractionEvent *interactionEvent) +{ + if (m_RequestParameters->model.IsInteractive()) + { + if (m_RequestParameters->model.Is2D() && ((nullptr == this->GetWorkingPlaneGeometry()) || + !mitk::Equal(*(interactionEvent->GetSender()->GetCurrentWorldPlaneGeometry()), + *(this->GetWorkingPlaneGeometry())))) + { + this->ClearSeeds(); + this->SetWorkingPlaneGeometry(interactionEvent->GetSender()->GetCurrentWorldPlaneGeometry()->Clone()); + this->ResetPreviewContent(); + } + 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::MonaiLabelTool::OnAddNegativePoint(StateMachineAction *, InteractionEvent *interactionEvent) +{ + if ("deepgrow" != m_RequestParameters->model.type) + { + return; + } + if (m_RequestParameters->model.Is2D() && (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::MonaiLabelTool::OnDelete(StateMachineAction *, InteractionEvent *) { if (!this->IsUpdating() && m_PointSetPositive.IsNotNull()) { PointSet::Pointer removeSet = m_PointSetPositive; itk::IdentifierType 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::MonaiLabelTool::ClearPicks() { this->ClearSeeds(); this->UpdatePreview(); } bool mitk::MonaiLabelTool::HasPicks() const { return this->m_PointSetPositive.IsNotNull() && this->m_PointSetPositive->GetSize() > 0; } void mitk::MonaiLabelTool::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); } } bool mitk::MonaiLabelTool::IsMonaiServerOn(const std::string &hostName, const int &port) const { httplib::SSLClient cli(hostName, port); cli.enable_server_certificate_verification(false); while (cli.is_socket_open()); return cli.Get("/info/"); } namespace mitk { // Converts the json GET response from the MonaiLabel server to MonaiAppMetadata object. void from_json(const nlohmann::json &jsonObj, mitk::MonaiAppMetadata &appData) { jsonObj["name"].get_to(appData.name); jsonObj["description"].get_to(appData.description); jsonObj["labels"].get_to(appData.labels); auto modelJsonMap = jsonObj["models"].get>(); for (const auto &[_name, _jsonObj] : modelJsonMap) { if (_jsonObj.is_discarded() || !_jsonObj.is_object()) { MITK_ERROR << "Could not parse JSON object."; } mitk::MonaiModelInfo modelInfo; modelInfo.name = _name; try { auto labels = _jsonObj["labels"].get>(); modelInfo.labels = labels; } catch (const std::exception &) { auto labels = _jsonObj["labels"].get>(); for (const auto &label : labels) { modelInfo.labels[label] = -1; // Hardcode -1 as label id } } _jsonObj["type"].get_to(modelInfo.type); _jsonObj["dimension"].get_to(modelInfo.dimension); _jsonObj["description"].get_to(modelInfo.description); appData.models.push_back(modelInfo); } } } namespace { /** * @brief Returns boundary string from Httplib response. */ std::string GetBoundaryString(const httplib::Result &response) { httplib::Headers headers = response->headers; std::string contentType = headers.find("content-type")->second; std::string delimiter = "boundary="; std::string boundaryString = contentType.substr(contentType.find(delimiter) + delimiter.length(), std::string::npos); boundaryString.insert(0, "--"); return boundaryString; } /** * @brief Returns image data string from monai label overall response. */ std::string GetResponseImageString(const std::string &imagePart) { std::string contentTypeStream = "Content-Type: application/octet-stream"; size_t ctPos = imagePart.find(contentTypeStream) + contentTypeStream.length(); std::string imageDataPart = imagePart.substr(ctPos); std::string whitespaces = " \n\r"; imageDataPart.erase(0, imageDataPart.find_first_not_of(whitespaces)); // clean up return imageDataPart; } /** * @brief Helper function to get the Parts of the POST response. */ std::vector GetPartsBetweenBoundary(const std::string &body, const std::string &boundary) { std::vector retVal; std::string master = body; size_t boundaryPos = master.find(boundary); size_t beginPos = 0; while (boundaryPos != std::string::npos) { std::string part = master.substr(beginPos, boundaryPos); if (!part.empty()) { retVal.push_back(part); } master.erase(beginPos, boundaryPos + boundary.length()); boundaryPos = master.find(boundary); } return retVal; } /** * @brief Applies the give std::map lookup table on the preview segmentation LabelSetImage. */ void MapLabelsToSegmentation(const mitk::LabelSetImage *source, mitk::LabelSetImage *dest, const std::map &labelMap) { if (labelMap.empty()) { auto label = mitk::LabelSetImageHelper::CreateNewLabel(dest, "object"); label->SetValue(1); dest->AddLabel(label, false); return; } std::map flippedLabelMap; for (auto const &[key, val] : labelMap) { flippedLabelMap[val] = key; } auto lookupTable = mitk::LookupTable::New(); lookupTable->SetType(mitk::LookupTable::LookupTableType::MULTILABEL); for (auto const &[key, val] : flippedLabelMap) { if (source->ExistLabel(key, source->GetActiveLayer())) { auto label = mitk::Label::New(key, val); std::array lookupTableColor; lookupTable->GetColor(key, lookupTableColor.data()); mitk::Color color; color.SetRed(lookupTableColor[0]); color.SetGreen(lookupTableColor[1]); color.SetBlue(lookupTableColor[2]); label->SetColor(color); dest->AddLabel(label, false); } else { MITK_INFO << "Label not found for " << val; } } } template void ITKWindowing(const itk::Image *inputImage, mitk::Image *mitkImage, mitk::ScalarType min, mitk::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, mitk::Image::ManageMemory); filter->GetOutput()->GetPixelContainer()->ContainerManageMemoryOff(); } } mitk::Image::Pointer mitk::MonaiLabelTool::ApplyLevelWindowEffect(const Image *inputAtTimeStep) const { mitk::LevelWindow levelWindow; this->GetToolManager()->GetReferenceData(0)->GetLevelWindow(levelWindow); auto filteredImage = mitk::Image::New(); filteredImage->Initialize(inputAtTimeStep); AccessByItk_n(inputAtTimeStep, ::ITKWindowing, // apply level window filter (filteredImage, levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound())); return filteredImage; } void mitk::MonaiLabelTool::DoUpdatePreview(const Image *inputAtTimeStep, const Image * /*oldSegAtTimeStep*/, LabelSetImage *previewImage, TimeStepType timeStep) { if (nullptr == m_RequestParameters || (m_RequestParameters->model.IsInteractive() && !this->HasPicks())) { this->ResetPreviewContentAtTimeStep(timeStep); RenderingManager::GetInstance()->ForceImmediateUpdateAll(); return; } const std::string &hostName = m_RequestParameters->hostName; const int port = m_RequestParameters->port; if (!IsMonaiServerOn(hostName, port)) { mitkThrow() << m_SERVER_503_ERROR_TEXT; } if (this->m_TempDir.empty()) { this->SetTempDir(IOUtil::CreateTemporaryDirectory("mitk-XXXXXX")); } std::string inputImagePath, outputImagePath; std::tie(inputImagePath, outputImagePath) = this->CreateTempDirs(m_TEMPLATE_FILENAME); try { mitk::Image::Pointer filteredImage = this->ApplyLevelWindowEffect(inputAtTimeStep); this->WriteImage(filteredImage, inputImagePath); this->PostInferRequest(hostName, port, inputImagePath, outputImagePath, inputAtTimeStep->GetGeometry()); auto outputImage = IOUtil::Load(outputImagePath); auto outputBuffer = mitk::LabelSetImage::New(); outputBuffer->InitializeByLabeledImage(outputImage); std::map labelMap; // empty map if (m_RequestParameters->model.IsInteractive()) { this->SetLabelTransferMode(LabelTransferMode::MapLabel); this->SetSelectedLabels({MASK_VALUE}); } else { outputBuffer->SetGeometry(inputAtTimeStep->GetGeometry()); labelMap = m_ResultMetadata["label_names"]; this->SetLabelTransferMode(LabelTransferMode::AddLabel); } ::MapLabelsToSegmentation(outputBuffer, previewImage, labelMap); this->WriteBackResults(previewImage, outputBuffer.GetPointer(), timeStep); MonaiStatusEvent.Send(true); } catch (const mitk::Exception &e) { MITK_ERROR << e.GetDescription(); mitkThrow() << e.GetDescription(); MonaiStatusEvent.Send(false); } } -void mitk::MonaiLabelTool::GetOverallInfo(const std::string &hostName, const int &port) +void mitk::MonaiLabelTool::FetchOverallInfo(const std::string &hostName, const int &port) { + m_InfoParameters.reset(); if (!IsMonaiServerOn(hostName, port)) { Tool::ErrorMessage.Send(m_SERVER_503_ERROR_TEXT); mitkThrow() << m_SERVER_503_ERROR_TEXT; } httplib::SSLClient cli(hostName, port); cli.enable_server_certificate_verification(false); if (auto response = cli.Get("/info/")) { if (response->status == 200) { auto jsonObj = nlohmann::json::parse(response->body); if (jsonObj.is_discarded() || !jsonObj.is_object()) { MITK_ERROR << "Could not parse response from MONAILabel server as JSON object!"; return; } auto appData = jsonObj.template get(); m_InfoParameters = std::make_unique(appData); if (nullptr != m_InfoParameters) { m_InfoParameters->hostName = hostName; m_InfoParameters->port = port; } } } else { Tool::ErrorMessage.Send(httplib::to_string(response.error()) + " error occured."); } } void mitk::MonaiLabelTool::PostInferRequest(const std::string &hostName, const int &port, const std::string &filePath, const std::string &outFile, const mitk::BaseGeometry *baseGeometry) { std::string &modelName = m_RequestParameters->model.name; // Get this from args as well. std::string postPath = "/infer/"; // make this separate class of constants postPath.append(modelName); std::ifstream input(filePath, std::ios::binary); if (!input) { MITK_WARN << "could not read file to POST"; } std::stringstream buffer_lf_img; buffer_lf_img << input.rdbuf(); input.close(); httplib::MultipartFormDataItems items; if (m_RequestParameters->model.IsInteractive()) { - std::stringstream foreground = this->GetPointsAsListString(baseGeometry, m_PointSetPositive); - std::stringstream background = this->GetPointsAsListString(baseGeometry, m_PointSetNegative); + std::string foreground = this->ConvertPointsAsListString(baseGeometry, m_PointSetPositive); + std::string background = this->ConvertPointsAsListString(baseGeometry, m_PointSetNegative); std::stringstream paramString; paramString << "{" - << "\"foreground\":" << foreground.str() - << ",\"background\":" << background.str() + << "\"foreground\":" << foreground + << ",\"background\":" << background << "}"; MITK_DEBUG << paramString.str(); items.push_back({"params", paramString.str(), "", ""}); } else // Auto models { items.push_back({"params", "{\"restore_label_idx\": true}", "", ""}); } items.push_back({"file", buffer_lf_img.str(), "post_from_mitk.nii.gz", "application/octet-stream"}); httplib::SSLClient cli(hostName, port); cli.set_read_timeout(60); // arbitary 1 minute time-out to avoid corner cases. cli.enable_server_certificate_verification(false); if (auto response = cli.Post(postPath, items)) { if (response->status == 200) { // Find boundary std::string boundaryString = ::GetBoundaryString(response); MITK_DEBUG << "boundary hash: " << boundaryString; // Parse metadata JSON std::string resBody = response->body; std::vector multiPartResponse = ::GetPartsBetweenBoundary(resBody, boundaryString); std::string metaData = multiPartResponse[0]; std::string contentTypeJson = "Content-Type: application/json"; size_t ctPos = metaData.find(contentTypeJson) + contentTypeJson.length(); std::string metaDataPart = metaData.substr(ctPos); MITK_DEBUG << metaDataPart; auto jsonObj = nlohmann::json::parse(metaDataPart); if (jsonObj.is_discarded() || !jsonObj.is_object()) { MITK_ERROR << "Could not parse response from MONAILabel server as JSON object!"; return; } else // use the metadata { m_ResultMetadata = jsonObj; } // Parse response image string std::string imagePart = multiPartResponse[1]; std::string imageData = ::GetResponseImageString(imagePart); std::ofstream output(outFile, std::ios::out | std::ios::app | std::ios::binary); output << imageData; output.unsetf(std::ios::skipws); output.flush(); } else { auto err = response.error(); MITK_ERROR << "An HTTP POST error: " << httplib::to_string(err) << " occured."; mitkThrow() << "An HTTP POST error: " << httplib::to_string(err) << " occured."; } } } -std::vector mitk::MonaiLabelTool::GetAutoSegmentationModels(int dim) const +const std::vector mitk::MonaiLabelTool::GetAutoSegmentationModels(const int dim) const { std::vector autoModels; if (nullptr != m_InfoParameters) { for (mitk::MonaiModelInfo &model : m_InfoParameters->models) { if (m_AUTO_SEG_TYPE_NAME.find(model.type) != m_AUTO_SEG_TYPE_NAME.end() && (!(dim > -1) || model.dimension == dim)) { autoModels.push_back(model); } } } return autoModels; } -std::vector mitk::MonaiLabelTool::GetInteractiveSegmentationModels(int dim) const +const std::vector mitk::MonaiLabelTool::GetInteractiveSegmentationModels(const int dim) const { std::vector interactiveModels; if (nullptr != m_InfoParameters) { for (mitk::MonaiModelInfo &model : m_InfoParameters->models) { if (m_INTERACTIVE_SEG_TYPE_NAME.find(model.type) != m_INTERACTIVE_SEG_TYPE_NAME.end() && (!(dim > -1) || model.dimension == dim)) { interactiveModels.push_back(model); } } } return interactiveModels; } -std::vector mitk::MonaiLabelTool::GetScribbleSegmentationModels(int dim) const +const std::vector mitk::MonaiLabelTool::GetScribbleSegmentationModels(const int dim) const { std::vector scribbleModels; if (nullptr != m_InfoParameters) { for (mitk::MonaiModelInfo &model : m_InfoParameters->models) { if (m_SCRIBBLE_SEG_TYPE_NAME.find(model.type) != m_SCRIBBLE_SEG_TYPE_NAME.end() && (!(dim > -1) || model.dimension == dim)) { scribbleModels.push_back(model); } } } return scribbleModels; } mitk::MonaiModelInfo mitk::MonaiLabelTool::GetModelInfoFromName(const std::string modelName) const { if (nullptr == m_InfoParameters) { mitkThrow() << "No model information found."; } mitk::MonaiModelInfo retVal; for (mitk::MonaiModelInfo &model : m_InfoParameters->models) { if (model.name == modelName) { retVal = model; break; } } return retVal; } -void mitk::MonaiLabelTool::WriteImage(const Image*, const std::string&) const {} - std::pair mitk::MonaiLabelTool::CreateTempDirs(const std::string &filePattern) const { std::string inDir, outDir, inputImagePath, outputImagePath; inDir = IOUtil::CreateTemporaryDirectory("monai-in-XXXXXX", this->GetTempDir()); std::ofstream tmpStream; inputImagePath = IOUtil::CreateTemporaryFile(tmpStream, filePattern, 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("monai-out-XXXXXX", this->GetTempDir()); outputImagePath = outDir + IOUtil::GetDirectorySeparator() + token + "_000.nii.gz"; return std::make_pair(inputImagePath, outputImagePath); } + +std::string mitk::MonaiLabelTool::ConvertPointsAsListString(const mitk::BaseGeometry *baseGeometry, + const PointSet::Pointer pointSet) const +{ + bool is3DMode = nullptr == this->GetWorkingPlaneGeometry(); + MITK_INFO << "No.of points: " << pointSet->GetSize() << " 3DMode: " << is3DMode; + std::stringstream pointsAndLabels; + pointsAndLabels << "["; + mitk::PointSet::PointsConstIterator pointSetIter = pointSet->Begin(); + const char COMMA = ','; + while (pointSetIter != pointSet->End()) + { + mitk::Point3D point3d = pointSetIter.Value(); + if (baseGeometry->IsInside(point3d)) + { + mitk::Point3D index3D; + baseGeometry->WorldToIndex(point3d, index3D); + pointsAndLabels << "["; + pointsAndLabels << static_cast(index3D[0]) << COMMA << static_cast(index3D[1]) << COMMA; + if (is3DMode) + { + pointsAndLabels << static_cast(index3D[2]); + } + else + { + pointsAndLabels << 0; + } + pointsAndLabels << "],"; + } + ++pointSetIter; + } + if (pointsAndLabels.tellp() > 1) + { + pointsAndLabels.seekp(-1, pointsAndLabels.end); // remove last added comma character + } + pointsAndLabels << "]"; + return pointsAndLabels.str(); +} + +const mitk::MonaiAppMetadata *mitk::MonaiLabelTool::GetInfoParameters() const +{ + return m_InfoParameters.get(); +} diff --git a/Modules/Segmentation/Interactions/mitkMonaiLabelTool.h b/Modules/Segmentation/Interactions/mitkMonaiLabelTool.h index b6fad46e23..1bf4e4bc0b 100644 --- a/Modules/Segmentation/Interactions/mitkMonaiLabelTool.h +++ b/Modules/Segmentation/Interactions/mitkMonaiLabelTool.h @@ -1,256 +1,264 @@ /*============================================================================ 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 mitkMonaiLabelTool_h #define mitkMonaiLabelTool_h #include "mitkSegWithPreviewTool.h" #include #include #include #include #include #include #include namespace us { class ModuleResource; } namespace mitk { /** * @brief Struct to hold featured models individual info * */ struct MonaiModelInfo { std::string name; std::string type; std::unordered_map labels; int dimension; std::string description; std::unordered_map config; inline bool operator==(const MonaiModelInfo &rhs) const { return (this->name == rhs.name && this->type == rhs.type); // Comparing only name and type, for now. } inline bool IsInteractive() const { return ("deepgrow" == type || "deepedit" == type); } + + inline bool Is2D() const + { + return dimension == 2; + } }; /** * @brief Struct to store MonaiLabel server metadata including all model infos * */ struct MonaiAppMetadata { std::string name; std::string description; std::vector labels; std::string version; std::string hostName; int port; std::string origin; std::vector models; }; /** * @brief Request class to pack model and other necessary server information * from GUI. * */ struct MonaiLabelRequest { MonaiModelInfo model; std::string hostName; std::string requestLabel; int port; inline bool operator==(const MonaiLabelRequest &rhs) const { return (this->model == rhs.model && this->hostName == rhs.hostName && this->port == rhs.port && this->requestLabel == rhs.requestLabel); } }; /** \brief MonaiLabel segmentation tool base class. \ingroup Interaction \ingroup ToolManagerEtAl \warning Only to be instantiated by mitk::ToolManager. */ class MITKSEGMENTATION_EXPORT MonaiLabelTool : public SegWithPreviewTool { public: mitkClassMacro(MonaiLabelTool, SegWithPreviewTool); itkCloneMacro(Self); void Activated() override; void Deactivated() override; void UpdatePrepare() override; - /** - * @brief Writes image to disk as the tool desires. - * Default implementation does nothing. - */ - virtual void WriteImage(const Image*, const std::string&) const; - /** * @brief Method does the GET Rest call to fetch MonaiLabel * server metadata including all models' info. */ - void GetOverallInfo(const std::string &hostName, const int &port); + void FetchOverallInfo(const std::string &hostName, const int &port); - /** - * @brief Holds all parameters of the server to serve the UI - * - */ - std::unique_ptr m_InfoParameters; - /** * @brief Variable to set selected model's and other data needed * for the POST call. */ std::unique_ptr m_RequestParameters; /** * @brief Get the Auto Segmentation Models info for the given * dimension. */ - std::vector GetAutoSegmentationModels(int dim = -1) const; + const std::vector GetAutoSegmentationModels(const int dim = -1) const; /** * @brief Get the Interactive Segmentation Models info for the given * dimension. */ - std::vector GetInteractiveSegmentationModels(int dim = -1) const; + const std::vector GetInteractiveSegmentationModels(const int dim = -1) const; /** * @brief Get the Scribble Segmentation Models info for the given * dimension. */ - std::vector GetScribbleSegmentationModels(int dim = -1) const; + const std::vector GetScribbleSegmentationModels(const int dim = -1) const; - /** - * @brief Function to prepare the Rest request and does the POST call. - * Writes the POST responses back to disk. - */ - void PostInferRequest(const std::string &hostName, const int &port, const std::string &filePath, const std::string &outFile, - const mitk::BaseGeometry *baseGeometry); /** * @brief Helper function to get full model info object from model name. */ MonaiModelInfo GetModelInfoFromName(const std::string) const; itkSetMacro(ModelName, std::string); itkGetConstMacro(ModelName, std::string); itkSetMacro(URL, std::string); itkGetConstMacro(URL, std::string); itkSetMacro(TempDir, std::string); itkGetConstMacro(TempDir, std::string); + const MonaiAppMetadata *GetInfoParameters() const; + /** * @brief Clears all picks and updates the preview. */ void ClearPicks(); /** * @brief Checks if any point exists in the either of the PointSetPositive * or PointSetNegative. */ bool HasPicks() const; Message1 MonaiStatusEvent; protected: MonaiLabelTool(); ~MonaiLabelTool(); void DoUpdatePreview(const Image* inputAtTimeStep, const Image* oldSegAtTimeStep, LabelSetImage* previewImage, TimeStepType timeStep) override; void ConnectActionsAndFunctions() override; + /** + * @brief Writes image to disk as the tool desires. + * Default implementation does nothing. + */ + virtual void WriteImage(const Image *, const std::string &) const = 0; + /* * @brief Add positive seed point action of StateMachine pattern. */ - virtual void OnAddPositivePoint(StateMachineAction *, InteractionEvent *interactionEvent) = 0; + virtual void OnAddPositivePoint(StateMachineAction *, InteractionEvent *interactionEvent); /* * @brief Add negative seed point action of StateMachine pattern */ - virtual void OnAddNegativePoint(StateMachineAction *, InteractionEvent *interactionEvent) = 0; + virtual void OnAddNegativePoint(StateMachineAction *, InteractionEvent *interactionEvent); /* * @brief Delete action of StateMachine pattern */ virtual void OnDelete(StateMachineAction *, InteractionEvent *); /* * @brief Clear all seed points and call UpdatePreview to reset the segmentation Preview */ void ClearSeeds(); /** * @brief Get the Points from given pointset as csv string. * */ - virtual std::stringstream GetPointsAsListString(const mitk::BaseGeometry *baseGeometry, - const PointSet::Pointer pointSet) const = 0; + virtual std::string ConvertPointsAsListString(const mitk::BaseGeometry *baseGeometry, + const PointSet::Pointer pointSet) const; /** * @brief Writes back segmentation results in 3D or 2D shape to preview LabelSetImage. */ virtual void WriteBackResults(LabelSetImage *, LabelSetImage *, TimeStepType) const = 0; PointSet::Pointer m_PointSetPositive; PointSet::Pointer m_PointSetNegative; DataNode::Pointer m_PointSetNodePositive; DataNode::Pointer m_PointSetNodeNegative; int m_PointSetCount = 0; private: + /** + * @brief Holds all parameters of the server to serve the UI + * + */ + std::unique_ptr m_InfoParameters; + /** * @brief Helper function to create temp directory for writing/reading images * Returns input and output file names expected as a pair. */ std::pair CreateTempDirs(const std::string &filePattern) const; /** * @brief Checks if MonaiLabel server is alive. */ bool IsMonaiServerOn(const std::string &hostName, const int &port) const; /** * @brief Applies level window filter on the input image. Current Level window bounds * are captured from the tool manager. */ mitk::Image::Pointer ApplyLevelWindowEffect(const Image *inputAtTimeStep) const; + + /** + * @brief Function to prepare the Rest request and does the POST call. + * Writes the POST responses back to disk. + */ + void PostInferRequest(const std::string &hostName, const int &port, const std::string &filePath, const std::string &outFile, + const mitk::BaseGeometry *baseGeometry); std::string m_TempDir; std::string m_ModelName; std::string m_URL; nlohmann::json m_ResultMetadata; const std::set m_AUTO_SEG_TYPE_NAME = {"segmentation"}; const std::set m_SCRIBBLE_SEG_TYPE_NAME = {"scribbles"}; const std::set m_INTERACTIVE_SEG_TYPE_NAME = {"deepgrow"}; // deepedit not supported yet const std::string m_TEMPLATE_FILENAME = "XXXXXX_000_0000.nii.gz"; const std::string m_SERVER_503_ERROR_TEXT = "A connection to MonaiLabel server cannot be established."; const Label::PixelType MASK_VALUE = 1; }; } #endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUI.cpp index 25d967f120..416ce18028 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUI.cpp @@ -1,333 +1,331 @@ /*============================================================================ 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 "QmitkMonaiLabelToolGUI.h" #include #include #include #include #include #include namespace { mitk::IPreferences *GetPreferences() { auto *preferencesService = mitk::CoreServices::GetPreferencesService(); return preferencesService->GetSystemPreferences()->Node("org.mitk.views.segmentation"); } } const QString QmitkMonaiLabelToolGUI::CONFIRM_QUESTION_TEXT = "Data will be sent to the processing server devoid of any patient information. Are you sure you want continue?"; const QMap QmitkMonaiLabelToolGUI::WHITELISTED_MODELS = {{"deepgrow_2d", "Radiology"}, {"deepgrow_3d", "Radiology"}, {"deepedit_seg", "Radiology"}, {"localization_vertebra", "Radiology"}, {"segmentation", "Radiology"}, {"segmentation_spleen", "Radiology"}, {"segmentation_vertebra", "Radiology"}, {"deepgrow_pipeline", "Radiology"}, {"vertebra_pipeline", "Radiology"}}; const QMap QmitkMonaiLabelToolGUI::BLACKLISTED_MODELS = {{"deepedit", "Radiology"}, {"localization_spine", "Radiology"}}; QmitkMonaiLabelToolGUI::QmitkMonaiLabelToolGUI(int dimension) : QmitkMultiLabelSegWithPreviewToolGUIBase(), m_SuperclassEnableConfirmSegBtnFnc(m_EnableConfirmSegBtnFnc), m_Dimension(dimension) { m_EnableConfirmSegBtnFnc = [this](bool enabled) { return !m_FirstPreviewComputation ? m_SuperclassEnableConfirmSegBtnFnc(enabled) : false; }; m_Preferences = GetPreferences(); m_Preferences->OnPropertyChanged += mitk::MessageDelegate1( this, &QmitkMonaiLabelToolGUI::OnPreferenceChangedEvent); } QmitkMonaiLabelToolGUI::~QmitkMonaiLabelToolGUI() { auto tool = this->GetConnectedToolAs(); if (nullptr != tool) { tool->MonaiStatusEvent -= mitk::MessageDelegate1(this, &QmitkMonaiLabelToolGUI::StatusMessageListener); } m_Preferences->OnPropertyChanged -= mitk::MessageDelegate1( this, &QmitkMonaiLabelToolGUI::OnPreferenceChangedEvent); } void QmitkMonaiLabelToolGUI::ConnectNewTool(mitk::SegWithPreviewTool *newTool) { Superclass::ConnectNewTool(newTool); m_FirstPreviewComputation = true; } void QmitkMonaiLabelToolGUI::InitializeUI(QBoxLayout *mainLayout) { m_Controls.setupUi(this); mainLayout->addLayout(m_Controls.verticalLayout); connect(m_Controls.previewButton, SIGNAL(clicked()), this, SLOT(OnPreviewBtnClicked())); connect(m_Controls.fetchUrl, SIGNAL(clicked()), this, SLOT(OnFetchBtnClicked())); connect(m_Controls.modelBox, QOverload::of(&QComboBox::activated), [=](int index) { OnModelChanged(m_Controls.modelBox->itemText(index)); }); QIcon refreshIcon = QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/view-refresh.svg")); m_Controls.fetchUrl->setIcon(refreshIcon); m_Controls.previewButton->setEnabled(false); Superclass::InitializeUI(mainLayout); } void QmitkMonaiLabelToolGUI::EnableWidgets(bool enabled) { Superclass::EnableWidgets(enabled); } void QmitkMonaiLabelToolGUI::StatusMessageListener(const bool status) { if (!status) { return; } auto tool = this->GetConnectedToolAs(); if (nullptr == tool) { return; } this->SetLabelSetPreview(tool->GetPreviewSegmentation()); this->ActualizePreviewLabelVisibility(); m_FirstPreviewComputation = false; } void QmitkMonaiLabelToolGUI::DisplayWidgets(bool enabled) { Superclass::DisplayTransferWidgets(enabled); m_Controls.previewButton->setVisible(enabled); } void QmitkMonaiLabelToolGUI::OnModelChanged(const QString &modelName) { auto tool = this->GetConnectedToolAs(); if (nullptr == tool) { return; } m_Controls.labelListLabel->clear(); mitk::MonaiModelInfo model = tool->GetModelInfoFromName(modelName.toStdString()); if (model.IsInteractive()) { this->WriteStatusMessage("Interactive model selected. Please press SHIFT + click on the render windows.\n"); m_Controls.previewButton->setEnabled(false); this->DisplayWidgets(false); } else { this->WriteStatusMessage("Auto-segmentation model selected. Please click on Preview.\n"); m_Controls.previewButton->setEnabled(true); this->DisplayWidgets(true); } auto selectedModel = m_Controls.modelBox->currentText().toStdString(); - for (const auto &modelObject : tool->m_InfoParameters->models) + for (const auto &modelObject : tool->GetInfoParameters()->models) { if (modelObject.name == selectedModel) { auto requestObject = std::make_unique(); requestObject->model = modelObject; - requestObject->hostName = tool->m_InfoParameters->hostName; - requestObject->port = tool->m_InfoParameters->port; + requestObject->hostName = tool->GetInfoParameters()->hostName; + requestObject->port = tool->GetInfoParameters()->port; if (modelObject.IsInteractive()) // set only if interactive model { tool->m_RequestParameters = std::move(requestObject); } QStringList supportedLabels; for (const auto &label : modelObject.labels) { supportedLabels << QString::fromStdString(label.first); } m_Controls.labelListLabel->setText(supportedLabels.join(QStringLiteral(", "))); break; } } tool->MonaiStatusEvent += mitk::MessageDelegate1(this, &QmitkMonaiLabelToolGUI::StatusMessageListener); } void QmitkMonaiLabelToolGUI::OnFetchBtnClicked() { m_Controls.previewButton->setEnabled(false); m_Controls.labelListLabel->clear(); auto reply = QMessageBox::question(this, "Confirm", CONFIRM_QUESTION_TEXT, QMessageBox::Yes | QMessageBox::No); if (reply == QMessageBox::No) { MITK_INFO << "Didn't went ahead with Monai Label inferencing"; return; } auto tool = this->GetConnectedToolAs(); if (nullptr == tool) { return; } - tool->m_InfoParameters.reset(); QString urlString = m_Controls.urlBox->text(); QUrl url(urlString); if (url.isValid() && !url.isLocalFile() && !url.hasFragment() && !url.hasQuery()) // sanity check { std::string hostName = url.host().toStdString(); int port = url.port(); try { - tool->GetOverallInfo(hostName, port); + tool->FetchOverallInfo(hostName, port); bool allowAllModels = m_Preferences->GetBool("monailabel allow all models", false); this->PopulateUI(allowAllModels); } catch (const mitk::Exception &e) { m_Controls.appBox->clear(); m_Controls.modelBox->clear(); MITK_ERROR << e.GetDescription(); this->WriteErrorMessage(e.GetDescription()); } } else { std::string invalidURLMessage = "Invalid URL entered: " + urlString.toStdString(); MITK_ERROR << invalidURLMessage; this->ShowErrorMessage(invalidURLMessage); } } void QmitkMonaiLabelToolGUI::OnPreviewBtnClicked() { auto tool = this->GetConnectedToolAs(); if (nullptr == tool) { return; } tool->ClearPicks(); // clear any interactive segmentation from before auto selectedModel = m_Controls.modelBox->currentText().toStdString(); - for (const auto &modelObject : tool->m_InfoParameters->models) + for (const auto &modelObject : tool->GetInfoParameters()->models) { if (modelObject.name == selectedModel) { auto requestObject = std::make_unique(); requestObject->model = modelObject; - requestObject->hostName = tool->m_InfoParameters->hostName; - requestObject->port = tool->m_InfoParameters->port; + requestObject->hostName = tool->GetInfoParameters()->hostName; + requestObject->port = tool->GetInfoParameters()->port; tool->m_RequestParameters = std::move(requestObject); break; } } try { tool->UpdatePreview(); } catch (const std::exception &e) { std::stringstream errorMsg; errorMsg << "STATUS: Error while processing parameters for MONAI Label 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 MONAI Label segmentation."; this->ShowErrorMessage(errorMsg); m_Controls.previewButton->setEnabled(true); return; } } void QmitkMonaiLabelToolGUI::PopulateUI(bool allowAllModels) { auto tool = this->GetConnectedToolAs(); if (nullptr == tool) { return; } m_Controls.appBox->clear(); m_Controls.labelListLabel->clear(); - if (nullptr != tool->m_InfoParameters) + if (nullptr != tool->GetInfoParameters()) { - QString appName = QString::fromStdString(tool->m_InfoParameters->name); + QString appName = QString::fromStdString(tool->GetInfoParameters()->name); auto autoModels = tool->GetAutoSegmentationModels(m_Dimension); auto interactiveModels = tool->GetInteractiveSegmentationModels(m_Dimension); autoModels.insert(autoModels.end(), interactiveModels.begin(), interactiveModels.end()); this->WriteStatusMessage(appName); m_Controls.appBox->addItem(appName); this->PopulateModelBox(appName, autoModels, allowAllModels); m_Controls.modelBox->setCurrentIndex(-1); } } void QmitkMonaiLabelToolGUI::PopulateModelBox(QString appName, std::vector models, bool allowAllModels) { m_Controls.modelBox->clear(); for (const auto &model : models) { QString modelName = QString::fromStdString(model.name); if (allowAllModels) { - if (BLACKLISTED_MODELS.contains(modelName)) + if (BLACKLISTED_MODELS.contains(modelName) && appName.contains(BLACKLISTED_MODELS[modelName])) { - if (appName.contains(BLACKLISTED_MODELS[modelName])) - continue; + continue; } m_Controls.modelBox->addItem(modelName); } else { if (WHITELISTED_MODELS.contains(modelName)) { m_Controls.modelBox->addItem(modelName); } } } } void QmitkMonaiLabelToolGUI::WriteStatusMessage(const QString &message) { m_Controls.responseNote->setText(message); m_Controls.responseNote->setStyleSheet("font-weight: bold; color: white"); qApp->processEvents(); } void QmitkMonaiLabelToolGUI::WriteErrorMessage(const QString &message) { m_Controls.responseNote->setText(message); m_Controls.responseNote->setStyleSheet("font-weight: bold; color: red"); qApp->processEvents(); } void QmitkMonaiLabelToolGUI::ShowErrorMessage(const std::string &message) { this->setCursor(Qt::ArrowCursor); QMessageBox::critical(nullptr, "MONAI Label", message.c_str()); MITK_WARN << message; } void QmitkMonaiLabelToolGUI::OnPreferenceChangedEvent(const mitk::IPreferences::ChangeEvent& event) { if (event.GetProperty().rfind("monai", 0) == 0) { bool allowAllModels = m_Preferences->GetBool("monailabel allow all models", false); this->PopulateUI(allowAllModels); } }