Ocean
IntegralImage.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_INTEGRAL_IMAGE_H
9 #define META_OCEAN_CV_INTEGRAL_IMAGE_H
10 
11 #include "ocean/cv/CV.h"
12 
13 #include "ocean/base/Frame.h"
14 
15 #include "ocean/math/Numeric.h"
16 
17 #include <limits>
18 #include <type_traits>
19 
20 namespace Ocean
21 {
22 
23 namespace CV
24 {
25 
26 /**
27  * This class provides functions to create an integral image from a gray scale image.
28  * @ingroup cv
29  */
30 class OCEAN_CV_EXPORT IntegralImage
31 {
32  public:
33 
34  /**
35  * The following comfort class provides comfortable functions simplifying prototyping applications but also increasing binary size of the resulting applications.
36  * Best practice is to avoid using these functions if binary size matters,<br>
37  * as for every comfort function a corresponding function exists with specialized functionality not increasing binary size significantly.<br>
38  */
39  class OCEAN_CV_EXPORT Comfort
40  {
41  public:
42 
43  /**
44  * Creates an integral image from a given 1-plane image and adds an extra line (one column and one row) with zeros to the left and top image border.
45  * In case, the given frame has more than one channel, the channels of the resulting integral image will be interleaved (not stored as individual planes).<br>
46  * The resulting lined integral image has the following scheme:
47  * <pre>
48  * ------------ ---------
49  * |000000000000| |
50  * |0|----------| |
51  * |0| | padding |
52  * |0| Integral | |
53  * |0| | |
54  * ------------ ---------
55  * </pre>
56  * The resolution of the integral image is: (width + 1)x(height + 1).
57  * @param frame The frame for which the integral image will be returned, must be valid
58  * @return The resulting integral image, invalid if the pixel format of the given frame is not supported
59  */
60  static Frame createLinedImage(const Frame& frame);
61 
62  /**
63  * Creates a bordered integral image from a given 1-plane image and adds an extra border to the resulting integral image.
64  * In case, the given frame has more than one channel, the channels of the resulting integral image will be interleaved (not stored as individual planes).<br>
65  * The resulting integral image has a (non interfering) border.<br>
66  * The bordered integral image has the following scheme:
67  * <pre>
68  * ------------------------- ---------
69  * |0000000000000000000000000| |
70  * |0|-----------------------| |
71  * |0| | | | |
72  * |0| 0 | 0 | 0 | |
73  * |0| | | | |
74  * |0|-----|-----------|-----| |
75  * |0| | | > | |
76  * |0| | | > | |
77  * |0| 0 | Integral | > | padding |
78  * |0| | | > | |
79  * |0| | | > | |
80  * |0|-----|-----------|-----| |
81  * |0| | | | |
82  * |0| 0 | V | V | |
83  * |0| | | | |
84  * -----------------------------------
85  * </pre>
86  * Columns or rows with '0' receive a null value.<br>
87  * Rows with '>' receive the last valid integral value from the left.<br>
88  * Rows with 'v' receive the last valid integral value from the top.<br>
89  * Further, additionally to the border, the integral image contains one column at the left border and one row at the top border with zeros.<br>
90  * The resolution of the integral image is: (width + 1 + 2 * border)x(height + 1 + 2 * border).
91  * @param frame The image for which the integral image will be returned, with size width x height, must be valid
92  * @param border The thickness of the border in pixel, with range [1, infinity)
93  * @return The resulting integral image, invalid if the pixel format of the given frame is not supported
94  */
95  static Frame createBorderedImage(const Frame& frame, const unsigned int border);
96 
97  protected:
98 
99  /**
100  * Creates an integral image from a given 1-plane image and adds an extra line (one column and one row) with zeros to the left and top image border.
101  * @param frame The frame for which the integral image will be returned, must be valid
102  * @return The resulting integral image, invalid if the pixel format of the given frame is not supported
103  * @tparam T The data type of each frame element
104  * @tparam TIntegral The data type of each integral element
105  */
106  template <typename T, typename TIntegral>
107  static Frame createLinedImage(const Frame& frame);
108 
109  /**
110  * Creates a bordered integral image from a given 1-plane image and adds an extra border to the resulting integral image.
111  * @param frame The frame for which the integral image will be returned, must be valid
112  * @param border The thickness of the border in pixel, with range [1, infinity)
113  * @return The resulting integral image, invalid if the pixel format of the given frame is not supported
114  * @tparam T The data type of each frame element
115  * @tparam TIntegral The data type of each integral element
116  */
117  template <typename T, typename TIntegral>
118  static Frame createBorderedImage(const Frame& frame, const unsigned int border);
119  };
120 
121  public:
122 
123  /**
124  * Creates an integral image from a given 1-plane image.
125  * The resulting integral image will have the same resolution as the given image (without any extra borders).<br>
126  * In case, the given frame has more than one channel, the channels of the resulting integral image will be interleaved (not stored as individual planes).<br>
127  * The resulting integral image has the following scheme:
128  * <pre>
129  * ---------- ---------
130  * | | |
131  * | Integral | padding |
132  * | | |
133  * ---------- ---------
134  * </pre>
135  * The resolution of the integral image is: width x height.
136  * @param source The source frame for which the integral image will be created, must be valid
137  * @param integral The target integral image, must be valid
138  * @param width The width of the source frame in pixel, with range [0, infinity)
139  * @param height The height of the source frame in pixel, with range [0, infinity)
140  * @param sourcePaddingElements The number of padding elements at the end of each row of the source frame, in elements, with range [0, infinity)
141  * @param integralPaddingElements The number of padding elements at the end of each row of the integral frame, in elements, with range [0, infinity)
142  * @tparam T The data type of each pixel element of the source frame e.g., 'uint8_t' or 'float'
143  * @tparam TIntegral The data type of each integral pixel element, e.g., 'unsigned int' or 'double'
144  * @tparam tChannels The number of channels the source frame has, with range [1, infinity)
145  * @see createLinedImage().
146  */
147  template <typename T, typename TIntegral, unsigned int tChannels>
148  static void createImage(const T* source, TIntegral* integral, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int integralPaddingElements);
149 
150  /**
151  * Creates an integral image from a given 1-plane image and adds an extra line (one column and one row) with zeros to the left and top image border.
152  * In case, the given frame has more than one channel, the channels of the resulting integral image will be interleaved (not stored as individual planes).<br>
153  * The resulting lined integral image has the following scheme:
154  * <pre>
155  * ------------ ---------
156  * |000000000000| |
157  * |0|----------| |
158  * |0| | padding |
159  * |0| Integral | |
160  * |0| | |
161  * ------------ ---------
162  * </pre>
163  * The resolution of the integral image is: (width + 1)x(height + 1).
164  * @param source The image for which the integral image will be determined, with size width x height, must be valid
165  * @param integral The resulting integral image, with size (width + 1)x(height + 1), must be valid
166  * @param width The width of the source frame in pixel, with range [0, infinity)
167  * @param height The height of the source frame in pixel, with range [0, infinity)
168  * @param sourcePaddingElements The number of padding elements at the end of each row of the source frame, in elements, with range [0, infinity)
169  * @param integralPaddingElements The number of padding elements at the end of each row of the integral frame, in elements, with range [0, infinity)
170  * @tparam T The data type of each pixel element of the source frame e.g., 'uint8_t' or 'float'
171  * @tparam TIntegral The data type of each integral pixel element, e.g., 'unsigned int' or 'double'
172  * @tparam tChannels The number of channels the source frame has, with range [1, infinity)
173  * @see createImage(), createBorderedImage().
174  */
175  template <typename T, typename TIntegral, unsigned int tChannels>
176  static void createLinedImage(const T* source, TIntegral* integral, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int integralPaddingElements);
177 
178  /**
179  * Creates an integral image with squared pixel intensities from a given 1-plane image and adds an extra line (one column and one row) with zeros to the left and top image border.
180  * In case, the given frame has more than one channel, the channels of the resulting integral image will be interleaved (not stored as individual planes).<br>
181  * The resulting lined integral image has the following scheme:
182  * <pre>
183  * -------------- ---------
184  * |00000000000000| |
185  * |0|------------| |
186  * |0| | padding |
187  * |0| Integral^2 | |
188  * |0| | |
189  * -------------- ---------
190  * </pre>
191  * The resolution of the integral image is: (width + 1)x(height + 1).
192  * @param source The image for which the integral image will be determined, with size width x height, must be valid
193  * @param integral The resulting integral image, with size (width + 1)x(height + 1), must be valid
194  * @param width The width of the source frame in pixel, with range [0, infinity)
195  * @param height The height of the source frame in pixel, with range [0, infinity)
196  * @param sourcePaddingElements The number of padding elements at the end of each row of the source frame, in elements, with range [0, infinity)
197  * @param integralPaddingElements The number of padding elements at the end of each row of the integral frame, in elements, with range [0, infinity)
198  * @tparam T The data type of each pixel element of the source frame e.g., 'uint8_t' or 'float'
199  * @tparam TIntegral The data type of each integral pixel element, e.g., 'unsigned int' or 'double'
200  * @tparam tChannels The number of channels the source frame has, with range [1, infinity)
201  * @see createLinedImageAndSquared(), createImage(), createBorderedImage().
202  */
203  template <typename T, typename TIntegral, unsigned int tChannels>
204  static void createLinedImageSquared(const T* source, TIntegral* integral, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int integralPaddingElements);
205 
206  /**
207  * Creates an integral image and squared integral image from a given 1-plane image and adds an extra line (one column and one row) with zeros to the left and top image border.
208  * The resulting integral image provides access to pixel intensities and squared pixel intensities.<br>
209  * Beware: As the same data type is used for both integral data, the depth of the data type must be large enough, or the image resolution must be small enough to avoid overflows.<br>
210  * Pixel intensities and squared pixel intensities are interleaved so that both values can be looked-up at the same memory location.<br>
211  * In case, the given frame has more than one channel, the channels of the resulting integral image will be interleaved (not stored as individual planes).<br>
212  * The resulting lined integral image has the following scheme:
213  * <pre>
214  * ------------------------------------ ---------
215  * |000000000000000000000000000000000000| |
216  * |00|---------------------------------| |
217  * |00| | padding |
218  * |00| I0 I1 I0^2 I1^2 I0 I1 I0^2 I1^2 | |
219  * |00| | |
220  * ------------------------------------ ---------
221  * With I0 integral value of the first channel,
222  * I1 integral value of the second channel,
223  * I0^2 integral squared value of the first channel,
224  * I1^2 integral squared value of the second channel.
225  * </pre>
226  * The resolution of the integral image is: ((width + 1) * 2)x(height + 1).
227  * @param source The image for which the integral image will be determined, with size width x height, must be valid
228  * @param integralAndSquared The resulting integral (and squared integral) image, with size ((width + 1) * 2)x(height + 1), must be valid
229  * @param width The width of the source frame in pixel, with range [1, infinity)
230  * @param height The height of the source frame in pixel, with range [1, infinity)
231  * @param sourcePaddingElements The number of padding elements at the end of each row of the source frame, in elements, with range [0, infinity)
232  * @param integralAndSquaredPaddingElements The number of padding elements at the end of each row of the integral frame, in elements, with range [0, infinity)
233  * @tparam T The data type of each pixel element of the source frame e.g., 'uint8_t' or 'float'
234  * @tparam TIntegralAndSquared The data type of each integral (and squared integral) pixel element, e.g., 'unsigned int' or 'double'
235  * @tparam tChannels The number of channels the source frame has, with range [1, infinity)
236  * @see createLinedImage(), createLinedImageSquared().
237  */
238  template <typename T, typename TIntegralAndSquared, unsigned int tChannels>
239  static void createLinedImageAndSquared(const T* source, TIntegralAndSquared* integralAndSquared, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int integralAndSquaredPaddingElements);
240 
241  /**
242  * Creates an integral image and squared integral image from a given 1-plane image and adds an extra line (one column and one row) with zeros to the left and top image border.
243  * The result are two individual integral images, one for the pixel intensities and one for the squared pixel intensities.<br>
244  * The resulting lined integral images have the following scheme:
245  * <pre>
246  * Integral image: Integral squared image:
247  * ---------------- --------- ------------------------ ---------
248  * |0000000000000000| | |000000000000000000000000| |
249  * |00|-------------| | |00|---------------------| |
250  * |00| | padding | |00| | padding |
251  * |00| I0 I1 I0 I1 | | |00| I0^2 I1^2 I0^2 I1^2 | |
252  * |00| | | |00| | |
253  * ---------------- --------- ------------------------ ---------
254  * With I0 integral value of the first channel,
255  * I1 integral value of the second channel,
256  * I0^2 integral squared value of the first channel,
257  * I1^2 integral squared value of the second channel.
258  * </pre>
259  * The resolution of the integral image are: (width + 1)x(height + 1).
260  * @param source The image for which the integral image will be determined, with size width x height, must be valid
261  * @param integral The resulting integral image, with size (width + 1)x(height + 1), must be valid
262  * @param integralSquared The resulting squared integral image, with size (width + 1)x(height + 1), must be valid
263  * @param width The width of the source frame in pixel, with range [1, infinity)
264  * @param height The height of the source frame in pixel, with range [1, infinity)
265  * @param sourcePaddingElements The number of padding elements at the end of each row of the source frame, in elements, with range [0, infinity)
266  * @param integralPaddingElements The number of padding elements at the end of each row of the integral frame, in elements, with range [0, infinity)
267  * @param integralSquaredPaddingElements The number of padding elements at the end of each row of the integral squared frame, in elements, with range [0, infinity)
268  * @tparam T The data type of each pixel element of the source frame e.g., 'uint8_t' or 'float'
269  * @tparam TIntegral The data type of each integral pixel element, e.g., 'unsigned int' or 'double'
270  * @tparam TIntegralSquared The data type of each squared integral pixel element, e.g., 'unsigned int' or 'double'
271  * @tparam tChannels The number of channels the source frame has, with range [1, infinity)
272  * @see createLinedImage(), createLinedImageSquared().
273  */
274  template <typename T, typename TIntegral, typename TIntegralSquared, unsigned int tChannels>
275  static void createLinedImageAndSquared(const T* source, TIntegral* integral, TIntegralSquared* integralSquared, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int integralPaddingElements, const unsigned int integralSquaredPaddingElements);
276 
277  /**
278  * Creates a bordered integral image from a given 1-plane image and adds an extra border to the resulting integral image.
279  * In case, the given frame has more than one channel, the channels of the resulting integral image will be interleaved (not stored as individual planes).<br>
280  * The resulting integral image has a (non interfering) border.<br>
281  * The bordered integral image has the following scheme:
282  * <pre>
283  * ------------------------- ---------
284  * |0000000000000000000000000| |
285  * |0|-----------------------| |
286  * |0| | | | |
287  * |0| 0 | 0 | 0 | |
288  * |0| | | | |
289  * |0|-----|-----------|-----| |
290  * |0| | | > | |
291  * |0| | | > | |
292  * |0| 0 | Integral | > | padding |
293  * |0| | | > | |
294  * |0| | | > | |
295  * |0|-----|-----------|-----| |
296  * |0| | | | |
297  * |0| 0 | V | V | |
298  * |0| | | | |
299  * -----------------------------------
300  * </pre>
301  * Columns or rows with '0' receive a null value.<br>
302  * Rows with '>' receive the last valid integral value from the left.<br>
303  * Rows with 'v' receive the last valid integral value from the top.<br>
304  * Further, additionally to the border, the integral image contains one column at the left border and one row at the top border with zeros.<br>
305  * Therefore, the entire integral image width is: 1 + 2 * border + originalWidth,<br>
306  * and the entire height is: 1 + 2 * border + originalHeight.<br>
307  * @param source The image for which the integral image will be determined, with size width x height, must be valid
308  * @param integral The resulting integral image, with size (width + 1 + 2 * border)x(height + 1 + 2 * border), must be valid
309  * @param width The width of the source frame in pixel, with range [0, infinity)
310  * @param height The height of the source frame in pixel, with range [0, infinity)
311  * @param border The thickness of the border in pixel, with range [1, infinity)
312  * @param sourcePaddingElements The number of padding elements at the end of each row of the source frame, in elements, with range [0, infinity)
313  * @param integralPaddingElements The number of padding elements at the end of each row of the integral frame, in elements, with range [0, infinity)
314  * @tparam T The data type of each pixel element of the source frame e.g., 'uint8_t' or 'float'
315  * @tparam TIntegral The data type of each integral pixel element, e.g., 'unsigned int' or 'double'
316  * @tparam tChannels The number of channels the source frame has, with range [1, infinity)
317  * @see createLinedImage().
318  */
319  template <typename T, typename TIntegral, unsigned int tChannels>
320  static void createBorderedImage(const T* source, TIntegral* integral, const unsigned int width, const unsigned int height, const unsigned int border, const unsigned int sourcePaddingElements, const unsigned int integralPaddingElements);
321 
322  /**
323  * Creates a bordered squared integral image from a given 1-plane image and adds an extra border to the resulting integral image.
324  * In case, the given frame has more than one channel, the channels of the resulting integral image will be interleaved (not stored as individual planes).<br>
325  * The resulting integral image has a (non interfering) border.<br>
326  * The bordered integral image has the following scheme:
327  * <pre>
328  * -------------------------- ---------
329  * |00000000000000000000000000| |
330  * |0|------------------------| |
331  * |0| | | | |
332  * |0| 0 | 0 | 0 | |
333  * |0| | | | |
334  * |0|-----|------------|-----| |
335  * |0| | | > | |
336  * |0| | | > | |
337  * |0| 0 | Integral^2 | > | padding |
338  * |0| | | > | |
339  * |0| | | > | |
340  * |0|-----|------------|-----| |
341  * |0| | | | |
342  * |0| 0 | V | V | |
343  * |0| | | | |
344  * ------------------------------------
345  * </pre>
346  * Columns or rows with '0' receive a null value.<br>
347  * Rows with '>' receive the last valid integral value from the left.<br>
348  * Rows with 'v' receive the last valid integral value from the top.<br>
349  * Further, additionally to the border, the integral image contains one column at the left border and one row at the top border with zeros.<br>
350  * Therefore, the entire integral image width is: 1 + 2 * border + originalWidth,<br>
351  * and the entire height is: 1 + 2 * border + originalHeight.<br>
352  * @param source The image for which the integral image will be determined, with size width x height, must be valid
353  * @param integral The resulting integral image, with size (width + 1)x(height + 1), must be valid
354  * @param width The width of the source frame in pixel, with range [0, infinity)
355  * @param height The height of the source frame in pixel, with range [0, infinity)
356  * @param border The thickness of the border in pixel, with range [1, infinity)
357  * @param sourcePaddingElements The number of padding elements at the end of each row of the source frame, in elements, with range [0, infinity)
358  * @param integralPaddingElements The number of padding elements at the end of each row of the integral frame, in elements, with range [0, infinity)
359  * @tparam T The data type of each pixel element of the source frame e.g., 'uint8_t' or 'float'
360  * @tparam TIntegral The data type of each integral pixel element, e.g., 'unsigned int' or 'double'
361  * @tparam tChannels The number of channels the source frame has, with range [1, infinity)
362  * @see createLinedImage().
363  */
364  template <typename T, typename TIntegral, unsigned int tChannels>
365  static void createBorderedImageSquared(const T* source, TIntegral* integral, const unsigned int width, const unsigned int height, const unsigned int border, const unsigned int sourcePaddingElements, const unsigned int integralPaddingElements);
366 
367  /**
368  * Creates a bordered integral image from a given 1-plane image and adds an extra border with mirrored image content to the resulting integral image.
369  * The resulting integral image has an extra border created from the original frame with mirrored border pixels.<br>
370  * Additionally, independent from the border size, the integral image contains one column at the left border and one row at the top border with zeros.<br>
371  * Therefore, the entire integral image width is: 1 + 2 * border + originalWidth,<br>
372  * and the entire height is: 1 + 2 * border + originalHeight.<br>
373  * The bordered integral image has the following scheme:
374  * <pre>
375  * ------------------------- ---------
376  * |0000000000000000000000000| |
377  * |0|-----------------------| |
378  * |0| | | | |
379  * |0| m | mirrored | m | |
380  * |0| | | | |
381  * |0|-----|-----------|-----| |
382  * |0| | | | |
383  * |0| | | | |
384  * |0| m | Integral | m | padding |
385  * |0| | | | |
386  * |0| | | | |
387  * |0|-----|-----------|-----| |
388  * |0| | | | |
389  * |0| m | mirrored | m | |
390  * |0| | | | |
391  * -----------------------------------
392  * </pre>
393  * @param source The image for which the integral image will be determined, with size width x height, must be valid
394  * @param integral The resulting integral image, with size (1 + border + width)x(1 + border + height), must be valid
395  * @param width The width of the source frame in pixel, with range [0, infinity)
396  * @param height The height of the source frame in pixel, with range [0, infinity)
397  * @param border The thickness of the border in pixel, with range [1, min(width, height)]
398  * @param sourcePaddingElements The number of padding elements at the end of each row of the source frame, in elements, with range [0, infinity)
399  * @param integralPaddingElements The number of padding elements at the end of each row of the integral frame, in elements, with range [0, infinity)
400  * @tparam T The data type of each pixel element of the source frame e.g., 'uint8_t' or 'float'
401  * @tparam TIntegral The data type of each integral pixel element, e.g., 'unsigned int' or 'double'
402  * @tparam tChannels The number of channels the source frame has, with range [1, infinity)
403  */
404  template <typename T, typename TIntegral, unsigned int tChannels>
405  static void createBorderedImageMirror(const T* source, TIntegral* integral, const unsigned int width, const unsigned int height, const unsigned int border, const unsigned int sourcePaddingElements, const unsigned int integralPaddingElements);
406 
407  /**
408  * Creates a bordered squared integral image from a given 1-plane image and adds an extra border with mirrored image content to the resulting integral image.
409  * The resulting integral image has an extra border created from the original frame with mirrored border pixels.<br>
410  * Additionally, independent from the border size, the integral image contains one column at the left border and one row at the top border with zeros.<br>
411  * Therefore, the entire integral image width is: 1 + 2 * border + originalWidth,<br>
412  * and the entire height is: 1 + 2 * border + originalHeight.<br>
413  * The bordered integral image has the following scheme:
414  * <pre>
415  * ------------------------------- ---------
416  * |0000000000000000000000000000000| |
417  * |0|-----------------------------| |
418  * |0| | | | |
419  * |0| m^2 | mirrored^2 | m^2 | |
420  * |0| | | | |
421  * |0|-------|-------------|-------| |
422  * |0| | | | |
423  * |0| | | | |
424  * |0| m^2 | Integral^2 | m^2 | padding |
425  * |0| | | | |
426  * |0| | | | |
427  * |0|-------|-------------|-------| |
428  * |0| | | | |
429  * |0| m^2 | mirrored^2 | m^2 | |
430  * |0| | | | |
431  * -----------------------------------------
432  * </pre>
433  * @param source The image for which the integral image will be determined, with size width x height, must be valid
434  * @param integral The resulting integral image, with size (1 + border + width)x(1 + border + height), must be valid
435  * @param width The width of the source frame in pixel, with range [0, infinity)
436  * @param height The height of the source frame in pixel, with range [0, infinity)
437  * @param border The thickness of the border in pixel, with range [1, min(width, height)]
438  * @param sourcePaddingElements The number of padding elements at the end of each row of the source frame, in elements, with range [0, infinity)
439  * @param integralPaddingElements The number of padding elements at the end of each row of the integral frame, in elements, with range [0, infinity)
440  * @tparam T The data type of each pixel element of the source frame e.g., 'uint8_t' or 'float'
441  * @tparam TIntegral The data type of each integral pixel element, e.g., 'unsigned int' or 'double'
442  * @tparam tChannels The number of channels the source frame has, with range [1, infinity)
443  */
444  template <typename T, typename TIntegral, unsigned int tChannels>
445  static void createBorderedImageSquaredMirror(const T* source, TIntegral* integral, const unsigned int width, const unsigned int height, const unsigned int border, const unsigned int sourcePaddingElements, const unsigned int integralPaddingElements);
446 
447  /**
448  * Determines the sum of elements within a window from an integral image.
449  * The lined integral image must have the following scheme:
450  * <pre>
451  * integralStrideElements
452  * |<------------------------>|
453  *
454  * ---------------- ---------
455  * |0000000000000000| |
456  * |0|--------------| |
457  * |0| | padding |
458  * |0| Integral | |
459  * |0| | |
460  * ----------------- ---------
461  *
462  * |<------------>|
463  * original width
464  *
465  * |<-------------->|
466  * lined integral width
467  * </pre>
468  * @param linedIntegral The lined integral image, must be valid
469  * @param linedIntegralStrideElements The number of elements between two row start positions in the lined integral image, in elements, may contain padding elements at the end of each row, with range [originalImageWidth + 1, infinity)
470  * @param windowLeft The left starting point of the window, in pixel, with range [0, originalImageWidth - 1]
471  * @param windowTop The top starting point of the window, in pixel, with range [0, originalImageHeight - 1]
472  * @param windowWidth The width of the window, in pixel, with range [1, originalImageWidth - windowLeft]
473  * @param windowHeight The height of the window, in pixel, with range [1, originalImageHeight - windowTop]
474  * @return The resulting sum of elements
475  * @tparam TIntegral The data type of the elements in the integral image
476  */
477  template <typename TIntegral>
478  static OCEAN_FORCE_INLINE TIntegral linedIntegralSum(const TIntegral* const linedIntegral, const unsigned int linedIntegralStrideElements, const unsigned int windowLeft, const unsigned int windowTop, const unsigned int windowWidth, const unsigned int windowHeight);
479 
480  /**
481  * Determines the variance of elements within a window from two integral images.
482  * Variance is calculated based on the following equation:
483  * <pre>
484  * Var(x) = E[(x - E[x])^2] = E[x^2] - E[x]^2
485  * </pre>
486  * The variance is determined based on the standard lined integral image and the lined integral image for squared values.<br>
487  * The lined integral image must have the following scheme:
488  * <pre>
489  * integralStrideElements integralSquaredStrideElements
490  * |<------------------------>| |<------------------------------>|
491  *
492  * ---------------- --------- -------------------- -----------
493  * |0000000000000000| | |00000000000000000000| |
494  * |0|--------------| | |0|------------------| |
495  * |0| | padding | |0| | padding |
496  * |0| Integral | | |0| Integral^2 | |
497  * |0| | | |0| | |
498  * ----------------- --------- -------------------- -----------
499  *
500  * |<------------>| |<---------------->|
501  * original width original width
502  *
503  * |<-------------->| |<------------------>|
504  * lined integral width lined integral squared width
505  * </pre>
506  * @param linedIntegral The lined integral image, must be valid
507  * @param linedIntegralSquared The lined integral image with squared values, must be valid
508  * @param integralStrideElements The number of elements between two row starts in the lined integral image, in elements, may contain padding elements at the end of each row, with range [2, infinity)
509  * @param integralStrideSquaredElements The number of elements between two row starts in the lined integral squared image, in elements, may contain padding elements at the end of each row, with range [2, infinity)
510  * @param windowLeft The left starting point of the window, in pixel, with range [0, originalImageWidth - 1]
511  * @param windowTop The top starting point of the window, in pixel, with range [0, originalImageHeight - 1]
512  * @param windowWidth The width of the window, in pixel, with range [1, originalImageWidth - windowLeft]
513  * @param windowHeight The height of the window, in pixel, with range [1, originalImageHeight - windowTop]
514  * @param mean Optional resulting mean value and (`tReturnMean = true`), nullptr otherwise (and `tReturnMean = false`)
515  * @return The resulting variance, with range [0, infinity)
516  * @tparam TIntegral The data type of the elements in the integral image
517  * @tparam TIntegralSquared The data type of the elements in the integral squared image
518  * @tparam TVariance The data type of the resulting variance (also used for intermediate calculations)
519  * @tparam tReturnMean True, to return the mean value (`mean` must be defined); False, to skip calculating of mean (`mean` must be nullptr)
520  */
521  template <typename TIntegral, typename TIntegralSquared, typename TVariance, bool tReturnMean = false>
522  static inline TVariance linedIntegralVariance(const TIntegral* linedIntegral, const TIntegralSquared* linedIntegralSquared, const unsigned int integralStrideElements, const unsigned int integralStrideSquaredElements, const unsigned int windowLeft, const unsigned int windowTop, const unsigned int windowWidth, const unsigned int windowHeight, TVariance* mean = nullptr);
523 
524  /**
525  * Determines the variance of elements within two windows from two integral images.
526  * The two windows are treated as one joined area.<br>
527  * Variance is calculated based on the following equation:
528  * <pre>
529  * Var(x) = E[(x - E[x])^2] = E[x^2] - E[x]^2
530  * </pre>
531  * The variance is determined based on the standard lined integral image and the lined integral image for squared values.<br>
532  * The lined integral image must have the following scheme:
533  * <pre>
534  * integralStrideElements integralSquaredStrideElements
535  * |<------------------------>| |<------------------------------>|
536  *
537  * ---------------- --------- -------------------- -----------
538  * |0000000000000000| | |00000000000000000000| |
539  * |0|--------------| | |0|------------------| |
540  * |0| | padding | |0| | padding |
541  * |0| Integral | | |0| Integral^2 | |
542  * |0| | | |0| | |
543  * ----------------- --------- -------------------- -----------
544  *
545  * |<------------>| |<---------------->|
546  * original width original width
547  *
548  * |<-------------->| |<------------------>|
549  * lined integral width lined integral squared width
550  * </pre>
551  * @param linedIntegral The lined integral image, must be valid
552  * @param linedIntegralSquared The lined integral image with squared values, must be valid
553  * @param integralStrideElements The number of elements between two row starts in the lined integral image, in elements, may contain padding elements at the end of each row, with range [2, infinity)
554  * @param integralStrideSquaredElements The number of elements between two row starts in the lined integral squared image, in elements, may contain padding elements at the end of each row, with range [2, infinity)
555  * @param windowALeft The left starting point of the first window, in pixel, with range [0, originalImageWidth - 1]
556  * @param windowATop The top starting point of the first window, in pixel, with range [0, originalImageHeight - 1]
557  * @param windowAWidth The width of the first window, in pixel, with range [1, originalImageWidth - windowALeft]
558  * @param windowAHeight The height of the first window, in pixel, with range [1, originalImageHeight - windowATop]
559  * @param windowBLeft The left starting point of the second window, in pixel, with range [0, originalImageWidth - 1]
560  * @param windowBTop The top starting point of the second window, in pixel, with range [0, originalImageHeight - 1]
561  * @param windowBWidth The width of the second window, in pixel, with range [1, originalImageWidth - windowBLeft]
562  * @param windowBHeight The height of the second window, in pixel, with range [1, originalImageHeight - windowBTop]
563  * @param mean Optional resulting mean value and (`tReturnMean = true`), nullptr otherwise (and `tReturnMean = false`)
564  * @return The resulting variance, with range [0, infinity)
565  * @tparam TIntegral The data type of the elements in the integral image
566  * @tparam TIntegralSquared The data type of the elements in the integral squared image
567  * @tparam TVariance The data type of the resulting variance (also used for intermediate calculations)
568  * @tparam tReturnMean True, to return the mean value (`mean` must be defined); False, to skip calculating of mean (`mean` must be nullptr)
569  */
570  template <typename TIntegral, typename TIntegralSquared, typename TVariance, bool tReturnMean = false>
571  static inline TVariance linedIntegralVariance(const TIntegral* linedIntegral, const TIntegralSquared* linedIntegralSquared, const unsigned int integralStrideElements, const unsigned int integralStrideSquaredElements, const unsigned int windowALeft, const unsigned int windowATop, const unsigned int windowAWidth, const unsigned int windowAHeight, const unsigned int windowBLeft, const unsigned int windowBTop, const unsigned int windowBWidth, const unsigned int windowBHeight, TVariance* mean = nullptr);
572 
573  /**
574  * Determines the variance of elements within three windows from two integral images.
575  * The three windows are treated as one joined area.<br>
576  * @param linedIntegral The lined integral image, must be valid
577  * @param linedIntegralSquared The lined integral image with squared values, must be valid
578  * @param integralStrideElements The number of elements between two row starts in the lined integral image, in elements, may contain padding elements at the end of each row, with range [2, infinity)
579  * @param integralStrideSquaredElements The number of elements between two row starts in the lined integral squared image, in elements, may contain padding elements at the end of each row, with range [2, infinity)
580  * @param windowALeft The left starting point of the first window, in pixel, with range [0, originalImageWidth - 1]
581  * @param windowATop The top starting point of the first window, in pixel, with range [0, originalImageHeight - 1]
582  * @param windowAWidth The width of the first window, in pixel, with range [1, originalImageWidth - windowALeft]
583  * @param windowAHeight The height of the first window, in pixel, with range [1, originalImageHeight - windowATop]
584  * @param windowBLeft The left starting point of the second window, in pixel, with range [0, originalImageWidth - 1]
585  * @param windowBTop The top starting point of the second window, in pixel, with range [0, originalImageHeight - 1]
586  * @param windowBWidth The width of the second window, in pixel, with range [1, originalImageWidth - windowBLeft]
587  * @param windowBHeight The height of the second window, in pixel, with range [1, originalImageHeight - windowBTop]
588  * @param windowCLeft The left starting point of the third window, in pixel, with range [0, originalImageWidth - 1]
589  * @param windowCTop The top starting point of the third window, in pixel, with range [0, originalImageHeight - 1]
590  * @param windowCWidth The width of the third window, in pixel, with range [1, originalImageWidth - windowCLeft]
591  * @param windowCHeight The height of the third window, in pixel, with range [1, originalImageHeight - windowCTop]
592  * @param mean Optional resulting mean value and (`tReturnMean = true`), nullptr otherwise (and `tReturnMean = false`)
593  * @return The resulting variance, with range [0, infinity)
594  * @tparam TIntegral The data type of the elements in the integral image
595  * @tparam TIntegralSquared The data type of the elements in the integral squared image
596  * @tparam TVariance The data type of the resulting variance (also used for intermediate calculations)
597  * @tparam tReturnMean True, to return the mean value (`mean` must be defined); False, to skip calculating of mean (`mean` must be nullptr)
598  */
599  template <typename TIntegral, typename TIntegralSquared, typename TVariance, bool tReturnMean = false>
600  static inline TVariance linedIntegralVariance(const TIntegral* linedIntegral, const TIntegralSquared* linedIntegralSquared, const unsigned int integralStrideElements, const unsigned int integralStrideSquaredElements, const unsigned int windowALeft, const unsigned int windowATop, const unsigned int windowAWidth, const unsigned int windowAHeight, const unsigned int windowBLeft, const unsigned int windowBTop, const unsigned int windowBWidth, const unsigned int windowBHeight, const unsigned int windowCLeft, const unsigned int windowCTop, const unsigned int windowCWidth, const unsigned int windowCHeight, TVariance* mean = nullptr);
601 
602  private:
603 
604 #if defined(OCEAN_HARDWARE_NEON_VERSION) && OCEAN_HARDWARE_NEON_VERSION >= 10
605 
606 #if defined(__aarch64__)
607 
608  /**
609  * Creates an integral image from 8 bit images with 1 channel and adds an extra line with zeros to the left and top image border and applies NEON instructions.
610  * @param source The image for which the integral image will be determined, with size width x height, must be valid
611  * @param integral The resulting integral image, with size (width + 1)x(height + 1), must be valid
612  * @param width The width of the given image in pixel, with range [8, 4096^2]
613  * @param height The height of the given image in pixel, with range [1, 4096^2 / width]
614  * @param sourcePaddingElements The number of padding elements at the end of each row of the source frame, in elements, with range [0, infinity)
615  * @param integralPaddingElements The number of padding elements at the end of each row of the integral frame, in elements, with range [0, infinity)
616  * @see createLinedImage8BitPerChannel<tChannels>().
617  */
618  static void createLinedImage1Channel8BitNEON(const uint8_t* source, uint32_t* integral, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int integralPaddingElements);
619 
620 #endif // defined(__aarch64__)
621 
622 #endif // OCEAN_HARDWARE_NEON_VERSION >= 10
623 
624  /**
625  * Returns the square value of the given parameter.
626  * @param value The value to be squared
627  * @return Square value
628  * @tparam T The data type of the value to be squared
629  * @tparam TSquared The data type of the squared value
630  */
631  template <typename T, typename TSquared>
632  static inline TSquared sqr(const T& value);
633 };
634 
635 template <typename T, typename TIntegral, unsigned int tChannels>
636 void IntegralImage::createImage(const T* source, TIntegral* integral, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int integralPaddingElements)
637 {
638  static_assert(std::is_signed<T>::value == std::is_signed<TIntegral>::value, "The integral image must have the same sign-properties as the source image!");
639  static_assert(sizeof(T) <= sizeof(TIntegral), "Invalid integral elements!");
640  static_assert(tChannels >= 1u, "Invalid channel number!");
641 
642  ocean_assert(source != nullptr && integral != nullptr);
643  ocean_assert(width >= 1u && height >= 1u);
644 
645  ocean_assert(sizeof(T) >=4 || double(NumericT<T>::maxValue()) * double(width * height) <= double(NumericT<TIntegral>::maxValue()));
646 
647  const T* const sourceFirstRowEnd = source + width * tChannels;
648  const T* const sourceEnd = source + (width * tChannels + sourcePaddingElements) * height;
649  const TIntegral* integralPreviousRow = integral;
650 
651  TIntegral previousIntegral[tChannels];
652  for (unsigned int n = 0u; n < tChannels; ++n)
653  {
654  previousIntegral[n] = TIntegral(0);
655  }
656 
657  // the first row
658 
659  while (source != sourceFirstRowEnd)
660  {
661  for (unsigned int n = 0u; n < tChannels; ++n)
662  {
663  previousIntegral[n] += TIntegral(*source++);
664  }
665 
666  for (unsigned int n = 0u; n < tChannels; ++n)
667  {
668  *integral++ = previousIntegral[n];
669  }
670  }
671 
672  source += sourcePaddingElements;
673  integral += integralPaddingElements;
674 
675 
676  // the remaining rows
677 
678  while (source != sourceEnd)
679  {
680  const T* const sourceRowEnd = source + width * tChannels;
681 
682  for (unsigned int n = 0u; n < tChannels; ++n)
683  {
684  previousIntegral[n] = TIntegral(0);
685  }
686 
687  while (source != sourceRowEnd)
688  {
689  for (unsigned int n = 0u; n < tChannels; ++n)
690  {
691  previousIntegral[n] += TIntegral(*source++);
692  }
693 
694  for (unsigned int n = 0u; n < tChannels; ++n)
695  {
696  *integral++ = previousIntegral[n] + *integralPreviousRow++;
697  }
698  }
699 
700  source += sourcePaddingElements;
701  integral += integralPaddingElements;
702  integralPreviousRow += integralPaddingElements;
703  }
704 }
705 
706 template <typename T, typename TIntegral, unsigned int tChannels>
707 void IntegralImage::createLinedImage(const T* source, TIntegral* integral, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int integralPaddingElements)
708 {
709  static_assert(std::is_signed<T>::value == std::is_signed<TIntegral>::value, "The integral image must have the same sign-properties as the source image!");
710  static_assert(sizeof(T) <= sizeof(TIntegral), "Invalid integral elements!");
711  static_assert(tChannels >= 1u, "Invalid channel number!");
712 
713  ocean_assert(source != nullptr && integral != nullptr);
714  ocean_assert(width >= 1u && height >= 1u);
715 
716  ocean_assert((std::is_floating_point<T>::value) || (double(NumericT<T>::maxValue()) * double(width * height) <= double(NumericT<TIntegral>::maxValue())));
717 
718 #if defined(OCEAN_HARDWARE_NEON_VERSION) && OCEAN_HARDWARE_NEON_VERSION >= 10 && defined(__aarch64__)
719 
720  if (std::is_same<T, uint8_t>::value && std::is_same<TIntegral, uint32_t>::value && tChannels == 1u && width >= 8u)
721  {
722  createLinedImage1Channel8BitNEON((const uint8_t*)source, (uint32_t*)integral, width, height, sourcePaddingElements, integralPaddingElements);
723  return;
724  }
725 
726 #endif // OCEAN_HARDWARE_NEON_VERSION >= 10 && defined(__aarch64__)
727 
728  /*
729  * This is the resulting lined integral image.
730  * ------------
731  * |000000000000|
732  * |0|----------|
733  * |0| |
734  * |0| Integral |
735  * |0| |
736  * |------------
737  */
738 
739  // entire top line will be set to zero
740  memset(integral, 0x00, (width + 1u) * tChannels * sizeof(TIntegral));
741 
742 #ifdef OCEAN_DEBUG
743  for (unsigned int n = 0u; n < width + 1u; ++n)
744  {
745  ocean_assert(integral[n] == TIntegral(0));
746  }
747 #endif
748 
749  // we calculate the first row of the integral image
750 
751  const T* const sourceFirstRowEnd = source + width * tChannels;
752  const T* const sourceEnd = source + (width * tChannels + sourcePaddingElements) * height;
753 
754  integral += (width + 1u) * tChannels + integralPaddingElements;
755  const TIntegral* integralPreviousRow = integral;
756 
757  TIntegral previousIntegral[tChannels] = {TIntegral(0)};
758 
759  // left pixel
760  for (unsigned int n = 0u; n < tChannels; ++n)
761  {
762  *integral++ = TIntegral(0);
763  }
764 
765  // the remaining pixels of the first row
766 
767  while (source != sourceFirstRowEnd)
768  {
769  for (unsigned int n = 0u; n < tChannels; ++n)
770  {
771  previousIntegral[n] += *source++;
772  }
773 
774  for (unsigned int n = 0u; n < tChannels; ++n)
775  {
776  *integral++ = previousIntegral[n];
777  }
778  }
779 
780  source += sourcePaddingElements;
781  integral += integralPaddingElements;
782 
783 
784  // we calculate the remaining rows
785 
786  while (source != sourceEnd)
787  {
788  const T* const sourceRowEnd = source + width * tChannels;
789 
790  for (unsigned int n = 0u; n < tChannels; ++n)
791  {
792  previousIntegral[n] = TIntegral(0);
793  }
794 
795  // left pixel
796  for (unsigned int n = 0u; n < tChannels; ++n)
797  {
798  *integral++ = TIntegral(0);
799  }
800 
801  integralPreviousRow += tChannels;
802 
803  while (source != sourceRowEnd)
804  {
805  for (unsigned int n = 0u; n < tChannels; ++n)
806  {
807  previousIntegral[n] += *source++;
808  }
809 
810  for (unsigned int n = 0u; n < tChannels; ++n)
811  {
812  *integral++ = previousIntegral[n] + *integralPreviousRow++;
813  }
814  }
815 
816  source += sourcePaddingElements;
817  integral += integralPaddingElements;
818  integralPreviousRow += integralPaddingElements;
819  }
820 }
821 
822 template <typename T, typename TIntegral, unsigned int tChannels>
823 void IntegralImage::createLinedImageSquared(const T* source, TIntegral* integral, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int integralPaddingElements)
824 {
825  static_assert(sizeof(T) <= sizeof(TIntegral), "Invalid integral elements!");
826  static_assert(tChannels >= 1u, "Invalid channel number!");
827 
828  ocean_assert(source != nullptr && integral != nullptr);
829  ocean_assert(width >= 1u && height >= 1u);
830 
831  ocean_assert((std::is_floating_point<T>::value) || (NumericD::sqr(double(NumericT<T>::maxValue())) * double(width * height) <= double(NumericT<TIntegral>::maxValue())));
832 
833  // entire top line will be set to zero
834  memset(integral, 0x00, (width + 1u) * tChannels * sizeof(TIntegral));
835 
836 #ifdef OCEAN_DEBUG
837  for (unsigned int n = 0u; n < width + 1u; ++n)
838  {
839  ocean_assert(integral[n] == TIntegral(0));
840  }
841 #endif
842 
843  // we calculate the first row of the integral image
844 
845  const T* const sourceFirstRowEnd = source + width * tChannels;
846  const T* const sourceEnd = source + (width * tChannels + sourcePaddingElements) * height;
847 
848  integral += (width + 1u) * tChannels + integralPaddingElements;
849  const TIntegral* integralPreviousRow = integral;
850 
851  TIntegral previousIntegral[tChannels] = {TIntegral(0)};
852 
853  // left pixel
854  for (unsigned int n = 0u; n < tChannels; ++n)
855  {
856  *integral++ = TIntegral(0);
857  }
858 
859  // the remaining pixels of the first row
860 
861  while (source != sourceFirstRowEnd)
862  {
863  for (unsigned int n = 0u; n < tChannels; ++n)
864  {
865  previousIntegral[n] += *source * *source;
866  ++source;
867  }
868 
869  for (unsigned int n = 0u; n < tChannels; ++n)
870  {
871  *integral++ = previousIntegral[n];
872  }
873  }
874 
875  source += sourcePaddingElements;
876  integral += integralPaddingElements;
877 
878 
879  // we calculate the remaining rows
880 
881  while (source != sourceEnd)
882  {
883  const T* const sourceRowEnd = source + width * tChannels;
884 
885  for (unsigned int n = 0u; n < tChannels; ++n)
886  {
887  previousIntegral[n] = TIntegral(0);
888  }
889 
890  // left pixel
891  for (unsigned int n = 0u; n < tChannels; ++n)
892  {
893  *integral++ = TIntegral(0);
894  }
895 
896  integralPreviousRow += tChannels;
897 
898  while (source != sourceRowEnd)
899  {
900  for (unsigned int n = 0u; n < tChannels; ++n)
901  {
902  previousIntegral[n] += *source * *source;
903  ++source;
904  }
905 
906  for (unsigned int n = 0u; n < tChannels; ++n)
907  {
908  *integral++ = previousIntegral[n] + *integralPreviousRow++;
909  }
910  }
911 
912  source += sourcePaddingElements;
913  integral += integralPaddingElements;
914  integralPreviousRow += integralPaddingElements;
915  }
916 }
917 
918 template <typename T, typename TIntegralAndSquared, unsigned int tChannels>
919 void IntegralImage::createLinedImageAndSquared(const T* source, TIntegralAndSquared* integralAndSquared, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int integralAndSquaredPaddingElements)
920 {
921  static_assert(sizeof(T) <= sizeof(TIntegralAndSquared), "Invalid integral elements!");
922  static_assert(tChannels >= 1u, "Invalid channel number!");
923 
924  ocean_assert(source != nullptr && integralAndSquared != nullptr);
925  ocean_assert(width >= 1u && height >= 1u);
926 
927  ocean_assert((std::is_floating_point<T>::value) || (NumericD::sqr(double(NumericT<T>::maxValue())) * double(width * height) <= double(NumericT<TIntegralAndSquared>::maxValue())));
928 
929  // entire top line will be set to zero
930  memset(integralAndSquared, 0x00, (width + 1u) * tChannels * sizeof(TIntegralAndSquared) * 2u);
931 
932 #ifdef OCEAN_DEBUG
933  for (unsigned int n = 0u; n < (width + 1u) * 2u; ++n)
934  {
935  ocean_assert(integralAndSquared[n] == TIntegralAndSquared(0));
936  }
937 #endif
938 
939  // we calculate the first row of the integral image
940 
941  const T* const sourceFirstRowEnd = source + width * tChannels;
942  const T* const sourceEnd = source + (width * tChannels + sourcePaddingElements) * height;
943 
944  integralAndSquared += (width + 1u) * tChannels * 2u + integralAndSquaredPaddingElements;
945  const TIntegralAndSquared* integralAndSquaredPreviousRow = integralAndSquared;
946 
947  TIntegralAndSquared previousIntegral[tChannels] = {TIntegralAndSquared(0)};
948  TIntegralAndSquared previousIntegralSquared[tChannels] = {TIntegralAndSquared(0)};
949 
950  // left pixel integral and squared integral
951  for (unsigned int n = 0u; n < tChannels * 2u; ++n)
952  {
953  *integralAndSquared++ = TIntegralAndSquared(0);
954  }
955 
956  // the remaining pixels of the first row
957 
958  while (source != sourceFirstRowEnd)
959  {
960  for (unsigned int n = 0u; n < tChannels; ++n)
961  {
962  previousIntegral[n] += *source;
963  previousIntegralSquared[n] += *source * *source;
964  ++source;
965  }
966 
967  for (unsigned int n = 0u; n < tChannels; ++n)
968  {
969  *integralAndSquared++ = previousIntegral[n];
970  }
971 
972  for (unsigned int n = 0u; n < tChannels; ++n)
973  {
974  *integralAndSquared++ = previousIntegralSquared[n];
975  }
976  }
977 
978  source += sourcePaddingElements;
979  integralAndSquared += integralAndSquaredPaddingElements;
980 
981  // we calculate the remaining rows
982 
983  while (source != sourceEnd)
984  {
985  const T* const sourceRowEnd = source + width * tChannels;
986 
987  for (unsigned int n = 0u; n < tChannels; ++n)
988  {
989  previousIntegral[n] = TIntegralAndSquared(0);
990  }
991  for (unsigned int n = 0u; n < tChannels; ++n)
992  {
993  previousIntegralSquared[n] = TIntegralAndSquared(0);
994  }
995 
996  // left pixel integral and squared integral
997  for (unsigned int n = 0u; n < tChannels * 2u; ++n)
998  {
999  *integralAndSquared++ = TIntegralAndSquared(0);
1000  }
1001 
1002  integralAndSquaredPreviousRow += tChannels * 2u;
1003 
1004  while (source != sourceRowEnd)
1005  {
1006  for (unsigned int n = 0u; n < tChannels; ++n)
1007  {
1008  previousIntegral[n] += *source;
1009  previousIntegralSquared[n] += *source * *source;
1010  ++source;
1011  }
1012 
1013  for (unsigned int n = 0u; n < tChannels; ++n)
1014  {
1015  *integralAndSquared++ = previousIntegral[n] + *integralAndSquaredPreviousRow++;
1016  }
1017  for (unsigned int n = 0u; n < tChannels; ++n)
1018  {
1019  *integralAndSquared++ = previousIntegralSquared[n] + *integralAndSquaredPreviousRow++;
1020  }
1021  }
1022 
1023  source += sourcePaddingElements;
1024  integralAndSquared += integralAndSquaredPaddingElements;
1025  integralAndSquaredPreviousRow += integralAndSquaredPaddingElements;
1026  }
1027 }
1028 
1029 template <typename T, typename TIntegral, typename TIntegralSquared, unsigned int tChannels>
1030 void IntegralImage::createLinedImageAndSquared(const T* source, TIntegral* integral, TIntegralSquared* integralSquared, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int integralPaddingElements, const unsigned int integralSquaredPaddingElements)
1031 {
1032  static_assert(sizeof(T) <= sizeof(TIntegral), "Invalid integral elements!");
1033  static_assert(sizeof(TIntegral) <= sizeof(TIntegralSquared), "Invalid integral elements!");
1034  static_assert(tChannels >= 1u, "Invalid channel number!");
1035 
1036  ocean_assert(source != nullptr && integral != nullptr && integralSquared != nullptr);
1037  ocean_assert(width >= 1u && height >= 1u);
1038 
1039  ocean_assert((std::is_floating_point<T>::value) || (double(NumericT<T>::maxValue()) * double(width * height) <= double(NumericT<TIntegral>::maxValue()) && NumericD::sqr(double(NumericT<T>::maxValue())) * double(width * height) <= double(NumericT<TIntegralSquared>::maxValue())));
1040 
1041  // entire top line will be set to zero
1042  memset(integral, 0x00, (width + 1u) * tChannels * sizeof(TIntegral));
1043  memset(integralSquared, 0x00, (width + 1u) * tChannels * sizeof(TIntegralSquared));
1044 
1045 #ifdef OCEAN_DEBUG
1046  for (unsigned int n = 0u; n < width + 1u; ++n)
1047  {
1048  ocean_assert(integral[n] == TIntegral(0));
1049  ocean_assert(integralSquared[n] == TIntegralSquared(0));
1050  }
1051 #endif
1052 
1053  // we calculate the first row of the integral image
1054 
1055  const T* const sourceFirstRowEnd = source + width * tChannels;
1056  const T* const sourceEnd = source + (width * tChannels + sourcePaddingElements) * height;
1057 
1058  integral += (width + 1u) * tChannels + integralPaddingElements;
1059  integralSquared += (width + 1u) * tChannels + integralSquaredPaddingElements;
1060  const TIntegral* integralPreviousRow = integral;
1061  const TIntegralSquared* integralSquaredPreviousRow = integralSquared;
1062 
1063  TIntegral previousIntegral[tChannels] = {TIntegral(0)};
1064  TIntegralSquared previousIntegralSquared[tChannels] = {TIntegralSquared(0)};
1065 
1066  // left pixel integral
1067  for (unsigned int n = 0u; n < tChannels; ++n)
1068  {
1069  *integral++ = TIntegral(0);
1070  }
1071  // left pixel squared integral
1072  for (unsigned int n = 0u; n < tChannels; ++n)
1073  {
1074  *integralSquared++ = TIntegralSquared(0);
1075  }
1076 
1077  // the remaining pixels of the first row
1078 
1079  while (source != sourceFirstRowEnd)
1080  {
1081  for (unsigned int n = 0u; n < tChannels; ++n)
1082  {
1083  previousIntegral[n] += *source;
1084  previousIntegralSquared[n] += *source * *source;
1085  ++source;
1086  }
1087 
1088  for (unsigned int n = 0u; n < tChannels; ++n)
1089  {
1090  *integral++ = previousIntegral[n];
1091  }
1092 
1093  for (unsigned int n = 0u; n < tChannels; ++n)
1094  {
1095  *integralSquared++ = previousIntegralSquared[n];
1096  }
1097  }
1098 
1099  source += sourcePaddingElements;
1100  integral += integralPaddingElements;
1101  integralSquared += integralSquaredPaddingElements;
1102 
1103  // we calculate the remaining rows
1104 
1105  while (source != sourceEnd)
1106  {
1107  const T* const sourceRowEnd = source + width * tChannels;
1108 
1109  for (unsigned int n = 0u; n < tChannels; ++n)
1110  {
1111  previousIntegral[n] = TIntegral(0);
1112  }
1113  for (unsigned int n = 0u; n < tChannels; ++n)
1114  {
1115  previousIntegralSquared[n] = TIntegralSquared(0);
1116  }
1117 
1118  // left pixel integral
1119  for (unsigned int n = 0u; n < tChannels; ++n)
1120  {
1121  *integral++ = TIntegral(0);
1122  }
1123  // left pixel squared integral
1124  for (unsigned int n = 0u; n < tChannels; ++n)
1125  {
1126  *integralSquared++ = TIntegralSquared(0);
1127  }
1128 
1129  integralPreviousRow += tChannels;
1130  integralSquaredPreviousRow += tChannels;
1131 
1132  while (source != sourceRowEnd)
1133  {
1134  for (unsigned int n = 0u; n < tChannels; ++n)
1135  {
1136  previousIntegral[n] += *source;
1137  previousIntegralSquared[n] += *source * *source;
1138  ++source;
1139  }
1140 
1141  for (unsigned int n = 0u; n < tChannels; ++n)
1142  {
1143  *integral++ = previousIntegral[n] + *integralPreviousRow++;
1144  }
1145  for (unsigned int n = 0u; n < tChannels; ++n)
1146  {
1147  *integralSquared++ = previousIntegralSquared[n] + *integralSquaredPreviousRow++;
1148  }
1149  }
1150 
1151  source += sourcePaddingElements;
1152 
1153  integral += integralPaddingElements;
1154  integralSquared += integralSquaredPaddingElements;
1155 
1156  integralPreviousRow += integralPaddingElements;
1157  integralSquaredPreviousRow += integralSquaredPaddingElements;
1158  }
1159 }
1160 
1161 template <typename T, typename TIntegral, unsigned int tChannels>
1162 void IntegralImage::createBorderedImage(const T* source, TIntegral* integral, const unsigned int width, const unsigned int height, const unsigned int border, const unsigned int sourcePaddingElements, const unsigned int integralPaddingElements)
1163 {
1164  static_assert(std::is_signed<T>::value == std::is_signed<TIntegral>::value, "The integral image must have the same sign-properties as the source image!");
1165  static_assert(sizeof(T) <= sizeof(TIntegral), "Invalid integral elements!");
1166  static_assert(tChannels >= 1u, "Invalid channel number!");
1167 
1168  ocean_assert(source != nullptr && integral != nullptr);
1169  ocean_assert(width >= 1u && height >= 1u);
1170  ocean_assert(border >= 1u);
1171 
1172  /**
1173  * This is the resulting bordered integral image.
1174  * Columns or rows with '0' receive a null value.
1175  * Rows with '>' receive the last valid integral value from the left
1176  * Rows with 'v' receive the last valid integral value from the top
1177  * -------------------------
1178  * |0000000000000000000000000|
1179  * |0|-----------------------|
1180  * |0| | | |
1181  * |0| 0 | 0 | 0 |
1182  * |0| | | |
1183  * |0|-----|-----------|-----|
1184  * |0| | | > |
1185  * |0| | | > |
1186  * |0| 0 | Integral | > |
1187  * |0| | | > |
1188  * |0| | | > |
1189  * |0|-----|-----------|-----|
1190  * |0| | | |
1191  * |0| 0 | V | V |
1192  * |0| | | |
1193  * -------------------------
1194  */
1195 
1196  const unsigned int integralWidth = width + (border * 2u) + 1u;
1197 
1198  const unsigned int sourceStrideElements = width * tChannels + sourcePaddingElements;
1199  const unsigned int integralStrideElements = integralWidth * tChannels + integralPaddingElements;
1200 
1201  // entire top border (plus the extra zero-row) will be set to zero
1202 
1203  if (integralPaddingElements == 0u)
1204  {
1205  memset(integral, 0x00, integralWidth * tChannels * sizeof(TIntegral) * (border + 1u));
1206 
1207 #ifdef OCEAN_DEBUG
1208  for (unsigned int n = 0u; n < integralWidth; ++n)
1209  {
1210  ocean_assert(integral[n] == TIntegral(0));
1211  }
1212 #endif
1213 
1214  integral += integralStrideElements * (border + 1u);
1215  }
1216  else
1217  {
1218  for (unsigned int y = 0u; y < border + 1u; ++y)
1219  {
1220  memset(integral, 0x00, integralWidth * tChannels * sizeof(TIntegral));
1221 
1222 #ifdef OCEAN_DEBUG
1223  for (unsigned int n = 0u; n < integralWidth; ++n)
1224  {
1225  ocean_assert(integral[n] == TIntegral(0));
1226  }
1227 #endif
1228 
1229  integral += integralStrideElements;
1230  }
1231  }
1232 
1233  // computing the first row of the integral image
1234 
1235  // setting left border (plus the extra zero-column) to zero
1236  memset(integral, 0x00, (border + 1u) * tChannels * sizeof(TIntegral));
1237  integral += (border + 1u) * tChannels;
1238 
1239  const T* const sourceFirstRowEnd = source + tChannels * width;
1240  const T* const sourceEnd = source + sourceStrideElements * height;
1241  const TIntegral* integralPreviousRow = integral;
1242 
1243  TIntegral previousIntegral[tChannels] = {0u};
1244 
1245  while (source != sourceFirstRowEnd)
1246  {
1247  for (unsigned int n = 0u; n < tChannels; ++n)
1248  {
1249  previousIntegral[n] += *source++;
1250  }
1251 
1252  for (unsigned int n = 0u; n < tChannels; ++n)
1253  {
1254  *integral++ = previousIntegral[n];
1255  }
1256  }
1257 
1258  // setting right border to last value
1259 
1260  for (unsigned int n = 0u; n < border; ++n)
1261  {
1262  for (unsigned int c = 0u; c < tChannels; ++c)
1263  {
1264  *integral++ = previousIntegral[c];
1265  }
1266  }
1267 
1268  integral += integralPaddingElements;
1269  source += sourcePaddingElements;
1270 
1271  // computing the following rows of the integral image
1272 
1273  while (source != sourceEnd)
1274  {
1275  ocean_assert(source < sourceEnd);
1276 
1277  // setting left border (plus the extra zero-column) to zero
1278  memset(integral, 0x00, (border + 1u) * tChannels * sizeof(TIntegral));
1279  integral += (border + 1u) * tChannels;
1280 
1281  const T* const sourceRowEnd = source + tChannels * width;
1282 
1283  for (unsigned int n = 0u; n < tChannels; ++n)
1284  {
1285  previousIntegral[n] = TIntegral(0);
1286  }
1287 
1288  while (source != sourceRowEnd)
1289  {
1290  for (unsigned int n = 0u; n < tChannels; ++n)
1291  {
1292  previousIntegral[n] += *source++;
1293  }
1294 
1295  for (unsigned int n = 0u; n < tChannels; ++n)
1296  {
1297  *integral++ = previousIntegral[n] + *integralPreviousRow++;
1298  }
1299  }
1300 
1301  // setting right border to last value
1302 
1303  for (unsigned int n = 0u; n < tChannels; ++n)
1304  {
1305  previousIntegral[n] = *(integral - tChannels + n);
1306  }
1307 
1308  for (unsigned int n = 0u; n < border; ++n)
1309  {
1310  for (unsigned int c = 0u; c < tChannels; ++c)
1311  {
1312  *integral++ = previousIntegral[c];
1313  }
1314  }
1315 
1316  source += sourcePaddingElements;
1317  integral += integralPaddingElements;
1318  integralPreviousRow += (border * 2u + 1u) * tChannels + integralPaddingElements;
1319  }
1320 
1321  // bottom border will be set to the last column of the integral image
1322 
1323  integralPreviousRow -= (border + 1u) * tChannels;
1324 
1325  for (unsigned int n = 0u; n < border; ++n)
1326  {
1327  memcpy(integral, integralPreviousRow, integralWidth * tChannels * sizeof(TIntegral));
1328  integral += integralStrideElements;
1329  }
1330 }
1331 
1332 template <typename T, typename TIntegral, unsigned int tChannels>
1333 void IntegralImage::createBorderedImageSquared(const T* source, TIntegral* integral, const unsigned int width, const unsigned int height, const unsigned int border, const unsigned int sourcePaddingElements, const unsigned int integralPaddingElements)
1334 {
1335  static_assert(sizeof(T) <= sizeof(TIntegral), "Invalid integral elements!");
1336  static_assert(tChannels >= 1u, "Invalid channel number!");
1337 
1338  ocean_assert(source != nullptr && integral != nullptr);
1339  ocean_assert(width >= 1u && height >= 1u);
1340  ocean_assert(border >= 1u);
1341 
1342  /**
1343  * This is the resulting bordered integral image.
1344  * Columns or rows with '0' receive a null value.
1345  * Rows with '>' receive the last valid integral value from the left
1346  * Rows with 'v' receive the last valid integral value from the top
1347  * --------------------------
1348  * |00000000000000000000000000|
1349  * |0|------------------------|
1350  * |0| | | |
1351  * |0| 0 | 0 | 0 |
1352  * |0| | | |
1353  * |0|-----|------------|-----|
1354  * |0| | | > |
1355  * |0| | | > |
1356  * |0| 0 | Integral^2 | > |
1357  * |0| | | > |
1358  * |0| | | > |
1359  * |0|-----|------------|-----|
1360  * |0| | | |
1361  * |0| 0 | V | V |
1362  * |0| | | |
1363  * --------------------------
1364  */
1365 
1366  const unsigned int integralWidth = width + (border * 2u) + 1u;
1367 
1368  const unsigned int sourceStrideElements = width * tChannels + sourcePaddingElements;
1369  const unsigned int integralStrideElements = integralWidth * tChannels + integralPaddingElements;
1370 
1371  // entire top border (plus the extra zero-row) will be set to zero
1372 
1373  if (integralPaddingElements == 0u)
1374  {
1375  memset(integral, 0x00, integralWidth * tChannels * sizeof(TIntegral) * (border + 1u));
1376 
1377 #ifdef OCEAN_DEBUG
1378  for (unsigned int n = 0u; n < integralWidth; ++n)
1379  {
1380  ocean_assert(integral[n] == TIntegral(0));
1381  }
1382 #endif
1383 
1384  integral += integralStrideElements * (border + 1u);
1385  }
1386  else
1387  {
1388  for (unsigned int y = 0u; y < border + 1u; ++y)
1389  {
1390  memset(integral, 0x00, integralWidth * tChannels * sizeof(TIntegral));
1391 
1392 #ifdef OCEAN_DEBUG
1393  for (unsigned int n = 0u; n < integralWidth; ++n)
1394  {
1395  ocean_assert(integral[n] == TIntegral(0));
1396  }
1397 #endif
1398 
1399  integral += integralStrideElements;
1400  }
1401  }
1402 
1403  // computing the first row of the integral image
1404 
1405  // setting left border (plus the extra zero-column) to zero
1406  memset(integral, 0x00, (border + 1u) * tChannels * sizeof(TIntegral));
1407  integral += (border + 1u) * tChannels;
1408 
1409  const T * const sourceFirstRowEnd = source + tChannels * width;
1410  const T * const sourceEnd = source + sourceStrideElements * height;
1411  const TIntegral * integralPreviousRow = integral;
1412 
1413  TIntegral previousIntegral[tChannels] = {TIntegral(0)};
1414 
1415  while (source != sourceFirstRowEnd)
1416  {
1417  for (unsigned int n = 0u; n < tChannels; ++n)
1418  {
1419  previousIntegral[n] += *source * *source;
1420  source++;
1421  }
1422 
1423  for (unsigned int n = 0u; n < tChannels; ++n)
1424  {
1425  *integral++ = previousIntegral[n];
1426  }
1427  }
1428 
1429  // setting right border to last value
1430 
1431  for (unsigned int n = 0u; n < border; ++n)
1432  {
1433  for (unsigned int c = 0u; c < tChannels; ++c)
1434  {
1435  *integral++ = previousIntegral[c];
1436  }
1437  }
1438 
1439  integral += integralPaddingElements;
1440  source += sourcePaddingElements;
1441 
1442  // computing the following rows of the integral image
1443 
1444  while (source != sourceEnd)
1445  {
1446  ocean_assert(source < sourceEnd);
1447 
1448  // setting left border (plus the extra zero-column) to zero
1449  memset(integral, 0x00, (border + 1u) * tChannels * sizeof(TIntegral));
1450  integral += (border + 1u) * tChannels;
1451 
1452  const T * const sourceRowEnd = source + tChannels * width;
1453 
1454  for (unsigned int n = 0u; n < tChannels; ++n)
1455  {
1456  previousIntegral[n] = TIntegral(0);
1457  }
1458 
1459  while (source != sourceRowEnd)
1460  {
1461  for (unsigned int n = 0u; n < tChannels; ++n)
1462  {
1463  previousIntegral[n] += *source * *source;
1464  source++;
1465  }
1466 
1467  for (unsigned int n = 0u; n < tChannels; ++n)
1468  {
1469  *integral++ = previousIntegral[n] + *integralPreviousRow++;
1470  }
1471  }
1472 
1473  // setting right border to last value
1474 
1475  for (unsigned int n = 0u; n < tChannels; ++n)
1476  {
1477  previousIntegral[n] = *(integral - tChannels + n);
1478  }
1479 
1480  for (unsigned int n = 0u; n < border; ++n)
1481  {
1482  for (unsigned int c = 0u; c < tChannels; ++c)
1483  {
1484  *integral++ = previousIntegral[c];
1485  }
1486  }
1487 
1488  source += sourcePaddingElements;
1489  integral += integralPaddingElements;
1490  integralPreviousRow += (border * 2u + 1u) * tChannels + integralPaddingElements;
1491  }
1492 
1493  // bottom border will be set to the last column of the integral image
1494 
1495  integralPreviousRow -= (border + 1u) * tChannels;
1496 
1497  for (unsigned int n = 0u; n < border; ++n)
1498  {
1499  memcpy(integral, integralPreviousRow, integralWidth * tChannels * sizeof(TIntegral));
1500  integral += integralStrideElements;
1501  }
1502 }
1503 
1504 template <typename T, typename TIntegral, unsigned int tChannels>
1505 void IntegralImage::createBorderedImageMirror(const T* source, TIntegral* integral, const unsigned int width, const unsigned int height, const unsigned int border, const unsigned int sourcePaddingElements, const unsigned int integralPaddingElements)
1506 {
1507  static_assert(sizeof(T) <= sizeof(TIntegral), "Invalid integral elements!");
1508  static_assert(tChannels >= 1u, "Invalid channel number!");
1509 
1510  ocean_assert(source != nullptr && integral != nullptr);
1511  ocean_assert(width >= 1u && height >= 1u);
1512  ocean_assert(border >= 1u && border <= min(width, height));
1513 
1514  /**
1515  * This is the resulting bordered integral image.
1516  * Columns or rows with '0' receive a null value.
1517  * -------------------------
1518  * |0000000000000000000000000|
1519  * |0|-----------------------|
1520  * |0| | | |
1521  * |0| m | mirrored | m |
1522  * |0| | | |
1523  * |0|-----|-----------|-----|
1524  * |0| | | |
1525  * |0| | | |
1526  * |0| m | Integral | m |
1527  * |0| | | |
1528  * |0| | | |
1529  * |0|-----|-----------|-----|
1530  * |0| | | |
1531  * |0| m | mirrored | m |
1532  * |0| | | |
1533  * -------------------------
1534  */
1535 
1536  const unsigned int integralWidth = width + border * 2u + 1u;
1537 
1538  const unsigned int sourceStrideElements = width * tChannels + sourcePaddingElements;
1539  const unsigned int integralStrideElements = integralWidth * tChannels + integralPaddingElements;
1540 
1541  // entire first row (plus the extra zero-column) will be set to zero
1542  memset(integral, 0, integralWidth * sizeof(TIntegral) * tChannels);
1543  integral += integralStrideElements;
1544 
1545  const TIntegral* integralPreviousRow = integral + tChannels;
1546  TIntegral previousIntegral[tChannels] = {TIntegral(0)};
1547 
1548  const T* sourcePtr = source + sourceStrideElements * (border - 1u);
1549 
1550  // setting first column to zero
1551 
1552  for (unsigned int n = 0u; n < tChannels; ++n)
1553  {
1554  integral[n] = TIntegral(0);
1555  }
1556  integral += tChannels;
1557 
1558  // first row left border
1559 
1560  for (unsigned int x = (border - 1u); x != (unsigned int)(-1); --x)
1561  {
1562  for (unsigned int n = 0u; n < tChannels; ++n)
1563  {
1564  previousIntegral[n] += TIntegral(sourcePtr[x * tChannels + n]);
1565  }
1566 
1567  for (unsigned int n = 0u; n < tChannels; ++n)
1568  {
1569  *integral++ = previousIntegral[n];
1570  }
1571  }
1572 
1573  // first row center pixels
1574 
1575  const T* const sourceRowEnd0 = sourcePtr + width * tChannels;
1576  while (sourcePtr != sourceRowEnd0)
1577  {
1578  for (unsigned int n = 0u; n < tChannels; ++n)
1579  {
1580  previousIntegral[n] += TIntegral(*sourcePtr++);
1581  }
1582 
1583  for (unsigned int n = 0u; n < tChannels; ++n)
1584  {
1585  *integral++ = previousIntegral[n];
1586  }
1587  }
1588 
1589  // first row right border
1590 
1591  for (unsigned int x = 0u; x < border; ++x)
1592  {
1593  for (unsigned int n = 0u; n < tChannels; ++n)
1594  {
1595  previousIntegral[n] += TIntegral(*(sourcePtr - int((x + 1u) * tChannels) + int(n)));
1596  }
1597 
1598  for (unsigned int n = 0u; n < tChannels; ++n)
1599  {
1600  *integral++ = previousIntegral[n];
1601  }
1602  }
1603 
1604  integral += integralPaddingElements;
1605 
1606  // following rows
1607 
1608  int y = -int(border) + 1;
1609 
1610  while (y != int(height + border))
1611  {
1612  if (y < 0)
1613  {
1614  ocean_assert(-y - 1 >= 0 && -y - 1 < int(border));
1615  sourcePtr = source + int(sourceStrideElements) * (-y - 1);
1616  }
1617  else if (y < int(height))
1618  {
1619  sourcePtr = source + int(sourceStrideElements) * y;
1620  }
1621  else
1622  {
1623  sourcePtr = source + int(sourceStrideElements) * (2 * int(height) - y - 1);
1624  }
1625 
1626  // left column
1627 
1628  for (unsigned int n = 0u; n < tChannels; ++n)
1629  {
1630  previousIntegral[n] = TIntegral(0);
1631  }
1632 
1633  for (unsigned int n = 0u; n < tChannels; ++n)
1634  {
1635  *integral++ = TIntegral(0);
1636  }
1637 
1638  // left border
1639 
1640  for (unsigned int x = border - 1u; x != (unsigned int)(-1); --x)
1641  {
1642  for (unsigned int n = 0u; n < tChannels; ++n)
1643  {
1644  previousIntegral[n] += TIntegral(sourcePtr[x * tChannels + n]);
1645  }
1646 
1647  for (unsigned int n = 0u; n < tChannels; ++n)
1648  {
1649  *integral++ = previousIntegral[n] + *integralPreviousRow++;
1650  }
1651  }
1652 
1653  // center
1654 
1655  const T* const followingSourceRowEnd0 = sourcePtr + width * tChannels;
1656  while (sourcePtr != followingSourceRowEnd0)
1657  {
1658  ocean_assert(sourcePtr < followingSourceRowEnd0);
1659 
1660  for (unsigned int n = 0u; n < tChannels; ++n)
1661  {
1662  previousIntegral[n] += TIntegral(*sourcePtr++);
1663  }
1664 
1665  for (unsigned int n = 0u; n < tChannels; ++n)
1666  {
1667  *integral++ = previousIntegral[n] + *integralPreviousRow++;
1668  }
1669  }
1670 
1671  // right border
1672 
1673  for (unsigned int x = 0u; x < border; ++x)
1674  {
1675  for (unsigned int n = 0u; n < tChannels; ++n)
1676  {
1677  previousIntegral[n] += TIntegral(*(sourcePtr - int((x + 1u) * tChannels) + int(n)));
1678  }
1679 
1680  for (unsigned int n = 0u; n < tChannels; ++n)
1681  {
1682  *integral++ = previousIntegral[n] + *integralPreviousRow++;
1683  }
1684  }
1685 
1686  integral += integralPaddingElements;
1687  integralPreviousRow += tChannels + integralPaddingElements;
1688 
1689  ++y;
1690  }
1691 }
1692 
1693 template <typename T, typename TIntegral, unsigned int tChannels>
1694 void IntegralImage::createBorderedImageSquaredMirror(const T* source, TIntegral* integral, const unsigned int width, const unsigned int height, const unsigned int border, const unsigned int sourcePaddingElements, const unsigned int integralPaddingElements)
1695 {
1696  static_assert(sizeof(T) <= sizeof(TIntegral), "Invalid integral elements!");
1697  static_assert(tChannels >= 1u, "Invalid channel number!");
1698 
1699  ocean_assert(source != nullptr && integral != nullptr);
1700  ocean_assert(width >= 1u && height >= 1u);
1701  ocean_assert(border >= 1u && border <= min(width, height));
1702 
1703  /**
1704  * This is the resulting bordered integral image.
1705  * Columns or rows with '0' receive a null value.
1706  * -------------------------------
1707  * |0000000000000000000000000000000|
1708  * |0|-----------------------------|
1709  * |0| | | |
1710  * |0| m^2 | mirrored^2 | m^2 |
1711  * |0| | | |
1712  * |0|-------|-------------|-------|
1713  * |0| | | |
1714  * |0| | | |
1715  * |0| m^2 | Integral^2 | m^2 |
1716  * |0| | | |
1717  * |0| | | |
1718  * |0|-------|-------------|-------|
1719  * |0| | | |
1720  * |0| m^2 | mirrored^2 | m^2 |
1721  * |0| | | |
1722  * -------------------------------
1723  */
1724 
1725  const unsigned int integralWidth = width + border * 2u + 1u;
1726 
1727  const unsigned int sourceStrideElements = width * tChannels + sourcePaddingElements;
1728  const unsigned int integralStrideElements = integralWidth * tChannels + integralPaddingElements;
1729 
1730  // entire first row (plus the extra zero-column) will be set to zero
1731  memset(integral, 0, integralWidth * sizeof(TIntegral) * tChannels);
1732  integral += integralStrideElements;
1733 
1734  const TIntegral* integralPreviousRow = integral + tChannels;
1735  TIntegral previousIntegral[tChannels] = {TIntegral(0)};
1736 
1737  const T* sourcePtr = source + sourceStrideElements * (border - 1u);
1738 
1739  // setting first column to zero
1740 
1741  for (unsigned int n = 0u; n < tChannels; ++n)
1742  {
1743  integral[n] = TIntegral(0);
1744  }
1745  integral += tChannels;
1746 
1747  // first row left border
1748 
1749  for (unsigned int x = (border - 1u); x != (unsigned int)(-1); --x)
1750  {
1751  for (unsigned int n = 0u; n < tChannels; ++n)
1752  {
1753  previousIntegral[n] += TIntegral(sqr<T, TIntegral>(sourcePtr[x * tChannels + n]));
1754  }
1755 
1756  for (unsigned int n = 0u; n < tChannels; ++n)
1757  {
1758  *integral++ = previousIntegral[n];
1759  }
1760  }
1761 
1762  // first row center pixels
1763 
1764  const T* const sourceRowEnd0 = sourcePtr + width * tChannels;
1765  while (sourcePtr != sourceRowEnd0)
1766  {
1767  for (unsigned int n = 0u; n < tChannels; ++n)
1768  {
1769  previousIntegral[n] += TIntegral(sqr<T, TIntegral>(*sourcePtr++));
1770  }
1771 
1772  for (unsigned int n = 0u; n < tChannels; ++n)
1773  {
1774  *integral++ = previousIntegral[n];
1775  }
1776  }
1777 
1778  // first row right border
1779 
1780  for (unsigned int x = 0u; x < border; ++x)
1781  {
1782  for (unsigned int n = 0u; n < tChannels; ++n)
1783  {
1784  previousIntegral[n] += TIntegral(sqr<T, TIntegral>(*(sourcePtr - int((x + 1u) * tChannels) + int(n))));
1785  }
1786 
1787  for (unsigned int n = 0u; n < tChannels; ++n)
1788  {
1789  *integral++ = previousIntegral[n];
1790  }
1791  }
1792 
1793  integral += integralPaddingElements;
1794 
1795  // following rows
1796 
1797  int y = -int(border) + 1;
1798 
1799  while (y != int(height + border))
1800  {
1801  if (y < 0)
1802  {
1803  ocean_assert(-y - 1 >= 0 && -y - 1 < int(border));
1804  sourcePtr = source + int(sourceStrideElements) * (-y - 1);
1805  }
1806  else if (y < int(height))
1807  {
1808  sourcePtr = source + int(sourceStrideElements) * y;
1809  }
1810  else
1811  {
1812  sourcePtr = source + int(sourceStrideElements) * (2 * int(height) - y - 1);
1813  }
1814 
1815  // left column
1816 
1817  for (unsigned int n = 0u; n < tChannels; ++n)
1818  {
1819  previousIntegral[n] = TIntegral(0);
1820  }
1821 
1822  for (unsigned int n = 0u; n < tChannels; ++n)
1823  {
1824  *integral++ = TIntegral(0);
1825  }
1826 
1827  // left border
1828 
1829  for (unsigned int x = border - 1u; x != (unsigned int)(-1); --x)
1830  {
1831  for (unsigned int n = 0u; n < tChannels; ++n)
1832  {
1833  previousIntegral[n] += TIntegral(sqr<T, TIntegral>(sourcePtr[x * tChannels + n]));
1834  }
1835 
1836  for (unsigned int n = 0u; n < tChannels; ++n)
1837  {
1838  *integral++ = previousIntegral[n] + *integralPreviousRow++;
1839  }
1840  }
1841 
1842  // center
1843 
1844  const T* const followingSourceRowEnd0 = sourcePtr + width * tChannels;
1845  while (sourcePtr != followingSourceRowEnd0)
1846  {
1847  ocean_assert(sourcePtr < followingSourceRowEnd0);
1848 
1849  for (unsigned int n = 0u; n < tChannels; ++n)
1850  {
1851  previousIntegral[n] += TIntegral(sqr<T, TIntegral>(*sourcePtr++));
1852  }
1853 
1854  for (unsigned int n = 0u; n < tChannels; ++n)
1855  {
1856  *integral++ = previousIntegral[n] + *integralPreviousRow++;
1857  }
1858  }
1859 
1860  // right border
1861 
1862  for (unsigned int x = 0u; x < border; ++x)
1863  {
1864  for (unsigned int n = 0u; n < tChannels; ++n)
1865  {
1866  previousIntegral[n] += TIntegral(sqr<T, TIntegral>(*(sourcePtr - int((x + 1u) * tChannels) + int(n))));
1867  }
1868 
1869  for (unsigned int n = 0u; n < tChannels; ++n)
1870  {
1871  *integral++ = previousIntegral[n] + *integralPreviousRow++;
1872  }
1873  }
1874 
1875  integral += integralPaddingElements;
1876  integralPreviousRow += tChannels + integralPaddingElements;
1877 
1878  ++y;
1879  }
1880 }
1881 
1882 template <typename TIntegral>
1883 inline TIntegral IntegralImage::linedIntegralSum(const TIntegral* const linedIntegral, const unsigned int linedIntegralStrideElements, const unsigned int windowLeft, const unsigned int windowTop, const unsigned int windowWidth, const unsigned int windowHeight)
1884 {
1885  ocean_assert(linedIntegral != nullptr);
1886  ocean_assert(windowLeft + windowWidth < linedIntegralStrideElements);
1887  ocean_assert(windowWidth != 0u);
1888  ocean_assert(windowHeight != 0u);
1889 
1890  const unsigned int windowRight = windowLeft + windowWidth;
1891  const unsigned int windowBottom = windowTop + windowHeight;
1892 
1893  return linedIntegral[windowTop * linedIntegralStrideElements + windowLeft] - linedIntegral[windowTop * linedIntegralStrideElements + windowRight]
1894  - linedIntegral[windowBottom * linedIntegralStrideElements + windowLeft] + linedIntegral[windowBottom * linedIntegralStrideElements + windowRight];
1895 }
1896 
1897 template <typename TIntegral, typename TIntegralSquared, typename TVariance, bool tReturnMean>
1898 inline TVariance IntegralImage::linedIntegralVariance(const TIntegral* linedIntegral, const TIntegralSquared* linedIntegralSquared, const unsigned int integralStrideElements, const unsigned int integralStrideSquaredElements, const unsigned int windowLeft, const unsigned int windowTop, const unsigned int windowWidth, const unsigned int windowHeight, TVariance* mean)
1899 {
1900  static_assert(std::is_floating_point<TVariance>::value, "Invalid TVariance must be a floating point data type!");
1901 
1902  ocean_assert(linedIntegral != nullptr);
1903  ocean_assert(windowLeft + windowWidth < integralStrideElements);
1904 
1905  ocean_assert(linedIntegralSquared != nullptr);
1906  ocean_assert(windowLeft + windowWidth < integralStrideSquaredElements);
1907 
1908  ocean_assert(windowWidth != 0u);
1909  ocean_assert(windowHeight != 0u);
1910 
1911  if (windowWidth == 1u && windowHeight == 1u)
1912  {
1913  return TVariance(0);
1914  }
1915 
1916  const TIntegral sum = linedIntegralSum<TIntegral>(linedIntegral, integralStrideElements, windowLeft, windowTop, windowWidth, windowHeight);
1917  const TIntegralSquared squaredSum = linedIntegralSum<TIntegralSquared>(linedIntegralSquared, integralStrideSquaredElements, windowLeft, windowTop, windowWidth, windowHeight);
1918 
1919  const TVariance size = TVariance(windowWidth * windowHeight);
1920 
1921  if constexpr (tReturnMean)
1922  {
1923  ocean_assert(mean != nullptr);
1924  *mean = TVariance(sum) / size;
1925  }
1926  else
1927  {
1928  ocean_assert(mean == nullptr);
1929  }
1930 
1931  const TVariance variance = (TVariance(squaredSum) - TVariance(TIntegralSquared(sum) * TIntegralSquared(sum)) / size) / size;
1932 
1933  return std::max(TVariance(0), variance); // due to floating point precision, always ensure that the variance is non-negative
1934 }
1935 
1936 template <typename TIntegral, typename TIntegralSquared, typename TVariance, bool tReturnMean>
1937 inline TVariance IntegralImage::linedIntegralVariance(const TIntegral* linedIntegral, const TIntegralSquared* linedIntegralSquared, const unsigned int integralStrideElements, const unsigned int integralStrideSquaredElements, const unsigned int windowALeft, const unsigned int windowATop, const unsigned int windowAWidth, const unsigned int windowAHeight, const unsigned int windowBLeft, const unsigned int windowBTop, const unsigned int windowBWidth, const unsigned int windowBHeight, TVariance* mean)
1938 {
1939  static_assert(std::is_floating_point<TVariance>::value, "Invalid TVariance must be a floating point data type!");
1940 
1941  ocean_assert(linedIntegral != nullptr);
1942  ocean_assert(windowALeft + windowAWidth < integralStrideElements);
1943  ocean_assert(windowBLeft + windowBWidth < integralStrideElements);
1944 
1945  ocean_assert(linedIntegralSquared != nullptr);
1946  ocean_assert(windowALeft + windowAWidth < integralStrideSquaredElements);
1947  ocean_assert(windowBLeft + windowBWidth < integralStrideSquaredElements);
1948 
1949  ocean_assert(windowAWidth != 0u && windowAHeight != 0u);
1950  ocean_assert(windowBWidth != 0u && windowBHeight != 0u);
1951 
1952  const TIntegral sumA = linedIntegralSum<TIntegral>(linedIntegral, integralStrideElements, windowALeft, windowATop, windowAWidth, windowAHeight);
1953  const TIntegral sumB = linedIntegralSum<TIntegral>(linedIntegral, integralStrideElements, windowBLeft, windowBTop, windowBWidth, windowBHeight);
1954 
1955  const TIntegralSquared squaredSumA = linedIntegralSum<TIntegralSquared>(linedIntegralSquared, integralStrideSquaredElements, windowALeft, windowATop, windowAWidth, windowAHeight);
1956  const TIntegralSquared squaredSumB = linedIntegralSum<TIntegralSquared>(linedIntegralSquared, integralStrideSquaredElements, windowBLeft, windowBTop, windowBWidth, windowBHeight);
1957 
1958  const unsigned int sizeA = windowAWidth * windowAHeight;
1959  const unsigned int sizeB = windowBWidth * windowBHeight;
1960  const TVariance size = TVariance(sizeA + sizeB);
1961 
1962  const TVariance squaredSum = TVariance(squaredSumA + squaredSumB);
1963  const TIntegralSquared sum = TIntegralSquared(sumA + sumB);
1964 
1965  if constexpr (tReturnMean)
1966  {
1967  ocean_assert(mean != nullptr);
1968  *mean = TVariance(sum) / size;
1969  }
1970  else
1971  {
1972  ocean_assert(mean == nullptr);
1973  }
1974 
1975  const TVariance variance = (squaredSum - TVariance(sum * sum) / size) / size;
1976 
1977  return std::max(TVariance(0), variance); // due to floating point precision, always ensure that the variance is non-negative
1978 }
1979 
1980 template <typename TIntegral, typename TIntegralSquared, typename TVariance, bool tReturnMean>
1981 inline TVariance IntegralImage::linedIntegralVariance(const TIntegral* linedIntegral, const TIntegralSquared* linedIntegralSquared, const unsigned int integralStrideElements, const unsigned int integralStrideSquaredElements, const unsigned int windowALeft, const unsigned int windowATop, const unsigned int windowAWidth, const unsigned int windowAHeight, const unsigned int windowBLeft, const unsigned int windowBTop, const unsigned int windowBWidth, const unsigned int windowBHeight, const unsigned int windowCLeft, const unsigned int windowCTop, const unsigned int windowCWidth, const unsigned int windowCHeight, TVariance* mean)
1982 {
1983  static_assert(std::is_floating_point<TVariance>::value, "Invalid TVariance must be a floating point data type!");
1984 
1985  ocean_assert(linedIntegral != nullptr);
1986  ocean_assert(windowALeft + windowAWidth < integralStrideElements);
1987  ocean_assert(windowBLeft + windowBWidth < integralStrideElements);
1988  ocean_assert(windowCLeft + windowCWidth < integralStrideElements);
1989 
1990  ocean_assert(linedIntegralSquared != nullptr);
1991  ocean_assert(windowALeft + windowAWidth < integralStrideSquaredElements);
1992  ocean_assert(windowBLeft + windowBWidth < integralStrideSquaredElements);
1993  ocean_assert(windowCLeft + windowCWidth < integralStrideSquaredElements);
1994 
1995  ocean_assert(windowAWidth != 0u && windowAHeight != 0u);
1996  ocean_assert(windowBWidth != 0u && windowBHeight != 0u);
1997  ocean_assert(windowCWidth != 0u && windowCHeight != 0u);
1998 
1999  const TIntegral sumA = CV::IntegralImage::linedIntegralSum<TIntegral>(linedIntegral, integralStrideElements, windowALeft, windowATop, windowAWidth, windowAHeight);
2000  const TIntegral sumB = CV::IntegralImage::linedIntegralSum<TIntegral>(linedIntegral, integralStrideElements, windowBLeft, windowBTop, windowBWidth, windowBHeight);
2001  const TIntegral sumC = CV::IntegralImage::linedIntegralSum<TIntegral>(linedIntegral, integralStrideElements, windowCLeft, windowCTop, windowCWidth, windowCHeight);
2002 
2003  const TIntegralSquared squaredSumA = CV::IntegralImage::linedIntegralSum<TIntegralSquared>(linedIntegralSquared, integralStrideSquaredElements, windowALeft, windowATop, windowAWidth, windowAHeight);
2004  const TIntegralSquared squaredSumB = CV::IntegralImage::linedIntegralSum<TIntegralSquared>(linedIntegralSquared, integralStrideSquaredElements, windowBLeft, windowBTop, windowBWidth, windowBHeight);
2005  const TIntegralSquared squaredSumC = CV::IntegralImage::linedIntegralSum<TIntegralSquared>(linedIntegralSquared, integralStrideSquaredElements, windowCLeft, windowCTop, windowCWidth, windowCHeight);
2006 
2007  const unsigned int sizeA = windowAWidth * windowAHeight;
2008  const unsigned int sizeB = windowBWidth * windowBHeight;
2009  const unsigned int sizeC = windowCWidth * windowCHeight;
2010  const TVariance size = TVariance(sizeA + sizeB + sizeC);
2011 
2012  const TVariance squaredSum = TVariance(squaredSumA + squaredSumB + squaredSumC);
2013  const TIntegralSquared sum = TIntegralSquared(sumA + sumB + sumC);
2014 
2015  if constexpr (tReturnMean)
2016  {
2017  ocean_assert(mean != nullptr);
2018  *mean = TVariance(sum) / size;
2019  }
2020  else
2021  {
2022  ocean_assert(mean == nullptr);
2023  }
2024 
2025  const TVariance variance = (squaredSum - TVariance(sum * sum) / size) / size;
2026 
2027  return std::max(TVariance(0), variance); // due to floating point precision, always ensure that the variance is non-negative
2028 }
2029 
2030 template <typename T, typename TSquared>
2031 inline TSquared IntegralImage::sqr(const T& value)
2032 {
2033  return TSquared(value * value);
2034 }
2035 
2036 }
2037 
2038 }
2039 
2040 #endif // META_OCEAN_CV_INTEGRAL_IMAGE_H
The following comfort class provides comfortable functions simplifying prototyping applications but a...
Definition: IntegralImage.h:40
static Frame createBorderedImage(const Frame &frame, const unsigned int border)
Creates a bordered integral image from a given 1-plane image and adds an extra border to the resultin...
static Frame createBorderedImage(const Frame &frame, const unsigned int border)
Creates a bordered integral image from a given 1-plane image and adds an extra border to the resultin...
static Frame createLinedImage(const Frame &frame)
Creates an integral image from a given 1-plane image and adds an extra line (one column and one row) ...
static Frame createLinedImage(const Frame &frame)
Creates an integral image from a given 1-plane image and adds an extra line (one column and one row) ...
This class provides functions to create an integral image from a gray scale image.
Definition: IntegralImage.h:31
static void createBorderedImageMirror(const T *source, TIntegral *integral, const unsigned int width, const unsigned int height, const unsigned int border, const unsigned int sourcePaddingElements, const unsigned int integralPaddingElements)
Creates a bordered integral image from a given 1-plane image and adds an extra border with mirrored i...
Definition: IntegralImage.h:1505
static void createLinedImage1Channel8BitNEON(const uint8_t *source, uint32_t *integral, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int integralPaddingElements)
Creates an integral image from 8 bit images with 1 channel and adds an extra line with zeros to the l...
static void createLinedImageSquared(const T *source, TIntegral *integral, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int integralPaddingElements)
Creates an integral image with squared pixel intensities from a given 1-plane image and adds an extra...
Definition: IntegralImage.h:823
static TVariance linedIntegralVariance(const TIntegral *linedIntegral, const TIntegralSquared *linedIntegralSquared, const unsigned int integralStrideElements, const unsigned int integralStrideSquaredElements, const unsigned int windowLeft, const unsigned int windowTop, const unsigned int windowWidth, const unsigned int windowHeight, TVariance *mean=nullptr)
Determines the variance of elements within a window from two integral images.
Definition: IntegralImage.h:1898
static void createImage(const T *source, TIntegral *integral, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int integralPaddingElements)
Creates an integral image from a given 1-plane image.
Definition: IntegralImage.h:636
static void createBorderedImageSquaredMirror(const T *source, TIntegral *integral, const unsigned int width, const unsigned int height, const unsigned int border, const unsigned int sourcePaddingElements, const unsigned int integralPaddingElements)
Creates a bordered squared integral image from a given 1-plane image and adds an extra border with mi...
Definition: IntegralImage.h:1694
static TSquared sqr(const T &value)
Returns the square value of the given parameter.
Definition: IntegralImage.h:2031
static void createBorderedImage(const T *source, TIntegral *integral, const unsigned int width, const unsigned int height, const unsigned int border, const unsigned int sourcePaddingElements, const unsigned int integralPaddingElements)
Creates a bordered integral image from a given 1-plane image and adds an extra border to the resultin...
Definition: IntegralImage.h:1162
static void createLinedImageAndSquared(const T *source, TIntegralAndSquared *integralAndSquared, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int integralAndSquaredPaddingElements)
Creates an integral image and squared integral image from a given 1-plane image and adds an extra lin...
Definition: IntegralImage.h:919
static OCEAN_FORCE_INLINE TIntegral linedIntegralSum(const TIntegral *const linedIntegral, const unsigned int linedIntegralStrideElements, const unsigned int windowLeft, const unsigned int windowTop, const unsigned int windowWidth, const unsigned int windowHeight)
Determines the sum of elements within a window from an integral image.
static void createBorderedImageSquared(const T *source, TIntegral *integral, const unsigned int width, const unsigned int height, const unsigned int border, const unsigned int sourcePaddingElements, const unsigned int integralPaddingElements)
Creates a bordered squared integral image from a given 1-plane image and adds an extra border to the ...
Definition: IntegralImage.h:1333
static void createLinedImage(const T *source, TIntegral *integral, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int integralPaddingElements)
Creates an integral image from a given 1-plane image and adds an extra line (one column and one row) ...
Definition: IntegralImage.h:707
This class implements Ocean's image class.
Definition: Frame.h:1760
This class provides basic numeric functionalities.
Definition: Numeric.h:57
static constexpr T sqr(const T value)
Returns the square of a given value.
Definition: Numeric.h:1495
unsigned int sqr(const char value)
Returns the square value of a given value.
Definition: base/Utilities.h:1029
The namespace covering the entire Ocean framework.
Definition: Accessor.h:15