OTB  10.0.0
Orfeo Toolbox
otbGridResampleImageFilter.hxx
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2005-2024 Centre National d'Etudes Spatiales (CNES)
3  *
4  * This file is part of Orfeo Toolbox
5  *
6  * https://www.orfeo-toolbox.org/
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 
21 #ifndef otbGridResampleImageFilter_hxx
22 #define otbGridResampleImageFilter_hxx
23 
25 
26 #include "otbStreamingTraits.h"
27 #include "otbImage.h"
28 
29 #include "itkNumericTraits.h"
30 #include "itkProgressReporter.h"
31 #include "itkImageScanlineIterator.h"
32 #include "itkContinuousIndex.h"
33 
34 namespace otb
35 {
36 
37 template <typename TInputImage, typename TOutputImage, typename TInterpolatorPrecision>
39  : m_OutputStartIndex(),
40  m_OutputSize(),
41  m_OutputOrigin(),
42  m_OutputSpacing(),
43  m_EdgePaddingValue(),
44  m_InterpolationMargin(0.0),
45  m_CheckOutputBounds(true),
46  m_Interpolator(),
47  m_ReachableOutputRegion()
48 {
49  // Set linear interpolator as default
50  m_Interpolator = dynamic_cast<InterpolatorType*>(DefaultInterpolatorType::New().GetPointer());
51 
52  // Initialize EdgePaddingValue
53  m_EdgePaddingValue = itk::NumericTraits<OutputPixelType>::ZeroValue(m_EdgePaddingValue);
54 
55  // Initialize origin and spacing
56  m_OutputOrigin.Fill(0.);
57  m_OutputSpacing.Fill(1.);
58  m_OutputStartIndex.Fill(0);
59  m_OutputSize.Fill(0);
60  this->DynamicMultiThreadingOn();
61 }
62 
63 
65 template <typename TInputImage, typename TOutputImage, typename TInterpolatorPrecision>
67 {
68  this->SetOutputOrigin(image->GetOrigin());
69  this->SetOutputSpacing(internal::GetSignedSpacing(image));
70  this->SetOutputStartIndex(image->GetLargestPossibleRegion().GetIndex());
71  this->SetOutputSize(image->GetLargestPossibleRegion().GetSize());
72 }
74 
75 template <typename TInputImage, typename TOutputImage, typename TInterpolatorPrecision>
77 {
78  // call the superclass' implementation of this method
79  Superclass::GenerateOutputInformation();
80 
81  // get pointers to the input and output
82  OutputImageType* outputPtr = this->GetOutput();
83  if (!outputPtr)
84  {
85  return;
86  }
87 
88  // Fill output image data
89  typename TOutputImage::RegionType outputLargestPossibleRegion;
90  outputLargestPossibleRegion.SetSize(m_OutputSize);
91  outputLargestPossibleRegion.SetIndex(m_OutputStartIndex);
92 
93  outputPtr->SetLargestPossibleRegion(outputLargestPossibleRegion);
94  outputPtr->SetSignedSpacing(m_OutputSpacing);
95  outputPtr->SetOrigin(m_OutputOrigin);
96 
97  // TODO: Report no data value here
98 }
99 
100 template <typename TInputImage, typename TOutputImage, typename TInterpolatorPrecision>
102 {
103  // call the superclass's implementation of this method
104  Superclass::GenerateInputRequestedRegion();
105 
106  // Check for input
107  if (!this->GetInput())
108  {
109  return;
110  }
111 
112  // get pointers to the input and output
113  typename TInputImage::Pointer inputPtr = const_cast<TInputImage*>(this->GetInput());
114 
115  // check for output
116  OutputImageType* outputPtr = this->GetOutput();
117  if (!outputPtr)
118  {
119  return;
120  }
121 
122  typename TOutputImage::RegionType outputRequestedRegion = outputPtr->GetRequestedRegion();
123 
124  IndexType outULIndex, outLRIndex;
125 
126  typedef itk::ContinuousIndex<double, TInputImage::ImageDimension> ContinuousIndexType;
127 
128  ContinuousIndexType inULCIndex, inLRCIndex;
129 
130  // Get output image requested region corners as Index
131  outULIndex = outputRequestedRegion.GetIndex();
132  outLRIndex = outULIndex + outputRequestedRegion.GetSize();
133  outLRIndex[0] -= 1;
134  outLRIndex[1] -= 1;
135 
136  // Transform to physiscal points
137  PointType outULPoint, outLRPoint;
138  outputPtr->TransformIndexToPhysicalPoint(outULIndex, outULPoint);
139  outputPtr->TransformIndexToPhysicalPoint(outLRIndex, outLRPoint);
140 
141  // Transform to input image Index
142  inputPtr->TransformPhysicalPointToContinuousIndex(outULPoint, inULCIndex);
143  inputPtr->TransformPhysicalPointToContinuousIndex(outLRPoint, inLRCIndex);
144 
145  SizeType inSize;
146  IndexType inULIndex, inLRIndex;
147 
148  // Reorder index properly and compute size
149  for (unsigned int dim = 0; dim < ImageDimension; ++dim)
150  {
151  if (inULCIndex[dim] > inLRCIndex[dim])
152  {
153  // swap
154  typename ContinuousIndexType::ValueType tmp(inULCIndex[dim]);
155  inULCIndex[dim] = inLRCIndex[dim];
156  inLRCIndex[dim] = tmp;
157  }
158 
159  // Ensure correct rounding of coordinates
160 
161  inULIndex[dim] = std::floor(inULCIndex[dim]);
162  inLRIndex[dim] = std::ceil(inLRCIndex[dim]);
163 
164  inSize[dim] = static_cast<typename SizeType::SizeValueType>(inLRIndex[dim] - inULIndex[dim]) + 1;
165  }
166 
167  // Build the input requested region
168  typename TInputImage::RegionType inputRequestedRegion;
169  inputRequestedRegion.SetIndex(inULIndex);
170  inputRequestedRegion.SetSize(inSize);
171 
172  // Compute the padding due to the interpolator
173  unsigned int interpolatorRadius = StreamingTraits<typename Superclass::InputImageType>::CalculateNeededRadiusForInterpolator(this->GetInterpolator());
174  inputRequestedRegion.PadByRadius(interpolatorRadius);
175 
176  // crop the input requested region at the input's largest possible region
177  if (inputRequestedRegion.Crop(inputPtr->GetLargestPossibleRegion()))
178  {
179  inputPtr->SetRequestedRegion(inputRequestedRegion);
180  }
181  else
182  {
183 
184  // store what we tried to request (prior to trying to crop)
185  inputPtr->SetRequestedRegion(inputRequestedRegion);
186 
187  // build an exception
188  itk::InvalidRequestedRegionError e(__FILE__, __LINE__);
189  e.SetLocation(ITK_LOCATION);
190  e.SetDescription("Requested region is (at least partially) outside the largest possible region.");
191  e.SetDataObject(inputPtr);
192  throw e;
193  }
194 }
195 
196 
197 template <typename TInputImage, typename TOutputImage, typename TInterpolatorPrecision>
199 {
200  if (!m_Interpolator)
201  {
202  itkExceptionMacro(<< "Interpolator not set");
203  }
204 
205  // Connect input image to interpolator
206  m_Interpolator->SetInputImage(this->GetInput());
207 
208 
209  unsigned int nComponents = itk::DefaultConvertPixelTraits<OutputPixelType>::GetNumberOfComponents(m_EdgePaddingValue);
210 
211  if (nComponents == 0)
212  {
213 
214  // Build a default value of the correct number of components
215  OutputPixelComponentType zeroComponent = itk::NumericTraits<OutputPixelComponentType>::ZeroValue(zeroComponent);
216 
217  nComponents = this->GetInput()->GetNumberOfComponentsPerPixel();
218 
219  itk::NumericTraits<OutputPixelType>::SetLength(m_EdgePaddingValue, nComponents);
220  for (unsigned int n = 0; n < nComponents; n++)
221  {
222  OutputPixelConvertType::SetNthComponent(n, m_EdgePaddingValue, zeroComponent);
223  }
224  }
225 
226  // Compute ReachableOutputRegion
227  // InputImage buffered region corresponds to a region of the output
228  // image. Computing it beforehand allows saving IsInsideBuffer
229  // calls in the interpolation loop
230 
231  // Compute the padding due to the interpolator
232 
233  IndexType inUL = this->GetInput()->GetBufferedRegion().GetIndex();
234  IndexType inLR = this->GetInput()->GetBufferedRegion().GetIndex() + this->GetInput()->GetBufferedRegion().GetSize();
235  inLR[0] -= 1;
236  inLR[1] -= 1;
237 
238  // We should take interpolation radius into account here, but this
239  // does not match the IsInsideBuffer method
240  // unsigned int interpolatorRadius =
241  // StreamingTraits<typename Superclass::InputImageType>::CalculateNeededRadiusForInterpolator(this->GetInterpolator());
242  // inUL[0]+=interpolatorRadius;
243  // inUL[1]+=interpolatorRadius;
244  // inLR[0]-=interpolatorRadius;
245  // inLR[1]-=interpolatorRadius;
246 
247  PointType inULp, inLRp;
248  this->GetInput()->TransformIndexToPhysicalPoint(inUL, inULp);
249  this->GetInput()->TransformIndexToPhysicalPoint(inLR, inLRp);
250 
251  inULp -= (0.5 - m_InterpolationMargin) * this->GetInput()->GetSignedSpacing();
252  inLRp += (0.5 - m_InterpolationMargin) * this->GetInput()->GetSignedSpacing();
253 
256  this->GetOutput()->TransformPhysicalPointToContinuousIndex(inULp, outUL);
257  this->GetOutput()->TransformPhysicalPointToContinuousIndex(inLRp, outLR);
258 
259  IndexType outputIndex;
260  // This needs to take into account negative spacing
261  outputIndex[0] = std::ceil(std::min(outUL[0], outLR[0]));
262  outputIndex[1] = std::ceil(std::min(outUL[1], outLR[1]));
263 
264  SizeType outputSize;
265  outputSize[0] = std::floor(std::max(outUL[0], outLR[0])) - outputIndex[0] + 1;
266  outputSize[1] = std::floor(std::max(outUL[1], outLR[1])) - outputIndex[1] + 1;
267 
268  m_ReachableOutputRegion.SetIndex(outputIndex);
269  m_ReachableOutputRegion.SetSize(outputSize);
270 
271  otbMsgDevMacro(<< "ReachableOutputRegion: " << m_ReachableOutputRegion);
272 }
273 
274 template <typename TInputImage, typename TOutputImage, typename TInterpolatorPrecision>
276 {
277  // Get the output pointers
278  OutputImageType* outputPtr = this->GetOutput();
279 
280  // Get this input pointers
281  const InputImageType* inputPtr = this->GetInput();
282 
283  // Min/max values of the output pixel type AND these values
284  // represented as the output type of the interpolator
285  const OutputPixelComponentType minValue = itk::NumericTraits<OutputPixelComponentType>::NonpositiveMin();
286  const OutputPixelComponentType maxValue = itk::NumericTraits<OutputPixelComponentType>::max();
287 
288  const InterpolatorComponentType minOutputValue = static_cast<InterpolatorComponentType>(minValue);
289  const InterpolatorComponentType maxOutputValue = static_cast<InterpolatorComponentType>(maxValue);
290 
291  // Iterator on the output region for current thread
292  OutputImageRegionType regionToCompute = outputRegionForThread;
293  bool cropSucceed = regionToCompute.Crop(m_ReachableOutputRegion);
294 
295  // Fill thread buffer
296  itk::ImageScanlineIterator<OutputImageType> outItFull(outputPtr, outputRegionForThread);
297  outItFull.GoToBegin();
298  while (!outItFull.IsAtEnd())
299  {
300  while (!outItFull.IsAtEndOfLine())
301  {
302  outItFull.Set(m_EdgePaddingValue);
303  ++outItFull;
304  }
305  outItFull.NextLine();
306  }
307 
308  if (!cropSucceed)
309  return;
310 
311  itk::ImageScanlineIterator<OutputImageType> outIt(outputPtr, regionToCompute);
312 
313 
314  // Temporary variables for loop
315  PointType outPoint;
316  ContinuousInputIndexType inCIndex;
317  InterpolatorOutputType interpolatorValue; //(this->GetOutput()->GetNumberOfComponentsPerPixel());
318  OutputPixelType outputValue; //(this->GetOutput()->GetNumberOfComponentsPerPixel());
319 
320  // TODO: assert outputPtr->GetSignedSpacing() != 0 here
321  assert(outputPtr->GetSignedSpacing()[0] != 0 && "Null spacing will cause division by zero.");
322  const double delta = outputPtr->GetSignedSpacing()[0] / inputPtr->GetSignedSpacing()[0];
323 
324  // Iterate through the output region
325  outIt.GoToBegin();
326 
327  while (!outIt.IsAtEnd())
328  {
329  // Map output index to input continuous index
330  outputPtr->TransformIndexToPhysicalPoint(outIt.GetIndex(), outPoint);
331  inputPtr->TransformPhysicalPointToContinuousIndex(outPoint, inCIndex);
332 
333  while (!outIt.IsAtEndOfLine())
334  {
335  // Interpolate
336  interpolatorValue = m_Interpolator->EvaluateAtContinuousIndex(inCIndex);
337 
338  // Cast and check bounds
339  this->CastPixelWithBoundsChecking(interpolatorValue, minOutputValue, maxOutputValue, outputValue);
340 
341  // Set output value
342  outIt.Set(outputValue);
343 
344  // move one pixel forward
345  ++outIt;
346 
347  // Update input position
348  inCIndex[0] += delta;
349  }
350 
351  // Move to next line
352  outIt.NextLine();
353  }
354 }
355 
356 template <typename TInputImage, typename TOutputImage, typename TInterpolatorPrecision>
358 {
359  // Disconnect input image from the interpolator
360  m_Interpolator->SetInputImage(nullptr);
361 }
362 
363 
364 template <typename TInputImage, typename TOutputImage, typename TInterpolatorPrecision>
366 {
367  itk::ModifiedTimeType latestTime = itk::Object::GetMTime();
368 
369  if (m_Interpolator)
370  {
371  if (latestTime < m_Interpolator->GetMTime())
372  {
373  latestTime = m_Interpolator->GetMTime();
374  }
375  }
376 
377  return latestTime;
378 }
379 
380 
381 template <typename TInputImage, typename TOutputImage, typename TInterpolatorPrecision>
383 {
384  Superclass::PrintSelf(os, indent);
385 
386  os << indent << "EdgePaddingValue: " << static_cast<typename itk::NumericTraits<OutputPixelType>::PrintType>(m_EdgePaddingValue) << std::endl;
387  os << indent << "OutputStartIndex: " << m_OutputStartIndex << std::endl;
388  os << indent << "OutputSize: " << m_OutputSize << std::endl;
389  os << indent << "OutputOrigin: " << m_OutputOrigin << std::endl;
390  os << indent << "OutputSpacing: " << m_OutputSpacing << std::endl;
391  os << indent << "Interpolator: " << m_Interpolator.GetPointer() << std::endl;
392  os << indent << "CheckOutputBounds: " << (m_CheckOutputBounds ? "On" : "Off") << std::endl;
393 }
394 
395 
396 } // namespace otb
397 
398 
399 #endif
void SetOutputParametersFromImage(const ImageBaseType *image)
InterpolatorConvertType::ComponentType InterpolatorComponentType
void PrintSelf(std::ostream &os, itk::Indent indent) const override
OutputImageType::RegionType OutputImageRegionType
itk::InterpolateImageFunction< InputImageType, TInterpolatorPrecision > InterpolatorType
itk::ModifiedTimeType GetMTime(void) const override
itk::ImageBase< OutputImageType::ImageDimension > ImageBaseType
OutputPixelConvertType::ComponentType OutputPixelComponentType
void DynamicThreadedGenerateData(const OutputImageRegionType &outputRegionForThread) override
itk::ContinuousIndex< double, InputImageDimension > ContinuousInputIndexType
InterpolatorType::OutputType InterpolatorOutputType
TOutputImage::PixelType OutputPixelType
static unsigned int CalculateNeededRadiusForInterpolator(const InterpolationType *interpolator)
ImageType::SpacingType GetSignedSpacing(const ImageType *input)
Definition: otbImage.h:41
unsigned int GetNumberOfComponents(PixelType const &pix)
The "otb" namespace contains all Orfeo Toolbox (OTB) classes.
#define otbMsgDevMacro(x)
Definition: otbMacro.h:116