OTB  10.0.0
Orfeo Toolbox
otbStreamingWarpImageFilter.hxx
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1999-2011 Insight Software Consortium
3  * Copyright (C) 2005-2024 Centre National d'Etudes Spatiales (CNES)
4  *
5  * This file is part of Orfeo Toolbox
6  *
7  * https://www.orfeo-toolbox.org/
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  * http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  */
21 
22 #ifndef otbStreamingWarpImageFilter_hxx
23 #define otbStreamingWarpImageFilter_hxx
24 
26 #include "itkImageRegionIteratorWithIndex.h"
27 #include "itkDefaultConvertPixelTraits.h"
28 #include "itkMetaDataObject.h"
29 #include "otbMetaDataKey.h"
30 #include "otbNoDataHelper.h"
31 
32 namespace otb
33 {
34 
35 template <class TInputImage, class TOutputImage, class TDisplacementField>
37 {
38  this->DynamicMultiThreadingOn();
39  // Fill the default maximum displacement
40  m_MaximumDisplacement.Fill(1);
41  m_OutputSignedSpacing = this->Superclass::GetOutputSpacing();
42 }
43 
44 
45 template <class TInputImage, class TOutputImage, class TDisplacementField>
47 {
48  m_OutputSignedSpacing = outputSpacing;
49  SpacingType spacing = outputSpacing;
50  typename TInputImage::DirectionType direction = this->GetOutput()->GetDirection();
51  for (unsigned int i = 0; i < TInputImage::ImageDimension; ++i)
52  {
53  if (spacing[i] < 0)
54  {
55  if (direction[i][i] > 0)
56  {
57  for (unsigned int j = 0; j < TInputImage::ImageDimension; ++j)
58  {
59  direction[j][i] = -direction[j][i];
60  }
61  }
62  spacing[i] = -spacing[i];
63  }
64  }
65  this->Superclass::SetOutputSpacing(spacing);
66  this->Superclass::SetOutputDirection(direction);
67  this->Modified();
68 }
69 
70 template <class TInputImage, class TOutputImage, class TDisplacementField>
72 {
73  SpacingType s;
74  for (unsigned int i = 0; i < TInputImage::ImageDimension; ++i)
75  {
76  s[i] = static_cast<typename SpacingType::ValueType>(values[i]);
77  }
78  this->SetOutputSpacing(s);
79 }
80 
81 template <class TInputImage, class TOutputImage, class TDisplacementField>
83 {
84  Superclass::GenerateInputRequestedRegion();
85 
86  // Get the input and displacement field pointers
87  InputImageType* inputPtr = const_cast<InputImageType*>(this->GetInput());
88  DisplacementFieldType* displacementPtr = const_cast<DisplacementFieldType*>(this->GetDisplacementField());
89  OutputImageType* outputPtr = this->GetOutput();
90  // Check if the input and the displacement field exist
91  if (!inputPtr || !displacementPtr || !outputPtr)
92  {
93  return;
94  }
95 
96  // Here we are breaking traditional pipeline steps because we need to access the displacement field data
97  // so as to compute the input image requested region
98 
99  // 1) First, evaluate the displacement field requested region corresponding to the output requested region
100  // (Here we suppose that the displacement field and the output image are in the same geometry/map projection)
101  typename OutputImageType::RegionType outputRequestedRegion = outputPtr->GetRequestedRegion();
102  typename OutputImageType::IndexType outIndexStart = outputRequestedRegion.GetIndex();
103  typename OutputImageType::IndexType outIndexEnd;
104  for (unsigned int dim = 0; dim < OutputImageType::ImageDimension; ++dim)
105  outIndexEnd[dim] = outIndexStart[dim] + outputRequestedRegion.GetSize()[dim] - 1;
106  typename OutputImageType::PointType outPointStart, outPointEnd;
107  outputPtr->TransformIndexToPhysicalPoint(outIndexStart, outPointStart);
108  outputPtr->TransformIndexToPhysicalPoint(outIndexEnd, outPointEnd);
109 
110  typename DisplacementFieldType::IndexType defIndexStart, defIndexEnd;
111  displacementPtr->TransformPhysicalPointToIndex(outPointStart, defIndexStart);
112  displacementPtr->TransformPhysicalPointToIndex(outPointEnd, defIndexEnd);
113 
114  typename DisplacementFieldType::SizeType defRequestedSize;
115  typename DisplacementFieldType::IndexType defRequestedIndex;
116 
117  for (unsigned int dim = 0; dim < OutputImageType::ImageDimension; ++dim)
118  {
119  defRequestedIndex[dim] = std::min(defIndexStart[dim], defIndexEnd[dim]);
120  defRequestedSize[dim] = std::max(defIndexStart[dim], defIndexEnd[dim]) - defRequestedIndex[dim] + 1;
121  }
122 
123  // Finally, build the displacement field requested region
124  typename DisplacementFieldType::RegionType displacementRequestedRegion;
125  displacementRequestedRegion.SetIndex(defRequestedIndex);
126  displacementRequestedRegion.SetSize(defRequestedSize);
127 
128  // Avoid extrapolation
129  displacementRequestedRegion.PadByRadius(1);
130 
131  // crop the input requested region at the input's largest possible region
132  if (displacementRequestedRegion.Crop(displacementPtr->GetLargestPossibleRegion()))
133  {
134  displacementPtr->SetRequestedRegion(displacementRequestedRegion);
135  }
136  else
137  {
138  // Couldn't crop the region (requested region is outside the largest
139  // possible region). Throw an exception.
140 
141  // store what we tried to request (prior to trying to crop)
142  displacementPtr->SetRequestedRegion(displacementRequestedRegion);
143 
144  // build an exception
145  itk::InvalidRequestedRegionError e(__FILE__, __LINE__);
146  e.SetLocation(ITK_LOCATION);
147  e.SetDescription("Requested region is (at least partially) outside the largest possible region of the displacement field.");
148  e.SetDataObject(inputPtr);
149  throw e;
150  }
151 
152  // 2) If we are still there, we have a correct displacement field requested region.
153  // This next step breaks pipeline rule but we need to do it to compute the input requested region,
154  // since it depends on displacement value.
155 
156  // Trigger pipeline update on the displacement field
157 
158  displacementPtr->PropagateRequestedRegion();
159  displacementPtr->UpdateOutputData();
160 
161  // Walk the loaded displacement field to derive maximum and minimum
162  itk::ImageRegionIteratorWithIndex<DisplacementFieldType> defIt(displacementPtr, displacementRequestedRegion);
163  defIt.GoToBegin();
164 
165  typename InputImageType::PointType currentPoint;
166  typename InputImageType::PointType inputStartPoint, inputEndPoint;
167 
168  // Initialise start and end points
169  displacementPtr->TransformIndexToPhysicalPoint(defIt.GetIndex(), currentPoint);
170  for (unsigned int dim = 0; dim < DisplacementFieldType::ImageDimension; ++dim)
171  {
172  currentPoint[dim] += defIt.Get()[dim];
173  }
174  inputStartPoint = currentPoint;
175  inputEndPoint = currentPoint;
176 
177  ++defIt;
178 
179  // 3) Now Walk the field and compute the physical bounding box
180  while (!defIt.IsAtEnd())
181  {
182  displacementPtr->TransformIndexToPhysicalPoint(defIt.GetIndex(), currentPoint);
183  for (unsigned int dim = 0; dim < DisplacementFieldType::ImageDimension; ++dim)
184  {
185  currentPoint[dim] += defIt.Get()[dim];
186  if (inputStartPoint[dim] > currentPoint[dim])
187  inputStartPoint[dim] = currentPoint[dim];
188  if (inputEndPoint[dim] < currentPoint[dim])
189  inputEndPoint[dim] = currentPoint[dim];
190  }
191  ++defIt;
192  }
193 
194  // Convert physical bounding box to requested region
195  typename InputImageType::IndexType inputStartIndex, inputEndIndex;
196  inputPtr->TransformPhysicalPointToIndex(inputStartPoint, inputStartIndex);
197  inputPtr->TransformPhysicalPointToIndex(inputEndPoint, inputEndIndex);
198 
199  typename InputImageType::SizeType inputFinalSize;
200  typename InputImageType::IndexType inputFinalIndex;
201 
202  for (unsigned int dim = 0; dim < DisplacementFieldType::ImageDimension; ++dim)
203  {
204  inputFinalIndex[dim] = std::min(inputStartIndex[dim], inputEndIndex[dim]);
205  inputFinalSize[dim] = std::max(inputStartIndex[dim], inputEndIndex[dim]) - inputFinalIndex[dim] + 1;
206  }
207 
208  typename InputImageType::RegionType inputRequestedRegion;
209  inputRequestedRegion.SetIndex(inputFinalIndex);
210  inputRequestedRegion.SetSize(inputFinalSize);
211 
212  // Compute the padding due to the interpolator
213  unsigned int interpolatorRadius = StreamingTraits<typename Superclass::InputImageType>::CalculateNeededRadiusForInterpolator(this->GetInterpolator());
214 
215  // pad the input requested region by the operator radius
216  inputRequestedRegion.PadByRadius(interpolatorRadius);
217 
218  // crop the input requested region at the input's largest possible region
219  if (inputRequestedRegion.Crop(inputPtr->GetLargestPossibleRegion()))
220  {
221  inputPtr->SetRequestedRegion(inputRequestedRegion);
222  }
223  else
224  {
225  // In this case we need to generate an empty region compatible
226  // with cropping by input largest possible region.
227 
228 
229  for (auto dim = 0U; dim < InputImageType::ImageDimension; ++dim)
230  {
231  auto largestInputRegion = inputPtr->GetLargestPossibleRegion();
232 
233  if (largestInputRegion.GetSize()[dim] > 1)
234  {
235  inputFinalIndex[dim] = largestInputRegion.GetIndex()[dim] + 1;
236  inputFinalSize[dim] = 0;
237  }
238  else
239  {
240  inputFinalIndex[dim] = largestInputRegion.GetIndex()[dim];
241  inputFinalSize[dim] = largestInputRegion.GetSize()[dim];
242  }
243  }
244 
245  inputRequestedRegion.SetSize(inputFinalSize);
246  inputRequestedRegion.SetIndex(inputFinalIndex);
247  inputPtr->SetRequestedRegion(inputRequestedRegion);
248  }
249 }
250 
251 
252 template <class TInputImage, class TOutputImage, class TDisplacementField>
254 {
255  Superclass::GenerateOutputInformation();
256 
257  // Set the NoData flag to the edge padding value
258  std::vector<bool> noDataValueAvailable;
259  std::vector<double> noDataValue;
260 
261  auto res = ReadNoDataFlags(this->GetOutput()->GetImageMetadata(), noDataValueAvailable, noDataValue);
262 
263  if (!res)
264  {
265  noDataValueAvailable.resize(this->GetOutput()->GetNumberOfComponentsPerPixel(), false);
266  noDataValue.resize(this->GetOutput()->GetNumberOfComponentsPerPixel(), 0.0);
267  }
268 
269  PixelType edgePadding = this->GetEdgePaddingValue();
270  if (itk::NumericTraits<PixelType>::GetLength(edgePadding) != this->GetOutput()->GetNumberOfComponentsPerPixel())
271  {
272  itk::NumericTraits<PixelType>::SetLength(edgePadding, this->GetOutput()->GetNumberOfComponentsPerPixel());
273  this->SetEdgePaddingValue(edgePadding);
274  }
275  for (unsigned int i = 0; i < noDataValueAvailable.size(); ++i)
276  {
277  if (!noDataValueAvailable[i])
278  {
279  noDataValueAvailable[i] = true;
280  noDataValue[i] = itk::DefaultConvertPixelTraits<PixelType>::GetNthComponent(i, edgePadding);
281  }
282  }
283 
284  WriteNoDataFlags(noDataValueAvailable, noDataValue, this->GetOutput()->GetImageMetadata());
285 }
286 
287 template <class TInputImage, class TOutputImage, class TDisplacementField>
289 {
290  // the superclass itk::WarpImageFilter is doing the actual warping
291  Superclass::DynamicThreadedGenerateData(outputRegionForThread);
292 
293  // second pass on the thread region to mask pixels outside the displacement grid
294  const PixelType paddingValue = this->GetEdgePaddingValue();
295  OutputImagePointerType outputPtr = this->GetOutput();
296 
297  // ITK 4.13 fix const correctness of GetDisplacementField.
298  // Related commit in ITK: https://github.com/InsightSoftwareConsortium/ITK/commit/0070848b91baf69f04893bc3ce85bcf110c3c63a
299 
300  // DisplacementFieldPointerType fieldPtr = this->GetDisplacementField();
301  const DisplacementFieldType* fieldPtr = this->GetDisplacementField();
302 
303 
304  DisplacementFieldRegionType defRegion = fieldPtr->GetLargestPossibleRegion();
305 
306  itk::ImageRegionIteratorWithIndex<OutputImageType> outputIt(outputPtr, outputRegionForThread);
307  IndexType currentIndex;
308  PointType currentPoint;
309  itk::ContinuousIndex<double, DisplacementFieldType::ImageDimension> contiIndex;
310 
311  while (!outputIt.IsAtEnd())
312  {
313  // get the output image index
314  currentIndex = outputIt.GetIndex();
315  outputPtr->TransformIndexToPhysicalPoint(currentIndex, currentPoint);
316  fieldPtr->TransformPhysicalPointToContinuousIndex(currentPoint, contiIndex);
317 
318  for (unsigned int dim = 0; dim < DisplacementFieldType::ImageDimension; ++dim)
319  {
320  if (contiIndex[dim] < static_cast<double>(defRegion.GetIndex(dim)) ||
321  contiIndex[dim] > static_cast<double>(defRegion.GetIndex(dim) + defRegion.GetSize(dim) - 1))
322  {
323  outputIt.Set(paddingValue);
324  break;
325  }
326  }
327  ++outputIt;
328  }
329 }
330 
331 template <class TInputImage, class TOutputImage, class TDisplacementField>
333 {
334  Superclass::PrintSelf(os, indent);
335  os << indent << "Maximum displacement: " << m_MaximumDisplacement << std::endl;
336 }
337 
338 } // end namespace otb
339 #endif
static unsigned int CalculateNeededRadiusForInterpolator(const InterpolationType *interpolator)
OutputImageType::SpacingType SpacingType
OutputImageType::PixelType PixelType
OutputImageType::IndexType IndexType
OutputImageType::PointType PointType
void SetOutputSpacing(const SpacingType OutputSpacing) override
void DynamicThreadedGenerateData(const OutputImageRegionType &outputRegionForThread) override
OutputImageType::Pointer OutputImagePointerType
void PrintSelf(std::ostream &os, itk::Indent indent) const override
OutputImageType::RegionType OutputImageRegionType
DisplacementFieldType::RegionType DisplacementFieldRegionType
The "otb" namespace contains all Orfeo Toolbox (OTB) classes.
bool OTBMetadata_EXPORT ReadNoDataFlags(const ImageMetadata &imd, std::vector< bool > &flags, std::vector< double > &values)
void OTBMetadata_EXPORT WriteNoDataFlags(const std::vector< bool > &flags, const std::vector< double > &values, ImageMetadata &imd)