Ocean
FrameInterpolatorBicubic.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) Meta Platforms, Inc. and affiliates.
3  *
4  * This source code is licensed under the MIT license found in the
5  * LICENSE file in the root directory of this source tree.
6  */
7 
8 #ifndef META_OCEAN_CV_FRAME_INTERPOLATOR_BICUBIC_H
9 #define META_OCEAN_CV_FRAME_INTERPOLATOR_BICUBIC_H
10 
11 #include "ocean/cv/CV.h"
12 
13 #include "ocean/base/Frame.h"
14 #include "ocean/base/Worker.h"
15 
16 #include "ocean/math/Numeric.h"
17 
18 namespace Ocean
19 {
20 
21 namespace CV
22 {
23 
24 /**
25  * This class implements a bicubic frame interpolator.
26  * @ingroup cv
27  */
28 class OCEAN_CV_EXPORT FrameInterpolatorBicubic
29 {
30  public:
31 
32  /**
33  * The following comfort class provides comfortable functions simplifying prototyping applications but also increasing binary size of the resulting applications.
34  * Best practice is to avoid using these functions if binary size matters,<br>
35  * as for every comfort function a corresponding function exists with specialized functionality not increasing binary size significantly.<br>
36  */
37  class OCEAN_CV_EXPORT Comfort
38  {
39  public:
40 
41  /**
42  * Resizes a given frame by a bicubic interpolation.
43  * The pixel format of the frame must be zipped with DT_UNSIGNED_INTEGER_8 as data type (e.g., FORMAT_Y8, FORMAT_RGB24, FORMAT_RGBA32, ...).
44  * @param frame The frame to resize, must be valid
45  * @param width The width of the resized frame in pixel, with range [1, infinity)
46  * @param height The height of the resized frame in pixel, with range [1, infinity)
47  * @param worker Optional worker object used for load distribution
48  * @return True, if the frame could be resized
49  */
50  static inline bool resize(Frame& frame, const unsigned int width, const unsigned int height, Worker* worker = nullptr);
51 
52  /**
53  * Resizes a given frame by a bicubic interpolation.
54  * The pixel format of the frame must be zipped with DT_UNSIGNED_INTEGER_8 as data type (e.g., FORMAT_Y8, FORMAT_RGB24, FORMAT_RGBA32, ...).
55  * @param source The source frame to resize, must be valid
56  * @param target Resulting target frame with identical frame pixel format and pixel origin as the source frame, must be valid
57  * @param worker Optional worker object used for load distribution
58  * @return True, if the frame could be resized
59  */
60  static inline bool resize(const Frame& source, Frame& target, Worker* worker = nullptr);
61  };
62 
63  /**
64  * Resizes a given frame by a bicubic interpolation.
65  * @param source The source frame buffer to resize, must be valid
66  * @param target The target frame buffer, must be valid
67  * @param sourceWidth Width of the source frame in pixel, with range [1, infinity)
68  * @param sourceHeight Height of the source frame in pixel, with range [1, infinity)
69  * @param targetWidth Width of the target frame in pixel, with range [1, infinity)
70  * @param targetHeight Height of the target frame in pixel, with range [1, infinity)
71  * @param format Pixel format of source and target frame
72  * @param sourcePaddingElements Number of padding elements used in the source frame, range: [0, infinity)
73  * @param targetPaddingElements Number of padding elements used in the target frame, range: [0, infinity)
74  * @param worker Optional worker object used for load distribution
75  * @return True, if the frame could be resized
76  */
77  static bool resize(const uint8_t* source, uint8_t* target, const unsigned int sourceWidth, const unsigned int sourceHeight, const unsigned int targetWidth, const unsigned int targetHeight, const FrameType::PixelFormat format, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, Worker* worker = nullptr);
78 
79  /**
80  * Resizes a given zipped frame by a bicubic interpolation.
81  * @param source The source frame buffer, must be valid
82  * @param target The target frame buffer, must be valid
83  * @param sourceWidth Width of the source frame in pixel, with range [1, infinity)
84  * @param sourceHeight Height of the source frame in pixel, with range [1, infinity)
85  * @param targetWidth Width of the target frame in pixel, with range [1, infinity)
86  * @param targetHeight Height of the target frame in pixel, with range [1, infinity)
87  * @param sourcePaddingElements Number of padding elements used in the source frame, range: [0, infinity)
88  * @param targetPaddingElements Number of padding elements used in the target frame, range: [0, infinity)
89  * @param worker Optional worker object to distribute the computation
90  * @tparam tChannels Number of frame channels, with range [1, infinity)
91  */
92  template <unsigned int tChannels>
93  static void resize8BitPerChannel(const uint8_t* source, uint8_t* target, const unsigned int sourceWidth, const unsigned int sourceHeight, const unsigned int targetWidth, const unsigned int targetHeight, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, Worker* worker = nullptr);
94 
95  private:
96 
97  /**
98  * Resizes a subset of a given zipped frame by a horizontal bicubic interpolation only.
99  * @param source The source frame buffer to resize, with size (sourceWidth x sourceHeight)
100  * @param target Intermediate target frame buffer, with size (targetWidth x sourceHeight)
101  * @param sourceWidth Width of the source frame in pixel, with range [1, infinity)
102  * @param targetWidth Width of the target frame in pixel, with range [1, infinity)
103  * @param height The height of the source and target frame in pixel, with range [1, infinity)
104  * @param sourcePaddingElements Number of padding elements used in the source frame, range: [0, infinity)
105  * @param targetPaddingElements Number of padding elements used in the target frame, range: [0, infinity)
106  * @param firstRow First (including) row to convert, with range [0, height)
107  * @param numberRows Number of rows to convert, with range [1, height - firstRow]
108  * @tparam tChannels Number of frame channels, with range [1, infinity)
109  */
110  template <unsigned int tChannels>
111  static void resizeHorizontal8BitPerChannelSubset(const uint8_t* source, uint8_t* target, const unsigned int sourceWidth, const unsigned int targetWidth, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, const unsigned int firstRow, const unsigned int numberRows);
112 
113  /**
114  * Resizes a subset of a given zipped frame by a vertical bicubic interpolation only.
115  * @param source Resized and horizontal filtered source frame buffer, with size (targetWidth x sourceHeight)
116  * @param target The target frame buffer, with size (targetWidth x targetHeight)
117  * @param sourceHeight Height of the source frame in pixel, with range [1, infinity)
118  * @param targetHeight Height of the target frame in pixel, with range [1, infinity)
119  * @param width The width of the source and target frame in pixel, with range [1, infinity)
120  * @param sourcePaddingElements Number of padding elements used in the source frame, range: [0, infinity)
121  * @param targetPaddingElements Number of padding elements used in the target frame, range: [0, infinity)
122  * @param firstColumn First (including) column to convert, with range [0, width)
123  * @param numberColumns Number of columns to convert, with range [1, width - firstColumn]
124  * @tparam tChannels Number of frame channels, with range [1, infinity)
125  */
126  template <unsigned int tChannels>
127  static void resizeVertical8BitPerChannelSubset(const uint8_t* source, uint8_t* target, const unsigned int sourceHeight, const unsigned int targetHeight, const unsigned int width, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, const unsigned int firstColumn, const unsigned int numberColumns);
128 };
129 
130 inline bool FrameInterpolatorBicubic::Comfort::resize(Frame& frame, const unsigned int width, const unsigned int height, Worker* worker)
131 {
132  ocean_assert(frame.isValid());
133  ocean_assert(width >= 1u && height >= 1u);
134 
136  {
137  // we need a generic pixel format with unsigned char data type e.g., FORMAT_Y8, FORAT_RGB24, etc.
138  ocean_assert(false && "Invalid frame type!");
139  return false;
140  }
141 
142  if (width == frame.width() && height == frame.height())
143  {
144  return true;
145  }
146 
147  Frame target(FrameType(frame, width, height));
148 
149  if (!Comfort::resize(frame, target, worker))
150  {
151  return false;
152  }
153 
154  frame = std::move(target);
155  return true;
156 }
157 
158 inline bool FrameInterpolatorBicubic::Comfort::resize(const Frame& source, Frame& target, Worker* worker)
159 {
160  ocean_assert(source && target);
161 
162  if (source.dataType() != FrameType::DT_UNSIGNED_INTEGER_8 || source.pixelFormat() != target.pixelFormat() || source.pixelOrigin() != target.pixelOrigin())
163  {
164  ocean_assert(false && "Invalid frame type!");
165  return false;
166  }
167 
168  return FrameInterpolatorBicubic::resize(source.constdata<uint8_t>(), target.data<uint8_t>(), source.width(), source.height(), target.width(), target.height(), source.pixelFormat(), source.paddingElements(), target.paddingElements(), worker);
169 }
170 
171 template <unsigned int tChannels>
172 void FrameInterpolatorBicubic::resize8BitPerChannel(const uint8_t* source, uint8_t* target, const unsigned int sourceWidth, const unsigned int sourceHeight, const unsigned int targetWidth, const unsigned int targetHeight, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, Worker* worker)
173 {
174  ocean_assert(source != nullptr && target != nullptr);
175  ocean_assert(sourceWidth != 0u && sourceHeight != 0u);
176  ocean_assert(targetWidth != 0u && targetHeight != 0u);
177 
178  if (sourceWidth == targetWidth)
179  {
180  if (worker)
181  {
182  worker->executeFunction(Worker::Function::createStatic(&FrameInterpolatorBicubic::resizeVertical8BitPerChannelSubset<tChannels>, source, target, sourceHeight, targetHeight, targetWidth, sourcePaddingElements, targetPaddingElements, 0u, 0u), 0u, targetWidth);
183  }
184  else
185  {
186  resizeVertical8BitPerChannelSubset<tChannels>(source, target, sourceHeight, targetHeight, targetWidth, sourcePaddingElements, targetPaddingElements, 0u, targetWidth);
187  }
188  }
189  else if (sourceHeight == targetHeight)
190  {
191  if (worker)
192  {
193  worker->executeFunction(Worker::Function::createStatic(&FrameInterpolatorBicubic::resizeHorizontal8BitPerChannelSubset<tChannels>, source, target, sourceWidth, targetWidth, targetHeight, sourcePaddingElements, targetPaddingElements, 0u, 0u), 0u, sourceHeight);
194  }
195  else
196  {
197  resizeHorizontal8BitPerChannelSubset<tChannels>(source, target, sourceWidth, targetWidth, targetHeight, sourcePaddingElements, targetPaddingElements, 0u, targetHeight);
198  }
199  }
200  else
201  {
202  Frame intermediateFrame(FrameType(targetWidth, sourceHeight, FrameType::genericPixelFormat<FrameType::DT_UNSIGNED_INTEGER_8, tChannels>(), FrameType::ORIGIN_UPPER_LEFT));
203 
204  if (worker)
205  {
206  worker->executeFunction(Worker::Function::createStatic(&FrameInterpolatorBicubic::resizeHorizontal8BitPerChannelSubset<tChannels>, source, intermediateFrame.data<uint8_t>(), sourceWidth, intermediateFrame.width(), sourceHeight, sourcePaddingElements, intermediateFrame.paddingElements(), 0u, 0u), 0u, sourceHeight);
207  worker->executeFunction(Worker::Function::createStatic(&FrameInterpolatorBicubic::resizeVertical8BitPerChannelSubset<tChannels>, intermediateFrame.constdata<uint8_t>(), target, intermediateFrame.height(), targetHeight, targetWidth, intermediateFrame.paddingElements(), targetPaddingElements, 0u, 0u), 0u, targetWidth);
208  }
209  else
210  {
211  resizeHorizontal8BitPerChannelSubset<tChannels>(source, intermediateFrame.data<uint8_t>(), sourceWidth, intermediateFrame.width(), sourceHeight, sourcePaddingElements, intermediateFrame.paddingElements(), 0u, sourceHeight);
212  resizeVertical8BitPerChannelSubset<tChannels>(intermediateFrame.constdata<uint8_t>(), target, intermediateFrame.height(), targetHeight, targetWidth, intermediateFrame.paddingElements(), targetPaddingElements, 0u, targetWidth);
213  }
214  }
215 }
216 
217 template <unsigned int tChannels>
218 void FrameInterpolatorBicubic::resizeHorizontal8BitPerChannelSubset(const uint8_t* source, uint8_t* target, const unsigned int sourceWidth, const unsigned int targetWidth, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, const unsigned int firstRow, const unsigned int numberRows)
219 {
220  static_assert(tChannels != 0u, "Invalid channel number!");
221 
222  ocean_assert(source != nullptr && target != nullptr);
223  ocean_assert(sourceWidth != 0u && targetWidth != 0u);
224  ocean_assert(height != 0u);
225 
226  ocean_assert_and_suppress_unused(firstRow + numberRows <= height, height);
227 
228  const float targetToSourceX = float(sourceWidth) / float(targetWidth);
229 
230  const unsigned int sourceStrideElements = sourceWidth * tChannels + sourcePaddingElements;
231  const unsigned int targetStrideElements = targetWidth * tChannels + targetPaddingElements;
232 
233  const uint8_t* sourceRow = source + firstRow * sourceStrideElements;
234  uint8_t* targetElement = target + firstRow * targetStrideElements;
235 
236  for (unsigned int y = firstRow; y < firstRow + numberRows; ++y)
237  {
238  for (unsigned int tx = 0; tx < targetWidth; ++tx)
239  {
240  const float sx = targetToSourceX * float(tx);
241 
242  ocean_assert(sx >= 0.0f && sx <= float(sourceWidth));
243 
244  const unsigned int sx1 = (unsigned int)(min(NumericF::floor(sx), float(sourceWidth - 1)));
245  ocean_assert(int(sx1) >= 0);
246 
247  const unsigned int sx0 = (unsigned int)(max(0, int(sx1) - 1));
248  const unsigned int sx2 = min(sx1 + 1u, sourceWidth - 1u);
249  const unsigned int sx3 = min(sx2 + 1u, sourceWidth - 1u);
250 
251  ocean_assert(sx >= float(sx1));
252  ocean_assert(sx1 >= sx0 && sx2 >= sx1 && sx3 >= sx2);
253 
254  const float d = sx - float(sx1);
255 
256  for (unsigned int n = 0u; n < tChannels; ++n)
257  {
258  const float color0 = sourceRow[sx0 * tChannels + n];
259  const float color1 = sourceRow[sx1 * tChannels + n];
260  const float color2 = sourceRow[sx2 * tChannels + n];
261  const float color3 = sourceRow[sx3 * tChannels + n];
262 
263  float color = 0.166666666667f * color0 + 0.66666666667f * color1 + 0.166666666667f * color2;
264 
266  {
267  color += (-0.166666666667f * color0 + 0.5f * color1 - 0.5f * color2 + 0.166666666667f * color3) * d * d * d
268  + (0.5f * color0 - 1.0f * color1 + 0.5f * color2) * d * d
269  + (-0.5f * color0 + 0.5f * color2) * d;
270  }
271 
272  ocean_assert(NumericF::isInsideRange(0.0f, color, 256.0f));
273 
274  *targetElement++ = uint8_t(color);
275  }
276  }
277 
278  targetElement += targetPaddingElements;
279  sourceRow += sourceStrideElements;
280  }
281 }
282 
283 template <unsigned int tChannels>
284 void FrameInterpolatorBicubic::resizeVertical8BitPerChannelSubset(const uint8_t* source, uint8_t* target, const unsigned int sourceHeight, const unsigned int targetHeight, const unsigned int width, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, const unsigned int firstColumn, const unsigned int numberColumns)
285 {
286  static_assert(tChannels != 0u, "Invalid channel number!");
287 
288  ocean_assert(source != nullptr && target != nullptr);
289  ocean_assert(sourceHeight != 0u && targetHeight != 0u);
290  ocean_assert(width != 0u);
291 
292  ocean_assert(firstColumn + numberColumns <= width);
293 
294  const float targetToSourceY = float(sourceHeight) / float(targetHeight);
295 
296  const unsigned int sourceStrideElements = width * tChannels + sourcePaddingElements;
297  const unsigned int targetStrideElements = width * tChannels + targetPaddingElements;
298 
299  uint8_t* targetColor = target + firstColumn * tChannels - 1u;
300 
301  for (unsigned int ty = 0u; ty < targetHeight; ++ty)
302  {
303  const float sy = targetToSourceY * float(ty);
304 
305  ocean_assert(sy >= 0.0f && sy <= float(sourceHeight));
306 
307  const unsigned int sy1 = (unsigned int)(min(NumericF::floor(sy), float(sourceHeight - 1u)));
308  ocean_assert(int(sy1) >= 0);
309 
310  const unsigned int sy0 = (unsigned int)(max(0, int(sy1) - 1));
311  const unsigned int sy2 = min(sy1 + 1u, sourceHeight - 1u);
312  const unsigned int sy3 = min(sy2 + 1u, sourceHeight - 1u);
313 
314  ocean_assert(sy >= float(sy1));
315  ocean_assert(sy1 >= sy0 && sy2 >= sy1 && sy3 >= sy2);
316 
317  const float d = sy - float(sy1);
318 
319  const uint8_t* color0 = source + sy0 * sourceStrideElements + tChannels * firstColumn;
320  const uint8_t* color1 = source + sy1 * sourceStrideElements + tChannels * firstColumn;
321  const uint8_t* color2 = source + sy2 * sourceStrideElements + tChannels * firstColumn;
322  const uint8_t* color3 = source + sy3 * sourceStrideElements + tChannels * firstColumn;
323 
324  for (unsigned int tx = firstColumn; tx < firstColumn + numberColumns; ++tx)
325  {
326  for (unsigned int n = 0u; n < tChannels; ++n)
327  {
328  float color = 0.166666666667f * float(color0[n]) + 0.66666666667f * float(color1[n]) + 0.166666666667f * float(color2[n]);
329 
331  {
332  color += (-0.166666666667f * float(color0[n]) + 0.5f * float(color1[n]) - 0.5f * float(color2[n]) + 0.166666666667f * float(color3[n])) * d * d * d
333  + (0.5f * float(color0[n]) - 1.0f * float(color1[n]) + 0.5f * float(color2[n])) * d * d
334  + (-0.5f * float(color0[n]) + 0.5f * float(color2[n])) * d;
335  }
336 
337  ocean_assert(color >= 0.0f && color < 256.0f);
338  *++targetColor = uint8_t(color);
339  }
340 
341  color0 += tChannels;
342  color1 += tChannels;
343  color2 += tChannels;
344  color3 += tChannels;
345  }
346 
347  targetColor += targetStrideElements - numberColumns * tChannels;
348  }
349 }
350 
351 }
352 
353 }
354 
355 #endif // META_OCEAN_CV_FRAME_INTERPOLATOR_BICUBIC_H
The following comfort class provides comfortable functions simplifying prototyping applications but a...
Definition: FrameInterpolatorBicubic.h:38
static bool resize(Frame &frame, const unsigned int width, const unsigned int height, Worker *worker=nullptr)
Resizes a given frame by a bicubic interpolation.
Definition: FrameInterpolatorBicubic.h:130
This class implements a bicubic frame interpolator.
Definition: FrameInterpolatorBicubic.h:29
static void resizeVertical8BitPerChannelSubset(const uint8_t *source, uint8_t *target, const unsigned int sourceHeight, const unsigned int targetHeight, const unsigned int width, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, const unsigned int firstColumn, const unsigned int numberColumns)
Resizes a subset of a given zipped frame by a vertical bicubic interpolation only.
Definition: FrameInterpolatorBicubic.h:284
static bool resize(const uint8_t *source, uint8_t *target, const unsigned int sourceWidth, const unsigned int sourceHeight, const unsigned int targetWidth, const unsigned int targetHeight, const FrameType::PixelFormat format, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, Worker *worker=nullptr)
Resizes a given frame by a bicubic interpolation.
static void resize8BitPerChannel(const uint8_t *source, uint8_t *target, const unsigned int sourceWidth, const unsigned int sourceHeight, const unsigned int targetWidth, const unsigned int targetHeight, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, Worker *worker=nullptr)
Resizes a given zipped frame by a bicubic interpolation.
Definition: FrameInterpolatorBicubic.h:172
static void resizeHorizontal8BitPerChannelSubset(const uint8_t *source, uint8_t *target, const unsigned int sourceWidth, const unsigned int targetWidth, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, const unsigned int firstRow, const unsigned int numberRows)
Resizes a subset of a given zipped frame by a horizontal bicubic interpolation only.
Definition: FrameInterpolatorBicubic.h:218
static Caller< void > createStatic(typename StaticFunctionPointerMaker< void, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass >::Type function)
Creates a new caller container for a static function with no function parameter.
Definition: Caller.h:2876
This class implements Ocean's image class.
Definition: Frame.h:1792
const T * constdata(const unsigned int planeIndex=0u) const
Returns a pointer to the read-only pixel data of a specific plane.
Definition: Frame.h:4168
T * data(const unsigned int planeIndex=0u)
Returns a pointer to the pixel data of a specific plane.
Definition: Frame.h:4159
bool isValid() const
Returns whether this frame is valid.
Definition: Frame.h:4448
unsigned int paddingElements(const unsigned int planeIndex=0u) const
Returns the optional number of padding elements at the end of each row for a specific plane.
Definition: Frame.h:4042
Definition of a frame type composed by the frame dimension, pixel format and pixel origin.
Definition: Frame.h:30
PixelFormat
Definition of all pixel formats available in the Ocean framework.
Definition: Frame.h:183
unsigned int width() const
Returns the width of the frame format in pixel.
Definition: Frame.h:3143
PixelOrigin pixelOrigin() const
Returns the pixel origin of the frame.
Definition: Frame.h:3188
PixelFormat pixelFormat() const
Returns the pixel format of the frame.
Definition: Frame.h:3153
@ ORIGIN_UPPER_LEFT
The first pixel lies in the upper left corner, the last pixel in the lower right corner.
Definition: Frame.h:1050
@ DT_UNSIGNED_INTEGER_8
Unsigned 8 bit integer data type (uint8_t).
Definition: Frame.h:41
unsigned int height() const
Returns the height of the frame in pixel.
Definition: Frame.h:3148
DataType dataType() const
Returns the data type of the pixel format of this frame.
Definition: Frame.h:3163
static constexpr bool isInsideRange(const T lower, const T value, const T upper, const T epsilon=NumericT< T >::eps())
Returns whether a value lies between a given range up to a provided epsilon border.
Definition: Numeric.h:2872
static T floor(const T value)
Returns the largest integer value that is not greater than the given value.
Definition: Numeric.h:2026
static constexpr bool isNotEqualEps(const T value)
Returns whether a value is not smaller than or equal to a small epsilon.
Definition: Numeric.h:2237
This class implements a worker able to distribute function calls over different threads.
Definition: Worker.h:33
bool executeFunction(const Function &function, const unsigned int first, const unsigned int size, const unsigned int firstIndex=(unsigned int)(-1), const unsigned int sizeIndex=(unsigned int)(-1), const unsigned int minimalIterations=1u, const unsigned int threadIndex=(unsigned int)(-1))
Executes a callback function separable by two function parameters.
The namespace covering the entire Ocean framework.
Definition: Accessor.h:15