/*========================================================================= * * 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 itkWindowedSincInterpolateImageFunction_hxx #define itkWindowedSincInterpolateImageFunction_hxx #include "itkMath.h" namespace itk { // Constant definitions for functions namespace Function { template const double CosineWindowFunction::m_Factor = itk::Math::pi / (2 * VRadius); template const double HammingWindowFunction::m_Factor = itk::Math::pi / VRadius; template const double WelchWindowFunction::m_Factor = 1.0 / (VRadius * VRadius); template const double LanczosWindowFunction::m_Factor = itk::Math::pi / VRadius; template const double BlackmanWindowFunction::m_Factor1 = itk::Math::pi / VRadius; template const double BlackmanWindowFunction::m_Factor2 = 2.0 * itk::Math::pi / VRadius; } // end namespace Function template void WindowedSincInterpolateImageFunction:: SetInputImage(const ImageType * image) { // Call the parent implementation Superclass::SetInputImage(image); if (image == nullptr) { return; } // Set the radius for the neighborhood Size radius; radius.Fill(VRadius); // Initialize the neighborhood IteratorType it(radius, image, image->GetBufferedRegion()); // Compute the offset tables (we ignore all the zero indices // in the neighborhood) unsigned int iOffset = 0; int empty = VRadius; for (unsigned int iPos = 0; iPos < it.Size(); ++iPos) { // Get the offset (index) typename IteratorType::OffsetType off = it.GetOffset(iPos); // Check if the offset has zero weights bool nonzero = true; for (unsigned int dim = 0; dim < ImageDimension; ++dim) { if (off[dim] == -empty) { nonzero = false; break; } } // Only use offsets with non-zero indices if (nonzero) { // Set the offset index m_OffsetTable[iOffset] = iPos; // Set the weight table indices for (unsigned int dim = 0; dim < ImageDimension; ++dim) { m_WeightOffsetTable[iOffset][dim] = off[dim] + VRadius - 1; } // Increment the index ++iOffset; } } } template void WindowedSincInterpolateImageFunction::PrintSelf( std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "OffsetTable: " << m_OffsetTable << std::endl; os << indent << "WeightOffsetTable: " << m_WeightOffsetTable << std::endl; } template auto WindowedSincInterpolateImageFunction:: EvaluateAtContinuousIndex(const ContinuousIndexType & index) const -> OutputType { IndexType baseIndex; double distance[ImageDimension]; // Compute the integer index based on the continuous one by // 'flooring' the index for (unsigned int dim = 0; dim < ImageDimension; ++dim) { baseIndex[dim] = Math::Floor(index[dim]); distance[dim] = index[dim] - static_cast(baseIndex[dim]); } // Position the neighborhood at the index of interest Size radius; radius.Fill(VRadius); IteratorType nit(radius, this->GetInputImage(), this->GetInputImage()->GetBufferedRegion()); nit.SetLocation(baseIndex); // Compute the sinc function for each dimension double xWeight[ImageDimension][2 * VRadius]; for (unsigned int dim = 0; dim < ImageDimension; ++dim) { // x is the offset, hence the parameter of the kernel double x = distance[dim] + VRadius; // If distance is zero, i.e. the index falls precisely on the // pixel boundary, the weights form a delta function. if (distance[dim] == 0.0) { for (unsigned int i = 0; i < m_WindowSize; ++i) { xWeight[dim][i] = static_cast(i) == VRadius - 1 ? 1 : 0; } } else { // i is the relative offset in dimension dim. for (unsigned int i = 0; i < m_WindowSize; ++i) { // Increment the offset, taking it through the range // (dist + rad - 1, ..., dist - rad), i.e. all x // such that itk::Math::abs(x) <= rad x -= 1.0; // Compute the weight for this m xWeight[dim][i] = m_WindowFunction(x) * Sinc(x); } } } // Iterate over the neighborhood, taking the correct set // of weights in each dimension using PixelType = typename NumericTraits::RealType; PixelType xPixelValue{}; for (unsigned int j = 0; j < m_OffsetTableSize; ++j) { // Get the offset for this neighbor unsigned int off = m_OffsetTable[j]; // Get the intensity value at the pixel PixelType xVal = nit.GetPixel(off); // Multiply the intensity by each of the weights. Gotta hope // that the compiler will unwrap this loop and pipeline this! for (unsigned int dim = 0; dim < ImageDimension; ++dim) { xVal *= xWeight[dim][m_WeightOffsetTable[j][dim]]; } // Increment the pixel value xPixelValue += xVal; } // Return the interpolated value return static_cast(xPixelValue); } } // namespace itk #endif