OTB  10.0.0
Orfeo Toolbox
otbFunctorImageFilter.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 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(
76  std::get<Is>(t),
77  region,
78  radius,
79  std::tuple_element<Is, HasNeighborhood>::type::value)...
80  );
81 }
82 
83 // Will be easier to write in c++17 with std::apply and fold expressions
84 template <typename HasNeighborhood, typename... T>
85 auto SetInputRequestedRegions(std::tuple<T...>&& t, const itk::ImageRegion<2>& region, const itk::Size<2>& radius)
86 {
87  return SetInputRequestedRegionsImpl<HasNeighborhood>(t, region, std::make_index_sequence<sizeof...(T)>{}, radius);
88 }
89 
90 // Will be easier to write in c++17 with std::apply and fold expressions
91 template <class Tuple, size_t... Is>
92 auto GetNumberOfComponentsPerInputImpl(Tuple& t, std::index_sequence<Is...>)
93 {
94  return std::array<size_t, sizeof...(Is)>{{std::get<Is>(t)->GetNumberOfComponentsPerPixel()...}};
95 }
96 
97 // Will be easier to write in c++17 with std::apply and fold expressions
98 template <typename... T>
99 auto GetNumberOfComponentsPerInput(std::tuple<T...>& t)
100 {
101  return GetNumberOfComponentsPerInputImpl(t, std::make_index_sequence<sizeof...(T)>{});
102 }
103 
104 template <typename N>
106 {
107 };
108 
109 template <>
110 struct MakeIterator<std::false_type>
111 {
112  template <class T>
113  static auto Make(const T* img, const itk::ImageRegion<2>& region, const itk::Size<2>&)
114  {
115  itk::ImageRegionConstIterator<T> it(img, region);
116  return it;
117  }
118 };
119 
120 template <>
121 struct MakeIterator<std::true_type>
122 {
123  template <class T>
124  static auto Make(const T* img, const itk::ImageRegion<2>& region, const itk::Size<2>& radius)
125  {
126  itk::ConstNeighborhoodIterator<T> it(radius, img, region);
127  return it;
128  }
129 };
130 
131 // Will be easier to write in c++17 with std::apply and fold expressions
132 template <class TNeigh, class Tuple, size_t... Is>
133 auto MakeIteratorsImpl(const Tuple& t, const itk::ImageRegion<2>& region, const itk::Size<2>& radius, std::index_sequence<Is...>, TNeigh)
134 {
135  return std::make_tuple(MakeIterator<typename std::tuple_element<Is, TNeigh>::type>::Make(std::get<Is>(t), region, radius)...);
136 }
137 
138 // Will be easier to write in c++17 with std::apply and fold expressions
139 template <class TNeigh, typename... T>
140 auto MakeIterators(std::tuple<T...>&& t, const itk::ImageRegion<2>& region, const itk::Size<2>& radius, TNeigh n)
141 {
142  return MakeIteratorsImpl(t, region, radius, std::make_index_sequence<sizeof...(T)>{}, n);
143 }
144 
145 // Variadic call of operator from iterator tuple
146 template <typename T>
147 struct GetProxy
148 {
149 };
150 
151 
152 template <typename T>
153 struct GetProxy<itk::ImageRegionConstIterator<T>>
154 {
155  static decltype(auto) Get(const itk::ImageRegionConstIterator<T>& t)
156  {
157  return t.Get();
158  }
159 };
160 
161 template <typename T>
162 struct GetProxy<itk::ConstNeighborhoodIterator<T>>
163 {
164  static decltype(auto) Get(const itk::ConstNeighborhoodIterator<T>& t)
165  {
166  return t;
167  }
168 };
169 
172 template <class Oper>
173 struct OperProxy : public OperProxy<typename RetrieveOperator<Oper>::Type>
174 {
175 };
176 
177 template <class Out, class... In>
178 struct OperProxy<Out (*)(In...)>
179 {
180  template <class Oper>
181  static void Compute(Oper& oper, Out& out, const In&... in)
182  {
183  out = oper(in...);
184  }
185 };
186 
187 template <class C, class Out, class... In>
188 struct OperProxy<Out (C::*)(In...)>
189 {
190  template <class Oper>
191  static void Compute(Oper& oper, Out& out, const In&... in)
192  {
193  out = oper(in...);
194  }
195 };
196 
197 template <class C, class Out, class... In>
198 struct OperProxy<Out (C::*)(In...) const>
199 {
200  template <class Oper>
201  static void Compute(Oper& oper, Out& out, const In&... in)
202  {
203  out = oper(in...);
204  }
205 };
206 
207 template <class Out, class... In>
208 struct OperProxy<void (*)(Out&, In...)>
209 {
210  template <class Oper>
211  static void Compute(Oper& oper, Out& out, const In&... in)
212  {
213  oper(out, in...);
214  }
215 };
216 
217 template <class C, class Out, class... In>
218 struct OperProxy<void (C::*)(Out&, In...)>
219 {
220  template <class Oper>
221  static void Compute(Oper& oper, Out& out, const In&... in)
222  {
223  oper(out, in...);
224  }
225 };
226 
227 template <class C, class Out, class... In>
228 struct OperProxy<void (C::*)(Out&, In...) const>
229 {
230  template <class Oper>
231  static void Compute(Oper& oper, Out& out, const In&... in)
232  {
233  oper(out, in...);
234  }
235 };
236 
237 
238 // Will be easier to write in c++17 with std::apply and fold expressions
239 template <class Tuple, class Out, class Oper, size_t... Is>
240 auto CallOperatorImpl(Tuple& t, Out& out, Oper& oper, std::index_sequence<Is...>)
241 {
242  OperProxy<Oper>::Compute(oper, out, GetProxy<typename std::remove_reference<decltype(std::get<Is>(t))>::type>::Get(std::get<Is>(t))...);
243 }
244 
245 // Will be easier to write in c++17 with std::apply and fold expressions
246 template <class Out, class Oper, typename... Args>
247 auto CallOperator(Out& out, Oper& oper, std::tuple<Args...>& t)
248 {
249  CallOperatorImpl(t, out, oper, std::make_index_sequence<sizeof...(Args)>{});
250 }
251 
252 // Variadic move of iterators
253 // Will be easier to write in c++17 with std::apply and fold expressions
254 template <class Tuple, size_t... Is>
255 auto MoveIteratorsImpl(Tuple& t, std::index_sequence<Is...>)
256 {
257  return std::make_tuple(++(std::get<Is>(t))...);
258 }
259 
260 template <typename... Args>
261 void MoveIterators(std::tuple<Args...>& t)
262 {
263  MoveIteratorsImpl(t, std::make_index_sequence<sizeof...(Args)>{});
264 }
265 
266 
267 // Default implementation does nothing
268 template <class F, class O, size_t N>
270 {
271 };
272 
273 template <class F, class T, size_t N>
275 {
276  // We can not be here if output type is VectorImage
277  static void Set(const F&, otb::Image<T>*, std::array<size_t, N>)
278  {
279  }
280 };
281 
282 // O is a VectorImage AND F has a fixed OutputSize static constexrp size_t;
283 template <class F, class T, size_t N>
285 {
286  static void Set(const F& f, otb::VectorImage<T>* outputImage, std::array<size_t, N> inNbBands)
287  {
288  outputImage->SetNumberOfComponentsPerPixel(f.OutputSize(inNbBands));
289  }
290 };
291 
292 } // end namespace functor_filter_details
293 
294 template <class TFunction, class TNameMap>
296 {
297  // Get requested region for output
298  typename Superclass::OutputImagePointer outputPtr = this->GetOutput();
299  auto requestedRegion = outputPtr->GetRequestedRegion();
300 
301  // Propagate to each variadic inputs, including possible radius
302  // TODO: For now all inputs are padded with the radius, even if they
303  // are not neighborhood based
304  functor_filter_details::SetInputRequestedRegions<InputHasNeighborhood>(this->GetInputs(), requestedRegion, m_Radius);
305 }
306 
307 template <class TFunction, class TNameMap>
309 {
310  // Call Superclass implementation
311  Superclass::GenerateOutputInformation();
312 
313  // Get All variadic inputs
314  auto inputs = this->GetInputs();
315 
316  // Retrieve an array of number of components per input
317  auto inputNbComps = functor_filter_details::GetNumberOfComponentsPerInput(inputs);
318 
319  // Call the helper to set the number of components for the output image
320  functor_filter_details::NumberOfOutputComponents<TFunction, OutputImageType, inputNbComps.size()>::Set(m_Functor, this->GetOutput(), inputNbComps);
321 }
322 
326 template <class TFunction, class TNameMap>
328 {
329  const auto& regionSize = outputRegionForThread.GetSize();
330 
331  if (regionSize[0] == 0)
332  {
333  return;
334  }
335 
336  // Build output iterator
337  itk::ImageScanlineIterator<OutputImageType> outIt(this->GetOutput(), outputRegionForThread);
338 
339  // This will build a tuple of iterators to be used
340  auto inputIterators = functor_filter_details::MakeIterators(this->GetInputs(), outputRegionForThread, m_Radius, InputHasNeighborhood{});
341 
342  // Build a default value
343  typename OutputImageType::PixelType outputValueHolder;
344  itk::NumericTraits<typename OutputImageType::PixelType>::SetLength(outputValueHolder, this->GetOutput()->GetNumberOfComponentsPerPixel());
345 
346  while (!outIt.IsAtEnd())
347  {
348  // MoveIterartors will ++ all iterators in the tuple
349  for (; !outIt.IsAtEndOfLine(); ++outIt, functor_filter_details::MoveIterators(inputIterators))
350  {
351  // This will call the operator with inputIterators Get() results
352  // and fill outputValueHolder with the result.
353  functor_filter_details::CallOperator(outputValueHolder, m_Functor, inputIterators);
354  outIt.Set(outputValueHolder);
355  }
356  outIt.NextLine();
357  }
358 }
359 
360 } // end namespace otb
361 
362 #endif
typename SuperclassHelper::InputHasNeighborhood InputHasNeighborhood
typename Superclass::OutputImageType OutputImageType
void GenerateInputRequestedRegion(void) override
void GenerateOutputInformation() override
typename OutputImageType::RegionType OutputImageRegionType
void DynamicThreadedGenerateData(const OutputImageRegionType &outputRegionForThread) override
Creation of an "otb" image which contains metadata.
Definition: otbImage.h:92
Creation of an "otb" vector image which contains metadata.
virtual void SetNumberOfComponentsPerPixel(unsigned int n) override
auto CallOperatorImpl(Tuple &t, Out &out, Oper &oper, std::index_sequence< Is... >)
void MoveIterators(std::tuple< Args... > &t)
auto GetNumberOfComponentsPerInput(std::tuple< T... > &t)
int SetInputRequestedRegion(const T *img, const itk::ImageRegion< 2 > &region, const itk::Size< 2 > &radius, bool pad)
auto SetInputRequestedRegionsImpl(Tuple &t, const itk::ImageRegion< 2 > &region, std::index_sequence< Is... >, const itk::Size< 2 > &radius)
auto GetNumberOfComponentsPerInputImpl(Tuple &t, std::index_sequence< Is... >)
auto MakeIteratorsImpl(const Tuple &t, const itk::ImageRegion< 2 > &region, const itk::Size< 2 > &radius, std::index_sequence< Is... >, TNeigh)
auto SetInputRequestedRegions(std::tuple< T... > &&t, const itk::ImageRegion< 2 > &region, const itk::Size< 2 > &radius)
auto MakeIterators(std::tuple< T... > &&t, const itk::ImageRegion< 2 > &region, const itk::Size< 2 > &radius, TNeigh n)
auto MoveIteratorsImpl(Tuple &t, std::index_sequence< Is... >)
auto CallOperator(Out &out, Oper &oper, std::tuple< Args... > &t)
The "otb" namespace contains all Orfeo Toolbox (OTB) classes.
static auto Make(const T *img, const itk::ImageRegion< 2 > &region, const itk::Size< 2 > &)
static auto Make(const T *img, const itk::ImageRegion< 2 > &region, const itk::Size< 2 > &radius)
static void Set(const F &, otb::Image< T > *, std::array< vcl_size_t, N >)
static void Set(const F &f, otb::VectorImage< T > *outputImage, std::array< vcl_size_t, N > inNbBands)
static void Compute(Oper &oper, Out &out, const In &... in)
static void Compute(Oper &oper, Out &out, const In &... in)
static void Compute(Oper &oper, Out &out, const In &... in)
static void Compute(Oper &oper, Out &out, const In &... in)
static void Compute(Oper &oper, Out &out, const In &... in)