diff --git a/Modules/Multilabel/mitkLabelSetImageConverter.cpp b/Modules/Multilabel/mitkLabelSetImageConverter.cpp index c571726cea..9373af8bd1 100644 --- a/Modules/Multilabel/mitkLabelSetImageConverter.cpp +++ b/Modules/Multilabel/mitkLabelSetImageConverter.cpp @@ -1,194 +1,298 @@ /*============================================================================ 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 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; +} diff --git a/Modules/Multilabel/mitkLabelSetImageConverter.h b/Modules/Multilabel/mitkLabelSetImageConverter.h index 09121dd627..c21bbce466 100644 --- a/Modules/Multilabel/mitkLabelSetImageConverter.h +++ b/Modules/Multilabel/mitkLabelSetImageConverter.h @@ -1,40 +1,45 @@ /*============================================================================ 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); + } #endif