OTB  9.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-2022 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  // Fill the default maximum displacement
39  m_MaximumDisplacement.Fill(1);
40  m_OutputSignedSpacing = this->Superclass::GetOutputSpacing();
41 }
42 
43 
44 template <class TInputImage, class TOutputImage, class TDisplacementField>
46 {
47  m_OutputSignedSpacing = outputSpacing;
48  SpacingType spacing = outputSpacing;
49  typename TInputImage::DirectionType direction = this->GetOutput()->GetDirection();
50  for (unsigned int i = 0; i < TInputImage::ImageDimension; ++i)
51  {
52  if (spacing[i] < 0)
53  {
54  if (direction[i][i] > 0)
55  {
56  for (unsigned int j = 0; j < TInputImage::ImageDimension; ++j)
57  {
58  direction[j][i] = -direction[j][i];
59  }
60  }
61  spacing[i] = -spacing[i];
62  }
63  }
64  this->Superclass::SetOutputSpacing(spacing);
65  this->Superclass::SetOutputDirection(direction);
66  this->Modified();
67 }
68 
69 template <class TInputImage, class TOutputImage, class TDisplacementField>
71 {
72  SpacingType s;
73  for (unsigned int i = 0; i < TInputImage::ImageDimension; ++i)
74  {
75  s[i] = static_cast<typename SpacingType::ValueType>(values[i]);
76  }
77  this->SetOutputSpacing(s);
78 }
79 
80 template <class TInputImage, class TOutputImage, class TDisplacementField>
82 {
83  Superclass::GenerateInputRequestedRegion();
84 
85  // Get the input and displacement field pointers
86  InputImageType* inputPtr = const_cast<InputImageType*>(this->GetInput());
87  DisplacementFieldType* displacementPtr = const_cast<DisplacementFieldType*>(this->GetDisplacementField());
88  OutputImageType* outputPtr = this->GetOutput();
89  // Check if the input and the displacement field exist
90  if (!inputPtr || !displacementPtr || !outputPtr)
91  {
92  return;
93  }
94 
95  // Here we are breaking traditional pipeline steps because we need to access the displacement field data
96  // so as to compute the input image requested region
97 
98  // 1) First, evaluate the displacement field requested region corresponding to the output requested region
99  // (Here we suppose that the displacement field and the output image are in the same geometry/map projection)
100  typename OutputImageType::RegionType outputRequestedRegion = outputPtr->GetRequestedRegion();
101  typename OutputImageType::IndexType outIndexStart = outputRequestedRegion.GetIndex();
102  typename OutputImageType::IndexType outIndexEnd;
103  for (unsigned int dim = 0; dim < OutputImageType::ImageDimension; ++dim)
104  outIndexEnd[dim] = outIndexStart[dim] + outputRequestedRegion.GetSize()[dim] - 1;
105  typename OutputImageType::PointType outPointStart, outPointEnd;
106  outputPtr->TransformIndexToPhysicalPoint(outIndexStart, outPointStart);
107  outputPtr->TransformIndexToPhysicalPoint(outIndexEnd, outPointEnd);
108 
109  typename DisplacementFieldType::IndexType defIndexStart, defIndexEnd;
110  displacementPtr->TransformPhysicalPointToIndex(outPointStart, defIndexStart);
111  displacementPtr->TransformPhysicalPointToIndex(outPointEnd, defIndexEnd);
112 
113  typename DisplacementFieldType::SizeType defRequestedSize;
114  typename DisplacementFieldType::IndexType defRequestedIndex;
115 
116  for (unsigned int dim = 0; dim < OutputImageType::ImageDimension; ++dim)
117  {
118  defRequestedIndex[dim] = std::min(defIndexStart[dim], defIndexEnd[dim]);
119  defRequestedSize[dim] = std::max(defIndexStart[dim], defIndexEnd[dim]) - defRequestedIndex[dim] + 1;
120  }
121 
122  // Finally, build the displacement field requested region
123  typename DisplacementFieldType::RegionType displacementRequestedRegion;
124  displacementRequestedRegion.SetIndex(defRequestedIndex);
125  displacementRequestedRegion.SetSize(defRequestedSize);
126 
127  // Avoid extrapolation
128  displacementRequestedRegion.PadByRadius(1);
129 
130  // crop the input requested region at the input's largest possible region
131  if (displacementRequestedRegion.Crop(displacementPtr->GetLargestPossibleRegion()))
132  {
133  displacementPtr->SetRequestedRegion(displacementRequestedRegion);
134  }
135  else
136  {
137  // Couldn't crop the region (requested region is outside the largest
138  // possible region). Throw an exception.
139 
140  // store what we tried to request (prior to trying to crop)
141  displacementPtr->SetRequestedRegion(displacementRequestedRegion);
142 
143  // build an exception
144  itk::InvalidRequestedRegionError e(__FILE__, __LINE__);
145  e.SetLocation(ITK_LOCATION);
146  e.SetDescription("Requested region is (at least partially) outside the largest possible region of the displacement field.");
147  e.SetDataObject(inputPtr);
148  throw e;
149  }
150 
151  // 2) If we are still there, we have a correct displacement field requested region.
152  // This next step breaks pipeline rule but we need to do it to compute the input requested region,
153  // since it depends on displacement value.
154 
155  // Trigger pipeline update on the displacement field
156 
157  displacementPtr->PropagateRequestedRegion();
158  displacementPtr->UpdateOutputData();
159 
160  // Walk the loaded displacement field to derive maximum and minimum
161  itk::ImageRegionIteratorWithIndex<DisplacementFieldType> defIt(displacementPtr, displacementRequestedRegion);
162  defIt.GoToBegin();
163 
164  typename InputImageType::PointType currentPoint;
165  typename InputImageType::PointType inputStartPoint, inputEndPoint;
166 
167  // Initialise start and end points
168  displacementPtr->TransformIndexToPhysicalPoint(defIt.GetIndex(), currentPoint);
169  for (unsigned int dim = 0; dim < DisplacementFieldType::ImageDimension; ++dim)
170  {
171  currentPoint[dim] += defIt.Get()[dim];
172  }
173  inputStartPoint = currentPoint;
174  inputEndPoint = currentPoint;
175 
176  ++defIt;
177 
178  // 3) Now Walk the field and compute the physical bounding box
179  while (!defIt.IsAtEnd())
180  {
181  displacementPtr->TransformIndexToPhysicalPoint(defIt.GetIndex(), currentPoint);
182  for (unsigned int dim = 0; dim < DisplacementFieldType::ImageDimension; ++dim)
183  {
184  currentPoint[dim] += defIt.Get()[dim];
185  if (inputStartPoint[dim] > currentPoint[dim])
186  inputStartPoint[dim] = currentPoint[dim];
187  if (inputEndPoint[dim] < currentPoint[dim])
188  inputEndPoint[dim] = currentPoint[dim];
189  }
190  ++defIt;
191  }
192 
193  // Convert physical bounding box to requested region
194  typename InputImageType::IndexType inputStartIndex, inputEndIndex;
195  inputPtr->TransformPhysicalPointToIndex(inputStartPoint, inputStartIndex);
196  inputPtr->TransformPhysicalPointToIndex(inputEndPoint, inputEndIndex);
197 
198  typename InputImageType::SizeType inputFinalSize;
199  typename InputImageType::IndexType inputFinalIndex;
200 
201  for (unsigned int dim = 0; dim < DisplacementFieldType::ImageDimension; ++dim)
202  {
203  inputFinalIndex[dim] = std::min(inputStartIndex[dim], inputEndIndex[dim]);
204  inputFinalSize[dim] = std::max(inputStartIndex[dim], inputEndIndex[dim]) - inputFinalIndex[dim] + 1;
205  }
206 
207  typename InputImageType::RegionType inputRequestedRegion;
208  inputRequestedRegion.SetIndex(inputFinalIndex);
209  inputRequestedRegion.SetSize(inputFinalSize);
210 
211  // Compute the padding due to the interpolator
212  unsigned int interpolatorRadius = StreamingTraits<typename Superclass::InputImageType>::CalculateNeededRadiusForInterpolator(this->GetInterpolator());
213 
214  // pad the input requested region by the operator radius
215  inputRequestedRegion.PadByRadius(interpolatorRadius);
216 
217  // crop the input requested region at the input's largest possible region
218  if (inputRequestedRegion.Crop(inputPtr->GetLargestPossibleRegion()))
219  {
220  inputPtr->SetRequestedRegion(inputRequestedRegion);
221  }
222  else
223  {
224  // In this case we need to generate an empty region compatible
225  // with cropping by input largest possible region.
226 
227 
228  for (auto dim = 0U; dim < InputImageType::ImageDimension; ++dim)
229  {
230  auto largestInputRegion = inputPtr->GetLargestPossibleRegion();
231 
232  if (largestInputRegion.GetSize()[dim] > 1)
233  {
234  inputFinalIndex[dim] = largestInputRegion.GetIndex()[dim] + 1;
235  inputFinalSize[dim] = 0;
236  }
237  else
238  {
239  inputFinalIndex[dim] = largestInputRegion.GetIndex()[dim];
240  inputFinalSize[dim] = largestInputRegion.GetSize()[dim];
241  }
242  }
243 
244  inputRequestedRegion.SetSize(inputFinalSize);
245  inputRequestedRegion.SetIndex(inputFinalIndex);
246  inputPtr->SetRequestedRegion(inputRequestedRegion);
247  }
248 }
249 
250 
251 template <class TInputImage, class TOutputImage, class TDisplacementField>
253 {
254  Superclass::GenerateOutputInformation();
255 
256  // Set the NoData flag to the edge padding value
257  std::vector<bool> noDataValueAvailable;
258  std::vector<double> noDataValue;
259 
260  auto res = ReadNoDataFlags(this->GetOutput()->GetImageMetadata(), noDataValueAvailable, noDataValue);
261 
262  if (!res)
263  {
264  noDataValueAvailable.resize(this->GetOutput()->GetNumberOfComponentsPerPixel(), false);
265  noDataValue.resize(this->GetOutput()->GetNumberOfComponentsPerPixel(), 0.0);
266  }
267 
268  PixelType edgePadding = this->GetEdgePaddingValue();
269  if (itk::NumericTraits<PixelType>::GetLength(edgePadding) != this->GetOutput()->GetNumberOfComponentsPerPixel())
270  {
271  itk::NumericTraits<PixelType>::SetLength(edgePadding, this->GetOutput()->GetNumberOfComponentsPerPixel());
272  this->SetEdgePaddingValue(edgePadding);
273  }
274  for (unsigned int i = 0; i < noDataValueAvailable.size(); ++i)
275  {
276  if (!noDataValueAvailable[i])
277  {
278  noDataValueAvailable[i] = true;
279  noDataValue[i] = itk::DefaultConvertPixelTraits<PixelType>::GetNthComponent(i, edgePadding);
280  }
281  }
282 
283  WriteNoDataFlags(noDataValueAvailable, noDataValue, this->GetOutput()->GetImageMetadata());
284 }
285 
286 template <class TInputImage, class TOutputImage, class TDisplacementField>
288  itk::ThreadIdType threadId)
289 {
290  // the superclass itk::WarpImageFilter is doing the actual warping
291  Superclass::ThreadedGenerateData(outputRegionForThread, threadId);
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
otb::StreamingWarpImageFilter::PixelType
OutputImageType::PixelType PixelType
Definition: otbStreamingWarpImageFilter.h:73
otb::StreamingWarpImageFilter::OutputImageType
TOutputImage OutputImageType
Definition: otbStreamingWarpImageFilter.h:70
otb::WriteNoDataFlags
void OTBMetadata_EXPORT WriteNoDataFlags(const std::vector< bool > &flags, const std::vector< double > &values, ImageMetadata &imd)
otb::StreamingWarpImageFilter::PrintSelf
void PrintSelf(std::ostream &os, itk::Indent indent) const override
Definition: otbStreamingWarpImageFilter.hxx:332
otb
The "otb" namespace contains all Orfeo Toolbox (OTB) classes.
Definition: otbJoinContainer.h:32
otb::ReadNoDataFlags
bool OTBMetadata_EXPORT ReadNoDataFlags(const ImageMetadata &imd, std::vector< bool > &flags, std::vector< double > &values)
otb::StreamingWarpImageFilter::IndexType
OutputImageType::IndexType IndexType
Definition: otbStreamingWarpImageFilter.h:72
otb::StreamingWarpImageFilter::InputImageType
TInputImage InputImageType
Definition: otbStreamingWarpImageFilter.h:65
otb::StreamingWarpImageFilter::GenerateInputRequestedRegion
void GenerateInputRequestedRegion() override
Definition: otbStreamingWarpImageFilter.hxx:81
otb::StreamingWarpImageFilter::SetOutputSpacing
void SetOutputSpacing(const SpacingType OutputSpacing) override
Definition: otbStreamingWarpImageFilter.hxx:45
otbNoDataHelper.h
otb::StreamingTraits::CalculateNeededRadiusForInterpolator
static unsigned int CalculateNeededRadiusForInterpolator(const InterpolationType *interpolator)
Definition: otbStreamingTraits.hxx:29
otbStreamingWarpImageFilter.h
otb::StreamingWarpImageFilter::ThreadedGenerateData
void ThreadedGenerateData(const OutputImageRegionType &outputRegionForThread, itk::ThreadIdType threadId) override
Definition: otbStreamingWarpImageFilter.hxx:287
otb::StreamingWarpImageFilter::DisplacementFieldRegionType
DisplacementFieldType::RegionType DisplacementFieldRegionType
Definition: otbStreamingWarpImageFilter.h:80
otbMetaDataKey.h
otb::StreamingWarpImageFilter::OutputImageRegionType
OutputImageType::RegionType OutputImageRegionType
Definition: otbStreamingWarpImageFilter.h:76
otb::StreamingWarpImageFilter::SpacingType
OutputImageType::SpacingType SpacingType
Definition: otbStreamingWarpImageFilter.h:74
otb::StreamingWarpImageFilter::PointType
OutputImageType::PointType PointType
Definition: otbStreamingWarpImageFilter.h:71
otb::StreamingWarpImageFilter::StreamingWarpImageFilter
StreamingWarpImageFilter()
Definition: otbStreamingWarpImageFilter.hxx:36
otb::StreamingWarpImageFilter::GenerateOutputInformation
void GenerateOutputInformation() override
Definition: otbStreamingWarpImageFilter.hxx:252
otb::StreamingWarpImageFilter::DisplacementFieldType
TDisplacementField DisplacementFieldType
Definition: otbStreamingWarpImageFilter.h:77
otb::StreamingWarpImageFilter::OutputImagePointerType
OutputImageType::Pointer OutputImagePointerType
Definition: otbStreamingWarpImageFilter.h:75