OTB  7.2.0
Orfeo Toolbox
otbFunctorImageFilter.hxx
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2005-2020 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 otbFunctorImageFilter_hxx
22 #define otbFunctorImageFilter_hxx
23 
24 #include "otbFunctorImageFilter.h"
25 #include "itkProgressReporter.h"
26 #include "itkConstNeighborhoodIterator.h"
27 #include "itkImageRegionConstIterator.h"
28 #include "itkImageScanlineIterator.h"
29 #include <array>
30 
31 namespace otb
32 {
33 namespace functor_filter_details
34 {
35 // Variadic SetRequestedRegion
36 
37 // This function sets the requested region for one image
38 template <class T>
39 int SetInputRequestedRegion(const T* img, const itk::ImageRegion<2>& region, const itk::Size<2>& radius, bool pad)
40 {
41  assert(img && "Input image is a nullptr");
42 
43  auto currentRegion = region;
44 
45  // Hopefully this will be optimized out by compiler
46  if (pad)
47  currentRegion.PadByRadius(radius);
48 
49  // The ugly cast in all ITK filters
50  T* nonConstImg = const_cast<T*>(img);
51 
52  if (currentRegion.GetNumberOfPixels()==0 || currentRegion.Crop(img->GetLargestPossibleRegion()))
53  {
54  nonConstImg->SetRequestedRegion(currentRegion);
55  return 0;
56  }
57  else
58  {
59  nonConstImg->SetRequestedRegion(currentRegion);
60 
61  // build an exception
62  itk::InvalidRequestedRegionError e(__FILE__, __LINE__);
63  e.SetLocation("::SetInputRequestedRegion<>()");
64  e.SetDescription("Requested region is (at least partially) outside the largest possible region.");
65  e.SetDataObject(nonConstImg);
66  throw e;
67  }
68 }
69 
70 // Will be easier to write in c++17 with std::apply and fold expressions
71 template <typename HasNeighborhood, class Tuple, size_t... Is>
72 auto SetInputRequestedRegionsImpl(Tuple& t, const itk::ImageRegion<2>& region, std::index_sequence<Is...>, const itk::Size<2>& radius)
73 {
74  return std::make_tuple(SetInputRequestedRegion(std::get<Is>(t), region, radius, typename std::tuple_element<Is, HasNeighborhood>::type::value_type())...);
75 }
76 
77 // Will be easier to write in c++17 with std::apply and fold expressions
78 template <typename HasNeighborhood, typename... T>
79 auto SetInputRequestedRegions(std::tuple<T...>&& t, const itk::ImageRegion<2>& region, const itk::Size<2>& radius)
80 {
81  return SetInputRequestedRegionsImpl<HasNeighborhood>(t, region, std::make_index_sequence<sizeof...(T)>{}, radius);
82 }
83 
84 // Will be easier to write in c++17 with std::apply and fold expressions
85 template <class Tuple, size_t... Is>
86 auto GetNumberOfComponentsPerInputImpl(Tuple& t, std::index_sequence<Is...>)
87 {
88  return std::array<size_t, sizeof...(Is)>{{std::get<Is>(t)->GetNumberOfComponentsPerPixel()...}};
89 }
90 
91 // Will be easier to write in c++17 with std::apply and fold expressions
92 template <typename... T>
93 auto GetNumberOfComponentsPerInput(std::tuple<T...>& t)
94 {
95  return GetNumberOfComponentsPerInputImpl(t, std::make_index_sequence<sizeof...(T)>{});
96 }
97 
98 template <typename N>
100 {
101 };
102 
103 template <>
104 struct MakeIterator<std::false_type>
105 {
106  template <class T>
107  static auto Make(const T* img, const itk::ImageRegion<2>& region, const itk::Size<2>&)
108  {
109  itk::ImageRegionConstIterator<T> it(img, region);
110  return it;
111  }
112 };
113 
114 template <>
115 struct MakeIterator<std::true_type>
116 {
117  template <class T>
118  static auto Make(const T* img, const itk::ImageRegion<2>& region, const itk::Size<2>& radius)
119  {
120  itk::ConstNeighborhoodIterator<T> it(radius, img, region);
121  return it;
122  }
123 };
124 
125 // Will be easier to write in c++17 with std::apply and fold expressions
126 template <class TNeigh, class Tuple, size_t... Is>
127 auto MakeIteratorsImpl(const Tuple& t, const itk::ImageRegion<2>& region, const itk::Size<2>& radius, std::index_sequence<Is...>, TNeigh)
128 {
129  return std::make_tuple(MakeIterator<typename std::tuple_element<Is, TNeigh>::type>::Make(std::get<Is>(t), region, radius)...);
130 }
131 
132 // Will be easier to write in c++17 with std::apply and fold expressions
133 template <class TNeigh, typename... T>
134 auto MakeIterators(std::tuple<T...>&& t, const itk::ImageRegion<2>& region, const itk::Size<2>& radius, TNeigh n)
135 {
136  return MakeIteratorsImpl(t, region, radius, std::make_index_sequence<sizeof...(T)>{}, n);
137 }
138 
139 // Variadic call of operator from iterator tuple
140 template <typename T>
141 struct GetProxy
142 {
143 };
144 
145 
146 template <typename T>
147 struct GetProxy<itk::ImageRegionConstIterator<T>>
148 {
149  static decltype(auto) Get(const itk::ImageRegionConstIterator<T>& t)
150  {
151  return t.Get();
152  }
153 };
154 
155 template <typename T>
156 struct GetProxy<itk::ConstNeighborhoodIterator<T>>
157 {
158  static decltype(auto) Get(const itk::ConstNeighborhoodIterator<T>& t)
159  {
160  return t;
161  }
162 };
163 
166 template <class Oper>
167 struct OperProxy : public OperProxy<typename RetrieveOperator<Oper>::Type>
168 {
169 };
170 
171 template <class Out, class... In>
172 struct OperProxy<Out (*)(In...)>
173 {
174  template <class Oper>
175  static void Compute(Oper& oper, Out& out, const In&... in)
176  {
177  out = oper(in...);
178  }
179 };
180 
181 template <class C, class Out, class... In>
182 struct OperProxy<Out (C::*)(In...)>
183 {
184  template <class Oper>
185  static void Compute(Oper& oper, Out& out, const In&... in)
186  {
187  out = oper(in...);
188  }
189 };
190 
191 template <class C, class Out, class... In>
192 struct OperProxy<Out (C::*)(In...) const>
193 {
194  template <class Oper>
195  static void Compute(Oper& oper, Out& out, const In&... in)
196  {
197  out = oper(in...);
198  }
199 };
200 
201 template <class Out, class... In>
202 struct OperProxy<void (*)(Out&, In...)>
203 {
204  template <class Oper>
205  static void Compute(Oper& oper, Out& out, const In&... in)
206  {
207  oper(out, in...);
208  }
209 };
210 
211 template <class C, class Out, class... In>
212 struct OperProxy<void (C::*)(Out&, In...)>
213 {
214  template <class Oper>
215  static void Compute(Oper& oper, Out& out, const In&... in)
216  {
217  oper(out, in...);
218  }
219 };
220 
221 template <class C, class Out, class... In>
222 struct OperProxy<void (C::*)(Out&, In...) const>
223 {
224  template <class Oper>
225  static void Compute(Oper& oper, Out& out, const In&... in)
226  {
227  oper(out, in...);
228  }
229 };
230 
231 
232 // Will be easier to write in c++17 with std::apply and fold expressions
233 template <class Tuple, class Out, class Oper, size_t... Is>
234 auto CallOperatorImpl(Tuple& t, Out& out, Oper& oper, std::index_sequence<Is...>)
235 {
236  OperProxy<Oper>::Compute(oper, out, GetProxy<typename std::remove_reference<decltype(std::get<Is>(t))>::type>::Get(std::get<Is>(t))...);
237 }
238 
239 // Will be easier to write in c++17 with std::apply and fold expressions
240 template <class Out, class Oper, typename... Args>
241 auto CallOperator(Out& out, Oper& oper, std::tuple<Args...>& t)
242 {
243  CallOperatorImpl(t, out, oper, std::make_index_sequence<sizeof...(Args)>{});
244 }
245 
246 // Variadic move of iterators
247 // Will be easier to write in c++17 with std::apply and fold expressions
248 template <class Tuple, size_t... Is>
249 auto MoveIteratorsImpl(Tuple& t, std::index_sequence<Is...>)
250 {
251  return std::make_tuple(++(std::get<Is>(t))...);
252 }
253 
254 template <typename... Args>
255 void MoveIterators(std::tuple<Args...>& t)
256 {
257  MoveIteratorsImpl(t, std::make_index_sequence<sizeof...(Args)>{});
258 }
259 
260 
261 // Default implementation does nothing
262 template <class F, class O, size_t N>
264 {
265 };
266 
267 template <class F, class T, size_t N>
269 {
270  // We can not be here if output type is VectorImage
271  static void Set(const F&, otb::Image<T>*, std::array<size_t, N>)
272  {
273  }
274 };
275 
276 // O is a VectorImage AND F has a fixed OuptutSize static constexrp size_t;
277 template <class F, class T, size_t N>
279 {
280  static void Set(const F& f, otb::VectorImage<T>* outputImage, std::array<size_t, N> inNbBands)
281  {
282  outputImage->SetNumberOfComponentsPerPixel(f.OutputSize(inNbBands));
283  }
284 };
285 
286 } // end namespace functor_filter_details
287 
288 template <class TFunction, class TNameMap>
290 {
291  // Get requested region for output
292  typename Superclass::OutputImagePointer outputPtr = this->GetOutput();
293  auto requestedRegion = outputPtr->GetRequestedRegion();
294 
295  // Propagate to each variadic inputs, including possible radius
296  // TODO: For now all inputs are padded with the radius, even if they
297  // are not neighborhood based
298  functor_filter_details::SetInputRequestedRegions<InputHasNeighborhood>(this->GetInputs(), requestedRegion, m_Radius);
299 }
300 
301 template <class TFunction, class TNameMap>
303 {
304  // Call Superclass implementation
305  Superclass::GenerateOutputInformation();
306 
307  // Get All variadic inputs
308  auto inputs = this->GetInputs();
309 
310  // Retrieve an array of number of components per input
311  auto inputNbComps = functor_filter_details::GetNumberOfComponentsPerInput(inputs);
312 
313  // Call the helper to set the number of components for the output image
314  functor_filter_details::NumberOfOutputComponents<TFunction, OutputImageType, inputNbComps.size()>::Set(m_Functor, this->GetOutput(), inputNbComps);
315 }
316 
320 template <class TFunction, class TNameMap>
321 void FunctorImageFilter<TFunction, TNameMap>::ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, itk::ThreadIdType threadId)
322 {
323  const auto& regionSize = outputRegionForThread.GetSize();
324 
325  if (regionSize[0] == 0)
326  {
327  return;
328  }
329  const auto numberOfLinesToProcess = outputRegionForThread.GetNumberOfPixels() / regionSize[0];
330  itk::ProgressReporter p(this, threadId, numberOfLinesToProcess);
331 
332  // Build output iterator
333  itk::ImageScanlineIterator<OutputImageType> outIt(this->GetOutput(), outputRegionForThread);
334 
335  // This will build a tuple of iterators to be used
336  auto inputIterators = functor_filter_details::MakeIterators(this->GetInputs(), outputRegionForThread, m_Radius, InputHasNeighborhood{});
337 
338  // Build a default value
339  typename OutputImageType::PixelType outputValueHolder;
340  itk::NumericTraits<typename OutputImageType::PixelType>::SetLength(outputValueHolder, this->GetOutput()->GetNumberOfComponentsPerPixel());
341 
342  while (!outIt.IsAtEnd())
343  {
344  // MoveIterartors will ++ all iterators in the tuple
345  for (; !outIt.IsAtEndOfLine(); ++outIt, functor_filter_details::MoveIterators(inputIterators))
346  {
347  // This will call the operator with inputIterators Get() results
348  // and fill outputValueHolder with the result.
349  functor_filter_details::CallOperator(outputValueHolder, m_Functor, inputIterators);
350  outIt.Set(outputValueHolder);
351  }
352  outIt.NextLine();
353  p.CompletedPixel(); // may throw
354  }
355 }
356 
357 } // end namespace otb
358 
359 #endif
static void Set(const F &, otb::Image< T > *, std::array< vcl_size_t, N >)
Creation of an "otb" vector image which contains metadata.
auto MoveIteratorsImpl(Tuple &t, std::index_sequence< Is... >)
static void Compute(Oper &oper, Out &out, const In &... in)
static void Compute(Oper &oper, Out &out, const In &... in)
static auto Make(const T *img, const itk::ImageRegion< 2 > &region, const itk::Size< 2 > &radius)
auto GetNumberOfComponentsPerInput(std::tuple< T... > &t)
STL namespace.
auto SetInputRequestedRegions(std::tuple< T... > &&t, const itk::ImageRegion< 2 > &region, const itk::Size< 2 > &radius)
void GenerateOutputInformation() override
auto CallOperatorImpl(Tuple &t, Out &out, Oper &oper, std::index_sequence< Is... >)
static void Compute(Oper &oper, Out &out, const In &... in)
static auto Make(const T *img, const itk::ImageRegion< 2 > &region, const itk::Size< 2 > &)
void ThreadedGenerateData(const OutputImageRegionType &outputRegionForThread, itk::ThreadIdType threadId) override
Creation of an "otb" image which contains metadata.
Definition: otbImage.h:89
static void Compute(Oper &oper, Out &out, const In &... in)
The "otb" namespace contains all Orfeo Toolbox (OTB) classes.
int SetInputRequestedRegion(const T *img, const itk::ImageRegion< 2 > &region, const itk::Size< 2 > &radius, bool pad)
auto CallOperator(Out &out, Oper &oper, std::tuple< Args... > &t)
auto SetInputRequestedRegionsImpl(Tuple &t, const itk::ImageRegion< 2 > &region, std::index_sequence< Is... >, const itk::Size< 2 > &radius)
auto MakeIterators(std::tuple< T... > &&t, const itk::ImageRegion< 2 > &region, const itk::Size< 2 > &radius, TNeigh n)
void GenerateInputRequestedRegion(void) override
auto MakeIteratorsImpl(const Tuple &t, const itk::ImageRegion< 2 > &region, const itk::Size< 2 > &radius, std::index_sequence< Is... >, TNeigh)
static void Set(const F &f, otb::VectorImage< T > *outputImage, std::array< vcl_size_t, N > inNbBands)
void MoveIterators(std::tuple< Args... > &t)
auto GetNumberOfComponentsPerInputImpl(Tuple &t, std::index_sequence< Is... >)
static void Compute(Oper &oper, Out &out, const In &... in)