/* * Copyright (C) 2005-2019 Centre National d'Etudes Spatiales (CNES) * * This file is part of Orfeo Toolbox * * https://www.orfeo-toolbox.org/ * * 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 * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 otbGenericInterpolateImageFunction_hxx #define otbGenericInterpolateImageFunction_hxx #include "otbGenericInterpolateImageFunction.h" #include "vnl/vnl_math.h" namespace otb { /** Constructor */ template GenericInterpolateImageFunction::GenericInterpolateImageFunction() { m_WindowSize = 1; this->SetRadius(1); m_OffsetTable = nullptr; m_WeightOffsetTable = nullptr; m_TablesHaveBeenGenerated = false; m_NormalizeWeight = false; } /** Destructor */ template GenericInterpolateImageFunction::~GenericInterpolateImageFunction() { this->ResetOffsetTable(); } /** Delete every tables. */ template void GenericInterpolateImageFunction::ResetOffsetTable() { // Clear the offset table if (m_OffsetTable != nullptr) { delete[] m_OffsetTable; m_OffsetTable = nullptr; } // Clear the weights tales if (m_WeightOffsetTable != nullptr) { for (unsigned int i = 0; i < m_OffsetTableSize; ++i) { delete[] m_WeightOffsetTable[i]; } delete[] m_WeightOffsetTable; m_WeightOffsetTable = nullptr; } } template void GenericInterpolateImageFunction::SetRadius(unsigned int rad) { // m_Radius = rad; this->GetFunction().SetRadius(rad); m_WindowSize = rad << 1; this->Modified(); } template void GenericInterpolateImageFunction::Modified() const { Superclass::Modified(); m_TablesHaveBeenGenerated = false; } /** Initialize used tables*/ template void GenericInterpolateImageFunction::InitializeTables() { // Compute the offset table size m_OffsetTableSize = 1; for (unsigned dim = 0; dim < ImageDimension; ++dim) { m_OffsetTableSize *= m_WindowSize; } // Allocate the offset table m_OffsetTable = new unsigned int[m_OffsetTableSize]; // Allocate the weights tables m_WeightOffsetTable = new unsigned int*[m_OffsetTableSize]; for (unsigned int i = 0; i < m_OffsetTableSize; ++i) { m_WeightOffsetTable[i] = new unsigned int[ImageDimension]; } } /** Fill the weight offset table*/ template void GenericInterpolateImageFunction::FillWeightOffsetTable() { // Initialize the neighborhood SizeType radius; radius.Fill(this->GetRadius()); if (this->GetInputImage() != nullptr) { IteratorType it = IteratorType(radius, this->GetInputImage(), this->GetInputImage()->GetBufferedRegion()); // Compute the offset tables (we ignore all the zero indices // in the neighborhood) unsigned int iOffset = 0; int empty = static_cast(this->GetRadius()); 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] + this->GetRadius() - 1; } // Increment the index iOffset++; } } } else { itkExceptionMacro(<< "An input has to be set"); } } /** Initialize tables: need to be call explicitly */ template void GenericInterpolateImageFunction::Initialize() { // Delete existing tables this->ResetOffsetTable(); // Tables initialization this->InitializeTables(); // fill the weight table this->FillWeightOffsetTable(); m_TablesHaveBeenGenerated = true; } /** Evaluate at image index position */ template typename GenericInterpolateImageFunction::OutputType GenericInterpolateImageFunction::EvaluateAtContinuousIndex(const ContinuousIndexType& index) const { if (!m_TablesHaveBeenGenerated) { itkExceptionMacro(<< "The Interpolation functor need to be explicitly intanciated with the method Initialize()"); } // unsigned int dim; 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) { // The following "if" block is equivalent to the following line without // having to call floor. // baseIndex[dim] = (long) std::floor(index[dim] ); if (index[dim] >= 0.0) { baseIndex[dim] = (long)index[dim]; } else { long tIndex = (long)index[dim]; if (double(tIndex) != index[dim]) { tIndex--; } baseIndex[dim] = tIndex; } distance[dim] = index[dim] - double(baseIndex[dim]); } // Position the neighborhood at the index of interest SizeType radius; radius.Fill(this->GetRadius()); IteratorType nit = IteratorType(radius, this->GetInputImage(), this->GetInputImage()->GetBufferedRegion()); nit.SetLocation(baseIndex); const unsigned int twiceRadius = static_cast(2 * this->GetRadius()); /* double xWeight[ImageDimension][ twiceRadius]; */ std::vector> xWeight; xWeight.resize(ImageDimension); for (unsigned int cpt = 0; cpt < xWeight.size(); ++cpt) { xWeight[cpt].resize(twiceRadius); } for (unsigned int dim = 0; dim < ImageDimension; ++dim) { // x is the offset, hence the parameter of the kernel double x = distance[dim] + this->GetRadius(); // 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) == (static_cast(this->GetRadius()) - 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 std::abs(x) <= rad x -= 1.0; // Compute the weight for this m xWeight[dim][i] = m_Function(x); } //} } if (m_NormalizeWeight == true) { for (unsigned int dim = 0; dim < ImageDimension; ++dim) { double sum = 0.; // Compute the weights sum for (unsigned int i = 0; i < m_WindowSize; ++i) { sum += xWeight[dim][i]; } if (sum != 1.) { // Normalize the weights for (unsigned int i = 0; i < m_WindowSize; ++i) { xWeight[dim][i] = xWeight[dim][i] / sum; } } } } // Iterate over the neighborhood, taking the correct set // of weights in each dimension RealType xPixelValue; itk::NumericTraits::SetLength(xPixelValue, this->GetInputImage()->GetNumberOfComponentsPerPixel()); xPixelValue = static_cast(0.0); 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 RealType 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); } template void GenericInterpolateImageFunction::PrintSelf(std::ostream& os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); } } // namespace otb #endif