diff --git a/Modules/Multilabel/mitkLabelSetImageConverter.cpp b/Modules/Multilabel/mitkLabelSetImageConverter.cpp index e18723bbc6..995ea66462 100644 --- a/Modules/Multilabel/mitkLabelSetImageConverter.cpp +++ b/Modules/Multilabel/mitkLabelSetImageConverter.cpp @@ -1,350 +1,407 @@ /*============================================================================ 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 #include #include #include template static void ConvertLabelSetImageToImage(const itk::Image *, mitk::LabelSetImage::ConstPointer labelSetImage, mitk::Image::Pointer &image) { typedef itk::Image ImageType; typedef itk::ComposeImageFilter ComposeFilterType; typedef itk::ImageDuplicator DuplicatorType; auto numberOfLayers = labelSetImage->GetNumberOfLayers(); if (numberOfLayers > 1) { auto vectorImageComposer = ComposeFilterType::New(); for (decltype(numberOfLayers) layer = 0; layer < numberOfLayers; ++layer) { auto layerImage = mitk::ImageToItkImage( labelSetImage->GetGroupImage(layer)); vectorImageComposer->SetInput(layer, layerImage); } vectorImageComposer->Update(); // mitk::GrabItkImageMemory does not support 4D, this will handle 4D correctly // and create a memory managed copy image = mitk::ImportItkImage(vectorImageComposer->GetOutput())->Clone(); } else { auto layerImage = mitk::ImageToItkImage(labelSetImage); auto duplicator = DuplicatorType::New(); duplicator->SetInputImage(layerImage); duplicator->Update(); // mitk::GrabItkImageMemory does not support 4D, this will handle 4D correctly // and create a memory managed copy image = mitk::ImportItkImage(duplicator->GetOutput())->Clone(); } } mitk::Image::Pointer mitk::ConvertLabelSetImageToImage(LabelSetImage::ConstPointer labelSetImage) { Image::Pointer image; if (labelSetImage->GetNumberOfLayers() > 0) { if (labelSetImage->GetDimension() == 4) { AccessFixedDimensionByItk_n(labelSetImage, ::ConvertLabelSetImageToImage, 4, (labelSetImage, image)); } else { AccessByItk_2(labelSetImage->GetGroupImage(0), ::ConvertLabelSetImageToImage, labelSetImage, image); } image->SetTimeGeometry(labelSetImage->GetTimeGeometry()->Clone()); } return image; } template static void SplitVectorImage(const itk::VectorImage* image, std::vector& result) { typedef itk::VectorImage VectorImageType; typedef itk::Image ImageType; typedef itk::VectorIndexSelectionCastImageFilter VectorIndexSelectorType; auto numberOfLayers = image->GetVectorLength(); for (decltype(numberOfLayers) layer = 0; layer < numberOfLayers; ++layer) { auto layerSelector = VectorIndexSelectorType::New(); layerSelector->SetInput(image); layerSelector->SetIndex(layer); layerSelector->Update(); mitk::Image::Pointer layerImage = mitk::GrabItkImageMemoryChannel(layerSelector->GetOutput(), nullptr, nullptr, false); result.push_back(layerImage); } } std::vector mitk::SplitVectorImage(const Image* vecImage) { if (nullptr == vecImage) { mitkThrow() << "Invalid usage; nullptr passed to SplitVectorImage."; } if (vecImage->GetChannelDescriptor().GetPixelType().GetPixelType() != itk::IOPixelEnum::VECTOR) { mitkThrow() << "Invalid usage of SplitVectorImage; passed image is not a vector image. Present pixel type: "<< vecImage->GetChannelDescriptor().GetPixelType().GetPixelTypeAsString(); } std::vector result; if (4 == vecImage->GetDimension()) { AccessVectorFixedDimensionByItk_n(vecImage, ::SplitVectorImage, 4, (result)); } else { AccessVectorPixelTypeByItk_n(vecImage, ::SplitVectorImage, (result)); } for (auto image : result) { image->SetTimeGeometry(vecImage->GetTimeGeometry()->Clone()); } return result; } mitk::LabelSetImage::Pointer mitk::ConvertImageToLabelSetImage(Image::Pointer image) { std::vector groupImages; if (image.IsNotNull()) { if (image->GetChannelDescriptor().GetPixelType().GetPixelType() == itk::IOPixelEnum::VECTOR) { groupImages = SplitVectorImage(image); } else { groupImages.push_back(image); } } auto labelSetImage = ConvertImageVectorToLabelSetImage(groupImages, image->GetTimeGeometry()); return labelSetImage; } mitk::LabelSetImage::Pointer mitk::ConvertImageVectorToLabelSetImage(const std::vector& images, const mitk::TimeGeometry* timeGeometry) { LabelSetImage::Pointer labelSetImage = mitk::LabelSetImage::New(); for (auto& groupImage : images) { if (groupImage== images.front()) { labelSetImage->InitializeByLabeledImage(groupImage); } else { labelSetImage->AddLayer(groupImage); } } labelSetImage->SetTimeGeometry(timeGeometry->Clone()); return labelSetImage; } mitk::LabelSetImage::LabelVectorType mitk::GenerateLabelSetWithMappedValues(const LabelSetImage::ConstLabelVectorType& sourceLabelset, LabelValueMappingVector labelMapping) { LabelSetImage::LabelVectorType result; for (auto oldLabel : sourceLabelset) { auto finding = std::find_if(labelMapping.begin(), labelMapping.end(), [oldLabel](const std::pair& mapping) {return oldLabel->GetValue() == mapping.first; }); if (finding != labelMapping.end()) { auto clonedLabel = oldLabel->Clone(); clonedLabel->SetValue(finding->second); result.push_back(clonedLabel); } } return result; } template void ConvertImageToGroupImageInternal(const SourceImageType* sourceImage, mitk::Image* groupImage, mitk::LabelSetImage::LabelValueVectorType& foundLabels) { using GroupImageType = typename SourceImageType::RebindImageType; typedef itk::ImageRegionConstIteratorWithIndex SourceIteratorType; typedef itk::ImageRegionIterator TargetIteratorType; auto targetImage = mitk::ImageToItkImage< mitk::LabelSetImage::LabelValueType, SourceImageType::ImageDimension>(groupImage); TargetIteratorType targetIter(targetImage, targetImage->GetRequestedRegion()); targetIter.GoToBegin(); SourceIteratorType sourceIter(sourceImage, sourceImage->GetRequestedRegion()); sourceIter.GoToBegin(); std::set< mitk::LabelSetImage::LabelValueType> detecteValues; while (!sourceIter.IsAtEnd()) { const auto originalSourceValue = sourceIter.Get(); const auto sourceValue = static_cast(originalSourceValue); if (originalSourceValue > mitk::Label::MAX_LABEL_VALUE) { mitkThrow() << "Cannot initialize MultiLabelSegmentation by image. Image contains a pixel value that exceeds the label value range. Invalid pixel value:" << originalSourceValue; } targetIter.Set(sourceValue); if (sourceValue != mitk::Label::UNLABELED_VALUE) detecteValues.insert(sourceValue); ++sourceIter; ++targetIter; } foundLabels.clear(); foundLabels.insert(foundLabels.begin(), detecteValues.begin(), detecteValues.end()); } mitk::Image::Pointer mitk::ConvertImageToGroupImage(const Image* inputImage, mitk::LabelSetImage::LabelValueVectorType& foundLabels) { if (nullptr == inputImage || inputImage->IsEmpty() || !inputImage->IsInitialized()) mitkThrow() << "Invalid labeled image."; auto result = Image::New(); result->Initialize(mitk::MakePixelType(), *(inputImage->GetTimeGeometry())); try { if (result->GetDimension() == 3) { AccessFixedDimensionByItk_2(inputImage, ConvertImageToGroupImageInternal, 3, result, foundLabels); } else if (result->GetDimension() == 4) { AccessFixedDimensionByItk_2(inputImage, ConvertImageToGroupImageInternal, 4, result, foundLabels); } else { mitkThrow() << result->GetDimension() << "-dimensional group images not yet supported"; } } catch (Exception& e) { mitkReThrow(e) << "Could not initialize by provided labeled image."; } catch (...) { mitkThrow() << "Could not initialize by provided labeled image due to unknown error."; } return result; } bool mitk::CheckForLabelValueConflictsAndResolve(const mitk::LabelSetImage::LabelValueVectorType& newValues, mitk::LabelSetImage::LabelValueVectorType& usedLabelValues, mitk::LabelSetImage::LabelValueVectorType& correctedLabelValues) { bool corrected = false; correctedLabelValues.clear(); for (const auto newV : newValues) { auto finding = std::find(usedLabelValues.begin(), usedLabelValues.end(), newV); if (finding == usedLabelValues.end()) { correctedLabelValues.push_back(newV); usedLabelValues.push_back(newV); } else { const auto maxFinding = std::max_element(usedLabelValues.begin(), usedLabelValues.end()); if (*maxFinding == Label::MAX_LABEL_VALUE) mitkThrow() << "Cannot correct label values. All available values are used. A segmentation cannot contain more labels."; const auto correctedV = *(maxFinding)+1; correctedLabelValues.push_back(correctedV); usedLabelValues.push_back(correctedV); corrected = true; } } return corrected; } namespace { template void ClearBufferProcessing(ImageType* itkImage) { itkImage->FillBuffer(0); } void ClearImageBuffer(mitk::Image* image) { if (image->GetDimension() == 4) { //remark: this extra branch was added, because LabelSetImage instances can be //dynamic (4D), but AccessByItk by support only supports 2D and 3D. //The option to change the CMake default dimensions for AccessByItk was //dropped (for details see discussion in T28756) AccessFixedDimensionByItk(image, ClearBufferProcessing, 4); } else { AccessByItk(image, ClearBufferProcessing); } } } mitk::Image::Pointer mitk::CreateLabelMask(const LabelSetImage* segmentation, LabelSetImage::LabelValueType labelValue, bool createBinaryMap) { if (nullptr==segmentation) mitkThrow() << "Error, cannot create label mask. Passed segmentation is nullptr."; if (!segmentation->ExistLabel(labelValue)) mitkThrow() << "Error, cannot create label mask. Label ID is invalid. Invalid ID: " << labelValue; auto mask = mitk::Image::New(); // mask->Initialize(segmentation) does not work here if this label set image has a single slice, // since the mask would be automatically flattened to a 2-d image, whereas we expect the // original dimension of this label set image. Hence, initialize the mask more explicitly: mask->Initialize(segmentation->GetPixelType(), segmentation->GetDimension(), segmentation->GetDimensions()); mask->SetTimeGeometry(segmentation->GetTimeGeometry()->Clone()); ClearImageBuffer(mask); const auto groupID = segmentation->GetGroupIndexOfLabel(labelValue); auto destinationLabel = segmentation->GetLabel(labelValue)->Clone(); if (createBinaryMap) destinationLabel->SetValue(1); TransferLabelContent(segmentation->GetGroupImage(groupID), mask.GetPointer(), { destinationLabel }, LabelSetImage::UNLABELED_VALUE, LabelSetImage::UNLABELED_VALUE, false, { { labelValue, destinationLabel->GetValue()} }, MultiLabelSegmentation::MergeStyle::Replace, MultiLabelSegmentation::OverwriteStyle::IgnoreLocks); return mask; } + +std::pair mitk::CreateLabelClassMap(const LabelSetImage* segmentation, LabelSetImage::GroupIndexType groupID, const LabelSetImage::LabelValueVectorType& selectedLabels) +{ + if (nullptr == segmentation) mitkThrow() << "Error, cannot create label class map. Passed segmentation is nullptr."; + if (!segmentation->ExistGroup(groupID)) mitkThrow() << "Error, cannot create label class map. GroupID is invalid. Invalid ID: " << groupID; + + auto map = mitk::Image::New(); + + // map->Initialize(segmentation) does not work here if this label set image has a single slice, + // since the map would be automatically flattened to a 2-d image, whereas we expect the + // original dimension of this label set image. Hence, initialize the map more explicitly: + map->Initialize(segmentation->GetPixelType(), segmentation->GetDimension(), segmentation->GetDimensions()); + map->SetTimeGeometry(segmentation->GetTimeGeometry()->Clone()); + + ClearImageBuffer(map); + + // get relevant labels (as intersect of groupLabels and selectedLabels + auto groupValues = segmentation->GetLabelValuesByGroup(groupID); + LabelSetImage::LabelValueVectorType relevantGroupValues = std::accumulate(groupValues.begin(), groupValues.end(), LabelSetImage::LabelValueVectorType(), + [&selectedLabels](LabelSetImage::LabelValueVectorType& result, LabelSetImage::LabelValueType element) { + if (std::find(selectedLabels.begin(), selectedLabels.end(), element) != selectedLabels.end()) { + result.push_back(element); + } + return result; + }); + + // construct class mapping + auto classToValueMap = LabelSetImageHelper::SplitLabelValuesByClassNamwe(segmentation, groupID, relevantGroupValues); + + ConstLabelVector destLabels; + LabelValueMappingVector transferMapping; + IDToLabelClassNameMapType classLookUp; + + for (const auto& [className, labelValues] : classToValueMap) + { + LabelSetImage::LabelValueType classValue = classLookUp.size() + 1; + classLookUp.insert(std::make_pair(classValue, className)); + destLabels.push_back(Label::New(classValue, className)); + for (const auto& labelValue : labelValues) + { + transferMapping.emplace_back(std::make_pair(labelValue, classValue)); + } + } + + TransferLabelContent(segmentation->GetGroupImage(groupID), map.GetPointer(), + destLabels, LabelSetImage::UNLABELED_VALUE, LabelSetImage::UNLABELED_VALUE, false, transferMapping, MultiLabelSegmentation::MergeStyle::Replace, MultiLabelSegmentation::OverwriteStyle::IgnoreLocks); + + return std::make_pair(map, classLookUp); +} + +std::pair mitk::CreateLabelClassMap(const LabelSetImage* segmentation, LabelSetImage::GroupIndexType groupID) +{ + return CreateLabelClassMap(segmentation, groupID, segmentation->GetLabelValuesByGroup(groupID)); +} diff --git a/Modules/Multilabel/mitkLabelSetImageConverter.h b/Modules/Multilabel/mitkLabelSetImageConverter.h index a0bacda5b4..349560bc4e 100644 --- a/Modules/Multilabel/mitkLabelSetImageConverter.h +++ b/Modules/Multilabel/mitkLabelSetImageConverter.h @@ -1,54 +1,76 @@ /*============================================================================ 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 mitkLabelSetImageConverter_h #define mitkLabelSetImageConverter_h #include namespace mitk { /** * \brief Convert mitk::LabelSetImage to mitk::Image (itk::VectorImage) */ MITKMULTILABEL_EXPORT Image::Pointer ConvertLabelSetImageToImage(LabelSetImage::ConstPointer labelSetImage); /** * \brief Convert mitk::Image to mitk::LabelSetImage, templating and differentation between itk::Image and * itk::VectorImage is internal */ MITKMULTILABEL_EXPORT LabelSetImage::Pointer ConvertImageToLabelSetImage(Image::Pointer image); MITKMULTILABEL_EXPORT LabelSetImage::Pointer ConvertImageVectorToLabelSetImage(const std::vector& images, const TimeGeometry* timeGeometry); MITKMULTILABEL_EXPORT std::vector SplitVectorImage(const Image* vecImage); /** Function takes a vector of labels and transfers all labels as clones with adapted label values to the result vector. The values will be adapted according to the provided mapping (key is the old value, value the new). @remark: Only labels will be transfered, nothing else. So things like message observers or m_ReservedLabelValuesFunctor must be copied explicitly.*/ MITKMULTILABEL_EXPORT LabelSetImage::LabelVectorType GenerateLabelSetWithMappedValues(const LabelSetImage::ConstLabelVectorType&, LabelValueMappingVector labelMapping); MITKMULTILABEL_EXPORT Image::Pointer ConvertImageToGroupImage(const Image* inputImage, mitk::LabelSetImage::LabelValueVectorType& foundLabels); MITKMULTILABEL_EXPORT bool CheckForLabelValueConflictsAndResolve(const mitk::LabelSetImage::LabelValueVectorType& newValues, mitk::LabelSetImage::LabelValueVectorType& usedLabelValues, mitk::LabelSetImage::LabelValueVectorType& correctedLabelValues); /** Function creates a binary mask representing only the specified label of the multi label segmentation. * @param segmentation Pointer to the segmentation that is the source for the mask. * @param labelValue the label that should be extracted. * @param createBinaryMap indicates if the label pixels should be indicated by the value 1 (createBinaryMap==true) or by the value of the label * (createBinaryMap==false). * @pre segmentation must point to a valid instance. * @pre labelValue must exist in segmentation.*/ MITKMULTILABEL_EXPORT Image::Pointer CreateLabelMask(const LabelSetImage* segmentation, LabelSetImage::LabelValueType labelValue, bool createBinaryMap = true); + /** Function creates a map of all label classes in a specified group. + * @param segmentation Pointer to the segmentation that is the source for the map. + * @param groupID the group that should be used. + * @param selectedLabels The selected labels that should be represented in the class map. This is meant as white list, therefore only + * label values listed in the list are used. Invalid label values (not existing in the group) will be ignored. + * @return Returns a pair where first is the pointer to the created map image and second is the look up table that indicated + * the pixel value of each found class in the map. + * @pre segmentation must point to a valid instance. + * @pre groupID must exist in segmentation.*/ + using IDToLabelClassNameMapType = std::map; + MITKMULTILABEL_EXPORT std::pair CreateLabelClassMap(const LabelSetImage* segmentation, LabelSetImage::GroupIndexType groupID, const LabelSetImage::LabelValueVectorType& selectedLabels); + /** Function creates a map of all label classes in a specified group. + * @overload + * This version always uses all labels of a group. + * @param segmentation Pointer to the segmentation that is the source for the map. + * @param groupID the group that should be used. + * @return Returns a pair where first is the pointer to the created map image and second is the look up table that indicated + * the pixel value of each found class in the map. + * @pre segmentation must point to a valid instance. + * @pre groupID must exist in segmentation.*/ + MITKMULTILABEL_EXPORT std::pair CreateLabelClassMap(const LabelSetImage* segmentation, LabelSetImage::GroupIndexType groupID); + } #endif diff --git a/Modules/Multilabel/mitkLabelSetImageHelper.cpp b/Modules/Multilabel/mitkLabelSetImageHelper.cpp index 4dda62b8bb..0b479c367d 100644 --- a/Modules/Multilabel/mitkLabelSetImageHelper.cpp +++ b/Modules/Multilabel/mitkLabelSetImageHelper.cpp @@ -1,144 +1,194 @@ /*============================================================================ 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 = CreateEmptySegmentationNode(newSegmentationName); newSegmentationNode->SetData(newLabelSetImage); return newSegmentationNode; } -mitk::Label::Pointer mitk::LabelSetImageHelper::CreateNewLabel(const LabelSetImage* labelSetImage, const std::string& namePrefix) +mitk::Label::Pointer mitk::LabelSetImageHelper::CreateNewLabel(const LabelSetImage* labelSetImage, const std::string& namePrefix, bool hideIDIfUnique) { if (nullptr == labelSetImage) return nullptr; const std::regex genericLabelNameRegEx(namePrefix + " ([1-9][0-9]*)"); int maxGenericLabelNumber = 0; std::vector> colorsInUse = { {0,0,0} }; //black is always in use. for (auto & label : labelSetImage->GetLabels()) { 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(namePrefix + " " + std::to_string(maxGenericLabelNumber + 1)); + if (hideIDIfUnique && 0==maxGenericLabelNumber) + newLabel->SetName(namePrefix); + else + newLabel->SetName(namePrefix + " " + 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; } + +mitk::LabelSetImageHelper::GroupIDToLabelValueMapType +mitk::LabelSetImageHelper::SplitLabelValuesByGroup(const LabelSetImage* labelSetImage, const LabelSetImage::LabelValueVectorType& labelValues) +{ + if (nullptr == labelSetImage) + mitkThrow() << "Cannot split label values. Invalid LabelSetImage pointer passed"; + + GroupIDToLabelValueMapType result; + + for (auto value : labelValues) + { + auto groupID = labelSetImage->GetGroupIndexOfLabel(value); + + auto& values = result[groupID]; //if groupID does not exist in result this call will init an empty vector. + result[groupID].push_back(value); + } + + return result; +} + +mitk::LabelSetImageHelper::LabelClassNameToLabelValueMapType +mitk::LabelSetImageHelper::SplitLabelValuesByClassNamwe(const LabelSetImage* labelSetImage, LabelSetImage::GroupIndexType groupID) +{ + if (nullptr == labelSetImage) + mitkThrow() << "Cannot split label values. Invalid LabelSetImage pointer passed"; + + return SplitLabelValuesByClassNamwe(labelSetImage, groupID, labelSetImage->GetLabelValuesByGroup(groupID)); +} + +mitk::LabelSetImageHelper::LabelClassNameToLabelValueMapType +mitk::LabelSetImageHelper::SplitLabelValuesByClassNamwe(const LabelSetImage* labelSetImage, LabelSetImage::GroupIndexType groupID, const LabelSetImage::LabelValueVectorType& labelValues) +{ + if (nullptr == labelSetImage) + mitkThrow() << "Cannot split label values. Invalid LabelSetImage pointer passed"; + + LabelClassNameToLabelValueMapType result; + + for (auto value : labelValues) + { + auto className = labelSetImage->GetLabel(value)->GetName(); + + auto& values = result[className]; //if className does not exist in result this call will init an empty vector. + result[className].push_back(value); + } + + return result; +} diff --git a/Modules/Multilabel/mitkLabelSetImageHelper.h b/Modules/Multilabel/mitkLabelSetImageHelper.h index 91c7f11d05..b2bc26daf1 100644 --- a/Modules/Multilabel/mitkLabelSetImageHelper.h +++ b/Modules/Multilabel/mitkLabelSetImageHelper.h @@ -1,71 +1,81 @@ /*============================================================================ 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 empty segmentation data node. * @remark The data is not set. Set it manually to have a properly setup node. * @param segmentationName A 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, otherwise a name * is passed explicitly. * Some properties are set 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 * @param namePrefix The prefix of the label name that is prepended by a sequential number + * @param hideIDIfUnique Indicates if the ID suffix should be added if the label name prefix would be already unique. + * true: only add if not unique; false: add always. * * @return The new label. */ - MITKMULTILABEL_EXPORT mitk::Label::Pointer CreateNewLabel(const LabelSetImage* labelSetImage, const std::string& namePrefix = "Label"); + MITKMULTILABEL_EXPORT Label::Pointer CreateNewLabel(const LabelSetImage* labelSetImage, const std::string& namePrefix = "Label", bool hideIDIfUnique = false); + + using GroupIDToLabelValueMapType = std::map; + MITKMULTILABEL_EXPORT GroupIDToLabelValueMapType SplitLabelValuesByGroup(const LabelSetImage* labelSetImage, const LabelSetImage::LabelValueVectorType& labelValues); + + using LabelClassNameToLabelValueMapType = std::map; + MITKMULTILABEL_EXPORT LabelClassNameToLabelValueMapType SplitLabelValuesByClassNamwe(const LabelSetImage* labelSetImage, LabelSetImage::GroupIndexType groupID); + MITKMULTILABEL_EXPORT LabelClassNameToLabelValueMapType SplitLabelValuesByClassNamwe(const LabelSetImage* labelSetImage, LabelSetImage::GroupIndexType groupID, const LabelSetImage::LabelValueVectorType& labelValues); + } // namespace LabelSetImageHelper } // namespace mitk #endif