diff --git a/Modules/Segmentation/Interactions/mitkMonaiLabel2DTool.cpp b/Modules/Segmentation/Interactions/mitkMonaiLabel2DTool.cpp index e7fc4611ef..950fc1f409 100644 --- a/Modules/Segmentation/Interactions/mitkMonaiLabel2DTool.cpp +++ b/Modules/Segmentation/Interactions/mitkMonaiLabel2DTool.cpp @@ -1,154 +1,148 @@ /*============================================================================ 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" // us #include "mitkIOUtil.h" #include #include #include #include #include #include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, MonaiLabel2DTool, "MonaiLabel2D"); } -mitk::MonaiLabel2DTool::MonaiLabel2DTool() -{ - this->ResetsToEmptyPreviewOn(); - this->IsTimePointChangeAwareOff(); -} - 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 "MonaiLabel2D"; } void mitk::MonaiLabel2DTool::OnAddPositivePoint(StateMachineAction *, InteractionEvent *interactionEvent) { if ("deepgrow" == m_RequestParameters->model.type || "deepedit" == m_RequestParameters->model.type) { if ((nullptr == this->GetWorkingPlaneGeometry()) || !mitk::Equal(*(interactionEvent->GetSender()->GetCurrentWorldPlaneGeometry()), *(this->GetWorkingPlaneGeometry()))) { 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::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, std::string &inputImagePath) { mitk::Image::Pointer extendedImg = mitk::Image::New(); unsigned int dim[] = {inputAtTimeStep->GetDimension(0), inputAtTimeStep->GetDimension(1), 1}; mitk::PixelType pt = inputAtTimeStep->GetPixelType(); extendedImg->Initialize(pt, 3, dim); mitk::ImageReadAccessor newMitkImgAcc(inputAtTimeStep); extendedImg->SetVolume(newMitkImgAcc.GetData()); IOUtil::Save(extendedImg.GetPointer(), inputImagePath); } std::stringstream mitk::MonaiLabel2DTool::GetPointsAsListString(const mitk::BaseGeometry *baseGeometry, PointSet::Pointer pointSet) { MITK_INFO << "No.of points: " << pointSet->GetSize(); std::stringstream pointsAndLabels; pointsAndLabels << "["; mitk::PointSet::PointsConstIterator pointSetItPos = pointSet->Begin(); const char COMMA = ','; while (pointSetItPos != pointSet->End()) { mitk::Point3D point3d = pointSetItPos.Value(); if (baseGeometry->IsInside(point3d)) { mitk::Point3D index3D; baseGeometry->WorldToIndex(point3d, index3D); pointsAndLabels << "["; pointsAndLabels << static_cast(index3D[0]) << COMMA << static_cast(index3D[1]) << COMMA << 0 << "],"; } ++pointSetItPos; } 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) { mitk::SegTool2D::WriteSliceToVolume(previewImage, this->GetWorkingPlaneGeometry(), segResults, timeStep, false); } diff --git a/Modules/Segmentation/Interactions/mitkMonaiLabel2DTool.h b/Modules/Segmentation/Interactions/mitkMonaiLabel2DTool.h index d93d388c38..9f455c88aa 100644 --- a/Modules/Segmentation/Interactions/mitkMonaiLabel2DTool.h +++ b/Modules/Segmentation/Interactions/mitkMonaiLabel2DTool.h @@ -1,57 +1,64 @@ /*============================================================================ 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 "mitkSegWithPreviewTool.h" #include "mitkMonaiLabelTool.h" #include #include #include #include #include #include #include "mitkMonaiLabelTool.h" 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 *, std::string &) override; std::stringstream GetPointsAsListString(const mitk::BaseGeometry *, PointSet::Pointer) override; void OnAddPositivePoint(StateMachineAction *, InteractionEvent *) override; void OnAddNegativePoint(StateMachineAction *, InteractionEvent *) override; void WriteBackResults(LabelSetImage *, LabelSetImage *, TimeStepType) override; protected: - MonaiLabel2DTool(); + MonaiLabel2DTool() = default; ~MonaiLabel2DTool() = default; - }; } #endif diff --git a/Modules/Segmentation/Interactions/mitkMonaiLabel3DTool.cpp b/Modules/Segmentation/Interactions/mitkMonaiLabel3DTool.cpp index aa1506ed84..d4727c9f27 100644 --- a/Modules/Segmentation/Interactions/mitkMonaiLabel3DTool.cpp +++ b/Modules/Segmentation/Interactions/mitkMonaiLabel3DTool.cpp @@ -1,102 +1,127 @@ /*============================================================================ 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 "mitkIOUtil.h" #include #include #include #include #include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, MonaiLabel3DTool, "MonaiLabel3D"); } -mitk::MonaiLabel3DTool::MonaiLabel3DTool() -{ - this->IsTimePointChangeAwareOff(); -} - 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 "MonaiLabel3D"; } void mitk::MonaiLabel3DTool::WriteImage(const Image *inputAtTimeStep, std::string &inputImagePath) { IOUtil::Save(inputAtTimeStep, inputImagePath); } void mitk::MonaiLabel3DTool::WriteBackResults(LabelSetImage *previewImage, LabelSetImage *segResults, TimeStepType timeStep) { 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, + PointSet::Pointer pointSet) +{ + MITK_INFO << "No.of points: " << pointSet->GetSize(); + std::stringstream pointsAndLabels; + pointsAndLabels << "["; + mitk::PointSet::PointsConstIterator pointSetItPos = pointSet->Begin(); + const char COMMA = ','; + while (pointSetItPos != pointSet->End()) + { + mitk::Point3D point3d = pointSetItPos.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]) << "],"; + } + ++pointSetItPos; + } + 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 1e0cb37e7f..60a9c69041 100644 --- a/Modules/Segmentation/Interactions/mitkMonaiLabel3DTool.h +++ b/Modules/Segmentation/Interactions/mitkMonaiLabel3DTool.h @@ -1,47 +1,55 @@ /*============================================================================ 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. - class MITKSEGMENTATION_EXPORT MonaiLabel3DTool : public MonaiLabelTool // SegWithPreviewTool + \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 *) override; void OnAddNegativePoint(StateMachineAction *, InteractionEvent *) override; + std::stringstream GetPointsAsListString(const mitk::BaseGeometry *, PointSet::Pointer) override; void WriteImage(const Image *, std::string &) override; void WriteBackResults(LabelSetImage *, LabelSetImage *, TimeStepType) override; protected: - MonaiLabel3DTool(); + MonaiLabel3DTool() = default; ~MonaiLabel3DTool() = default; }; } // namespace mitk #endif diff --git a/Modules/Segmentation/Interactions/mitkMonaiLabelTool.cpp b/Modules/Segmentation/Interactions/mitkMonaiLabelTool.cpp index f6b9055f06..99a448c21d 100644 --- a/Modules/Segmentation/Interactions/mitkMonaiLabelTool.cpp +++ b/Modules/Segmentation/Interactions/mitkMonaiLabelTool.cpp @@ -1,528 +1,500 @@ /*============================================================================ 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" #include "mitkIOUtil.h" #include #include #include "mitkPointSetShapeProperty.h" #include "mitkProperties.h" #include "mitkToolManager.h" #include -mitk::MonaiLabelTool::MonaiLabelTool() : SegWithPreviewTool(true, "PressMoveReleaseAndPointSetting") +mitk::MonaiLabelTool::MonaiLabelTool() : SegWithPreviewTool(true, "PressMoveReleaseAndPointSetting") { - this->SetMitkTempDir(IOUtil::CreateTemporaryDirectory("mitk-XXXXXX")); + this->ResetsToEmptyPreviewOn(); + this->IsTimePointChangeAwareOff(); } mitk::MonaiLabelTool::~MonaiLabelTool() { std::filesystem::remove_all(this->GetMitkTempDir()); } 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(); GetDataStorage()->Remove(m_PointSetNodePositive); 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(); for (LabelSetImage::GroupIndexType i = 0; i < preview->GetNumberOfLayers(); ++i) { preview->GetLabelSet(i)->RemoveAllLabels(); } } void mitk::MonaiLabelTool::OnDelete(StateMachineAction *, InteractionEvent *) { if (!this->IsUpdating() && m_PointSetPositive.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::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(std::string &hostName, int &port) { httplib::Client cli(hostName, port); - while (cli.is_socket_open()) - ; + while (cli.is_socket_open()); if (auto response = cli.Get("/info/")) { return true; } return false; } void mitk::MonaiLabelTool::DoUpdatePreview(const Image *inputAtTimeStep, const Image * /*oldSegAtTimeStep*/, LabelSetImage *previewImage, TimeStepType timeStep) { std::string &hostName = m_RequestParameters->hostName; int port = m_RequestParameters->port; if (!IsMonaiServerOn(hostName, port)) { mitkThrow() << m_SERVER_503_ERROR_TEXT; } + if (this->m_MitkTempDir.empty()) + { + this->SetMitkTempDir(IOUtil::CreateTemporaryDirectory("mitk-XXXXXX")); + } std::string inDir, outDir, inputImagePath, outputImagePath; inDir = IOUtil::CreateTemporaryDirectory("monai-in-XXXXXX", this->GetMitkTempDir()); std::ofstream tmpStream; inputImagePath = IOUtil::CreateTemporaryFile(tmpStream, m_TEMPLATE_FILENAME, inDir + IOUtil::GetDirectorySeparator()); tmpStream.close(); std::size_t found = inputImagePath.find_last_of(IOUtil::GetDirectorySeparator()); std::string fileName = inputImagePath.substr(found + 1); std::string token = fileName.substr(0, fileName.find("_")); outDir = IOUtil::CreateTemporaryDirectory("monai-out-XXXXXX", this->GetMitkTempDir()); outputImagePath = outDir + IOUtil::GetDirectorySeparator() + token + "_000.nii.gz"; - try { - m_IsLastSuccess = false; this->WriteImage(inputAtTimeStep, inputImagePath); - PostInferRequest(hostName, port, inputImagePath, outputImagePath, inputAtTimeStep->GetGeometry()); + this->PostInferRequest(hostName, port, inputImagePath, outputImagePath, inputAtTimeStep->GetGeometry()); Image::Pointer outputImage = IOUtil::Load(outputImagePath); auto outputBuffer = mitk::LabelSetImage::New(); outputBuffer->InitializeByLabeledImage(outputImage); - if (m_AUTO_SEG_TYPE_NAME.find(m_RequestParameters->model.type) != m_AUTO_SEG_TYPE_NAME.end()) + std::map labelMap; // empty map + if (m_RequestParameters->model.IsInteractive()) { - outputBuffer->SetGeometry(inputAtTimeStep->GetGeometry()); - std::map labelMap = m_ResultMetadata["label_names"]; - this->MapLabelsToSegmentation(outputBuffer, previewImage, labelMap); - this->SetLabelTransferMode(LabelTransferMode::AddLabel); + this->SetLabelTransferMode(LabelTransferMode::MapLabel); + this->SetSelectedLabels({MASK_VALUE}); } else { - std::map labelMap; // empty map. - this->MapLabelsToSegmentation(outputBuffer, previewImage, labelMap); - this->SetLabelTransferMode(LabelTransferMode::MapLabel); - this->SetSelectedLabels({MASK_VALUE}); + outputBuffer->SetGeometry(inputAtTimeStep->GetGeometry()); + labelMap = m_ResultMetadata["label_names"]; + this->SetLabelTransferMode(LabelTransferMode::AddLabel); } + this->MapLabelsToSegmentation(outputBuffer, previewImage, labelMap); this->WriteBackResults(previewImage, outputBuffer.GetPointer(), timeStep); - this->SetIsLastSuccess(true); MonaiStatusEvent.Send(true); } catch (const mitk::Exception &e) { - m_IsLastSuccess = false; MITK_ERROR << e.GetDescription(); mitkThrow() << e.GetDescription(); MonaiStatusEvent.Send(false); } } void mitk::MonaiLabelTool::MapLabelsToSegmentation(const mitk::LabelSetImage *source, mitk::LabelSetImage *dest, std::map &labelMap) { auto labelset = dest->GetLabelSet(); if (labelMap.empty()) { auto label = LabelSetImageHelper::CreateNewLabel(dest, "object"); label->SetValue(1); labelset->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())) { Label::Pointer label = Label::New(key, val); std::array lookupTableColor; lookupTable->GetColor(key, lookupTableColor.data()); Color color; color.SetRed(lookupTableColor[0]); color.SetGreen(lookupTableColor[1]); color.SetBlue(lookupTableColor[2]); label->SetColor(color); labelset->AddLabel(label, false); } else { MITK_INFO << "Label not found for " << val; } } } void mitk::MonaiLabelTool::GetOverallInfo(std::string &hostName, int &port) { MITK_INFO << "URL..." << hostName << ":" << port; if (!IsMonaiServerOn(hostName, port)) { Tool::ErrorMessage.Send(m_SERVER_503_ERROR_TEXT); mitkThrow() << m_SERVER_503_ERROR_TEXT; } httplib::Client cli(hostName, port); 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 \"" << /* jsonPath.toStdString() << */ "\" as JSON object!"; return; } m_InfoParameters = DataMapper(jsonObj); 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(std::string &hostName, int &port, std::string &filePath, 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_INTERACTIVE_SEG_TYPE_NAME.find(m_RequestParameters->model.type) != m_INTERACTIVE_SEG_TYPE_NAME.end()) { std::stringstream foreground = this->GetPointsAsListString(baseGeometry, m_PointSetPositive); std::stringstream background = this->GetPointsAsListString(baseGeometry, m_PointSetNegative); std::stringstream paramString; paramString << "{" << "\"foreground\":" << foreground.str() << ",\"background\":" << background.str() << "}"; MITK_INFO << 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::Client cli(hostName, port); cli.set_read_timeout(60); // arbitary 1 minute time-out to avoid corner cases. if (auto response = cli.Post(postPath, items)) { if (response->status == 200) { // Find boundary 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, "--"); MITK_INFO << "boundary hash: " << boundaryString; // Parse metadata JSON std::string resBody = response->body; std::vector multiPartResponse = this->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_INFO << metaDataPart; auto jsonObj = nlohmann::json::parse(metaDataPart); if (jsonObj.is_discarded() || !jsonObj.is_object()) { MITK_ERROR << "Could not parse \"" << /* jsonPath.toStdString() << */ "\" as JSON object!"; return; } else // use the metadata { m_ResultMetadata = jsonObj; } // Parse response image string std::string imagePart = multiPartResponse[1]; std::string contentTypeStream = "Content-Type: application/octet-stream"; ctPos = imagePart.find(contentTypeStream) + contentTypeStream.length(); std::string imageDataPart = imagePart.substr(ctPos); MITK_INFO << imageDataPart.substr(0, 50); std::string whitespaces = " \n\r"; imageDataPart.erase(0, imageDataPart.find_first_not_of(whitespaces)); // clean up std::ofstream output(outFile, std::ios::out | std::ios::app | std::ios::binary); output << imageDataPart; output.unsetf(std::ios::skipws); output.flush(); output.close(); // necessary to close? RAII } 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::getPartsBetweenBoundary(const std::string &body, const std::string &boundary) { std::vector retVal; std::string master = body; size_t found = master.find(boundary); size_t beginPos = 0; while (found != std::string::npos) { std::string part = master.substr(beginPos, found); if (!part.empty()) { retVal.push_back(part); } master.erase(beginPos, found + boundary.length()); found = master.find(boundary); } return retVal; } std::unique_ptr mitk::MonaiLabelTool::DataMapper(nlohmann::json &jsonObj) { auto object = std::make_unique(); object->name = jsonObj["name"].get(); object->description = jsonObj["description"].get(); object->labels = jsonObj["labels"].get>(); 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."; } 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 } } modelInfo.type = _jsonObj["type"].get(); modelInfo.dimension = _jsonObj["dimension"].get(); modelInfo.description = _jsonObj["description"].get(); object->models.push_back(modelInfo); } return object; } std::vector mitk::MonaiLabelTool::GetAutoSegmentationModels(int dim) { std::vector autoModels; bool checkDims = dim > -1; 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() && (!checkDims || model.dimension == dim)) { autoModels.push_back(model); } } } return autoModels; } std::vector mitk::MonaiLabelTool::GetInteractiveSegmentationModels(int dim) { std::vector interactiveModels; bool checkDims = dim > -1; 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() && (!checkDims || model.dimension == dim)) { interactiveModels.push_back(model); } } } return interactiveModels; } std::vector mitk::MonaiLabelTool::GetScribbleSegmentationModels(int dim) { std::vector scribbleModels; bool checkDims = dim > -1; 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() && (!checkDims || model.dimension == dim)) { scribbleModels.push_back(model); } } } return scribbleModels; } mitk::MonaiModelInfo mitk::MonaiLabelTool::GetModelInfoFromName(std::string &modelName) { 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; } -std::stringstream mitk::MonaiLabelTool::GetPointsAsListString(const mitk::BaseGeometry *baseGeometry, - PointSet::Pointer pointSet) -{ - MITK_INFO << "No.of points: " << pointSet->GetSize(); - std::stringstream pointsAndLabels; - pointsAndLabels << "["; - mitk::PointSet::PointsConstIterator pointSetItPos = pointSet->Begin(); - const char COMMA = ','; - while (pointSetItPos != pointSet->End()) - { - mitk::Point3D point3d = pointSetItPos.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]) << "],"; - } - ++pointSetItPos; - } - if (pointsAndLabels.tellp() > 1) - { - pointsAndLabels.seekp(-1, pointsAndLabels.end); // remove last added comma character - } - pointsAndLabels << "]"; - return pointsAndLabels; -} +void mitk::MonaiLabelTool::WriteImage(const Image*, std::string&) {} diff --git a/Modules/Segmentation/Interactions/mitkMonaiLabelTool.h b/Modules/Segmentation/Interactions/mitkMonaiLabelTool.h index 52b9969219..9c1693e33e 100644 --- a/Modules/Segmentation/Interactions/mitkMonaiLabelTool.h +++ b/Modules/Segmentation/Interactions/mitkMonaiLabelTool.h @@ -1,180 +1,292 @@ /*============================================================================ 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 "mitkPointSet.h" #include "mitkInteractionPositionEvent.h" 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; //TODO: find the full extent + std::unordered_map config; inline bool operator==(const MonaiModelInfo &rhs) const { if (this->name == rhs.name && this->type == rhs.type) // Comparing only name and type, for now. { return true; } return false; } + + inline bool IsInteractive() const + { + if ("deepgrow" == type || "deepedit" == type) + { + return true; + } + return false; + } }; + /** + * @brief Struct to store MonaiLabel server metadata including all model infos + * + */ struct MonaiAppMetadata { std::string hostName; int port; std::string origin; std::string name; std::string description; std::string version; std::vector labels; std::vector models; }; /** - * Parameters that goes into infer POST - */ + * @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 { if (this->model == rhs.model && this->hostName == rhs.hostName && this->port == rhs.port && this->requestLabel == rhs.requestLabel) { return true; } return false; } }; + /** + \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; - virtual void WriteImage(const Image*, std::string&) = 0; + /** + * @brief Writes image to disk as the tool desires. + * Default implementation does nothing. + */ + virtual void WriteImage(const Image*, std::string&); + + /** + * @brief Method does the GET Rest call to fetch MonaiLabel + * server metadata including all models' info. + */ void GetOverallInfo(std::string&, int&); - std::unique_ptr m_InfoParameters; //contains all parameters from Server to serve the GUI - std::unique_ptr m_RequestParameters; + + /** + * @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. + * + * @param dim + * @return std::vector + */ std::vector GetAutoSegmentationModels(int dim = -1); + + /** + * @brief Get the Interactive Segmentation Models info for the given + * dimension. + * + * @param dim + * @return std::vector + */ std::vector GetInteractiveSegmentationModels(int dim = -1); + + /** + * @brief Get the Scribble Segmentation Models info for the given + * dimension. + * + * @param dim + * @return std::vector + */ std::vector GetScribbleSegmentationModels(int dim = -1); + + /** + * @brief Function to prepare the Rest request and does the POST call. + * Writes the POST responses back to disk. + */ void PostInferRequest(std::string &, int &, std::string &, std::string &, const mitk::BaseGeometry *); + + /** + * @brief Helper function to get full model info object from model name. + * + * @return MonaiModelInfo + */ MonaiModelInfo GetModelInfoFromName(std::string&); itkSetMacro(ModelName, std::string); itkGetConstMacro(ModelName, std::string); itkSetMacro(URL, std::string); itkGetConstMacro(URL, std::string); itkSetMacro(MitkTempDir, std::string); itkGetConstMacro(MitkTempDir, std::string); - itkSetMacro(IsLastSuccess, bool); - itkGetConstMacro(IsLastSuccess, bool); /** * @brief Clears all picks and updates the preview. */ void ClearPicks(); /** * @brief Checks if any point exists in the either * of the pointsets * * @return bool */ 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 Add positive seed point action of StateMachine pattern + * @brief Add positive seed point action of StateMachine pattern. */ virtual void OnAddPositivePoint(StateMachineAction *, InteractionEvent *interactionEvent) = 0; /* * @brief Add negative seed point action of StateMachine pattern */ virtual void OnAddNegativePoint(StateMachineAction *, InteractionEvent *interactionEvent) = 0; /* * @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(); - virtual std::stringstream GetPointsAsListString(const mitk::BaseGeometry*, PointSet::Pointer); + /** + * @brief Get the Points from given pointset as csv string. + * + * @param baseGeometry + * @param pointSet + * @return std::stringstream + */ + virtual std::stringstream GetPointsAsListString(const mitk::BaseGeometry*, PointSet::Pointer) = 0; + + /** + * @brief Writes back segmentation results in 3D or 2D shape to preview LabelSetImage. + * + */ virtual void WriteBackResults(LabelSetImage *, LabelSetImage *, TimeStepType) = 0; PointSet::Pointer m_PointSetPositive; PointSet::Pointer m_PointSetNegative; DataNode::Pointer m_PointSetNodePositive; DataNode::Pointer m_PointSetNodeNegative; int m_PointSetCount = 0; private: std::string m_MitkTempDir; + + /** + * @brief Helper function to get the Parts of the POST response + * + * @return std::vector + */ std::vector getPartsBetweenBoundary(const std::string &, const std::string &); + + /** + * @brief Converts the json GET response from the MonaiLabel server to MonaiAppMetadata object + * + * @return std::unique_ptr + */ std::unique_ptr mitk::MonaiLabelTool::DataMapper(nlohmann::json&); + /** + * @brief Applies the give std::map lookup table on the preview segmentation LabelSetImage. + * + */ void MapLabelsToSegmentation(const mitk::LabelSetImage*, mitk::LabelSetImage*, std::map&); + + /** + * @brief Checks if MonaiLabel server is alive + * + * @return bool + */ bool IsMonaiServerOn(std::string &, int &); - bool m_IsLastSuccess = false; 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/Segmentation/Interactions/mitkSegmentAnythingTool.h b/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.h index e096072cc2..a200b2c09b 100644 --- a/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.h +++ b/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.h @@ -1,226 +1,217 @@ /*============================================================================ 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(); /** * @brief Checks if any point exists in the either * of the pointsets * * @return bool */ 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(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. * * @param baseGeometry of Image * @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 */ 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&); - /** - * @brief Checks if the image has valid size across each dimension. This function is - * critical for 2D images since 2D image are not valid in Saggital and Coronal views. - * - * @param inputAtTimeStep - * @return bool - */ - bool IsImageAtTimeStepValid(const Image *inputAtTimeStep); - std::string m_MitkTempDir; std::string m_PythonPath; std::string m_ModelType; std::string m_CheckpointPath; 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/QmitkMonaiLabelToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUI.cpp index 6c8c9e071e..72fe23af93 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUI.cpp @@ -1,267 +1,256 @@ /*============================================================================ 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 "mitkMonaiLabelTool.h" -#include "usServiceReference.h" #include #include #include #include QmitkMonaiLabelToolGUI::QmitkMonaiLabelToolGUI(int dimension) : QmitkMultiLabelSegWithPreviewToolGUIBase(), m_SuperclassEnableConfirmSegBtnFnc(m_EnableConfirmSegBtnFnc) { m_Dimension = dimension; m_EnableConfirmSegBtnFnc = [this](bool enabled) { return !m_FirstPreviewComputation ? m_SuperclassEnableConfirmSegBtnFnc(enabled) : false; }; } QmitkMonaiLabelToolGUI::~QmitkMonaiLabelToolGUI() { auto tool = this->GetConnectedToolAs(); if (nullptr != tool) { tool->MonaiStatusEvent -= mitk::MessageDelegate1(this, &QmitkMonaiLabelToolGUI::StatusMessageListener); } } void QmitkMonaiLabelToolGUI::ConnectNewTool(mitk::SegWithPreviewTool *newTool) { Superclass::ConnectNewTool(newTool); - newTool->IsTimePointChangeAwareOff(); 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); 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::DisplayWidgets(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 ("deepgrow" == model.type || "deepedit" == model.type) + 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. Label selection will be ignored.\n"); + this->WriteStatusMessage("Auto-segmentation model selected. Please click on Preview. Label selection will be ignored.\n"); m_Controls.previewButton->setEnabled(true); this->DisplayWidgets(true); } std::string selectedModel = m_Controls.modelBox->currentText().toStdString(); for (const mitk::MonaiModelInfo &modelObject : tool->m_InfoParameters->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; 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(',')); + m_Controls.labelListLabel->setText(supportedLabels.join(QChar(',') + QChar::Space)); break; } } tool->MonaiStatusEvent += mitk::MessageDelegate1(this, &QmitkMonaiLabelToolGUI::StatusMessageListener); } void QmitkMonaiLabelToolGUI::OnFetchBtnClicked() { QMessageBox::StandardButton reply; reply = QMessageBox::question(this, "Confirm", m_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(); m_Controls.modelBox->clear(); m_Controls.appBox->clear(); QString urlString = m_Controls.urlBox->text(); QUrl url(urlString); if (url.isValid() && !url.isLocalFile() && !url.hasFragment() && !url.hasQuery()) // sanity check { QString hostName = url.host(); int port = url.port(); try { - tool->GetOverallInfo(hostName.toStdString(), port); // tool->GetOverallInfo("localhost",8000"); + tool->GetOverallInfo(hostName.toStdString(), port); if (nullptr != tool->m_InfoParameters) { std::string response = tool->m_InfoParameters->name; std::vector autoModels = tool->GetAutoSegmentationModels(m_Dimension); std::vector interactiveModels = tool->GetInteractiveSegmentationModels(m_Dimension); autoModels.insert(autoModels.end(), interactiveModels.begin(), interactiveModels.end()); this->WriteStatusMessage(QString::fromStdString(response)); m_Controls.appBox->addItem(QString::fromStdString(response)); for (auto &model : autoModels) { QString modelName = QString::fromStdString(model.name); if (SUPPORTED_MODELS.contains(modelName)) { m_Controls.modelBox->addItem(modelName); } } m_Controls.modelBox->setCurrentIndex(-1); } } catch (const mitk::Exception &e) { 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; } std::string selectedModel = m_Controls.modelBox->currentText().toStdString(); for (const mitk::MonaiModelInfo &modelObject : tool->m_InfoParameters->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; - if (!m_FirstPreviewComputation && tool->GetIsLastSuccess() && *requestObject == *(tool->m_RequestParameters)) - { - MITK_INFO << "won't do segmentation..."; - return; - } - else - { - tool->m_RequestParameters = std::move(requestObject); - } + 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 TotalSegmentator segmentation. Reason: " << e.what(); this->ShowErrorMessage(errorMsg.str()); this->WriteErrorMessage(QString::fromStdString(errorMsg.str())); m_Controls.previewButton->setEnabled(true); return; } catch (...) { std::string errorMsg = "Unkown error occured while generation TotalSegmentator segmentation."; this->ShowErrorMessage(errorMsg); m_Controls.previewButton->setEnabled(true); return; } } 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, QMessageBox::Icon icon) { this->setCursor(Qt::ArrowCursor); QMessageBox *messageBox = new QMessageBox(icon, nullptr, message.c_str()); messageBox->exec(); delete messageBox; MITK_WARN << message; } diff --git a/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUI.h index 8e36d35797..30e6be4887 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUI.h +++ b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUI.h @@ -1,86 +1,86 @@ /*============================================================================ 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 QmitkMonaiLabelToolGUI_h_Included #define QmitkMonaiLabelToolGUI_h_Included #include "QmitkMultiLabelSegWithPreviewToolGUIBase.h" #include "ui_QmitkMonaiLabelToolGUIControls.h" #include #include class MITKSEGMENTATIONUI_EXPORT QmitkMonaiLabelToolGUI : public QmitkMultiLabelSegWithPreviewToolGUIBase { Q_OBJECT public: mitkClassMacro(QmitkMonaiLabelToolGUI, QmitkMultiLabelSegWithPreviewToolGUIBase); itkCloneMacro(Self); protected slots: void OnPreviewBtnClicked(); void OnFetchBtnClicked(); void OnModelChanged(const QString &); protected: QmitkMonaiLabelToolGUI(int); ~QmitkMonaiLabelToolGUI(); void ConnectNewTool(mitk::SegWithPreviewTool *newTool) override; void InitializeUI(QBoxLayout *mainLayout) override; void EnableWidgets(bool enabled) override; virtual void DisplayWidgets(bool enabled); /** * @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 Creates a QMessage object and shows on screen. */ void ShowErrorMessage(const std::string &, QMessageBox::Icon = QMessageBox::Critical); /** * @brief Function to listen to tool class status emitters. */ void StatusMessageListener(const bool); private: Ui_QmitkMonaiLabelToolGUIControls m_Controls; bool m_FirstPreviewComputation = true; EnableConfirmSegBtnFunctionType m_SuperclassEnableConfirmSegBtnFnc; int m_Dimension; QString m_CONFIRM_QUESTION_TEXT = "Data will be sent to the processing server devoid of any patient information. Are you sure you want continue?"; - const QStringList SUPPORTED_MODELS = { + const QStringList SUPPORTED_MODELS = { // deepedit and localization_spine not supported "deepgrow_2d", "deepgrow_3d", "deepedit_seg", "localization_vertebra", "segmentation", "segmentation_spleen", "segmentation_vertebra", "deepgrow_pipeline", "vertebra_pipeline," }; }; #endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUIControls.ui b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUIControls.ui index acab0cf818..533ecceadb 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUIControls.ui +++ b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUIControls.ui @@ -1,209 +1,209 @@ QmitkMonaiLabelToolGUIControls 0 0 699 352 0 0 100 0 100000 100000 QmitkMonaiToolWidget 0 0 0 0 0 0 0 Monai Server URL: http://localhost:8000 0 0 Available Apps: 0 0 <html><head/><body><p>Welcome to MONAI Label App in MITK. [Experimental]</p><p>Please note that this is only an interface to MONAI Label. MITK does not ship with any apps. Make sure to have a started a MONAI Label server beforehand. </p><p>Refer to <a href="https://docs.monai.io/projects/label/en/latest/"><span style=" text-decoration: underline; color:#0000ff;">https://docs.monai.io/projects/label/en/latest/</span></a> to learn everything about the MONAI Label App.</p><p><br/></p><p>Provide a valid URL (eg. http://localhost:8000) to the server &amp; start the workflow.</p><p><br/></p></body></html> Qt::RichText true 0 0 Models: 0 0 - Supported Labels: + Supported Classes: 0 0 Qt::RichText true 0 0 <html><head/><body><p>Welcome to Monai Label in MITK. [Experimental]</p></body></html> Qt::RichText true 0 0 100000 16777215 Preview ctkComboBox QComboBox
ctkComboBox.h
1
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.cpp b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.cpp index e955220fab..ede9e2fd23 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.cpp @@ -1,167 +1,162 @@ /*============================================================================ 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 "QmitkMultiLabelSegWithPreviewToolGUIBase.h" #include "mitkSegWithPreviewTool.h" #include #include QmitkMultiLabelSegWithPreviewToolGUIBase::QmitkMultiLabelSegWithPreviewToolGUIBase() : QmitkSegWithPreviewToolGUIBase(false) { auto enableMLSelectedDelegate = [this](bool enabled) { auto tool = this->GetConnectedToolAs(); return nullptr != tool ? (tool->GetLabelTransferScope() == mitk::SegWithPreviewTool::LabelTransferScope::AllLabels || !tool->GetSelectedLabels().empty()) && enabled : false; }; m_EnableConfirmSegBtnFnc = enableMLSelectedDelegate; } void QmitkMultiLabelSegWithPreviewToolGUIBase::InitializeUI(QBoxLayout* mainLayout) { auto radioTransferAll = new QRadioButton("Transfer all labels", this); radioTransferAll->setToolTip("Transfer all preview labels when confirmed."); radioTransferAll->setChecked(true); connect(radioTransferAll, &QAbstractButton::toggled, this, &QmitkMultiLabelSegWithPreviewToolGUIBase::OnRadioTransferAllClicked); mainLayout->addWidget(radioTransferAll); m_RadioTransferAll = radioTransferAll; auto radioTransferSelected = new QRadioButton("Transfer selected labels", this); radioTransferSelected->setToolTip("Transfer the selected preview labels when confirmed."); radioTransferSelected->setChecked(false); mainLayout->addWidget(radioTransferSelected); m_RadioTransferSelected = radioTransferSelected; m_LabelSelectionList = new QmitkSimpleLabelSetListWidget(this); m_LabelSelectionList->setObjectName(QString::fromUtf8("m_LabelSelectionList")); QSizePolicy sizePolicy2(QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); sizePolicy2.setHorizontalStretch(0); sizePolicy2.setVerticalStretch(0); sizePolicy2.setHeightForWidth(m_LabelSelectionList->sizePolicy().hasHeightForWidth()); m_LabelSelectionList->setSizePolicy(sizePolicy2); m_LabelSelectionList->setMaximumSize(QSize(10000000, 10000000)); m_LabelSelectionList->setVisible(false); mainLayout->addWidget(m_LabelSelectionList); connect(m_LabelSelectionList, &QmitkSimpleLabelSetListWidget::SelectedLabelsChanged, this, &QmitkMultiLabelSegWithPreviewToolGUIBase::OnLabelSelectionChanged); this->OnRadioTransferAllClicked(true); Superclass::InitializeUI(mainLayout); } void QmitkMultiLabelSegWithPreviewToolGUIBase::OnLabelSelectionChanged(const QmitkSimpleLabelSetListWidget::LabelVectorType& selectedLabels) { auto tool = this->GetConnectedToolAs(); if (nullptr != tool) { mitk::SegWithPreviewTool::SelectedLabelVectorType labelIDs; for (const auto& label : selectedLabels) { labelIDs.push_back(label->GetValue()); } tool->SetSelectedLabels(labelIDs); this->ActualizePreviewLabelVisibility(); this->EnableWidgets(true); //used to actualize the ConfirmSeg btn via the delegate; } } void QmitkMultiLabelSegWithPreviewToolGUIBase::ActualizePreviewLabelVisibility() { auto tool = this->GetConnectedToolAs(); if (nullptr != tool) { auto preview = tool->GetPreviewSegmentation(); if (nullptr != preview) { auto labelSet = preview->GetActiveLabelSet(); auto selectedLabels = tool->GetSelectedLabels(); for (auto labelIter = labelSet->IteratorBegin(); labelIter != labelSet->IteratorEnd(); ++labelIter) { bool isVisible = tool->GetLabelTransferScope() == mitk::SegWithPreviewTool::LabelTransferScope::AllLabels || (std::find(selectedLabels.begin(), selectedLabels.end(), labelIter->second->GetValue()) != selectedLabels.end()); labelIter->second->SetVisible(isVisible); labelSet->UpdateLookupTable(labelIter->second->GetValue()); } } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkMultiLabelSegWithPreviewToolGUIBase::OnRadioTransferAllClicked(bool checked) { m_LabelSelectionList->setVisible(!checked); auto tool = this->GetConnectedToolAs(); if (nullptr != tool) { if (checked) { tool->SetLabelTransferScope(mitk::SegWithPreviewTool::LabelTransferScope::AllLabels); } else { tool->SetLabelTransferScope(mitk::SegWithPreviewTool::LabelTransferScope::SelectedLabels); } } this->ActualizePreviewLabelVisibility(); } void QmitkMultiLabelSegWithPreviewToolGUIBase::EnableWidgets(bool enabled) { Superclass::EnableWidgets(enabled); if (nullptr != m_LabelSelectionList) { m_LabelSelectionList->setEnabled(enabled); } if (nullptr != m_RadioTransferAll) { m_RadioTransferAll->setEnabled(enabled); } if (nullptr != m_RadioTransferSelected) { m_RadioTransferSelected->setEnabled(enabled); } } void QmitkMultiLabelSegWithPreviewToolGUIBase::SetLabelSetPreview(const mitk::LabelSetImage* preview) { if (nullptr != m_LabelSelectionList) { m_LabelSelectionList->SetLabelSetImage(preview); } } -void QmitkMultiLabelSegWithPreviewToolGUIBase::DisplayWidgets(bool enabled) +void QmitkMultiLabelSegWithPreviewToolGUIBase::DisplayTransferWidgets(bool enabled) { - if (nullptr != m_LabelSelectionList) - { - m_LabelSelectionList->setVisible(enabled); - } - if (nullptr != m_RadioTransferAll) { m_RadioTransferAll->setVisible(enabled); } if (nullptr != m_RadioTransferSelected) { m_RadioTransferSelected->setVisible(enabled); } } diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.h b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.h index ab75508704..f309330917 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.h +++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.h @@ -1,57 +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. ============================================================================*/ #ifndef QmitkMultiLabelSegWithPreviewToolGUIBase_h #define QmitkMultiLabelSegWithPreviewToolGUIBase_h #include "QmitkSegWithPreviewToolGUIBase.h" #include "QmitkSimpleLabelSetListWidget.h" #include /** \ingroup org_mitk_gui_qt_interactivesegmentation_internal \brief GUI for tools based on mitk::AutoMLSegmentationWithPreviewTool. This GUI offers an additional list to select the label that should be confirmed. */ class MITKSEGMENTATIONUI_EXPORT QmitkMultiLabelSegWithPreviewToolGUIBase : public QmitkSegWithPreviewToolGUIBase { Q_OBJECT public: mitkClassMacro(QmitkMultiLabelSegWithPreviewToolGUIBase, QmitkSegWithPreviewToolGUIBase); protected slots : void OnLabelSelectionChanged(const QmitkSimpleLabelSetListWidget::LabelVectorType& selectedLabels); void OnRadioTransferAllClicked(bool checked); protected: QmitkMultiLabelSegWithPreviewToolGUIBase(); ~QmitkMultiLabelSegWithPreviewToolGUIBase() = default; void InitializeUI(QBoxLayout* mainLayout) override; void EnableWidgets(bool enabled) override; - void DisplayWidgets(bool enabled); void SetLabelSetPreview(const mitk::LabelSetImage* preview); void ActualizePreviewLabelVisibility(); + /** + * @brief To toggle visibility of "Transfer all labels" and + * "Transfer selected labels" radio buttons. + */ + void DisplayTransferWidgets(bool enabled); + private: QmitkSimpleLabelSetListWidget* m_LabelSelectionList = nullptr; QWidget* m_RadioTransferAll = nullptr; QWidget* m_RadioTransferSelected = nullptr; }; #endif