diff --git a/Modules/Multilabel/mitkLabelSetImageHelper.cpp b/Modules/Multilabel/mitkLabelSetImageHelper.cpp index 6aa795e9dc..b2147ee2d1 100644 --- a/Modules/Multilabel/mitkLabelSetImageHelper.cpp +++ b/Modules/Multilabel/mitkLabelSetImageHelper.cpp @@ -1,149 +1,157 @@ /*============================================================================ 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 #include #include #include #include #include #include namespace { template std::array QuantizeColor(const T* color) { return { static_cast(std::round(color[0] * 255)), static_cast(std::round(color[1] * 255)), static_cast(std::round(color[2] * 255)) }; } mitk::Color FromLookupTableColor(const double* lookupTableColor) { mitk::Color color; color.Set( static_cast(lookupTableColor[0]), static_cast(lookupTableColor[1]), static_cast(lookupTableColor[2])); return color; } } +mitk::DataNode::Pointer mitk::LabelSetImageHelper::CreateEmptySegmentationNode(const std::string& segmentationName) +{ + auto newSegmentationNode = mitk::DataNode::New(); + newSegmentationNode->SetName(segmentationName); + + // initialize "showVolume"-property to false to prevent recalculating the volume while working on the segmentation + newSegmentationNode->SetProperty("showVolume", mitk::BoolProperty::New(false)); + + return newSegmentationNode; +} + + mitk::DataNode::Pointer mitk::LabelSetImageHelper::CreateNewSegmentationNode(const DataNode* referenceNode, const Image* initialSegmentationImage, const std::string& segmentationName) { std::string newSegmentationName = segmentationName; if (newSegmentationName.empty()) { newSegmentationName = referenceNode->GetName(); newSegmentationName.append("-labels"); } if (nullptr == initialSegmentationImage) { return nullptr; } auto newLabelSetImage = mitk::LabelSetImage::New(); try { newLabelSetImage->Initialize(initialSegmentationImage); } catch (mitk::Exception &e) { mitkReThrow(e) << "Could not initialize new label set image."; return nullptr; } - auto newSegmentationNode = mitk::DataNode::New(); - newSegmentationNode->SetData(newLabelSetImage); - newSegmentationNode->SetName(newSegmentationName); - // set additional image information newLabelSetImage->GetExteriorLabel()->SetProperty("name.parent", mitk::StringProperty::New(referenceNode->GetName())); newLabelSetImage->GetExteriorLabel()->SetProperty("name.image", mitk::StringProperty::New(newSegmentationName)); - // initialize "showVolume"-property to false to prevent recalculating the volume while working on the segmentation - newSegmentationNode->SetProperty("showVolume", mitk::BoolProperty::New(false)); + auto newSegmentationNode = CreateEmptySegmentationNode(newSegmentationName); + newSegmentationNode->SetData(newLabelSetImage); return newSegmentationNode; } mitk::Label::Pointer mitk::LabelSetImageHelper::CreateNewLabel(const LabelSetImage* labelSetImage) { if (nullptr == labelSetImage) return nullptr; const std::regex genericLabelNameRegEx("Label ([1-9][0-9]*)"); int maxGenericLabelNumber = 0; std::vector> colorsInUse; const auto numLabelSets = labelSetImage->GetNumberOfLayers(); for (std::remove_const_t i = 0; i < numLabelSets; ++i) { auto labelSet = labelSetImage->GetLabelSet(i); auto labelEndIter = labelSet->IteratorConstEnd(); for (auto labelIter = labelSet->IteratorConstBegin(); labelIter != labelEndIter; ++labelIter) { auto label = labelIter->second; auto labelName = label->GetName(); std::smatch match; if (std::regex_match(labelName, match, genericLabelNameRegEx)) maxGenericLabelNumber = std::max(maxGenericLabelNumber, std::stoi(match[1].str())); const auto quantizedLabelColor = QuantizeColor(label->GetColor().data()); if (std::find(colorsInUse.begin(), colorsInUse.end(), quantizedLabelColor) == std::end(colorsInUse)) colorsInUse.push_back(quantizedLabelColor); } } auto newLabel = mitk::Label::New(); newLabel->SetName("Label " + std::to_string(maxGenericLabelNumber + 1)); auto lookupTable = mitk::LookupTable::New(); lookupTable->SetType(mitk::LookupTable::LookupTableType::MULTILABEL); std::array lookupTableColor; const int maxTries = 25; bool newColorFound = false; for (int i = 0; i < maxTries; ++i) { lookupTable->GetColor(i, lookupTableColor.data()); auto quantizedLookupTableColor = QuantizeColor(lookupTableColor.data()); if (std::find(colorsInUse.begin(), colorsInUse.end(), quantizedLookupTableColor) == std::end(colorsInUse)) { newLabel->SetColor(FromLookupTableColor(lookupTableColor.data())); newColorFound = true; break; } } if (!newColorFound) { lookupTable->GetColor(labelSetImage->GetTotalNumberOfLabels(), lookupTableColor.data()); newLabel->SetColor(FromLookupTableColor(lookupTableColor.data())); } return newLabel; } diff --git a/Modules/Multilabel/mitkLabelSetImageHelper.h b/Modules/Multilabel/mitkLabelSetImageHelper.h index ddcdfa39ab..aa65522ec5 100644 --- a/Modules/Multilabel/mitkLabelSetImageHelper.h +++ b/Modules/Multilabel/mitkLabelSetImageHelper.h @@ -1,59 +1,70 @@ /*============================================================================ 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 MITKLABELSETIMAGEHELPER_H #define MITKLABELSETIMAGEHELPER_H #include #include #include namespace mitk { /** * */ namespace LabelSetImageHelper { /** - * @brief This function creates and returns a new data node containing the given image as data. - * The segmentation node is named according to the given reference data node. - * Some properties are set to automatically link the segmentation node with its parent node. + * @brief This function creates and returns a new empty segmentation data node. + * @remark The data is not set, and must be done "by hand" to have a proper setup node. + * @param segmentationName An name for the new segmentation node. + * @return The new segmentation node as a data node pointer. + */ + MITKMULTILABEL_EXPORT mitk::DataNode::Pointer CreateEmptySegmentationNode(const std::string& segmentationName = std::string()); + + /** + * @brief This function creates and returns a new data node with a new empty segmentation + * data structure. + * The segmentation node is named according to the given reference data node, if not a name + * is passed explicitly. + * Some properties are set to automatically to ensure a proper setup segmentation and node + * (e.g. link the segmentation node with its parent node). * * @param referenceNode The reference node from which the name of the new segmentation node * is derived. * @param initialSegmentationImage The segmentation image that is used to initialize the label set image. * @param segmentationName An optional name for the new segmentation node. * * @return The new segmentation node as a data node pointer. */ MITKMULTILABEL_EXPORT mitk::DataNode::Pointer CreateNewSegmentationNode(const DataNode* referenceNode, const Image* initialSegmentationImage = nullptr, const std::string& segmentationName = std::string()); /** * @brief This function creates and returns a new label. The label is automatically assigned an * unused generic label name, depending on existing label names in all label sets of the * given label set image. * The color of the label is selected from the MULTILABEL lookup table, following the same * rules of the naming to likely chose a unique color. * * @param labelSetImage The label set image that the new label is added to * * @return The new label. */ MITKMULTILABEL_EXPORT mitk::Label::Pointer CreateNewLabel(const LabelSetImage* labelSetImage); } // namespace LabelSetImageHelper } // namespace mitk #endif // MITKLABELSETIMAGEHELPER_H diff --git a/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.cpp b/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.cpp index 44e836b3d6..03813c8731 100644 --- a/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.cpp +++ b/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.cpp @@ -1,148 +1,164 @@ /*============================================================================ 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 "mitkAutoSegmentationTool.h" #include "mitkImage.h" #include "mitkToolManager.h" #include +#include mitk::AutoSegmentationTool::AutoSegmentationTool() : Tool("dummy"), m_OverwriteExistingSegmentation(false) { } mitk::AutoSegmentationTool::AutoSegmentationTool(const char* interactorType, const us::Module* interactorModule) : Tool(interactorType, interactorModule), m_OverwriteExistingSegmentation(false) { } mitk::AutoSegmentationTool::~AutoSegmentationTool() { } void mitk::AutoSegmentationTool::Activated() { Superclass::Activated(); m_NoneOverwriteTargetSegmentationNode = nullptr; } void mitk::AutoSegmentationTool::Deactivated() { m_NoneOverwriteTargetSegmentationNode = nullptr; Superclass::Deactivated(); } const char *mitk::AutoSegmentationTool::GetGroup() const { return "autoSegmentation"; } mitk::Image::ConstPointer mitk::AutoSegmentationTool::GetImageByTimeStep(const mitk::Image* image, unsigned int timestep) { if (nullptr == image) return image; if (image->GetDimension() != 4) return image; mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput(image); imageTimeSelector->SetTimeNr(static_cast(timestep)); imageTimeSelector->UpdateLargestPossibleRegion(); return imageTimeSelector->GetOutput(); } mitk::Image::Pointer mitk::AutoSegmentationTool::GetImageByTimeStep(mitk::Image* image, unsigned int timestep) { if (nullptr == image) return image; if (image->GetDimension() != 4) return image; mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput(image); imageTimeSelector->SetTimeNr(static_cast(timestep)); imageTimeSelector->UpdateLargestPossibleRegion(); return imageTimeSelector->GetOutput(); } mitk::Image::ConstPointer mitk::AutoSegmentationTool::GetImageByTimePoint(const mitk::Image* image, TimePointType timePoint) { if (nullptr == image) return image; if (!image->GetTimeGeometry()->IsValidTimePoint(timePoint)) return nullptr; return AutoSegmentationTool::GetImageByTimeStep(image, image->GetTimeGeometry()->TimePointToTimeStep(timePoint)); } void mitk::AutoSegmentationTool::SetOverwriteExistingSegmentation(bool overwrite) { if (m_OverwriteExistingSegmentation != overwrite) { m_OverwriteExistingSegmentation = overwrite; m_NoneOverwriteTargetSegmentationNode = nullptr; } } std::string mitk::AutoSegmentationTool::GetCurrentSegmentationName() { if (this->GetToolManager()->GetWorkingData(0)) return this->GetToolManager()->GetWorkingData(0)->GetName(); else return ""; } mitk::DataNode *mitk::AutoSegmentationTool::GetTargetSegmentationNode() const { mitk::DataNode::Pointer segmentationNode = this->GetToolManager()->GetWorkingData(0); if (!m_OverwriteExistingSegmentation) { if (m_NoneOverwriteTargetSegmentationNode.IsNull()) { mitk::DataNode::Pointer refNode = this->GetToolManager()->GetReferenceData(0); if (refNode.IsNull()) { // TODO create and use segmentation exceptions instead!! MITK_ERROR << "No valid reference data!"; return nullptr; } - std::string nodename = refNode->GetName() + "_" + this->GetName(); - mitk::Color color; - color.SetRed(1); - color.SetBlue(0); - color.SetGreen(0); - //create a new segmentation node based on the current segmentation as template - m_NoneOverwriteTargetSegmentationNode = CreateEmptySegmentationNode(dynamic_cast(segmentationNode->GetData()), nodename, color); + + const auto labelSetImage = dynamic_cast(segmentationNode->GetData()); + if (nullptr == labelSetImage) + { + //TODO: this part of the if statement is old legacy code and should be removed. + //Keept because I didn't want to break/rework to many things before + //the release 2022.04. Should be removed when the seg tool classes are streamlined and the + //multi data structure is the only one used in seg APIs and code. + + mitk::Color color; + color.SetRed(1); + color.SetBlue(0); + color.SetGreen(0); + //create a new segmentation node based on the current segmentation as template + m_NoneOverwriteTargetSegmentationNode = CreateEmptySegmentationNode(dynamic_cast(segmentationNode->GetData()), nodename, color); + } + else + { + auto clonedSegmentation = labelSetImage->Clone(); + m_NoneOverwriteTargetSegmentationNode = LabelSetImageHelper::CreateEmptySegmentationNode(nodename); + m_NoneOverwriteTargetSegmentationNode->SetData(clonedSegmentation); + } } segmentationNode = m_NoneOverwriteTargetSegmentationNode; } return segmentationNode; } void mitk::AutoSegmentationTool::EnsureTargetSegmentationNodeInDataStorage() const { auto targetNode = this->GetTargetSegmentationNode(); if (!this->GetToolManager()->GetDataStorage()->Exists(targetNode)) { this->GetToolManager()->GetDataStorage()->Add(targetNode, this->GetToolManager()->GetReferenceData(0)); } }