diff --git a/Modules/MatchPointRegistration/cmdapps/StitchImagesMiniApp.cpp b/Modules/MatchPointRegistration/cmdapps/StitchImagesMiniApp.cpp index 2178b3e399..4a9833ba08 100644 --- a/Modules/MatchPointRegistration/cmdapps/StitchImagesMiniApp.cpp +++ b/Modules/MatchPointRegistration/cmdapps/StitchImagesMiniApp.cpp @@ -1,202 +1,218 @@ /*============================================================================ 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. ============================================================================*/ // std includes #include #include // itk includes #include "itksys/SystemTools.hxx" // CTK includes #include "mitkCommandLineParser.h" // MITK includes #include #include #include #include #include mitkCommandLineParser::StringContainerType inFilenames; mitkCommandLineParser::StringContainerType regFilenames; std::string outFileName; std::string refGeometryFileName; std::vector images; std::vector registrations; mitk::BaseGeometry::Pointer refGeometry; mitk::ImageMappingInterpolator::Type interpolatorType = mitk::ImageMappingInterpolator::Linear; double paddingValue = 0; +itk::StitchStrategy stitchStratgy = itk::StitchStrategy::Mean; void setupParser(mitkCommandLineParser& parser) { // set general information about your MiniApp parser.setCategory("Mapping Tools"); parser.setTitle("Stitch 3D Images"); parser.setDescription("MiniApp that allows to map and stitch 3D images into a given output geometry."); parser.setContributor("DKFZ MIC"); //! [create parser] //! [add arguments] // how should arguments be prefixed parser.setArgumentPrefix("--", "-"); // add each argument, unless specified otherwise each argument is optional // see mitkCommandLineParser::addArgument for more information parser.beginGroup("Required I/O parameters"); parser.addArgument( "inputs", "i", mitkCommandLineParser::StringList, "Input files", "Pathes to the input images that should be mapped and stitched", us::Any(), false, false, false, mitkCommandLineParser::Input); parser.addArgument("output", "o", mitkCommandLineParser::File, "Output file path", "Path to the fused 3D+t image.", us::Any(), false, false, false, mitkCommandLineParser::Output); parser.endGroup(); parser.beginGroup("Optional parameters"); parser.addArgument("reference", "x", mitkCommandLineParser::File, "Output reference image.", "File path to an image that serves as reference for the output geometry.", us::Any(), false, false, false, mitkCommandLineParser::Input); parser.addArgument( "registrations", "r", mitkCommandLineParser::StringList, "Registration files", "Pathes to the registrations that should be used to map the input images. If this parameter is not set, identity transforms are assumed. If this parameter is set, it must have the same number of entries then the parameter inputs. If you want to use and identity transform for a specific input, specify an empty string. The application assumes that inputs and registrations have the same order, so the n-th input should use thr n-th registration.", us::Any(), true, false, false, mitkCommandLineParser::Input); parser.addArgument("interpolator", "i", mitkCommandLineParser::Int, "Interpolator type", "Interpolator used for mapping the images. Default: 2; allowed values: 1: Nearest Neighbour, 2: Linear, 3: BSpline 3, 4: WSinc Hamming, 5: WSinc Welch", us::Any(2), true); + parser.addArgument("strategy", "s", mitkCommandLineParser::Int, "Stitch strategy", "Strategy used for stitching the images. 0: Mean -> computes the mean value of all input images that cover an output pixel (default strategy). 1: BorderDistance -> Uses the input pixel that has the largest minimal distance to its image borders", us::Any(2), true); parser.addArgument("padding", "p", mitkCommandLineParser::Float, "Padding value", "Value used for output voxels that are not covered by any input image.", us::Any(0.), true); parser.addArgument("help", "h", mitkCommandLineParser::Bool, "Help:", "Show this help text"); parser.endGroup(); //! [add arguments] } bool configureApplicationSettings(std::map parsedArgs) { if (parsedArgs.size() == 0) return false; inFilenames = us::any_cast(parsedArgs["inputs"]); outFileName = us::any_cast(parsedArgs["output"]); if (parsedArgs.count("reference")) { refGeometryFileName = us::any_cast(parsedArgs["reference"]); } if (parsedArgs.count("registrations")) { regFilenames = us::any_cast(parsedArgs["registrations"]); - if (regFilenames.empty()) - { - regFilenames.resize(inFilenames.size()); - std::fill(regFilenames.begin(), regFilenames.end(), ""); - } + } + else + { + regFilenames.resize(inFilenames.size()); + std::fill(regFilenames.begin(), regFilenames.end(), ""); } if (parsedArgs.count("interpolator")) { auto interpolator = us::any_cast(parsedArgs["interpolator"]); interpolatorType = static_cast(interpolator); } if (parsedArgs.count("padding")) { - paddingValue = us::any_cast(parsedArgs["padding"]); + paddingValue = us::any_cast(parsedArgs["padding"]); + } + + if (parsedArgs.count("strategy")) + { + auto temp = us::any_cast(parsedArgs["strategy"]); + stitchStratgy = static_cast(temp); } return true; } int main(int argc, char* argv[]) { mitkCommandLineParser parser; setupParser(parser); mitk::PreferenceListReaderOptionsFunctor readerFilterFunctor = mitk::PreferenceListReaderOptionsFunctor({ "MITK DICOM Reader v2 (autoselect)" }, { "" }); const std::map& parsedArgs = parser.parseArguments(argc, argv); if (!configureApplicationSettings(parsedArgs)) { return EXIT_FAILURE; }; // Show a help message if (parsedArgs.count("help") || parsedArgs.count("h")) { std::cout << parser.helpText(); return EXIT_SUCCESS; } if(regFilenames.size() != inFilenames.size()) { MITK_ERROR << "Cannot stitch inputs. The number of specified registrations does not match the number of inputs."; return EXIT_FAILURE; } //! [do processing] try { std::cout << "Load images:" << std::endl; unsigned int index = 0; for (auto path : inFilenames) { std::cout << "#"<(path, &readerFilterFunctor); images.push_back(image.GetPointer()); if (regFilenames[index].empty()) { std::cout << " associated registration: identity" << std::endl; registrations.push_back(mitk::GenerateIdentityRegistration3D().GetPointer()); } else { std::cout << " associated registration: " << regFilenames[index] << std::endl; - auto reg = mitk::IOUtil::Load(path); + auto reg = mitk::IOUtil::Load(regFilenames[index]); registrations.push_back(reg.GetPointer()); } ++index; } - std::cout << "Padding value: " << paddingValue << std::endl; std::cout << "Reference image: " << refGeometryFileName << std::endl << std::endl; auto refImage = mitk::IOUtil::Load(refGeometryFileName, &readerFilterFunctor); if (refImage.IsNotNull()) { refGeometry = refImage->GetGeometry(); } + std::cout << "Padding value: " << paddingValue << std::endl; + std::cout << "Stitch strategy: "; + if (itk::StitchStrategy::Mean == stitchStratgy) + { + std::cout << "Mean " << std::endl; + } + else + { + std::cout << "BorderDistance" << std::endl; + } + std::cout << "Stitch the images ..." << std::endl; - std::cout << "Stitch the the images ..." << std::endl; - - auto output = mitk::StitchImages(images, registrations, refGeometry,paddingValue,interpolatorType); + auto output = mitk::StitchImages(images, registrations, refGeometry,paddingValue,stitchStratgy,interpolatorType); std::cout << "Save output image: " << outFileName << std::endl; mitk::IOUtil::Save(output, outFileName); std::cout << "Processing finished." << std::endl; return EXIT_SUCCESS; } catch (const std::exception& e) { MITK_ERROR << e.what(); return EXIT_FAILURE; } catch (...) { MITK_ERROR << "Unexpected error encountered."; return EXIT_FAILURE; } } diff --git a/Modules/MatchPointRegistration/include/itkStitchImageFilter.h b/Modules/MatchPointRegistration/include/itkStitchImageFilter.h index b42d255e1c..0727bd6a28 100644 --- a/Modules/MatchPointRegistration/include/itkStitchImageFilter.h +++ b/Modules/MatchPointRegistration/include/itkStitchImageFilter.h @@ -1,308 +1,329 @@ /*============================================================================ 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 itkStitchImageFilter_h #define itkStitchImageFilter_h #include "itkFixedArray.h" #include "itkTransform.h" #include "itkImageRegionIterator.h" #include "itkImageToImageFilter.h" #include "itkLinearInterpolateImageFunction.h" #include "itkSize.h" #include "itkDefaultConvertPixelTraits.h" #include "itkDataObjectDecorator.h" namespace itk { -/** \class StitchImageFilter + enum class StitchStrategy + { + Mean = 0, //use the mean value of all inputs that can provide a pixel vaule + BorderDistance = 1 //use the value that is largest minimal distance to its image borders (use e.g. if vaules tend to be not relyable at borders) + }; + + std::ostream& operator<< (std::ostream& os, const itk::StitchStrategy& strategy) + { + if (itk::StitchStrategy::Mean == strategy) + os << "Mean"; + else if (itk::StitchStrategy::BorderDistance == strategy) + os << "BorderDistance"; + else + os << "unkown"; + + return os; + }; + + /** \class StitchImageFilter * \brief ITK filter that resamples/stitches multiple images into a given reference geometry. * * StitchImageFilter is simelar to itk's ResampleImageFilter, but in difference to the last * mentioned StitchImageFilter is able to resample multiple input images at once (with a transform * for each input image). If mutliple input images cover the output region a wighted sum of all * mapped input pixel values will be calculated. * It resamples images image through some coordinate * transform, interpolating via some image function. The class is templated * over the types of the input and output images. * * All other behaviors are simelar to itk::ResampleImageFilter. See the filter's description for * more details. */ template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType = double, typename TTransformPrecisionType = TInterpolatorPrecisionType> class StitchImageFilter : public ImageToImageFilter< TInputImage, TOutputImage > { public: /** Standard class typedefs. */ typedef StitchImageFilter Self; typedef ImageToImageFilter< TInputImage, TOutputImage > Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; typedef TInputImage InputImageType; typedef TOutputImage OutputImageType; typedef typename InputImageType::Pointer InputImagePointer; typedef typename InputImageType::ConstPointer InputImageConstPointer; typedef typename OutputImageType::Pointer OutputImagePointer; typedef typename InputImageType::RegionType InputImageRegionType; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(StitchImageFilter, ImageToImageFilter); /** Number of dimensions. */ itkStaticConstMacro(ImageDimension, unsigned int, TOutputImage::ImageDimension); itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension); /** base type for images of the current ImageDimension */ typedef ImageBase< itkGetStaticConstMacro(ImageDimension) > ImageBaseType; /** * Transform typedef. */ typedef Transform< TTransformPrecisionType, itkGetStaticConstMacro(ImageDimension), itkGetStaticConstMacro(ImageDimension) > TransformType; typedef typename TransformType::ConstPointer TransformPointerType; typedef DataObjectDecorator DecoratedTransformType; typedef typename DecoratedTransformType::Pointer DecoratedTransformPointer; /** Interpolator typedef. */ typedef InterpolateImageFunction< InputImageType, TInterpolatorPrecisionType > InterpolatorType; typedef typename InterpolatorType::Pointer InterpolatorPointerType; typedef typename InterpolatorType::OutputType InterpolatorOutputType; typedef DefaultConvertPixelTraits< InterpolatorOutputType > InterpolatorConvertType; typedef typename InterpolatorConvertType::ComponentType ComponentType; typedef LinearInterpolateImageFunction< InputImageType, TInterpolatorPrecisionType > LinearInterpolatorType; typedef typename LinearInterpolatorType::Pointer LinearInterpolatorPointerType; /** Image size typedef. */ typedef Size< itkGetStaticConstMacro(ImageDimension) > SizeType; /** Image index typedef. */ typedef typename TOutputImage::IndexType IndexType; /** Image point typedef. */ typedef typename InterpolatorType::PointType PointType; //typedef typename TOutputImage::PointType PointType; /** Image pixel value typedef. */ typedef typename TOutputImage::PixelType PixelType; typedef typename TInputImage::PixelType InputPixelType; typedef DefaultConvertPixelTraits PixelConvertType; typedef typename PixelConvertType::ComponentType PixelComponentType; /** Input pixel continuous index typdef */ typedef ContinuousIndex< TTransformPrecisionType, ImageDimension > ContinuousInputIndexType; /** Typedef to describe the output image region type. */ typedef typename TOutputImage::RegionType OutputImageRegionType; /** Image spacing,origin and direction typedef */ typedef typename TOutputImage::SpacingType SpacingType; typedef typename TOutputImage::PointType OriginPointType; typedef typename TOutputImage::DirectionType DirectionType; using Superclass::GetInput; /** Typedef the reference image type to be the ImageBase of the OutputImageType */ typedef ImageBase ReferenceImageBaseType; virtual void SetInput(unsigned int index, const InputImageType* image); /** Convinience methods that allows setting of input image and its transform in one call.*/ virtual void SetInput(unsigned int index, const InputImageType* image, const TransformType* transform); virtual void SetInput(unsigned int index, const InputImageType* image, const TransformType* transform, InterpolatorType* interpolator); const TransformType* GetTransform(unsigned int index) const; const InterpolatorType* GetInterpolator(unsigned int index) const; /** Get/Set the size of the output image. */ itkSetMacro(Size, SizeType); itkGetConstReferenceMacro(Size, SizeType); /** Get/Set the pixel value when a transformed pixel is outside of the * image. The default default pixel value is 0. */ itkSetMacro(DefaultPixelValue, PixelType); itkGetConstReferenceMacro(DefaultPixelValue, PixelType); /** Set the output image spacing. */ itkSetMacro(OutputSpacing, SpacingType); virtual void SetOutputSpacing(const double *values); /** Get the output image spacing. */ itkGetConstReferenceMacro(OutputSpacing, SpacingType); /** Set the output image origin. */ itkSetMacro(OutputOrigin, OriginPointType); virtual void SetOutputOrigin(const double *values); /** Get the output image origin. */ itkGetConstReferenceMacro(OutputOrigin, OriginPointType); /** Set the output direciton cosine matrix. */ itkSetMacro(OutputDirection, DirectionType); itkGetConstReferenceMacro(OutputDirection, DirectionType); /** Helper method to set the output parameters based on this image. */ void SetOutputParametersFromImage(const ImageBaseType *image); /** Set the start index of the output largest possible region. * The default is an index of all zeros. */ itkSetMacro(OutputStartIndex, IndexType); /** Get the start index of the output largest possible region. */ itkGetConstReferenceMacro(OutputStartIndex, IndexType); /** Set a reference image to use to define the output information. * By default, output information is specificed through the * SetOutputSpacing, Origin, and Direction methods. Alternatively, * this method can be used to specify an image from which to * copy the information. UseReferenceImageOn must be set to utilize the * reference image. */ itkSetInputMacro(ReferenceImage, ReferenceImageBaseType); /** Get the reference image that is defining the output information. */ itkGetInputMacro(ReferenceImage, ReferenceImageBaseType); /** Turn on/off whether a specified reference image should be used to define * the output information. */ itkSetMacro(UseReferenceImage, bool); itkBooleanMacro(UseReferenceImage); itkGetConstMacro(UseReferenceImage, bool); + itkSetMacro(StitchStrategy, StitchStrategy); + itkGetConstMacro(StitchStrategy, StitchStrategy); + /** StitchImageFilter produces an image which is a different size * than its input. As such, it needs to provide an implementation * for GenerateOutputInformation() in order to inform the pipeline * execution model. The original documentation of this method is * below. \sa ProcessObject::GenerateOutputInformaton() */ virtual void GenerateOutputInformation() ITK_OVERRIDE; /** StitchImageFilter needs a different input requested region than * the output requested region. As such, StitchImageFilter needs * to provide an implementation for GenerateInputRequestedRegion() * in order to inform the pipeline execution model. * \sa ProcessObject::GenerateInputRequestedRegion() */ virtual void GenerateInputRequestedRegion() ITK_OVERRIDE; /** Set up state of filter before multi-threading. * InterpolatorType::SetInputImage is not thread-safe and hence * has to be set up before ThreadedGenerateData */ virtual void BeforeThreadedGenerateData() ITK_OVERRIDE; /** Set the state of the filter after multi-threading. */ virtual void AfterThreadedGenerateData() ITK_OVERRIDE; /** Compute the Modified Time based on the changed components. */ ModifiedTimeType GetMTime(void) const ITK_OVERRIDE; #ifdef ITK_USE_CONCEPT_CHECKING // Begin concept checking itkConceptMacro( OutputHasNumericTraitsCheck, ( Concept::HasNumericTraits< PixelComponentType > ) ); // End concept checking #endif protected: StitchImageFilter(); ~StitchImageFilter() ITK_OVERRIDE {} void PrintSelf(std::ostream & os, Indent indent) const ITK_OVERRIDE; /** Override VeriyInputInformation() since this filter's inputs do * not need to occoupy the same physical space. * * \sa ProcessObject::VerifyInputInformation */ virtual void VerifyInputInformation() ITK_OVERRIDE { } /** StitchImageFilter can be implemented as a multithreaded filter. * Therefore, this implementation provides a ThreadedGenerateData() * routine which is called for each processing thread. The output * image data is allocated automatically by the superclass prior * to calling ThreadedGenerateData(). * ThreadedGenerateData can only write to the portion of the output image * specified by the parameter "outputRegionForThread" * \sa ImageToImageFilter::ThreadedGenerateData(), * ImageToImageFilter::GenerateData() */ virtual void ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType threadId) ITK_OVERRIDE; /** Cast pixel from interpolator output to PixelType. */ virtual PixelType CastPixelWithBoundsChecking( const InterpolatorOutputType value, const ComponentType minComponent, const ComponentType maxComponent) const; void SetTransform(unsigned int index, const TransformType* transform); /** Helper that ensures that a transform is specified for every input image. If a input image has no specified transforms, an identity transform will be created and set as default.*/ void EnsureTransforms(); /** Helper that ensures that an interpolator is specified for every input image. If a input image has no specified interpolator, a linear interpolator will be created and set as default.*/ void EnsureInterpolators(); static std::string GetTransformInputName(unsigned int index); private: ITK_DISALLOW_COPY_AND_ASSIGN(StitchImageFilter); typedef std::vector InputImageVectorType; typedef std::map TransformMapType; typedef std::map InterpolatorMapType; InputImageVectorType GetInputs(); TransformMapType GetTransforms(); InterpolatorMapType m_Interpolators; // Image function for // interpolation PixelType m_DefaultPixelValue; // default pixel value // if the point is // outside the image SizeType m_Size; // Size of the output image SpacingType m_OutputSpacing; // output image spacing OriginPointType m_OutputOrigin; // output image origin DirectionType m_OutputDirection; // output image direction cosines IndexType m_OutputStartIndex; // output image start index bool m_UseReferenceImage; - + StitchStrategy m_StitchStrategy; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkStitchImageFilter.tpp" #endif #endif diff --git a/Modules/MatchPointRegistration/include/itkStitchImageFilter.tpp b/Modules/MatchPointRegistration/include/itkStitchImageFilter.tpp index fa1ffaa4b4..fbff61d979 100644 --- a/Modules/MatchPointRegistration/include/itkStitchImageFilter.tpp +++ b/Modules/MatchPointRegistration/include/itkStitchImageFilter.tpp @@ -1,600 +1,628 @@ /*============================================================================ 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 itkStitchImageFilter_hxx #define itkStitchImageFilter_hxx #include "itkStitchImageFilter.h" #include "itkObjectFactory.h" #include "itkIdentityTransform.h" #include "itkProgressReporter.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkImageScanlineIterator.h" #include "itkSpecialCoordinatesImage.h" #include "itkDefaultConvertPixelTraits.h" #include "itkSimpleDataObjectDecorator.h" #include namespace itk { template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::StitchImageFilter() : m_OutputSpacing( 1.0 ), m_OutputOrigin( 0.0 ), - m_UseReferenceImage( false ) + m_UseReferenceImage( false ), + m_StitchStrategy(StitchStrategy::Mean) { m_Size.Fill( 0 ); m_OutputStartIndex.Fill( 0 ); m_OutputDirection.SetIdentity(); // Pipeline input configuration // implicit input index set: // #1 "ReferenceImage" optional Self::AddOptionalInputName("ReferenceImage"); m_DefaultPixelValue = NumericTraits::ZeroValue( m_DefaultPixelValue ); } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::SetInput(unsigned int index, const InputImageType* image) { this->SetInput(index, image, itk::IdentityTransform< TTransformPrecisionType, ImageDimension>::New().GetPointer(), LinearInterpolatorType::New().GetPointer()); } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::SetInput(unsigned int index, const InputImageType* image, const TransformType* transform) { this->SetInput(index, image, transform, LinearInterpolatorType::New().GetPointer()); } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::SetInput(unsigned int index, const InputImageType* image, const TransformType* transform, InterpolatorType* interpolator) { Superclass::SetInput(index, image); m_Interpolators[image] = interpolator; this->SetTransform(index, transform); } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::SetTransform(unsigned int index, const TransformType* transform) { const auto transformName = this->GetTransformInputName(index); typedef SimpleDataObjectDecorator< TransformPointerType > DecoratorType; const DecoratorType* oldInput = itkDynamicCastInDebugMode< const DecoratorType* >(this->ProcessObject::GetInput(transformName)); if (!oldInput || oldInput->Get() != transform) { typename DecoratorType::Pointer newInput = DecoratorType::New(); // Process object is not const-correct so the const_cast is required here newInput->Set(const_cast(transform)); this->ProcessObject::SetInput(transformName, newInput); } } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > const typename StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >::TransformType* StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::GetTransform(unsigned int index) const { typedef SimpleDataObjectDecorator< TransformPointerType > DecoratorType; const DecoratorType* input = itkDynamicCastInDebugMode< const DecoratorType* >(this->ProcessObject::GetInput(this->GetTransformInputName(index))); if (nullptr != input) { return input->Get(); } return nullptr; } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > const typename StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >::InterpolatorType* StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::GetInterpolator(unsigned int index) const { auto input = this->GetInput(index); if (m_Interpolators.find(input) != std::end(m_Interpolators)) { return m_Interpolators[input]; } return nullptr; } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::SetOutputSpacing(const double *spacing) { SpacingType s; for(unsigned int i = 0; i < TOutputImage::ImageDimension; ++i) { s[i] = static_cast< typename SpacingType::ValueType >(spacing[i]); } this->SetOutputSpacing(s); } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::SetOutputOrigin(const double *origin) { OriginPointType p(origin); this->SetOutputOrigin(p); } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::SetOutputParametersFromImage(const ImageBaseType *image) { this->SetOutputOrigin ( image->GetOrigin() ); this->SetOutputSpacing ( image->GetSpacing() ); this->SetOutputDirection ( image->GetDirection() ); this->SetOutputStartIndex ( image->GetLargestPossibleRegion().GetIndex() ); this->SetSize ( image->GetLargestPossibleRegion().GetSize() ); } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::BeforeThreadedGenerateData() { this->EnsureInterpolators(); this->EnsureTransforms(); for (const auto& interpolator : m_Interpolators) { interpolator.second->SetInputImage(interpolator.first); } unsigned int nComponents = DefaultConvertPixelTraits::GetNumberOfComponents( m_DefaultPixelValue ); if (nComponents == 0) { PixelComponentType zeroComponent = NumericTraits::ZeroValue( zeroComponent ); nComponents = this->GetInput()->GetNumberOfComponentsPerPixel(); NumericTraits::SetLength(m_DefaultPixelValue, nComponents ); for (unsigned int n=0; n void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::AfterThreadedGenerateData() { // Disconnect input image from the interpolator for (auto& interpolator : m_Interpolators) { interpolator.second->SetInputImage(ITK_NULLPTR); } } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType threadId) { if( outputRegionForThread.GetNumberOfPixels() == 0 ) { return; } // Get the output pointers OutputImageType* outputPtr = this->GetOutput(); - // Get this input pointers InputImageVectorType inputs = this->GetInputs(); TransformMapType transforms = this->GetTransforms(); + std::map lowerIndices; + std::map upperIndices; + for (const auto& input : inputs) + { + const auto largestRegion = input->GetLargestPossibleRegion(); + lowerIndices[input] = largestRegion.GetIndex(); + upperIndices[input] = largestRegion.GetUpperIndex(); + } + // Create an iterator that will walk the output region for this thread. - typedef ImageRegionIteratorWithIndex< TOutputImage > OutputIterator; + typedef ImageRegionIteratorWithIndex< OutputImageType > OutputIterator; OutputIterator outIt(outputPtr, outputRegionForThread); // Define a few indices that will be used to translate from an input pixel // to an output pixel PointType outputPoint; // Coordinates of current output pixel PointType inputPoint; // Coordinates of current input pixel ContinuousInputIndexType inputIndex; // Support for progress methods/callbacks ProgressReporter progress(this, threadId, outputRegionForThread.GetNumberOfPixels()); // Min/max values of the output pixel type AND these values // represented as the output type of the interpolator const PixelComponentType minValue = NumericTraits< PixelComponentType >::NonpositiveMin(); const PixelComponentType maxValue = NumericTraits< PixelComponentType >::max(); typedef typename InterpolatorType::OutputType OutputType; const ComponentType minOutputValue = static_cast(minValue); const ComponentType maxOutputValue = static_cast(maxValue); // Walk the output region outIt.GoToBegin(); while (!outIt.IsAtEnd()) { // Determine the index of the current output pixel outputPtr->TransformIndexToPhysicalPoint(outIt.GetIndex(), outputPoint); std::vector pixvals; + std::vector pixDistance; for (const auto& input : inputs) { // Compute corresponding input pixel position inputPoint = transforms[input]->TransformPoint(outputPoint); const bool isInsideInput = input->TransformPhysicalPointToContinuousIndex(inputPoint, inputIndex); // Evaluate input at right position and copy to the output if (m_Interpolators[input]->IsInsideBuffer(inputIndex) && isInsideInput) { OutputType value = m_Interpolators[input]->EvaluateAtContinuousIndex(inputIndex); pixvals.emplace_back(this->CastPixelWithBoundsChecking(value, minOutputValue, maxOutputValue)); + + ContinuousInputIndexType indexDistance; + const auto spacing = input->GetSpacing(); + + double minBorderDistance = std::numeric_limits::max(); + for (unsigned int i = 0; i < ImageDimension; ++i) + { + minBorderDistance = std::min(minBorderDistance, std::min(std::abs(lowerIndices[input][i] - inputIndex[i]) * spacing[i], std::abs(upperIndices[input][i] - inputIndex[i]) * spacing[i])); + } + pixDistance.emplace_back(minBorderDistance); } } if (!pixvals.empty()) - { //at least one input provided a value -> compute weighted sum - double sum = std::accumulate(pixvals.begin(), pixvals.end(), 0.0); - outIt.Set(sum / pixvals.size()); + { //at least one input provided a value + if (StitchStrategy::Mean == m_StitchStrategy) + { + double sum = std::accumulate(pixvals.begin(), pixvals.end(), 0.0); + outIt.Set(sum / pixvals.size()); + } + else + { + auto finding = std::max_element(pixDistance.begin(), pixDistance.end()); + outIt.Set(pixvals[std::distance(pixDistance.begin(), finding)]); + } } else { outIt.Set(m_DefaultPixelValue); // default background value } progress.CompletedPixel(); ++outIt; } } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > typename StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::PixelType StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::CastPixelWithBoundsChecking(const InterpolatorOutputType value, const ComponentType minComponent, const ComponentType maxComponent ) const { const unsigned int nComponents = InterpolatorConvertType::GetNumberOfComponents(value); PixelType outputValue; NumericTraits::SetLength( outputValue, nComponents ); for (unsigned int n = 0; n < nComponents; n++) { ComponentType component = InterpolatorConvertType::GetNthComponent( n, value ); if ( component < minComponent ) { PixelConvertType::SetNthComponent( n, outputValue, static_cast( minComponent ) ); } else if ( component > maxComponent ) { PixelConvertType::SetNthComponent( n, outputValue, static_cast( maxComponent ) ); } else { PixelConvertType::SetNthComponent(n, outputValue, static_cast( component ) ); } } return outputValue; } template typename StitchImageFilter::InputImageVectorType StitchImageFilter ::GetInputs() { InputImageVectorType inputs; for (unsigned int i = 0; i < this->GetNumberOfIndexedInputs(); ++i) { auto input = this->GetInput(i); if (nullptr != input) { inputs.push_back(input); } } return inputs; } template typename StitchImageFilter::TransformMapType StitchImageFilter ::GetTransforms() { TransformMapType transforms; for (unsigned int i = 0; i < this->GetNumberOfIndexedInputs(); ++i) { auto input = this->GetInput(i); auto transform = this->GetTransform(i); transforms[input] = transform; } return transforms; } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::GenerateInputRequestedRegion() { // Call the superclass' implementation of this method Superclass::GenerateInputRequestedRegion(); if ( !this->GetInput() ) { return; } // Get pointers to the input auto inputs = this->GetInputs(); for (auto& input : inputs) { InputImagePointer inputPtr = const_cast(input); // Determining the actual input region is non-trivial, especially // when we cannot assume anything about the transform being used. // So we do the easy thing and request the entire input image. // inputPtr->SetRequestedRegionToLargestPossibleRegion(); } } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::GenerateOutputInformation() { // Call the superclass' implementation of this method Superclass::GenerateOutputInformation(); // Get pointers to the input and output OutputImageType *outputPtr = this->GetOutput(); if ( !outputPtr ) { return; } const ReferenceImageBaseType *referenceImage = this->GetReferenceImage(); // Set the size of the output region if ( m_UseReferenceImage && referenceImage ) { outputPtr->SetLargestPossibleRegion( referenceImage->GetLargestPossibleRegion() ); } else { typename TOutputImage::RegionType outputLargestPossibleRegion; outputLargestPossibleRegion.SetSize(m_Size); outputLargestPossibleRegion.SetIndex(m_OutputStartIndex); outputPtr->SetLargestPossibleRegion(outputLargestPossibleRegion); } // Set spacing and origin if ( m_UseReferenceImage && referenceImage ) { outputPtr->SetSpacing( referenceImage->GetSpacing() ); outputPtr->SetOrigin( referenceImage->GetOrigin() ); outputPtr->SetDirection( referenceImage->GetDirection() ); } else { outputPtr->SetSpacing(m_OutputSpacing); outputPtr->SetOrigin(m_OutputOrigin); outputPtr->SetDirection(m_OutputDirection); } } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > ModifiedTimeType StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::GetMTime(void) const { ModifiedTimeType latestTime = Object::GetMTime(); for (const auto& interpolator : m_Interpolators) { if (interpolator.second.GetPointer()) { if (latestTime < interpolator.second->GetMTime()) { latestTime = interpolator.second->GetMTime(); } } } return latestTime; } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "DefaultPixelValue: " << static_cast< typename NumericTraits< PixelType >::PrintType > ( m_DefaultPixelValue ) << std::endl; os << indent << "Size: " << m_Size << std::endl; os << indent << "OutputStartIndex: " << m_OutputStartIndex << std::endl; os << indent << "OutputSpacing: " << m_OutputSpacing << std::endl; os << indent << "OutputOrigin: " << m_OutputOrigin << std::endl; os << indent << "OutputDirection: " << m_OutputDirection << std::endl; for (const auto& interpolator : m_Interpolators) { os << indent << "Interpolator: " << interpolator.second.GetPointer() << std::endl; } os << indent << "UseReferenceImage: " << ( m_UseReferenceImage ? "On" : "Off" ) << std::endl; } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::EnsureTransforms() { const auto inputCount = this->GetNumberOfIndexedInputs(); for (unsigned int i = 0; i < inputCount; ++i) { auto input = this->GetInput(i); if (nullptr == input) { itkExceptionMacro(<< "Nth input image is not set (n: " << i << ")."); } auto transform = this->GetTransform(i); if (nullptr == transform) { this->SetTransform(i, itk::IdentityTransform< TTransformPrecisionType, ImageDimension>::New().GetPointer()); } } } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::EnsureInterpolators() { const auto inputCount = this->GetNumberOfIndexedInputs(); InterpolatorMapType newInterpolatorMap; for (unsigned int i = 0; i < inputCount; ++i) { auto input = this->GetInput(i); if (nullptr == input) { itkExceptionMacro(<< "Nth input image is not set (n: " << i << ")."); } if (m_Interpolators[input].IsNull()) { newInterpolatorMap[input] = LinearInterpolatorType::New().GetPointer(); } else { newInterpolatorMap[input] = m_Interpolators[input]; } } m_Interpolators = newInterpolatorMap; } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > std::string StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::GetTransformInputName(unsigned int index) { return "transform_" + std::to_string(index); } } // end namespace itk #endif diff --git a/Modules/MatchPointRegistration/include/mitkImageStitchingHelper.h b/Modules/MatchPointRegistration/include/mitkImageStitchingHelper.h index f82f0fdad0..4e84acadfd 100644 --- a/Modules/MatchPointRegistration/include/mitkImageStitchingHelper.h +++ b/Modules/MatchPointRegistration/include/mitkImageStitchingHelper.h @@ -1,59 +1,61 @@ /*============================================================================ 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 MITK_IMAGE_STITCHING_HELPER_H #define MITK_IMAGE_STITCHING_HELPER_H #include "mapRegistrationBase.h" #include "mitkImage.h" #include "mitkGeometry3D.h" #include "mitkMAPRegistrationWrapper.h" #include "mitkImageMappingHelper.h" +#include + #include "MitkMatchPointRegistrationExports.h" namespace mitk { /**Helper that maps a given input image * @param input Image that should be mapped. * @param registration Pointer to the registration instance that should be used for mapping * @param resultGeometry Pointer to the Geometry object that specifies the grid of the result image. If not defined the geometry of the input image will be used. * @param paddingValue Indicates the value that should be used if an out of input error occurs (and throwOnOutOfInputAreaError is false). * @param interpolatorType Indicates the type of interpolation strategy that should be used. * @pre input must be valid * @pre registration must be valid * @pre Dimensionality of the registration must match with the input imageinput must be valid * @remark Depending in the settings of throwOnOutOfInputAreaError and throwOnMappingError it may also throw * due to inconsistencies in the mapping process. See parameter description. * @result Pointer to the resulting mapped image.h*/ MITKMATCHPOINTREGISTRATION_EXPORT Image::Pointer StitchImages(std::vector inputs, std::vector<::map::core::RegistrationBase::ConstPointer> registrations, const BaseGeometry* resultGeometry, - const double& paddingValue = 0, + const double& paddingValue = 0, itk::StitchStrategy stitchStrategy = itk::StitchStrategy::Mean, mitk::ImageMappingInterpolator::Type interpolatorType = mitk::ImageMappingInterpolator::Linear); MITKMATCHPOINTREGISTRATION_EXPORT Image::Pointer StitchImages(std::vector inputs, std::vector registrations, const BaseGeometry* resultGeometry, - const double& paddingValue = 0, + const double& paddingValue = 0, itk::StitchStrategy stitchStrategy = itk::StitchStrategy::Mean, mitk::ImageMappingInterpolator::Type interpolatorType = mitk::ImageMappingInterpolator::Linear); MITKMATCHPOINTREGISTRATION_EXPORT Image::Pointer StitchImages(std::vector inputs, const BaseGeometry* resultGeometry, - const double& paddingValue = 0, + const double& paddingValue = 0, itk::StitchStrategy stitchStrategy = itk::StitchStrategy::Mean, mitk::ImageMappingInterpolator::Type interpolatorType = mitk::ImageMappingInterpolator::Linear); } #endif diff --git a/Modules/MatchPointRegistration/src/Helper/mitkImageStitchingHelper.cpp b/Modules/MatchPointRegistration/src/Helper/mitkImageStitchingHelper.cpp index eaedfee5e4..b2b139e8ae 100644 --- a/Modules/MatchPointRegistration/src/Helper/mitkImageStitchingHelper.cpp +++ b/Modules/MatchPointRegistration/src/Helper/mitkImageStitchingHelper.cpp @@ -1,212 +1,229 @@ /*============================================================================ 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 "mitkImageStitchingHelper.h" #include #include #include #include #include -#include #include #include #include #include #include #include "mapRegistration.h" #include "mitkRegistrationHelper.h" template typename ::itk::InterpolateImageFunction< TImage >::Pointer generateInterpolator(mitk::ImageMappingInterpolator::Type interpolatorType) { typedef ::itk::InterpolateImageFunction< TImage > BaseInterpolatorType; typename BaseInterpolatorType::Pointer result; switch (interpolatorType) { case mitk::ImageMappingInterpolator::NearestNeighbor: { result = ::itk::NearestNeighborInterpolateImageFunction::New(); break; } case mitk::ImageMappingInterpolator::BSpline_3: { typename ::itk::BSplineInterpolateImageFunction::Pointer spInterpolator = ::itk::BSplineInterpolateImageFunction::New(); spInterpolator->SetSplineOrder(3); result = spInterpolator; break; } case mitk::ImageMappingInterpolator::WSinc_Hamming: { result = ::itk::WindowedSincInterpolateImageFunction::New(); break; } case mitk::ImageMappingInterpolator::WSinc_Welch: { result = ::itk::WindowedSincInterpolateImageFunction >::New(); break; } default: { result = ::itk::LinearInterpolateImageFunction::New(); break; } } return result; }; template void doMITKStitching(const ::itk::Image* input1, mitk::Image::Pointer& result, std::vector inputs, std::vector<::map::core::RegistrationBase::ConstPointer> registrations, const mitk::BaseGeometry* resultGeometry, - const double& paddingValue, mitk::ImageMappingInterpolator::Type interpolatorType) + const double& paddingValue, itk::StitchStrategy stitchStrategy, mitk::ImageMappingInterpolator::Type interpolatorType) { using ConcreteRegistrationType = ::map::core::Registration; using ItkImageType = itk::Image; using StitchingFilterType = ::itk::StitchImageFilter; auto stitcher = StitchingFilterType::New(); stitcher->SetDefaultPixelValue(paddingValue); - stitcher->SetOutputSpacing(resultGeometry->GetSpacing()); stitcher->SetOutputOrigin(resultGeometry->GetOrigin()); - stitcher->SetOutputDirection(resultGeometry->GetIndexToWorldTransform()->GetMatrix()); + + const auto spacing = resultGeometry->GetSpacing(); + stitcher->SetOutputSpacing(spacing); + + StitchingFilterType::DirectionType itkDirection; + const auto mitkDirection = resultGeometry->GetIndexToWorldTransform()->GetMatrix(); + for (unsigned int i = 0; i < VImageDimension; ++i) + { + for (unsigned int j = 0; j < VImageDimension; ++j) + { + itkDirection[i][j] = mitkDirection[i][j] / spacing[j]; + } + } + stitcher->SetOutputDirection(itkDirection); + ItkImageType::SizeType size; size[0] = resultGeometry->GetExtent(0); size[1] = resultGeometry->GetExtent(1); size[2] = resultGeometry->GetExtent(2); stitcher->SetSize(size); + stitcher->SetNumberOfThreads(1); + stitcher->SetStitchStrategy(stitchStrategy); auto inputIter = inputs.begin(); auto regIter = registrations.begin(); unsigned int index = 0; while (inputIter != inputs.end()) { auto itkInput = mitk::ImageToItkImage(*inputIter); auto castedReg = dynamic_cast(regIter->GetPointer()); auto kernel = dynamic_cast* >(&(castedReg->getInverseMapping())); if (nullptr == kernel) { mitkThrow() << "Cannot stitch images. At least passed registration object #"<SetInput(index, itkInput, kernel->getTransformModel(), generateInterpolator< ::itk::Image >(interpolatorType)); + ++inputIter; + ++regIter; + ++index; } stitcher->Update(); mitk::CastToMitkImage<>(stitcher->GetOutput(),result); } mitk::Image::Pointer mitk::StitchImages(std::vector inputs, std::vector<::map::core::RegistrationBase::ConstPointer> registrations, const BaseGeometry* resultGeometry, - const double& paddingValue, + const double& paddingValue, itk::StitchStrategy stitchStrategy, mitk::ImageMappingInterpolator::Type interpolatorType) { if (inputs.size() != registrations.size()) { mitkThrow() << "Cannot stitch images. Passed inputs vector and registrations vector have different sizes."; } if (inputs.empty()) { mitkThrow() << "Cannot stitch images. No input images are defined."; } auto inputDim = inputs.front()->GetDimension(); auto inputPixelType = inputs.front()->GetPixelType(); for (const auto& input : inputs) { if (input->GetDimension() != inputDim) { mitkThrow() << "Cannot stitch images. Images have different dimensions. Dimeonsion of first input: " << inputDim << "; wrong dimension: " << input->GetDimension(); } if (input->GetPixelType() != inputPixelType) { mitkThrow() << "Cannot stitch images. Input images have different pixeltypes. The current implementation does only support stitching of images with same pixel type. Dimeonsion of first input: " << inputPixelType.GetTypeAsString() << "; wrong dimension: " << input->GetPixelType().GetTypeAsString(); } if (input->GetTimeSteps() > 1) { mitkThrow() << "Cannot stitch dynamic images. At least one input image has multiple time steps."; } } for (const auto& reg : registrations) { if (reg->getMovingDimensions() != inputDim) { mitkThrow() << "Cannot stitch images. At least one registration has a different moving dimension then the inputs. Dimeonsion of inputs: " << inputDim << "; wrong dimension: " << reg->getMovingDimensions(); } if (reg->getTargetDimensions() != inputDim) { mitkThrow() << "Cannot stitch images. At least one registration has a different target dimension then the inputs. Dimeonsion of inputs: " << inputDim << "; wrong dimension: " << reg->getTargetDimensions(); } } Image::Pointer result; - AccessFixedDimensionByItk_n(inputs.front(), doMITKStitching, 3, (result, inputs, registrations, resultGeometry, paddingValue, interpolatorType)); + AccessFixedDimensionByItk_n(inputs.front(), doMITKStitching, 3, (result, inputs, registrations, resultGeometry, paddingValue, stitchStrategy, interpolatorType)); return result; } mitk::Image::Pointer mitk::StitchImages(std::vector inputs, std::vector registrations, const BaseGeometry* resultGeometry, - const double& paddingValue, + const double& paddingValue, itk::StitchStrategy stitchStrategy, mitk::ImageMappingInterpolator::Type interpolatorType) { std::vector<::map::core::RegistrationBase::ConstPointer> unwrappedRegs; for (const auto& reg : registrations) { if (!reg) { mitkThrow() << "Cannot stitch images. At least one passed registration wrapper pointer is nullptr."; } unwrappedRegs.push_back(reg->GetRegistration()); } - Image::Pointer result = StitchImages(inputs, unwrappedRegs, resultGeometry, paddingValue, interpolatorType); + Image::Pointer result = StitchImages(inputs, unwrappedRegs, resultGeometry, paddingValue, stitchStrategy, interpolatorType); return result; } mitk::Image::Pointer mitk::StitchImages(std::vector inputs, const BaseGeometry* resultGeometry, - const double& paddingValue, + const double& paddingValue, itk::StitchStrategy stitchStrategy, mitk::ImageMappingInterpolator::Type interpolatorType) { auto defaultReg = GenerateIdentityRegistration3D(); std::vector<::map::core::RegistrationBase::ConstPointer> defaultRegs; defaultRegs.resize(inputs.size()); std::fill(defaultRegs.begin(), defaultRegs.end(), defaultReg->GetRegistration()); - Image::Pointer result = StitchImages(inputs, defaultRegs, resultGeometry, paddingValue, interpolatorType); + Image::Pointer result = StitchImages(inputs, defaultRegs, resultGeometry, paddingValue, stitchStrategy, interpolatorType); return result; }