diff --git a/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp b/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp index 2808f95126..f85d05554e 100644 --- a/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp +++ b/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp @@ -1,185 +1,182 @@ /*============================================================================ 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 "mitkBooleanOperation.h" -#include <itkAndImageFilter.h> -#include <itkNotImageFilter.h> -#include <itkOrImageFilter.h> +#include <itkNaryFunctorImageFilter.h> #include <mitkExceptionMacro.h> #include <mitkImageCast.h> #include <mitkImageTimeSelector.h> +#include <mitkITKEventObserverGuard.h> -typedef itk::Image<mitk::Label::PixelType, 3> ImageType; +using ITKGroupImageType = itk::Image<mitk::Label::PixelType, 3>; +using BoolOpsFunctionType = mitk::Label::PixelType(const std::vector<mitk::Label::PixelType>& inputArray); -static mitk::Image::Pointer Get3DSegmentation(mitk::Image::Pointer segmentation, mitk::TimePointType time) +/** Functor class that implements the label transfer and is used in conjunction with the itk::BinaryFunctorImageFilter. +* For details regarding the usage of the filter and the functor patterns, please see info of itk::BinaryFunctorImageFilter. +*/ +template <class TPixelType> +class BoolOpsFunctor { - if (segmentation->GetDimension() != 4) - return segmentation; - auto imageTimeSelector = mitk::ImageTimeSelector::New(); - imageTimeSelector->SetInput(segmentation); - imageTimeSelector->SetTimeNr(segmentation->GetTimeGeometry()->TimePointToTimeStep(time)); +public: + BoolOpsFunctor() {}; - imageTimeSelector->UpdateLargestPossibleRegion(); - - return imageTimeSelector->GetOutput(); -} - -static ImageType::Pointer CastTo3DItkImage(mitk::Image::Pointer segmentation, mitk::TimePointType time) -{ - ImageType::Pointer result; - mitk::CastToItkImage(Get3DSegmentation(segmentation, time), result); - return result; -} - -mitk::BooleanOperation::BooleanOperation(Type type, - mitk::Image::Pointer segmentationA, - mitk::Image::Pointer segmentationB, - TimePointType time) - : m_Type(type), m_SegmentationA(segmentationA), m_SegmentationB(segmentationB), m_TimePoint(time) -{ - this->ValidateSegmentations(); -} + BoolOpsFunctor(std::function<BoolOpsFunctionType> opsFunction) : m_Function(opsFunction) + { + }; -mitk::BooleanOperation::~BooleanOperation() -{ -} + ~BoolOpsFunctor() {}; -mitk::LabelSetImage::Pointer mitk::BooleanOperation::GetResult() const -{ - switch (m_Type) + bool operator!=(const BoolOpsFunctor& other)const { - case Difference: - return this->GetDifference(); + return !(*this == other); + } - case Intersection: - return this->GetIntersection(); + bool operator==(const BoolOpsFunctor& other) const + { + if ((this->m_Function.target_type() == other.m_Function.target_type()) + && (this->m_Function.target<int(*)(int)>() && other.m_Function.target<int(*)(int)>()) + // If both std::function objects hold function pointers of the same signature, + // we can compare the pointers to check if they point to the same function. + && (*this->m_Function.target<int(*)(int)>() == *other.m_Function.target<int(*)(int)>())) + { + return true; + } + return false; + } - case Union: - return this->GetUnion(); + BoolOpsFunctor& operator=(const BoolOpsFunctor& other) + { + this->m_Function = other.m_Function; - default: - mitkThrow() << "Unknown boolean operation type '" << m_Type << "'!"; + return *this; } -} -mitk::LabelSetImage::Pointer mitk::BooleanOperation::GetDifference() const -{ - auto input1 = CastTo3DItkImage(m_SegmentationA, m_TimePoint); - auto input2 = CastTo3DItkImage(m_SegmentationB, m_TimePoint); - - auto notFilter = itk::NotImageFilter<ImageType, ImageType>::New(); - notFilter->SetInput(input2); + inline TPixelType operator()(const std::vector<TPixelType>& inputArray) + { + return m_Function(inputArray); + } - auto andFilter = itk::AndImageFilter<ImageType, ImageType>::New(); - andFilter->SetInput1(input1); - andFilter->SetInput2(notFilter->GetOutput()); +private: + std::function<BoolOpsFunctionType> m_Function; +}; - andFilter->UpdateLargestPossibleRegion(); - auto tempResult = Image::New(); - CastToMitkImage<ImageType>(andFilter->GetOutput(), tempResult); +mitk::Image::Pointer GenerateInternal(const mitk::LabelSetImage* segmentation, mitk::LabelSetImage::LabelValueVectorType labeValues, std::function<BoolOpsFunctionType> opsFunction, + std::function<void(float progress)> progressCallback = [](float) {}) +{ + if (nullptr == segmentation) mitkThrow() << "Cannot perform boolean operation. Passed segmentation is not valid"; - tempResult->DisconnectPipeline(); + mitk::Image::Pointer result = mitk::Image::New(); + result->Initialize(mitk::MakeScalarPixelType<mitk::LabelSetImage::LabelValueType>(), *(segmentation->GetTimeGeometry())); - auto result = mitk::LabelSetImage::New(); - result->InitializeByLabeledImage(tempResult); + const auto timeStepCount = segmentation->GetTimeGeometry()->CountTimeSteps(); - return result; -} + for (mitk::TimeStepType i = 0; i < timeStepCount; ++i) + { + using OpsFilterType = itk::NaryFunctorImageFilter<ITKGroupImageType, ITKGroupImageType, BoolOpsFunctor<mitk::LabelSetImage::LabelValueType> >; + auto opsFilter = OpsFilterType::New(); -mitk::LabelSetImage::Pointer mitk::BooleanOperation::GetIntersection() const -{ - auto input1 = CastTo3DItkImage(m_SegmentationA, m_TimePoint); - auto input2 = CastTo3DItkImage(m_SegmentationB, m_TimePoint); + mitk::ITKEventObserverGuard eventGuard(opsFilter, itk::ProgressEvent(), [&opsFilter, progressCallback, timeStepCount](const itk::EventObject& event) + { progressCallback(opsFilter->GetProgress() / static_cast<float>(timeStepCount)); }); - auto andFilter = itk::AndImageFilter<ImageType, ImageType>::New(); - andFilter->SetInput1(input1); - andFilter->SetInput2(input2); + BoolOpsFunctor<mitk::LabelSetImage::LabelValueType> functor(opsFunction); + opsFilter->SetFunctor(functor); + std::vector < ITKGroupImageType::ConstPointer > inputImages; - andFilter->UpdateLargestPossibleRegion(); + unsigned int inputIndex = 0; + for (auto value : labeValues) + { + auto groupImage = segmentation->GetGroupImage(segmentation->GetGroupIndexOfLabel(value)); + auto groupImageAtTS = mitk::SelectImageByTimeStep(groupImage, i); - auto tempResult = Image::New(); - CastToMitkImage<ImageType>(andFilter->GetOutput(), tempResult); + ITKGroupImageType::Pointer itkImage; + mitk::CastToItkImage(groupImageAtTS, itkImage); + inputImages.push_back(itkImage); + opsFilter->SetInput(inputIndex, itkImage); + ++inputIndex; + } - tempResult->DisconnectPipeline(); + opsFilter->Update(); + auto resultTS = opsFilter->GetOutput(); - auto result = mitk::LabelSetImage::New(); - result->InitializeByLabeledImage(tempResult); + result->SetVolume(resultTS->GetBufferPointer(), i); + } return result; } -mitk::LabelSetImage::Pointer mitk::BooleanOperation::GetUnion() const -{ - auto input1 = CastTo3DItkImage(m_SegmentationA, m_TimePoint); - auto input2 = CastTo3DItkImage(m_SegmentationB, m_TimePoint); - - auto orFilter = itk::OrImageFilter<ImageType, ImageType>::New(); - orFilter->SetInput1(input1); - orFilter->SetInput2(input2); - orFilter->UpdateLargestPossibleRegion(); - - auto tempResult = Image::New(); - CastToMitkImage<ImageType>(orFilter->GetOutput(), tempResult); - - tempResult->DisconnectPipeline(); - - auto result = mitk::LabelSetImage::New(); - result->InitializeByLabeledImage(tempResult); - - return result; +mitk::Image::Pointer mitk::BooleanOperation::GenerateUnion(const LabelSetImage* segmentation, LabelSetImage::LabelValueVectorType labelValues, + std::function<void(float progress)> progressCallback) +{ + auto unionOps = [labelValues](const std::vector<mitk::Label::PixelType>& inputArray) + { + mitk::Label::PixelType result = 0; + for (auto inIt = inputArray.cbegin(), refIt = labelValues.cbegin(); inIt != inputArray.cend(); ++inIt, ++refIt) + { + if (*inIt == *refIt) + { + result = 1; + break; + } + } + return result; + }; + + return GenerateInternal(segmentation, labelValues, unionOps, progressCallback); } -void mitk::BooleanOperation::ValidateSegmentation(mitk::Image::Pointer segmentation) const +mitk::Image::Pointer mitk::BooleanOperation::GenerateIntersection(const LabelSetImage* segmentation, LabelSetImage::LabelValueVectorType labelValues, + std::function<void(float progress)> progressCallback) { - if (segmentation.IsNull()) - mitkThrow() << "Segmentation is nullptr!"; - - if (segmentation->GetImageDescriptor()->GetNumberOfChannels() != 1) - mitkThrow() << "Segmentation has more than one channel!"; - - auto pixelType = segmentation->GetImageDescriptor()->GetChannelDescriptor().GetPixelType(); - - if (pixelType.GetPixelType() != itk::IOPixelEnum::SCALAR || - (pixelType.GetComponentType() != itk::IOComponentEnum::UCHAR && - pixelType.GetComponentType() != itk::IOComponentEnum::USHORT)) - mitkThrow() << "Segmentation is neither of type 'unsigned char' nor type 'unsigned short'!"; - - auto dimension = segmentation->GetDimension(); - - if (dimension > 4) - mitkThrow() << "Segmentation has more than four dimensions!"; - - if (dimension < 3) - mitkThrow() << "Segmentation has less than three dimensions!"; - - if (!segmentation->GetTimeGeometry()->IsValidTimePoint(m_TimePoint)) - mitkThrow() << "Segmentation is not defined for specified time point. Time point: " << m_TimePoint; + auto intersectOps = [labelValues](const std::vector<mitk::Label::PixelType>& inputArray) + { + mitk::Label::PixelType result = 1; + for (auto inIt = inputArray.cbegin(), refIt = labelValues.cbegin(); inIt != inputArray.cend(); ++inIt, ++refIt) + { + if (*inIt != *refIt) + { + result = 0; + break; + } + } + return result; + }; + + return GenerateInternal(segmentation, labelValues, intersectOps, progressCallback); } -void mitk::BooleanOperation::ValidateSegmentations() const +mitk::Image::Pointer mitk::BooleanOperation::GenerateDifference(const LabelSetImage* segmentation, LabelSetImage::LabelValueType minuendLabelValue, + LabelSetImage::LabelValueVectorType subtrahendLabelValues, std::function<void(float progress)> progressCallback) { - this->ValidateSegmentation(m_SegmentationA); - this->ValidateSegmentation(m_SegmentationB); - - if (m_SegmentationA->GetDimension() != m_SegmentationB->GetDimension()) - mitkThrow() << "Segmentations have different dimensions!"; - - const auto geometryA = m_SegmentationA->GetTimeGeometry()->GetGeometryForTimePoint(m_TimePoint); - const auto geometryB = m_SegmentationB->GetTimeGeometry()->GetGeometryForTimePoint(m_TimePoint); - if (!mitk::Equal(*(geometryA.GetPointer()), *(geometryB.GetPointer()),eps,false)) - { - mitkThrow() << "Segmentations have different geometries and cannot be used for boolean operations!"; - } + auto intersectOps = [minuendLabelValue, subtrahendLabelValues](const std::vector<mitk::Label::PixelType>& inputArray) + { + if (minuendLabelValue != inputArray.front()) + return mitk::Label::PixelType(0); + + mitk::Label::PixelType result = 1; + for (auto inIt = inputArray.cbegin()+1, refIt = subtrahendLabelValues.cbegin(); inIt != inputArray.cend(); ++inIt, ++refIt) + { + if (*inIt == *refIt) + { + result = 0; + break; + } + } + return result; + }; + + auto labelValues = subtrahendLabelValues; + labelValues.insert(labelValues.begin(), minuendLabelValue); + return GenerateInternal(segmentation, labelValues, intersectOps, progressCallback); } diff --git a/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.h b/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.h index 8822957cd2..613e8d21bf 100644 --- a/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.h +++ b/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.h @@ -1,73 +1,38 @@ /*============================================================================ 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 mitkBooleanOperation_h #define mitkBooleanOperation_h #include <MitkSegmentationExports.h> #include <mitkLabelSetImage.h> namespace mitk { /** \brief Executes a boolean operation on two different segmentations. - * * All parameters of the boolean operations must be specified during construction. * The actual operation is executed when calling GetResult(). */ - class MITKSEGMENTATION_EXPORT BooleanOperation + namespace BooleanOperation { - public: - enum Type - { - None, - Difference, - Intersection, - Union - }; - /* \brief Construct a boolean operation. - * - * Throws an mitk::Exception when segmentations are somehow invalid. - * - * \param[in] type The type of the boolean operation. - * \param[in] segmentationA The first operand of the boolean operation. - * \param[in] segmentationB The second operand of the boolean operation. - * \param[in] The time point at which the operation will be executed. - */ - BooleanOperation(Type type, Image::Pointer segmentationA, Image::Pointer segmentationB, TimePointType time = 0.); - ~BooleanOperation(); + Image::Pointer MITKSEGMENTATION_EXPORT GenerateUnion(const LabelSetImage*, LabelSetImage::LabelValueVectorType labeValues, + std::function<void(float progress)> progressCallback = [](float) {}); + Image::Pointer MITKSEGMENTATION_EXPORT GenerateIntersection(const LabelSetImage*, LabelSetImage::LabelValueVectorType labelValues, + std::function<void(float progress)> progressCallback = [](float) {}); + Image::Pointer MITKSEGMENTATION_EXPORT GenerateDifference(const LabelSetImage*, LabelSetImage::LabelValueType minuendLabelValue, + LabelSetImage::LabelValueVectorType subtrahendLabelValues, std::function<void(float progress)> progressCallback = [](float) {}); - /* \brief Execute boolean operation and return resulting segmentation. - * - * \return The resulting segmentation. - */ - LabelSetImage::Pointer GetResult() const; - - private: - BooleanOperation(const BooleanOperation &); - BooleanOperation &operator=(const BooleanOperation &); - - LabelSetImage::Pointer GetDifference() const; - LabelSetImage::Pointer GetIntersection() const; - LabelSetImage::Pointer GetUnion() const; - - void ValidateSegmentation(Image::Pointer segmentation) const; - void ValidateSegmentations() const; - - Type m_Type; - Image::Pointer m_SegmentationA; - Image::Pointer m_SegmentationB; - TimePointType m_TimePoint; }; } #endif