/*========================================================================= * * Copyright NumFOCUS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef itkLinearInterpolateImageFunction_h #define itkLinearInterpolateImageFunction_h #include "itkInterpolateImageFunction.h" #include "itkVariableLengthVector.h" #include // For max. namespace itk { /** * \class LinearInterpolateImageFunction * \brief Linearly interpolate an image at specified positions. * * LinearInterpolateImageFunction linearly interpolates image intensity at * a non-integer pixel position. This class is templated * over the input image type and the coordinate representation type * (e.g. float or double). * * This function works for N-dimensional images. * * This function works for images with scalar and vector pixel * types, and for images of type VectorImage. * * \sa VectorLinearInterpolateImageFunction * * \ingroup ImageFunctions ImageInterpolators * \ingroup ITKImageFunction * * \sphinx * \sphinxexample{Core/ImageFunction/LinearlyInterpolatePositionInImage,Linearly Interpolate Position In Image} * \endsphinx */ template class ITK_TEMPLATE_EXPORT LinearInterpolateImageFunction : public InterpolateImageFunction { public: ITK_DISALLOW_COPY_AND_MOVE(LinearInterpolateImageFunction); /** Standard class type aliases. */ using Self = LinearInterpolateImageFunction; using Superclass = InterpolateImageFunction; using Pointer = SmartPointer; using ConstPointer = SmartPointer; /** \see LightObject::GetNameOfClass() */ itkOverrideGetNameOfClassMacro(LinearInterpolateImageFunction); /** Method for creation through the object factory. */ itkNewMacro(Self); /** OutputType type alias support */ using typename Superclass::OutputType; /** InputImageType type alias support */ using typename Superclass::InputImageType; /** InputPixelType type alias support */ using typename Superclass::InputPixelType; /** RealType type alias support */ using typename Superclass::RealType; /** Dimension underlying input image. */ static constexpr unsigned int ImageDimension = Superclass::ImageDimension; /** Index type alias support */ using typename Superclass::IndexType; /** Size type alias support */ using typename Superclass::SizeType; /** ContinuousIndex type alias support */ using typename Superclass::ContinuousIndexType; using InternalComputationType = typename ContinuousIndexType::ValueType; /** Evaluate the function at a ContinuousIndex position * * Returns the linearly interpolated image intensity at a * specified point position. No bounds checking is done. * The point is assume to lie within the image buffer. * * ImageFunction::IsInsideBuffer() can be used to check bounds before * calling the method. */ OutputType EvaluateAtContinuousIndex(const ContinuousIndexType & index) const override { return this->EvaluateOptimized(Dispatch(), index); } SizeType GetRadius() const override { return SizeType::Filled(1); } protected: LinearInterpolateImageFunction() = default; ~LinearInterpolateImageFunction() override = default; void PrintSelf(std::ostream & os, Indent indent) const override; private: struct DispatchBase {}; template struct Dispatch : public DispatchBase {}; inline OutputType EvaluateOptimized(const Dispatch<0> &, const ContinuousIndexType &) const { return 0; } inline OutputType EvaluateOptimized(const Dispatch<1> &, const ContinuousIndexType & index) const { IndexType basei; basei[0] = std::max(Math::Floor(index[0]), this->m_StartIndex[0]); const InternalComputationType distance = index[0] - static_cast(basei[0]); const TInputImage * const inputImagePtr = this->GetInputImage(); const RealType & val0 = inputImagePtr->GetPixel(basei); if (distance <= 0.) { return (static_cast(val0)); } ++basei[0]; if (basei[0] > this->m_EndIndex[0]) { return (static_cast(val0)); } const RealType & val1 = inputImagePtr->GetPixel(basei); return (static_cast(val0 + (val1 - val0) * distance)); } inline OutputType EvaluateOptimized(const Dispatch<2> &, const ContinuousIndexType & index) const { IndexType basei; basei[0] = std::max(Math::Floor(index[0]), this->m_StartIndex[0]); const InternalComputationType distance0 = index[0] - static_cast(basei[0]); basei[1] = std::max(Math::Floor(index[1]), this->m_StartIndex[1]); const InternalComputationType distance1 = index[1] - static_cast(basei[1]); const TInputImage * const inputImagePtr = this->GetInputImage(); const RealType & val00 = inputImagePtr->GetPixel(basei); if (distance0 <= 0. && distance1 <= 0.) { return (static_cast(val00)); } else if (distance1 <= 0.) // if they have the same "y" { ++basei[0]; // then interpolate across "x" if (basei[0] > this->m_EndIndex[0]) { return (static_cast(val00)); } const RealType & val10 = inputImagePtr->GetPixel(basei); return (static_cast(val00 + (val10 - val00) * distance0)); } else if (distance0 <= 0.) // if they have the same "x" { ++basei[1]; // then interpolate across "y" if (basei[1] > this->m_EndIndex[1]) { return (static_cast(val00)); } const RealType & val01 = inputImagePtr->GetPixel(basei); return (static_cast(val00 + (val01 - val00) * distance1)); } // fall-through case: // interpolate across "xy" ++basei[0]; if (basei[0] > this->m_EndIndex[0]) // interpolate across "y" { --basei[0]; ++basei[1]; if (basei[1] > this->m_EndIndex[1]) { return (static_cast(val00)); } const RealType & val01 = inputImagePtr->GetPixel(basei); return (static_cast(val00 + (val01 - val00) * distance1)); } const RealType & val10 = inputImagePtr->GetPixel(basei); const RealType & valx0 = val00 + (val10 - val00) * distance0; ++basei[1]; if (basei[1] > this->m_EndIndex[1]) // interpolate across "x" { return (static_cast(valx0)); } const RealType & val11 = inputImagePtr->GetPixel(basei); --basei[0]; const RealType & val01 = inputImagePtr->GetPixel(basei); const RealType & valx1 = val01 + (val11 - val01) * distance0; return (static_cast(valx0 + (valx1 - valx0) * distance1)); } inline OutputType EvaluateOptimized(const Dispatch<3> &, const ContinuousIndexType & index) const { IndexType basei; basei[0] = std::max(Math::Floor(index[0]), this->m_StartIndex[0]); const InternalComputationType distance0 = index[0] - static_cast(basei[0]); basei[1] = std::max(Math::Floor(index[1]), this->m_StartIndex[1]); const InternalComputationType distance1 = index[1] - static_cast(basei[1]); basei[2] = std::max(Math::Floor(index[2]), this->m_StartIndex[2]); const InternalComputationType distance2 = index[2] - static_cast(basei[2]); const TInputImage * const inputImagePtr = this->GetInputImage(); const RealType & val000 = inputImagePtr->GetPixel(basei); if (distance0 <= 0. && distance1 <= 0. && distance2 <= 0.) { return (static_cast(val000)); } if (distance2 <= 0.) { if (distance1 <= 0.) // interpolate across "x" { ++basei[0]; if (basei[0] > this->m_EndIndex[0]) { return (static_cast(val000)); } const RealType & val100 = inputImagePtr->GetPixel(basei); return static_cast(val000 + (val100 - val000) * distance0); } else if (distance0 <= 0.) // interpolate across "y" { ++basei[1]; if (basei[1] > this->m_EndIndex[1]) { return (static_cast(val000)); } const RealType & val010 = inputImagePtr->GetPixel(basei); return static_cast(val000 + (val010 - val000) * distance1); } else // interpolate across "xy" { ++basei[0]; if (basei[0] > this->m_EndIndex[0]) // interpolate across "y" { --basei[0]; ++basei[1]; if (basei[1] > this->m_EndIndex[1]) { return (static_cast(val000)); } const RealType & val010 = inputImagePtr->GetPixel(basei); return static_cast(val000 + (val010 - val000) * distance1); } const RealType & val100 = inputImagePtr->GetPixel(basei); const RealType & valx00 = val000 + (val100 - val000) * distance0; ++basei[1]; if (basei[1] > this->m_EndIndex[1]) // interpolate across "x" { return (static_cast(valx00)); } const RealType & val110 = inputImagePtr->GetPixel(basei); --basei[0]; const RealType & val010 = inputImagePtr->GetPixel(basei); const RealType & valx10 = val010 + (val110 - val010) * distance0; return static_cast(valx00 + (valx10 - valx00) * distance1); } } else { if (distance1 <= 0.) { if (distance0 <= 0.) // interpolate across "z" { ++basei[2]; if (basei[2] > this->m_EndIndex[2]) { return (static_cast(val000)); } const RealType & val001 = inputImagePtr->GetPixel(basei); return static_cast(val000 + (val001 - val000) * distance2); } else // interpolate across "xz" { ++basei[0]; if (basei[0] > this->m_EndIndex[0]) // interpolate across "z" { --basei[0]; ++basei[2]; if (basei[2] > this->m_EndIndex[2]) { return (static_cast(val000)); } const RealType & val001 = inputImagePtr->GetPixel(basei); return static_cast(val000 + (val001 - val000) * distance2); } const RealType & val100 = inputImagePtr->GetPixel(basei); const RealType & valx00 = val000 + (val100 - val000) * distance0; ++basei[2]; if (basei[2] > this->m_EndIndex[2]) // interpolate across "x" { return (static_cast(valx00)); } const RealType & val101 = inputImagePtr->GetPixel(basei); --basei[0]; const RealType & val001 = inputImagePtr->GetPixel(basei); const RealType & valx01 = val001 + (val101 - val001) * distance0; return static_cast(valx00 + (valx01 - valx00) * distance2); } } else if (distance0 <= 0.) // interpolate across "yz" { ++basei[1]; if (basei[1] > this->m_EndIndex[1]) // interpolate across "z" { --basei[1]; ++basei[2]; if (basei[2] > this->m_EndIndex[2]) { return (static_cast(val000)); } const RealType & val001 = inputImagePtr->GetPixel(basei); return static_cast(val000 + (val001 - val000) * distance2); } const RealType & val010 = inputImagePtr->GetPixel(basei); const RealType & val0x0 = val000 + (val010 - val000) * distance1; ++basei[2]; if (basei[2] > this->m_EndIndex[2]) // interpolate across "y" { return (static_cast(val0x0)); } const RealType & val011 = inputImagePtr->GetPixel(basei); --basei[1]; const RealType & val001 = inputImagePtr->GetPixel(basei); const RealType & val0x1 = val001 + (val011 - val001) * distance1; return static_cast(val0x0 + (val0x1 - val0x0) * distance2); } else // interpolate across "xyz" { ++basei[0]; if (basei[0] > this->m_EndIndex[0]) // interpolate across "yz" { --basei[0]; ++basei[1]; if (basei[1] > this->m_EndIndex[1]) // interpolate across "z" { --basei[1]; ++basei[2]; if (basei[2] > this->m_EndIndex[2]) { return (static_cast(val000)); } const RealType & val001 = inputImagePtr->GetPixel(basei); return static_cast(val000 + (val001 - val000) * distance2); } const RealType & val010 = inputImagePtr->GetPixel(basei); const RealType & val0x0 = val000 + (val010 - val000) * distance1; ++basei[2]; if (basei[2] > this->m_EndIndex[2]) // interpolate across "y" { return (static_cast(val0x0)); } const RealType & val011 = inputImagePtr->GetPixel(basei); --basei[1]; const RealType & val001 = inputImagePtr->GetPixel(basei); const RealType & val0x1 = val001 + (val011 - val001) * distance1; return static_cast(val0x0 + (val0x1 - val0x0) * distance2); } const RealType & val100 = inputImagePtr->GetPixel(basei); const RealType & valx00 = val000 + (val100 - val000) * distance0; ++basei[1]; if (basei[1] > this->m_EndIndex[1]) // interpolate across "xz" { --basei[1]; ++basei[2]; if (basei[2] > this->m_EndIndex[2]) // interpolate across "x" { return (static_cast(valx00)); } const RealType & val101 = inputImagePtr->GetPixel(basei); --basei[0]; const RealType & val001 = inputImagePtr->GetPixel(basei); const RealType & valx01 = val001 + (val101 - val001) * distance0; return static_cast(valx00 + (valx01 - valx00) * distance2); } const RealType & val110 = inputImagePtr->GetPixel(basei); --basei[0]; const RealType & val010 = inputImagePtr->GetPixel(basei); const RealType & valx10 = val010 + (val110 - val010) * distance0; const RealType & valxx0 = valx00 + (valx10 - valx00) * distance1; ++basei[2]; if (basei[2] > this->m_EndIndex[2]) // interpolate across "xy" { return (static_cast(valxx0)); } const RealType & val011 = inputImagePtr->GetPixel(basei); ++basei[0]; const RealType & val111 = inputImagePtr->GetPixel(basei); --basei[1]; const RealType & val101 = inputImagePtr->GetPixel(basei); --basei[0]; const RealType & val001 = inputImagePtr->GetPixel(basei); const RealType & valx01 = val001 + (val101 - val001) * distance0; const RealType & valx11 = val011 + (val111 - val011) * distance0; const RealType & valxx1 = valx01 + (valx11 - valx01) * distance1; return (static_cast(valxx0 + (valxx1 - valxx0) * distance2)); } } } inline OutputType EvaluateOptimized(const DispatchBase &, const ContinuousIndexType & index) const { return this->EvaluateUnoptimized(index); } /** Evaluate interpolator at image index position. */ virtual inline OutputType EvaluateUnoptimized(const ContinuousIndexType & index) const; /** \brief A method to generically set all components to zero */ template void MakeZeroInitializer(const TInputImage * const inputImagePtr, VariableLengthVector & tempZeros) const { // Variable length vector version to get the size of the pixel correct. constexpr typename TInputImage::IndexType idx = { { 0 } }; const typename TInputImage::PixelType & tempPixel = inputImagePtr->GetPixel(idx); const unsigned int sizeOfVarLengthVector = tempPixel.GetSize(); tempZeros.SetSize(sizeOfVarLengthVector); tempZeros.Fill(RealTypeScalarRealType{}); } template void MakeZeroInitializer(const TInputImage * const itkNotUsed(inputImagePtr), RealTypeScalarRealType & tempZeros) const { // All other cases tempZeros = RealTypeScalarRealType{}; } }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION # include "itkLinearInterpolateImageFunction.hxx" #endif #endif