Ocean
AdvancedMotion.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_ADVANCED_ADVANCED_MOTION_H
9 #define META_OCEAN_CV_ADVANCED_ADVANCED_MOTION_H
10 
15 
16 #include "ocean/base/Frame.h"
17 #include "ocean/base/RandomI.h"
18 #include "ocean/base/Worker.h"
19 
21 #include "ocean/cv/FramePyramid.h"
22 #include "ocean/cv/Motion.h"
23 #include "ocean/cv/PixelPosition.h"
25 #include "ocean/cv/SubRegion.h"
26 
28 
31 
32 #include "ocean/math/Box2.h"
33 
34 namespace Ocean
35 {
36 
37 namespace CV
38 {
39 
40 namespace Advanced
41 {
42 
43 // Forward declaration.
44 template <typename TMetricInteger, typename TMetricFloat> class AdvancedMotion;
45 
46 /**
47  * Definition of an AdvancedMotion class that applies sum square difference calculations as metric.
48  * @see AdvancedMotionZeroMeanSSD, AdvancedMotion.
49  * @ingroup cvadvanced
50  */
52 
53 /**
54  * Definition of an AdvancedMotion class that applies zero-mean sum square difference calculations as metric.
55  * @see AdvancedMotionSSD, AdvancedMotion.
56  * @ingroup cvadvanced
57  */
59 
60 /**
61  * This class implements advanced motion techniques (mainly with sub-pixel accuracy or binary masks) allowing to determine the motion (movement) of individual image points between two frames.
62  * @tparam TMetricInteger The metric that is applied for measurements with pixel accuracy
63  * @tparam TMetricFloat The metric that is applied for measurement with sub-pixel accuracy
64  * @see AdvancedMotionSSD, AdvancedMotionZeroMeanSSD.
65  * @ingroup cvadvanced
66  */
67 template <typename TMetricInteger = SumSquareDifferences, typename TMetricFloat = AdvancedSumSquareDifferences>
69 {
70  public:
71 
72  /**
73  * Definition of a vector holding metric results.
74  */
75  typedef std::vector<uint32_t> MetricResults;
76 
77  public:
78 
79  /**
80  * Tracks a set of given points between two frames, with sub-pixel accuracy.
81  * Actually, this function simply creates two frame pyramids and invokes the corresponding function needing frame pyramid as parameters.<br>
82  * The motion is determined by application of an image patch centered around the point to be tracked.<br>
83  * The points are tracked unidirectional (from the previous frame to the current frame).<br>
84  * If a point is near the frame border, a mirrored image patch is applied.
85  * @param previousFrame The previous frame in which the previous points are located, must be valid
86  * @param currentFrame The current frame, with same pixel format and pixel orientation as the previous frame, must be valid
87  * @param previousPoints The points that are located in the previous frame (the points to be tracked from the previous frame to the current frame), with ranges [0, previousFrame.width())x[0, previousFrame.height())
88  * @param roughPoints Rough locations of the previous points in the current frame (if known), otherwise simply provide the previous points again, one for each previous point, with ranges [0, currentFrame.width())x[0, currentFrame.height())
89  * @param currentPoints Resulting current points, that have been tracked from the previous frame to the current frame, with ranges [0, currentFrame.width())x[0, currentFrame.height())
90  * @param maximalOffset Maximal expected offset between two corresponding points in pixel, defined in the domain of previous/current frame, with range [1, infinity)
91  * @param coarsestLayerRadius The search radius on the coarsest layer in pixel, with range [2, infinity)
92  * @param downsamplingMode The downsampling mode that is applied to create the pyramid layers
93  * @param subPixelIterations The number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
94  * @param worker Optional worker object to distribute the computation
95  * @param metricResults Optional resulting matching quality of the applied metric, nullptr if the results do not matter
96  * @param metricIdentityResults Optional resulting matching quality of the applied metric between both frames at the identity location (the previous and rough position), nullptr if the results do not matter
97  * @return True, if succeeded
98  * @tparam tSize Size of the image patch that is used to determine the motion, with range [3, infinity), must be odd, recommended is 5, 7, 15, 31, or 63
99  * @see trackPointsSubPixelMask().
100  */
101  template <unsigned int tSize>
102  static bool trackPointsSubPixelMirroredBorder(const Frame& previousFrame, const Frame& currentFrame, const Vectors2& previousPoints, const Vectors2& roughPoints, Vectors2& currentPoints, const unsigned int maximalOffset, const unsigned int coarsestLayerRadius, const FramePyramid::DownsamplingMode downsamplingMode = FramePyramid::DM_FILTER_14641, const unsigned int subPixelIterations = 4u, Worker* worker = nullptr, MetricResults* metricResults = nullptr, MetricResults* metricIdentityResults = nullptr);
103 
104  /**
105  * Tracks a set of given points between two frame pyramids, with sub-pixel accuracy.
106  * The points are tracked unidirectional (from the previous frame to the current frame).<br>
107  * The motion is determined by application of an image patch centered around the point to be tracked.<br>
108  * If a point is near the frame border, a mirrored image patch is applied.<br>
109  * This function can apply a larger search radius on the coarsest pyramid layer than on all other layers.<br>
110  * The search radius on the intermediate layers and on the finest layer is always 2.
111  * @param previousPyramid The frame pyramid of the previous frame in which the previous points are located, must be valid
112  * @param currentPyramid The frame pyramid of the current frame, with same pixel format and pixel origin as the previous frame pyramid, must be valid
113  * @param previousPoints The points that are located in the previous frame (the points to be tracked from the previous frame to the current frame), with ranges [0, previousPyramid.finestWidth())x[0, previousPyramid.finestHeight())
114  * @param roughPoints Rough locations of the previous points in the current frame (if known), otherwise simply provide the previous points again, one for each previous point, with ranges [0, currentPyramid.finestWidth())x[0, currentPyramid.finestHeight())
115  * @param currentPoints Resulting current points, that have been tracked from the previous frame to the current frame, with ranges [0, currentPyramid.finestWidth())x[0, currentPyramid.finestHeight())
116  * @param coarsestLayerRadius The search radius on the coarsest layer in pixel, with range [2, infinity)
117  * @param subPixelIterations Number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
118  * @param worker Optional worker object to distribute the computation
119  * @param metricResults Optional resulting matching quality of the applied metric, nullptr if the results do not matter
120  * @param metricIdentityResults Optional resulting matching quality of the applied metric between both frames at the identity location (the previous and rough position), nullptr if the results do not matter
121  * @return True, if succeeded
122  * @tparam tSize Size of the image patch that is used to determine the motion, with range [3, infinity), must be odd, recommended is 5, 7, 15, 31, or 63
123  * @see trackPointsSubPixelMask().
124  */
125  template <unsigned int tSize>
126  static inline bool trackPointsSubPixelMirroredBorder(const FramePyramid& previousPyramid, const FramePyramid& currentPyramid, const Vectors2& previousPoints, const Vectors2& roughPoints, Vectors2& currentPoints, const unsigned int coarsestLayerRadius, const unsigned int subPixelIterations = 4u, Worker* worker = nullptr, MetricResults* metricResults = nullptr, MetricResults* metricIdentityResults = nullptr);
127 
128  /**
129  * Tracks a set of given points between two frame pyramids, with sub-pixel accuracy.
130  * The points are tracked unidirectional (from the previous frame to the current frame).<br>
131  * The motion is determined by application of an image patch centered around the point to be tracked.<br>
132  * If a point is near the frame border, a mirrored image patch is applied.<br>
133  * This function can apply a larger search radius on the coarsest pyramid layer than on all other layers.<br>
134  * The search radius on the intermediate layers and on the finest layer is always 2.<br>
135  * This function has two template parameters: a) the number of frame channels and b) the patch size, both known at compile time.<br>
136  * @param previousPyramid The frame pyramid of the previous frame in which the previous points are located, with pixel format matching with the number of frame channels 'tChannels', must be valid
137  * @param currentPyramid The frame pyramid of the current frame, with same pixel format and pixel origin as the previous frame pyramid, must be valid
138  * @param previousPoints The points that are located in the previous frame (the points to be tracked from the previous frame to the current frame), with ranges [0, previousPyramid.finestWidth())x[0, previousPyramid.finestHeight())
139  * @param roughPoints Rough locations of the previous points in the current frame (if known), otherwise simply provide the previous points again, one for each previous point, with ranges [0, currentPyramid.finestWidth())x[0, currentPyramid.finestHeight())
140  * @param currentPoints Resulting current points, that have been tracked from the previous frame to the current frame, with ranges [0, currentPyramid.finestWidth())x[0, currentPyramid.finestHeight())
141  * @param coarsestLayerRadius The search radius on the coarsest layer in pixel, with range [2, infinity)
142  * @param subPixelIterations Number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
143  * @param worker Optional worker object to distribute the computation
144  * @param metricResults Optional resulting matching quality of the applied metric, nullptr if the results do not matter
145  * @param metricIdentityResults Optional resulting matching quality of the applied metric between both frames at the identity location (the previous and rough position), nullptr if the results do not matter
146  * @return True, if succeeded
147  * @tparam tChannels The number of channels the frame pyramids have, with range [1, 4]
148  * @tparam tSize Size of the image patch that is used to determine the motion, with range [3, infinity), must be odd, recommended is 5, 7, 15, 31, or 63
149  * @see trackPointsSubPixelMask().
150  */
151  template <unsigned int tChannels, unsigned int tSize>
152  static inline bool trackPointsSubPixelMirroredBorder(const FramePyramid& previousPyramid, const FramePyramid& currentPyramid, const Vectors2& previousPoints, const Vectors2& roughPoints, Vectors2& currentPoints, const unsigned int coarsestLayerRadius, const unsigned int subPixelIterations = 4u, Worker* worker = nullptr, MetricResults* metricResults = nullptr, MetricResults* metricIdentityResults = nullptr);
153 
154  /**
155  * Tracks a set of given points between two frame pyramids with pixel accuracy while each pyramid layer can contain valid and invalid pixels specified by two individual (pyramid) masks.
156  * The points are tracked unidirectional (from the previous frame to the current frame).<br>
157  * The motion is determined by application of an image patch centered around the point to be tracked.<br>
158  * Patch pixels outside the any of the frames will be treated as invalid mask pixels and thus will not be considered.
159  * @param previousPyramid The frame pyramid of the previous frame which will be used for the motion detection, must be valid
160  * @param currentPyramid The frame pyramid of the current frame which will be used for the motion detection, with same pixel origin as the first frame (pyramid), must be valid
161  * @param previousMaskPyramid The corresponding 8 bit mask pyramid of the previous frame identifying valid pixels, pixel values not equal to 0x00 are valid, with same layer number, frame dimension and pixel origin as the previous frame (pyramid), must be valid
162  * @param currentMaskPyramid The corresponding 8 bit mask pyramid of the current frame identifying valid pixels, pixel values not equal to 0x00 are valid, with same layer number, frame dimension and pixel origin as the current frame (pyramid), must be valid
163  * @param previousPoints The positions to be tracked in the current frame, with range [0, previousPyramid.finestWidth())x[0, previousPyramid.finestHeight())
164  * @param roughCurrentPoints Already known positions of the tracked points in the current frame (pyramid) one for each previous point, with range [0, currentPyramid.finestWidth())x[0, currentPyramid.finestHeight()), zero locations if no rough location is known
165  * @param currentPoints The resulting positions of the tracked points located in the current frame, one for each previous point
166  * @param coarsestLayerRadius The search radius on the coarsest layer, with range [2, infinity)
167  * @param worker Optional worker object to distribute the computation
168  * @return True, if succeeded
169  * @tparam tSize The size of the square patch which is used for tracking in pixel, with range [1, min('finestWidth', 'finestHeight') * 2], must be odd
170  * @see trackPointsSubPixelMask().
171  */
172  template <unsigned int tSize>
173  static inline bool trackPointsMask(const FramePyramid& previousPyramid, const FramePyramid& currentPyramid, const FramePyramid& previousMaskPyramid, const FramePyramid& currentMaskPyramid, const PixelPositions& previousPoints, const PixelPositions& roughCurrentPoints, PixelPositions& currentPoints, const unsigned int coarsestLayerRadius, Worker* worker = nullptr);
174 
175  /**
176  * Tracks a set of given points between two frame pyramids with sub-pixel accuracy while each pyramid layer can contain valid and invalid pixels specified by two individual (pyramid) masks.
177  * The points are tracked unidirectional (from the previous frame to the current frame).<br>
178  * The motion is determined by application of an image patch centered around the point to be tracked.<br>
179  * Patch pixels outside the any of the frames will be treated as invalid mask pixels and thus will not be considered.
180  * @param previousPyramid The frame pyramid of the previous frame which will be used for the motion detection, must be valid
181  * @param currentPyramid The frame pyramid of the current frame which will be used for the motion detection, with same pixel origin as the first frame (pyramid), must be valid
182  * @param previousMaskPyramid The corresponding 8 bit mask pyramid of the previous frame identifying valid pixels, pixel values not equal to 0x00 are valid, with same layer number, frame dimension and pixel origin as the previous frame (pyramid), must be valid
183  * @param currentMaskPyramid The corresponding 8 bit mask pyramid of the current frame identifying valid pixels, pixel values not equal to 0x00 are valid, with same layer number, frame dimension and pixel origin as the current frame (pyramid), must be valid
184  * @param previousPoints The positions to be tracked in the current frame, with range [0, previousPyramid.finestWidth())x[0, previousPyramid.finestHeight())
185  * @param roughCurrentPoints Already known positions of the tracked points in the current frame (pyramid) one for each previous point, with range [0, currentPyramid.finestWidth())x[0, currentPyramid.finestHeight()), zero locations if no rough location is known
186  * @param currentPoints The resulting positions of the tracked points located in the current frame, one for each previous point
187  * @param coarsestLayerRadius The search radius on the coarsest layer, with range [2, infinity)
188  * @param subPixelIterations Number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
189  * @param worker Optional worker object to distribute the computation
190  * @tparam tSize The size of the square patch which is used for tracking in pixel, with range [1, min('finestWidth', 'finestHeight') * 2], must be odd
191  * @return True, if succeeded
192  * @see trackPointsMask().
193  */
194  template <unsigned int tSize>
195  static inline bool trackPointsSubPixelMask(const FramePyramid& previousPyramid, const FramePyramid& currentPyramid, const FramePyramid& previousMaskPyramid, const FramePyramid& currentMaskPyramid, const Vectors2& previousPoints, const Vectors2& roughCurrentPoints, Vectors2& currentPoints, const unsigned int coarsestLayerRadius, const unsigned int subPixelIterations = 4u, Worker* worker = nullptr);
196 
197  /**
198  * Tracks a set of arbitrary (unknown) points between two frame pyramids with sub-pixel accuracy.
199  * An optional sub-region can be specified shrinking the tracking area.<br>
200  * Further, the arbitrary points can be separated into individual bin arrays allowing to distribute the points over the image area.<br>
201  * The points are tracked bidirectional, thus the points are tracked from the previous to the current and from the current to the previous frame.<br>
202  * Point correspondences with an inaccurate bidirectional tracking are discarded.<br>
203  * The number of pyramid layers on which points are tracked can be specified. Both pyramids need to contain at least this number of layers.
204  * If a point is near the frame border, a mirrored image patch is applied.
205  * @param previousPyramid Previous frame pyramid
206  * @param nextPyramid Next frame pyramid, with same frame type as the previous frame
207  * @param coarsestLayerRadius The search radius on the coarsest layer, with range [2, infinity)
208  * @param previousImagePoints Resulting set of points that are located in the previous frame
209  * @param nextImagePoints Resulting points in the next frame, each point corresponds to one previous point (by index)
210  * @param maximalSqrError Maximal square distance between forward and backward tracking for a valid point
211  * @param previousSubRegion An optional sub-region in that the points are tracked only
212  * @param horizontalBins Optional horizontal bins that can be used to distribute the tracked points into array bins (in each bin there will be at most one point)
213  * @param verticalBins Optional vertical bins that can be used to distribute the tracked points into array bins (in each bin there will be at most one point)
214  * @param strength Minimal strength parameter of the tracked feature points
215  * @param worker Optional worker object to distribute the computation
216  * @param trackingLayers Number of pyramid layers on which points are tracked
217  * @return True, if succeeded
218  * @tparam tSize Size of the image patch that is used to determine the motion, must be odd
219  */
220  template <unsigned int tSize>
221  static bool trackArbitraryPointsBidirectionalSubPixelMirroredBorder(const CV::FramePyramid& previousPyramid, const CV::FramePyramid& nextPyramid, const unsigned int coarsestLayerRadius, Vectors2& previousImagePoints, Vectors2& nextImagePoints, const Scalar maximalSqrError = Scalar(0.9 * 0.9), const SubRegion& previousSubRegion = SubRegion(), const unsigned int horizontalBins = 0u, const unsigned int verticalBins = 0u, const unsigned int strength = 30u, Worker* worker = nullptr, const unsigned int trackingLayers = 1u);
222 
223  /**
224  * Tracks a set of arbitrary (unknown) points between two frame pyramids with sub-pixel accuracy.
225  * An optional sub-region can be specified shrinking the tracking area.<br>
226  * Further, the arbitrary points can be separated into individual bin arrays allowing to distribute the points over the image area.<br>
227  * The points are tracked bidirectional, thus the points are tracked from the previous to the current and from the current to the previous frame.<br>
228  * Point correspondences with an inaccurate bidirectional tracking are discarded.<br>
229  * If a point is near the frame border, a mirrored image patch is applied.<br>
230  * The number of pyramid layers on which points are tracked can be specified.
231  * @param previousFrame Previous frame
232  * @param nextFrame Next frame, with same frame type as the previous frame
233  * @param maximalOffset Maximal offset between two corresponding points, in pixel
234  * @param coarsestLayerRadius The search radius on the coarsest layer, with range [2, infinity)
235  * @param previousImagePoints Resulting set of points that are located in the previous frame
236  * @param nextImagePoints Resulting points in the next frame, each point corresponds to one previous point (by index)
237  * @param maximalSqrError Maximal square distance between forward and backward tracking for a valid point
238  * @param previousSubRegion An optional sub-region in that the points are tracked only
239  * @param horizontalBins Optional horizontal bins that can be used to distribute the tracked points into array bins (in each bin there will be at most one point)
240  * @param verticalBins Optional vertical bins that can be used to distribute the tracked points into array bins (in each bin there will be at most one point)
241  * @param strength Minimal strength parameter of the tracked feature points
242  * @param downsamplingMode The downsampling mode that is applied to create the pyramid layers
243  * @param worker Optional worker object to distribute the computation
244  * @param trackingLayers Number of pyramid layers on which points are tracked
245  * @return True, if succeeded
246  * @tparam tSize Size of the image patch that is used to determine the motion, must be odd
247  */
248  template <unsigned int tSize>
249  static bool trackArbitraryPointsBidirectionalSubPixelMirroredBorder(const Frame& previousFrame, const Frame& nextFrame, const unsigned int maximalOffset, const unsigned int coarsestLayerRadius, Vectors2& previousImagePoints, Vectors2& nextImagePoints, const Scalar maximalSqrError = Scalar(0.9 * 0.9), const SubRegion& previousSubRegion = SubRegion(), const unsigned int horizontalBins = 0u, const unsigned int verticalBins = 0u, const unsigned int strength = 30u, const FramePyramid::DownsamplingMode downsamplingMode = FramePyramid::DM_FILTER_14641, Worker* worker = nullptr, const unsigned int trackingLayers = 1u);
250 
251  /**
252  * Tracks a set of given points between two frame pyramids with sub-pixel accuracy.
253  * The points are tracked bidirectional, thus the points are tracked from the previous pyramid to the next pyramid and from the next pyramid back to the previous pyramid.<br>
254  * Point correspondences with an inaccurate bidirectional tracking are discarded.<br>
255  * If a point is near the frame border, a mirrored image patch is applied.
256  * @param previousPyramid The previous frame pyramid, must be valid
257  * @param nextPyramid The next frame pyramid, with same pixel format and pixel origin as the previous pyramid, must be valid
258  * @param coarsestLayerRadius The search radius on the coarsest layer, with range [2, infinity)
259  * @param previousImagePoints A set of points that are located in the previous frame, this set may be reduced as some points may be discarded (if no validIndices parameter is specified)
260  * @param nextImagePoints Resulting points in the next frame, each point corresponds to one previous point
261  * @param maximalSqrError Maximal square distance between forward and backward tracking for a valid point
262  * @param worker Optional worker object to distribute the computation
263  * @param validIndices Optional vector receiving the indices of the valid point correspondences. Beware: If this parameter is defined, the resulting previousPoints and nextImagePoints have to be explicitly filtered by application of the indices
264  * @param subPixelIterations Number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
265  * @return True, if succeeded
266  * @tparam tSize Size of the image patch that is used to determine the motion, must be odd
267  * @see trackPointsBidirectionalSubPixelMirroredBorderWithRoughLocations().
268  */
269  template <unsigned int tSize>
270  static bool trackPointsBidirectionalSubPixelMirroredBorder(const CV::FramePyramid& previousPyramid, const CV::FramePyramid& nextPyramid, const unsigned int coarsestLayerRadius, Vectors2& previousImagePoints, Vectors2& nextImagePoints, const Scalar maximalSqrError = Scalar(0.9 * 0.9), Worker* worker = nullptr, Indices32* validIndices = nullptr, const unsigned int subPixelIterations = 4u);
271 
272  /**
273  * Tracks a set of given points between two frame pyramids with sub-pixel accuracy.
274  * The points are tracked bidirectional, thus the points are tracked from the previous to the current and from the current to the previous frame.<br>
275  * Point correspondences with an inaccurate bidirectional tracking are discarded.<br>
276  * If a point is near the frame border, a mirrored image patch is applied.<br>
277  * This function has two template parameters: a) the number of frame channels and b) the patch size, both known at compile time.
278  * @param previousPyramid Previous frame pyramid with pixel format matching with the number of frame channels 'tChannels', must be valid
279  * @param nextPyramid Next frame pyramid, with same frame type as the previous frame, with same frame type as the previous pyramid
280  * @param coarsestLayerRadius The search radius on the coarsest layer, with range [2, infinity)
281  * @param previousImagePoints A set of points that are located in the previous frame, this set may be reduced as some points may be discarded (if no validIndices parameter is specified)
282  * @param nextImagePoints Resulting points in the next frame, each point corresponds to one previous point
283  * @param maximalSqrError Maximal square distance between forward and backward tracking for a valid point
284  * @param worker Optional worker object to distribute the computation
285  * @param validIndices Optional vector receiving the indices of the valid point correspondences. Beware: If this parameter is defined, the resulting previousPoints and nextImagePoints have to be explicitly filtered by application of the indices
286  * @param subPixelIterations Number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
287  * @return True, if succeeded
288  * @tparam tChannels The number of channels both frame pyramids have, with range [1, 4]
289  * @tparam tSize Size of the image patch that is used to determine the motion, must be odd
290  * @see trackPointsBidirectionalSubPixelMirroredBorderWithRoughLocations().
291  */
292  template <unsigned int tChannels, unsigned int tSize>
293  static bool trackPointsBidirectionalSubPixelMirroredBorder(const CV::FramePyramid& previousPyramid, const CV::FramePyramid& nextPyramid, const unsigned int coarsestLayerRadius, Vectors2& previousImagePoints, Vectors2& nextImagePoints, const Scalar maximalSqrError = Scalar(0.9 * 0.9), Worker* worker = nullptr, Indices32* validIndices = nullptr, const unsigned int subPixelIterations = 4u);
294 
295  /**
296  * Tracks a set of given points between two frame pyramids with sub-pixel accuracy.
297  * The points are tracked bidirectional, thus the points are tracked from the previous to the current and from the current to the previous frame.<br>
298  * This function uses a rough guess for the next image points allowing to improve the tracking quality as long as the rough guess is better than no guess.<br>
299  * Point correspondences with an inaccurate bidirectional tracking are discarded.<br>
300  * If a point is near the frame border, a mirrored image patch is applied.
301  * @param previousPyramid Previous frame pyramid, must be valid
302  * @param nextPyramid Next frame pyramid, with same pixel format and pixel orientation as the previous frame, must be valid
303  * @param coarsestLayerRadius The search radius on the coarsest layer, with range [2, infinity)
304  * @param previousImagePoints A set of points that are located in the previous frame, this set may be reduced as some points may be discarded (if no validIndices parameter is specified)
305  * @param roughNextImagePoints The rough locations of the previous image points in the current next frame, one for each previous image point
306  * @param nextImagePoints Resulting points in the next frame, each point corresponds to one previous point
307  * @param maximalSqrError Maximal square distance between forward and backward tracking for a valid point
308  * @param worker Optional worker object to distribute the computation
309  * @param validIndices Optional vector receiving the indices of the valid point correspondences. Beware: If this parameter is defined, the resulting previousPoints and nextImagePoints have to be explicitly filtered by application of the indices
310  * @param subPixelIterations Number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
311  * @return True, if succeeded
312  * @tparam tSize Size of the image patch that is used to determine the motion, must be odd
313  * @see trackPointsBidirectionalSubPixelMirroredBorder().
314  */
315  template <unsigned int tSize>
316  static bool trackPointsBidirectionalSubPixelMirroredBorderWithRoughLocations(const CV::FramePyramid& previousPyramid, const CV::FramePyramid& nextPyramid, const unsigned int coarsestLayerRadius, Vectors2& previousImagePoints, const Vectors2& roughNextImagePoints, Vectors2& nextImagePoints, const Scalar maximalSqrError = Scalar(0.9 * 0.9), Worker* worker = nullptr, Indices32* validIndices = nullptr, const unsigned int subPixelIterations = 4u);
317 
318  /**
319  * Tracks a set of given points between two frame pyramids with sub-pixel accuracy.
320  * The points are tracked bidirectional, thus the points are tracked from the previous to the current and from the current to the previous frame.<br>
321  * This function uses a rough guess for the next image points allowing to improve the tracking quality as long as the rough guess is better than no guess.<br>
322  * Point correspondences with an inaccurate bidirectional tracking are discarded.<br>
323  * If a point is near the frame border, a mirrored image patch is applied.<br>
324  * This function has two template parameters: a) the number of frame channels and b) the patch size, both known at compile time.
325  * @param previousPyramid Previous frame pyramid with pixel format matching with the number of frame channels 'tChannels', must be valid
326  * @param nextPyramid Next frame pyramid, with same pixel format and pixel orientation as the previous frame, must be valid
327  * @param coarsestLayerRadius The search radius on the coarsest layer, with range [2, infinity)
328  * @param previousImagePoints A set of points that are located in the previous frame, this set may be reduced as some points may be discarded (if no validIndices parameter is specified)
329  * @param roughNextImagePoints The rough locations of the previous image points in the current next frame, one for each previous image point
330  * @param nextImagePoints Resulting points in the next frame, each point corresponds to one previous point
331  * @param maximalSqrError Maximal square distance between forward and backward tracking for a valid point
332  * @param worker Optional worker object to distribute the computation
333  * @param validIndices Optional vector receiving the indices of the valid point correspondences. Beware: If this parameter is defined, the resulting previousPoints and nextImagePoints have to be explicitly filtered by application of the indices
334  * @param subPixelIterations Number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
335  * @return True, if succeeded
336  * @tparam tChannels The number of channels both frame pyramids have, with range [1, 4]
337  * @tparam tSize Size of the image patch that is used to determine the motion, must be odd
338  * @see trackPointsBidirectionalSubPixelMirroredBorder().
339  */
340  template <unsigned int tChannels, unsigned int tSize>
341  static bool trackPointsBidirectionalSubPixelMirroredBorderWithRoughLocations(const CV::FramePyramid& previousPyramid, const CV::FramePyramid& nextPyramid, const unsigned int coarsestLayerRadius, Vectors2& previousImagePoints, const Vectors2& roughNextImagePoints, Vectors2& nextImagePoints, const Scalar maximalSqrError = Scalar(0.9 * 0.9), Worker* worker = nullptr, Indices32* validIndices = nullptr, const unsigned int subPixelIterations = 4u);
342 
343  /**
344  * Tracks a set of given points between two frames with sub-pixel accuracy.
345  * The points are tracked bidirectional, thus the points are tracked from the previous frame to the next frame and from the next frame back to the previous frame.<br>
346  * Point correspondences with an inaccurate bidirectional tracking are discarded.<br>
347  * If a point is near the frame border, a mirrored image patch is applied.
348  * @param previousFrame The previous frame, must be valid
349  * @param nextFrame The next frame, with same pixel format and pixel origin as the previous frame, must be valid
350  * @param maximalOffset Maximal offset between two corresponding points, in pixel
351  * @param coarsestLayerRadius The search radius on the coarsest layer, with range [2, infinity)
352  * @param previousImagePoints A set of points that are located in the previous frame, this set may be reduced as some points may be discarded
353  * @param nextImagePoints Resulting points in the next frame, each point corresponds to one previous point
354  * @param maximalSqrError Maximal square distance between forward and backward tracking for a valid point
355  * @param worker Optional worker object to distribute the computation
356  * @param downsamplingMode The downsampling mode that is applied to create the pyramid layers
357  * @param validIndices Optional vector receiving the indices of the valid point correspondences
358  * @param subPixelIterations Number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
359  * @return True, if succeeded
360  * @tparam tSize Size of the image patch that is used to determine the motion, must be odd
361  */
362  template <unsigned int tSize>
363  static bool trackPointsBidirectionalSubPixelMirroredBorder(const Frame& previousFrame, const Frame& nextFrame, const unsigned int maximalOffset, const unsigned int coarsestLayerRadius, Vectors2& previousImagePoints, Vectors2& nextImagePoints, const Scalar maximalSqrError = Scalar(0.9 * 0.9), const FramePyramid::DownsamplingMode downsamplingMode = FramePyramid::DM_FILTER_14641, Worker* worker = nullptr, Indices32* validIndices = nullptr, const unsigned int subPixelIterations = 4u);
364 
365  /**
366  * Detects and tracks reliable arbitrary reference points between two frames.
367  * The reference points are distributed into an array to receive wide spread points.
368  * @param previousPyramid Frame pyramid of the previous image
369  * @param currentPyramid Frame pyramid of the current image
370  * @param previousReferencePoints Resulting reliable reference points inside the previous frame
371  * @param currentReferencePoints Resulting and tracked reliable reference points inside the current frame
372  * @param horizontalBins Number of horizontal bins to subdivide the specified bounding box
373  * @param verticalBins Number of vertical bins to subdivide the specified bounding box
374  * @param boundingBox Optional bounding box to shrink the search area of reliable tracking points
375  * @param maskFrame Optional mask frame allowing to use only reference points outside a given mask
376  * @param worker Optional worker object to distribute the computation
377  * @return True, if succeeded
378  * @tparam tSize Size of the tracking patch in pixels
379  */
380  template <unsigned int tSize>
381  static bool trackReliableReferencePoints(const FramePyramid& previousPyramid, const FramePyramid& currentPyramid, Vectors2& previousReferencePoints, Vectors2& currentReferencePoints, const unsigned int horizontalBins = 16u, const unsigned int verticalBins = 16u, const PixelBoundingBox& boundingBox = PixelBoundingBox(), const Frame& maskFrame = Frame(), Worker* worker = nullptr);
382 
383  /**
384  * Tracks the location of one given 2D point from one image to another image with sub-pixel precision by application of an image patch without the use of a multi-resolution approach.
385  * Patch pixels outside the frame are mirrored into the frame before compared.
386  * @param frame0 The first frame, must be valid
387  * @param frame1 The second frame, must be valid
388  * @param width0 Width of the first frame in pixel, with range [tPatchSize / 2u, infinity)
389  * @param height0 Height of the first frame in pixel, with range [tPatchSize / 2u, infinity)
390  * @param width1 Width of the second frame in pixel, with range [tPatchSize / 2u, infinity)
391  * @param height1 Height of the second frame in pixel, with range [tPatchSize / 2u, infinity)
392  * @param frame0PaddingElements The number of padding elements at the end of each first frame row, in elements, with range [0, infinity)
393  * @param frame1PaddingElements The number of padding elements at the end of each second frame row, in elements, with range [0, infinity)
394  * @param position0 Position in the first frame, with range [0, width)x[0, height)
395  * @param radiusX The search radius in horizontal direction, in pixel, with range [0, width - 1]
396  * @param radiusY The search radius in vertical direction, in pixel, with range [0, height - 1]
397  * @param rough1 The optional rough guess of the point in the second frame, (Numeric::maxValue(), Numeric::maxValue()) if unknown
398  * @param subPixelIterations Number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
399  * @param metricResult Optional resulting matching quality of the applied metric, nullptr if the result does not matter
400  * @param metricIdentityResult Optional resulting matching quality of the applied metric between both frames at the identity location (the previous and rough position), nullptr if the result does not matter
401  * @return Best matching position in the second frame
402  * @tparam tChannels The number of frame channels, with range [1, infinity)
403  * @tparam tPatchSize The size of the square patch (the edge length) in pixel, with range [3, infinity), must be odd, recommended is 5, 7, 15, 31, or 63
404  */
405  template <unsigned int tChannels, unsigned int tPatchSize>
406  static Vector2 trackPointSubPixelMirroredBorder(const uint8_t* frame0, const uint8_t* frame1, const unsigned int width0, const unsigned int height0, const unsigned int width1, const unsigned int height1, const unsigned int frame0PaddingElements, const unsigned int frame1PaddingElements, const Vector2& position0, const unsigned int radiusX, const unsigned int radiusY, const Vector2& rough1 = Vector2(Numeric::maxValue(), Numeric::maxValue()), const unsigned int subPixelIterations = 4u, uint32_t* metricResult = nullptr, uint32_t* metricIdentityResult = nullptr);
407 
408  /**
409  * Tracks the location of one given 2D point from one image to another image with sub-pixel precision by application of an image patch without the use of a multi-resolution approach.
410  * Patch pixels outside the frame are mirrored into the frame before compared.
411  * @param frame0 The first frame, must be valid
412  * @param frame1 The second frame, must be valid
413  * @param channels The number of frame channels, with range [1, 4]
414  * @param width0 Width of the first frame in pixel, with range [tPatchSize / 2u, infinity)
415  * @param height0 Height of the first frame in pixel, with range [tPatchSize / 2u, infinity)
416  * @param width1 Width of the second frame in pixel, with range [tPatchSize / 2u, infinity)
417  * @param height1 Height of the second frame in pixel, with range [tPatchSize / 2u, infinity)
418  * @param frame0PaddingElements The number of padding elements at the end of each first frame row, in elements, with range [0, infinity)
419  * @param frame1PaddingElements The number of padding elements at the end of each second frame row, in elements, with range [0, infinity)
420  * @param position0 Position in the first frame, with range [0, width)x[0, height)
421  * @param radiusX The search radius in horizontal direction, in pixel, with range [0, width - 1]
422  * @param radiusY The search radius in vertical direction, in pixel, with range [0, height - 1]
423  * @param rough1 The optional rough guess of the point in the second frame, (Numeric::maxValue(), Numeric::maxValue()) if unknown
424  * @param subPixelIterations Number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
425  * @param metricResult Optional resulting result of the applied metric, nullptr if the result does not matter
426  * @param metricIdentityResult Optional resulting result of the applied metric in both frames at the same previous position, nullptr if the results do not matter
427  * @return Best matching position in the second frame
428  * @tparam tPatchSize The size of the square patch (the edge length) in pixel, with range [3, infinity), must be odd, recommended is 5, 7, 15, 31, or 63
429  */
430  template <unsigned int tPatchSize>
431  static inline Vector2 trackPointSubPixelMirroredBorder(const uint8_t* frame0, const uint8_t* frame1, const unsigned int channels, const unsigned int width0, const unsigned int height0, const unsigned int width1, const unsigned int height1, const unsigned int frame0PaddingElements, const unsigned int frame1PaddingElements, const Vector2& position0, const unsigned int radiusX, const unsigned int radiusY, const Vector2& rough1 = Vector2(Numeric::maxValue(), Numeric::maxValue()), const unsigned int subPixelIterations = 4u, uint32_t* metricResult = nullptr, uint32_t* metricIdentityResult = nullptr);
432 
433  private:
434 
435  /**
436  * Tracks the location of one given 2D point from one image to another image with sub-pixel precision by application of an image patch without the use of a multi-resolution approach.
437  * Patch pixels outside the frame are mirrored into the frame before compared.
438  * @param buffer0 The buffer containing the (interpolated) first image patch as one continuous memory block, must be valid
439  * @param frame1 The second image, with same pixel format as the buffer, must be valid
440  * @param width1 Width of the second frame in pixel, with range [tSize, infinity)
441  * @param height1 Height of the second frame in pixel, with range [tSize, infinity)
442  * @param frame1PaddingElements The number of padding elements at the end of each second image row, in elements, with range [0, infinity)
443  * @param roughPosition1 The rough position in the second frame, with range [0, width1)x[0, height1)
444  * @param subPixelIterations Number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
445  * @param metricResult Optional resulting result of the applied metric, nullptr if the result does not matter
446  * @return Best matching position in the second frame
447  * @tparam tChannels The number of data channel each frame has, with range [1, infinity)
448  * @tparam tPatchSize The size of the square patch (the edge length) in pixel, with range [3, infinity), must be odd, recommended is 5, 7, 15, 31, or 63
449  */
450  template <unsigned int tChannels, unsigned int tPatchSize>
451  static Vector2 trackPointBufferSubPixelMirroredBorder(const uint8_t* buffer0, const uint8_t* frame1, const unsigned int width1, const unsigned int height1, const unsigned int frame1PaddingElements, const Vector2& roughPosition1, const unsigned int subPixelIterations, uint32_t* metricResult = nullptr);
452 
453  /**
454  * Tracks a subset of given points between two frame pyramids with sub-pixel accuracy.
455  * The points are tracked unidirectional (from the previous frame to the current frame).<br>
456  * If a point is near the frame border, a mirrored image patch is applied.
457  * @param previousPyramid The previous frame pyramid, must be valid
458  * @param nextPyramid The next frame pyramid, with same pixel format and pixel orientation as the previous frame pyramid, must be valid
459  * @param numberLayers The number of pyramid layers that will be used for tracking, with range [1, min(pyramids->layers(), 'coarsest layer that match with the patch size')]
460  * @param previousPoints A set of points that are located in the previous frame
461  * @param roughNextPoints The rough points located in the finest pyramid layer of the next pyramid (if known), nullptr if unknown
462  * @param nextPoints Resulting tracked points located in the finest layer of the next pyramid
463  * @param coarsestLayerRadius The search radius on the coarsest pyramid layer, with range [2, infinity)
464  * @param subPixelIterations Number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
465  * @param metricResults Optional resulting results of the applied metric, nullptr if the results do not matter
466  * @param metricIdentityResults Optional resulting results of the applied metric in both frames at the same previous position, nullptr if the results do not matter
467  * @param firstPoint First point to be applied
468  * @param numberPoints Number of points to be applied
469  * @tparam tSize The size of the applied image patches in pixel, with range [1, infinity), must be odd
470  */
471  template <unsigned int tSize>
472  static void trackPointsSubPixelMirroredBorderSubset(const FramePyramid* previousPyramid, const FramePyramid* nextPyramid, const unsigned int numberLayers, const Vectors2* previousPoints, const Vectors2* roughNextPoints, Vectors2* nextPoints, const unsigned int coarsestLayerRadius, const unsigned int subPixelIterations, unsigned int* metricResults, unsigned int* metricIdentityResults, const unsigned int firstPoint, const unsigned int numberPoints);
473 
474  /**
475  * Tracks a subset of given points between two frame pyramids with sub-pixel accuracy.
476  * The points are tracked unidirectional (from the previous frame to the current frame).<br>
477  * If a point is near the frame border, a mirrored image patch is applied.<br>
478  * This function has two template parameters: a) the number of frame channels and b) the patch size, both known at compile time.<br>
479  * @param previousPyramid Previous frame pyramid, must be valid
480  * @param currentPyramid Current frame pyramid, with same pixel format and pixel orientation as the previous frame pyramid, must be valid
481  * @param numberLayers The number of pyramid layers that will be used for tracking, with range [1, min(pyramids->layers(), 'coarsest layer that match with the patch size')]
482  * @param previousPoints A set of points that are located in the previous frame
483  * @param roughPoints The rough points in the current frame (if known), otherwise the prevousPoints may be provided
484  * @param currentPoints Resulting current points, that have been tracking between the two points
485  * @param coarsestLayerRadius The search radius on the coarsest layer, with range [2, infinity)
486  * @param subPixelIterations Number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
487  * @param metricResults Optional resulting results of the applied metric, nullptr if the results do not matter
488  * @param metricIdentityResults Optional resulting results of the applied metric in both frames at the same previous position, nullptr if the results do not matter
489  * @param firstPoint First point to be applied
490  * @param numberPoints Number of points to be applied
491  * @tparam tChannels The number of channels both frame pyramids have, with range [1, 4]
492  * @tparam tSize Defines the size of the applied image patches in pixel, must be odd
493  */
494  template <unsigned int tChannels, unsigned int tSize>
495  static void trackPointsSubPixelMirroredBorderSubset(const FramePyramid* previousPyramid, const FramePyramid* currentPyramid, const unsigned int numberLayers, const Vectors2* previousPoints, const Vectors2* roughPoints, Vectors2* currentPoints, const unsigned int coarsestLayerRadius, const unsigned int subPixelIterations, unsigned int* metricResults, unsigned int* metricIdentityResults, const unsigned int firstPoint, const unsigned int numberPoints);
496 };
497 
498 template <typename TMetricInteger, typename TMetricFloat>
499 template <unsigned int tSize>
500 bool AdvancedMotion<TMetricInteger, TMetricFloat>::trackPointsSubPixelMirroredBorder(const Frame& previousFrame, const Frame& currentFrame, const Vectors2& previousPoints, const Vectors2& roughPoints, Vectors2& currentPoints, const unsigned int maximalOffset, const unsigned int coarsestLayerRadius, const FramePyramid::DownsamplingMode downsamplingMode, const unsigned int subPixelIterations, Worker* worker, MetricResults* metricResults, MetricResults* metricIdentityResults)
501 {
502  static_assert(tSize % 2u == 1u, "Invalid image patch size, must be odd!");
503  static_assert(tSize >= 3u, "Invalid image patch size, must be larger than 2!");
504 
505  ocean_assert(previousFrame && currentFrame);
506 
507  ocean_assert(previousFrame.frameType().pixelFormat() == currentFrame.frameType().pixelFormat());
508  ocean_assert(previousFrame.frameType().pixelOrigin() == currentFrame.frameType().pixelOrigin());
509 
510  ocean_assert(previousPoints.size() == roughPoints.size());
511 
512  ocean_assert(maximalOffset >= 1u);
513  ocean_assert(subPixelIterations >= 1u);
514 
515  const unsigned int idealLayers = FramePyramid::idealLayers(previousFrame.width(), previousFrame.height(), (tSize / 2u) * 4u, (tSize / 2u) * 4u, 2u, maximalOffset, coarsestLayerRadius);
516  ocean_assert(idealLayers >= 1u);
517 
518  if (idealLayers == 0u)
519  {
520  return false;
521  }
522 
523  const FramePyramid previousPyramid(previousFrame, downsamplingMode, idealLayers, false /*copyFirstLayer*/, worker);
524  const FramePyramid currentPyramid(currentFrame, downsamplingMode, idealLayers, false /*copyFirstLayer*/, worker);
525 
526  return trackPointsSubPixelMirroredBorder<tSize>(previousPyramid, currentPyramid, previousPoints, roughPoints, currentPoints, coarsestLayerRadius, subPixelIterations, worker, metricResults, metricIdentityResults);
527 }
528 
529 template <typename TMetricInteger, typename TMetricFloat>
530 template <unsigned int tSize>
531 inline bool AdvancedMotion<TMetricInteger, TMetricFloat>::trackPointsSubPixelMirroredBorder(const FramePyramid& previousPyramid, const FramePyramid& currentPyramid, const Vectors2& previousPoints, const Vectors2& roughPoints, Vectors2& currentPoints, const unsigned int coarsestLayerRadius, const unsigned int subPixelIterations, Worker* worker, MetricResults* metricResults, MetricResults* metricIdentityResults)
532 {
533  static_assert(tSize % 2u == 1u, "Invalid image patch size, must be odd!");
534  static_assert(tSize >= 3u, "Invalid image patch size, must be larger than 2!");
535 
536  ocean_assert(previousPyramid.frameType().pixelFormat() == currentPyramid.frameType().pixelFormat());
537  ocean_assert(previousPyramid.frameType().pixelOrigin() == currentPyramid.frameType().pixelOrigin());
538 
539  ocean_assert(previousPoints.size() == roughPoints.size());
540 
541  ocean_assert(subPixelIterations >= 1u);
542 
543  const unsigned int idealLayers = CV::FramePyramid::idealLayers(previousPyramid.finestWidth(), previousPyramid.finestHeight(), (tSize / 2u) * 4u, (tSize / 2u) * 4u, 2u);
544  const unsigned int numberLayers = min(min(previousPyramid.layers(), currentPyramid.layers()), idealLayers);
545 
546  if (numberLayers == 0u)
547  {
548  return false;
549  }
550 
551  currentPoints.resize(previousPoints.size());
552 
553  if (metricResults)
554  {
555  metricResults->resize(previousPoints.size());
556  }
557 
558  if (metricIdentityResults)
559  {
560  metricIdentityResults->resize(previousPoints.size());
561  }
562 
563  if (worker)
564  {
565  worker->executeFunction(Worker::Function::createStatic(&AdvancedMotion::trackPointsSubPixelMirroredBorderSubset<tSize>, &previousPyramid, &currentPyramid, numberLayers, &previousPoints, &roughPoints, &currentPoints, coarsestLayerRadius, subPixelIterations, metricResults ? metricResults->data() : nullptr, metricIdentityResults ? metricIdentityResults->data() : nullptr, 0u, 0u), 0u, (unsigned int)(previousPoints.size()));
566  }
567  else
568  {
569  trackPointsSubPixelMirroredBorderSubset<tSize>(&previousPyramid, &currentPyramid, numberLayers, &previousPoints, &roughPoints, &currentPoints, coarsestLayerRadius, subPixelIterations, metricResults ? metricResults->data() : nullptr, metricIdentityResults ? metricIdentityResults->data() : nullptr, 0u, (unsigned int)(previousPoints.size()));
570  }
571 
572  return true;
573 }
574 
575 template <typename TMetricInteger, typename TMetricFloat>
576 template <unsigned int tChannels, unsigned int tSize>
577 inline bool AdvancedMotion<TMetricInteger, TMetricFloat>::trackPointsSubPixelMirroredBorder(const FramePyramid& previousPyramid, const FramePyramid& currentPyramid, const Vectors2& previousPoints, const Vectors2& roughPoints, Vectors2& currentPoints, const unsigned int coarsestLayerRadius, const unsigned int subPixelIterations, Worker* worker, MetricResults* metricResults, MetricResults* metricIdentityResults)
578 {
579  static_assert(tSize % 2u == 1u, "Invalid image patch size, must be odd!");
580  static_assert(tSize >= 3u, "Invalid image patch size, must be larger than 2!");
581 
582  ocean_assert(FrameType::arePixelFormatsCompatible(previousPyramid.frameType().pixelFormat(), currentPyramid.frameType().pixelFormat()));
583  ocean_assert(previousPyramid.frameType().pixelOrigin() == currentPyramid.frameType().pixelOrigin());
584 
585  ocean_assert(previousPyramid.frameType().channels() == tChannels);
586  ocean_assert(currentPyramid.frameType().channels() == tChannels);
587 
588  ocean_assert(previousPoints.size() == roughPoints.size());
589 
590  ocean_assert(subPixelIterations >= 1u);
591 
592  const unsigned int idealLayers = CV::FramePyramid::idealLayers(previousPyramid.finestWidth(), previousPyramid.finestHeight(), (tSize / 2u) * 4u, (tSize / 2u) * 4u, 2u);
593  const unsigned int numberLayers = min(min(previousPyramid.layers(), currentPyramid.layers()), idealLayers);
594 
595  if (numberLayers == 0u)
596  {
597  return false;
598  }
599 
600  currentPoints.resize(previousPoints.size());
601 
602  if (metricResults)
603  {
604  metricResults->resize(previousPoints.size());
605  }
606 
607  if (metricIdentityResults)
608  {
609  metricIdentityResults->resize(previousPoints.size());
610  }
611 
612  if (worker)
613  {
614  worker->executeFunction(Worker::Function::createStatic(&AdvancedMotion::trackPointsSubPixelMirroredBorderSubset<tChannels, tSize>, &previousPyramid, &currentPyramid, numberLayers, &previousPoints, &roughPoints, &currentPoints, coarsestLayerRadius, subPixelIterations, metricResults ? metricResults->data() : nullptr, metricIdentityResults ? metricIdentityResults->data() : nullptr, 0u, 0u), 0u, (unsigned int)(previousPoints.size()));
615  }
616  else
617  {
618  trackPointsSubPixelMirroredBorderSubset<tChannels, tSize>(&previousPyramid, &currentPyramid, numberLayers, &previousPoints, &roughPoints, &currentPoints, coarsestLayerRadius, subPixelIterations, metricResults ? metricResults->data() : nullptr, metricIdentityResults ? metricIdentityResults->data() : nullptr, 0u, (unsigned int)(previousPoints.size()));
619  }
620 
621  return true;
622 }
623 
624 template <typename TMetricInteger, typename TMetricFloat>
625 template <unsigned int tSize>
626 bool AdvancedMotion<TMetricInteger, TMetricFloat>::trackArbitraryPointsBidirectionalSubPixelMirroredBorder(const CV::FramePyramid& previousPyramid, const CV::FramePyramid& nextPyramid, const unsigned int coarsestLayerRadius, Vectors2& previousImagePoints, Vectors2& nextImagePoints, const Scalar maximalSqrError, const SubRegion& previousSubRegion, const unsigned int horizontalBins, const unsigned int verticalBins, const unsigned int strength, Worker* worker, const unsigned int trackingLayers)
627 {
628  ocean_assert(previousPyramid && nextPyramid);
629 
630  ocean_assert(previousImagePoints.empty() && nextImagePoints.empty());
631 
632  if (!previousPyramid || previousPyramid.frameType() != nextPyramid.frameType())
633  {
634  return false;
635  }
636 
637  const unsigned int maximalTrackingLayers = min(trackingLayers, min(previousPyramid.layers(), nextPyramid.layers()));
638 
639  for (unsigned int n = 0u; n < maximalTrackingLayers; ++n)
640  {
641  const Frame& previousLayer = previousPyramid[n];
642 
643  if (previousLayer.width() < tSize || previousLayer.height() < tSize)
644  {
645  break;
646  }
647 
648  Frame yFrame;
650  {
651  return false;
652  }
653 
654  if (yFrame.width() == 0u || yFrame.height() == 0u)
655  {
656  return false;
657  }
658 
659  const Scalar layerFactor = Scalar((unsigned int)(1 << n));
660  const Scalar invLayerFactor = Scalar(1) / layerFactor;
661 
662  // Calculate bounding box:
663  Box2 boundingBox = previousSubRegion.boundingBox().isValid() ? previousSubRegion.boundingBox() * invLayerFactor : Box2();
664  if (!boundingBox.isValid())
665  {
666  boundingBox = Box2(0, 0, Scalar(yFrame.width()), Scalar(yFrame.height()));
667  }
668 
669  // Calculate clip window by intersecting bounding box with image borders:
670  unsigned int windowLeft, windowTop, windowWidth, windowHeight;
671  if (!boundingBox.box2integer(yFrame.width(), yFrame.height(), windowLeft, windowTop, windowWidth, windowHeight))
672  {
673  continue;
674  }
675 
676  ocean_assert(windowWidth >= 1u && windowWidth <= yFrame.width());
677  ocean_assert(windowHeight >= 1u && windowHeight <= yFrame.height());
678 
679  Detector::HarrisCorners corners;
680  if (!Detector::HarrisCornerDetector::detectCorners(yFrame, windowLeft, windowTop, windowWidth, windowHeight, strength, true, corners, true, worker))
681  {
682  return false;
683  }
684 
685  // If first run went bad, we try again with lowered expectations:
686  if (n == 0u && corners.size() < 50)
687  {
688  corners.clear();
689  if (!Detector::HarrisCornerDetector::detectCorners(yFrame, windowLeft, windowTop, windowWidth, windowHeight, strength / 2u, true, corners, true, worker))
690  {
691  return false;
692  }
693  }
694 
695  // If second run went bad, we try once more with even lower expectations:
696  if (n == 0u && corners.size() < 20)
697  {
698  corners.clear();
699  if (!Detector::HarrisCornerDetector::detectCorners(yFrame, windowLeft, windowTop, windowWidth, windowHeight, strength / 4u, true, corners, true, worker))
700  {
701  return false;
702  }
703  }
704 
705  if (corners.empty())
706  {
707  continue;
708  }
709 
710  // Restrict corners to those lying inside sub regions:
711  Detector::HarrisCorners cornersSubRegion;
712 
713  // if no sub-area filter is provided, use all corners
714  if (previousSubRegion.isEmpty())
715  {
716  cornersSubRegion = std::move(corners);
717  }
718  else
719  {
720  cornersSubRegion.reserve(corners.size());
721 
722  for (Detector::HarrisCorners::const_iterator i = corners.begin(); i != corners.end(); ++i)
723  {
724  if (previousSubRegion.isInside(i->observation() * layerFactor))
725  {
726  cornersSubRegion.push_back(*i);
727  }
728  }
729  }
730 
731  if (cornersSubRegion.empty())
732  {
733  continue;
734  }
735 
736  std::sort(cornersSubRegion.begin(), cornersSubRegion.end()); // Sort by corner strength in descending order
737  Vectors2 smallPreviousImagePoints = Detector::HarrisCorner::corners2imagePoints(cornersSubRegion);
738 
739  if (!smallPreviousImagePoints.empty() && horizontalBins != 0u && verticalBins != 0u)
740  {
741  smallPreviousImagePoints = Geometry::SpatialDistribution::distributeAndFilter(smallPreviousImagePoints.data(), smallPreviousImagePoints.size(), Scalar(windowLeft), Scalar(windowTop), Scalar(windowWidth), Scalar(windowHeight), horizontalBins, verticalBins);
742  }
743 
744  if (smallPreviousImagePoints.empty())
745  {
746  continue;
747  }
748 
749  // Create sub pyramid using fast frame data referencing:
750  const FramePyramid previousSmall = FramePyramid(previousPyramid, n, previousPyramid.layers() - n, false);
751  const FramePyramid nextSmall = FramePyramid(nextPyramid, n, nextPyramid.layers() - n, false);
752 
753  Vectors2 smallNextImagePoints;
754 
755  // Find corresponding points in next frame:
756  if (trackPointsBidirectionalSubPixelMirroredBorder<tSize>(previousSmall, nextSmall, coarsestLayerRadius, smallPreviousImagePoints, smallNextImagePoints, maximalSqrError, worker))
757  {
758  for (unsigned int i = 0u; i < smallPreviousImagePoints.size(); ++i)
759  {
760  previousImagePoints.push_back(smallPreviousImagePoints[i] * layerFactor);
761  nextImagePoints.push_back(smallNextImagePoints[i] * layerFactor);
762  }
763  }
764  }
765 
766  return true;
767 }
768 
769 template <typename TMetricInteger, typename TMetricFloat>
770 template <unsigned int tSize>
771 bool AdvancedMotion<TMetricInteger, TMetricFloat>::trackArbitraryPointsBidirectionalSubPixelMirroredBorder(const Frame& previousFrame, const Frame& nextFrame, const unsigned int maximalOffset, const unsigned int coarsestLayerRadius, Vectors2& previousImagePoints, Vectors2& nextImagePoints, const Scalar maximalSqrError, const SubRegion& previousSubRegion, const unsigned int horizontalBins, const unsigned int verticalBins, const unsigned int strength, const FramePyramid::DownsamplingMode downsamplingMode, Worker* worker, const unsigned int trackingLayers)
772 {
773  ocean_assert(previousFrame && nextFrame);
774 
775  if (!previousFrame || previousFrame.frameType() != nextFrame.frameType())
776  {
777  return false;
778  }
779 
780  const unsigned int layers = min(trackingLayers, FramePyramid::idealLayers(previousFrame.width(), previousFrame.height(), (tSize / 2u) * 4u, (tSize / 2u) * 4u, 2u, maximalOffset, coarsestLayerRadius));
781 
782  if (layers == 0u)
783  {
784  return false;
785  }
786 
787  const FramePyramid previousPyramid(previousFrame, downsamplingMode, layers, false /*copyFirstLayer*/, worker);
788  const FramePyramid nextPyramid(nextFrame, downsamplingMode, layers, false /*copyFirstLayer*/, worker);
789 
790  return trackArbitraryPointsBidirectionalSubPixelMirroredBorder<tSize>(previousPyramid, nextPyramid, coarsestLayerRadius, previousImagePoints, nextImagePoints, maximalSqrError, previousSubRegion, horizontalBins, verticalBins, strength, worker, trackingLayers);
791 }
792 
793 template <typename TMetricInteger, typename TMetricFloat>
794 template <unsigned int tSize>
795 bool AdvancedMotion<TMetricInteger, TMetricFloat>::trackPointsBidirectionalSubPixelMirroredBorder(const CV::FramePyramid& previousPyramid, const CV::FramePyramid& nextPyramid, const unsigned int coarsestLayerRadius, Vectors2& previousImagePoints, Vectors2& nextImagePoints, const Scalar maximalSqrError, Worker* worker, Indices32* validIndices, const unsigned int subPixelIterations)
796 {
797  ocean_assert(previousPyramid && nextPyramid);
798 
799  ocean_assert(!previousImagePoints.empty() && nextImagePoints.empty());
800  ocean_assert(!validIndices || validIndices->empty());
801 
802  if (previousImagePoints.empty())
803  {
804  return true;
805  }
806 
807  if (!previousPyramid || !previousPyramid.frameType().isPixelFormatCompatible(nextPyramid.frameType().pixelFormat()) || previousPyramid.frameType().pixelOrigin() != nextPyramid.frameType().pixelOrigin())
808  {
809  return false;
810  }
811 
812  Vectors2 previousPointCandidates(std::move(previousImagePoints));
813  ocean_assert(previousImagePoints.empty());
814 
815  // forward point motion
816  Vectors2 nextPointCandidates(previousPointCandidates.size());
817  if (!trackPointsSubPixelMirroredBorder<tSize>(previousPyramid, nextPyramid, previousPointCandidates, previousPointCandidates, nextPointCandidates, coarsestLayerRadius, subPixelIterations, worker))
818  {
819  return false;
820  }
821 
822  // backward point motion
823  Vectors2 backwardsPreviousPointCandidates(previousPointCandidates.size());
824  if (!trackPointsSubPixelMirroredBorder<tSize>(nextPyramid, previousPyramid, nextPointCandidates, nextPointCandidates, backwardsPreviousPointCandidates, coarsestLayerRadius, subPixelIterations, worker))
825  {
826  return false;
827  }
828 
829  ocean_assert(previousPointCandidates.size() == nextPointCandidates.size());
830  ocean_assert(previousPointCandidates.size() == backwardsPreviousPointCandidates.size());
831 
832  previousImagePoints = Vectors2();
833  previousImagePoints.reserve(previousPointCandidates.size());
834 
835  nextImagePoints.clear();
836  nextImagePoints.reserve(previousPointCandidates.size());
837 
838  if (validIndices != nullptr)
839  {
840  validIndices->clear();
841  validIndices->reserve(previousPointCandidates.size());
842 
843  for (size_t n = 0; n < previousPointCandidates.size(); ++n)
844  {
845  const Scalar sqrDistance(previousPointCandidates[n].sqrDistance(backwardsPreviousPointCandidates[n]));
846  const Vector2 nextImagePoint(nextPointCandidates[n] + (previousPointCandidates[n] - backwardsPreviousPointCandidates[n]) * Scalar(0.5));
847 
848  previousImagePoints.push_back(previousPointCandidates[n]);
849  nextImagePoints.push_back(nextImagePoint);
850 
851  // identify point pairs with almost identical point motion
852  if (sqrDistance <= maximalSqrError && nextImagePoint.x() >= 0 && nextImagePoint.y() >= 0 && nextImagePoint.x() < Scalar(nextPyramid.finestWidth()) && nextImagePoint.y() < Scalar(nextPyramid.finestHeight()))
853  {
854  validIndices->emplace_back(Index32(n));
855  }
856  }
857  }
858  else
859  {
860  // identify point pairs with almost identical point motion
861  for (size_t n = 0; n < previousPointCandidates.size(); ++n)
862  {
863  const Scalar sqrDistance(previousPointCandidates[n].sqrDistance(backwardsPreviousPointCandidates[n]));
864 
865  if (sqrDistance <= maximalSqrError)
866  {
867  const Vector2 nextImagePoint(nextPointCandidates[n] + (previousPointCandidates[n] - backwardsPreviousPointCandidates[n]) * Scalar(0.5));
868 
869  if (nextImagePoint.x() >= Scalar(0) && nextImagePoint.y() >= Scalar(0) && nextImagePoint.x() < Scalar(nextPyramid.finestWidth()) && nextImagePoint.y() < Scalar(nextPyramid.finestHeight()))
870  {
871  previousImagePoints.push_back(previousPointCandidates[n]);
872  nextImagePoints.push_back(nextImagePoint);
873  }
874  }
875  }
876  }
877 
878  ocean_assert(previousImagePoints.size() == nextImagePoints.size());
879 
880  return true;
881 }
882 
883 template <typename TMetricInteger, typename TMetricFloat>
884 template <unsigned int tChannels, unsigned int tSize>
885 bool AdvancedMotion<TMetricInteger, TMetricFloat>::trackPointsBidirectionalSubPixelMirroredBorder(const CV::FramePyramid& previousPyramid, const CV::FramePyramid& nextPyramid, const unsigned int coarsestLayerRadius, Vectors2& previousImagePoints, Vectors2& nextImagePoints, const Scalar maximalSqrError, Worker* worker, Indices32* validIndices, const unsigned int subPixelIterations)
886 {
887  ocean_assert(previousPyramid && nextPyramid);
888  ocean_assert(previousPyramid.frameType().channels() == tChannels);
889  ocean_assert(nextPyramid.frameType().channels() == tChannels);
890 
891  ocean_assert(!previousImagePoints.empty() && nextImagePoints.empty());
892  ocean_assert(!validIndices || validIndices->empty());
893 
894  if (previousImagePoints.empty())
895  {
896  return true;
897  }
898 
899  if (!previousPyramid || previousPyramid.frameType() != nextPyramid.frameType())
900  {
901  return false;
902  }
903 
904  Vectors2 previousPointCandidates(std::move(previousImagePoints));
905  ocean_assert(previousImagePoints.empty());
906 
907  // forward point motion
908  Vectors2 nextPointCandidates(previousPointCandidates.size());
909  if (!trackPointsSubPixelMirroredBorder<tChannels, tSize>(previousPyramid, nextPyramid, previousPointCandidates, previousPointCandidates, nextPointCandidates, coarsestLayerRadius, subPixelIterations, worker))
910  {
911  return false;
912  }
913 
914  // backward point motion
915  Vectors2 backwardsPreviousPointCandidates(previousPointCandidates.size());
916  if (!trackPointsSubPixelMirroredBorder<tChannels, tSize>(nextPyramid, previousPyramid, nextPointCandidates, nextPointCandidates, backwardsPreviousPointCandidates, coarsestLayerRadius, subPixelIterations, worker))
917  {
918  return false;
919  }
920 
921  ocean_assert(previousPointCandidates.size() == nextPointCandidates.size());
922  ocean_assert(previousPointCandidates.size() == backwardsPreviousPointCandidates.size());
923 
924  previousImagePoints = Vectors2();
925  previousImagePoints.reserve(previousPointCandidates.size());
926 
927  nextImagePoints.clear();
928  nextImagePoints.reserve(previousPointCandidates.size());
929 
930  if (validIndices)
931  {
932  validIndices->clear();
933  validIndices->reserve(previousPointCandidates.size());
934 
935  for (size_t n = 0; n < previousPointCandidates.size(); ++n)
936  {
937  const Scalar sqrDistance(previousPointCandidates[n].sqrDistance(backwardsPreviousPointCandidates[n]));
938  const Vector2 nextImagePoint(nextPointCandidates[n] + (previousPointCandidates[n] - backwardsPreviousPointCandidates[n]) * Scalar(0.5));
939 
940  previousImagePoints.push_back(previousPointCandidates[n]);
941  nextImagePoints.push_back(nextImagePoint);
942 
943  // identify point pairs with almost identical point motion
944  if (sqrDistance <= maximalSqrError && nextImagePoint.x() >= 0 && nextImagePoint.y() >= 0 && nextImagePoint.x() < Scalar(nextPyramid.finestWidth()) && nextImagePoint.y() < Scalar(nextPyramid.finestHeight()))
945  {
946  validIndices->push_back((unsigned int)(n));
947  }
948  }
949  }
950  else
951  {
952  // identify point pairs with almost identical point motion
953  for (size_t n = 0; n < previousPointCandidates.size(); ++n)
954  {
955  const Scalar sqrDistance(previousPointCandidates[n].sqrDistance(backwardsPreviousPointCandidates[n]));
956 
957  if (sqrDistance <= maximalSqrError)
958  {
959  const Vector2 nextImagePoint(nextPointCandidates[n] + (previousPointCandidates[n] - backwardsPreviousPointCandidates[n]) * Scalar(0.5));
960 
961  if (nextImagePoint.x() >= 0 && nextImagePoint.y() >= 0 && nextImagePoint.x() < Scalar(nextPyramid.finestWidth()) && nextImagePoint.y() < Scalar(nextPyramid.finestHeight()))
962  {
963  previousImagePoints.push_back(previousPointCandidates[n]);
964  nextImagePoints.push_back(nextImagePoint);
965  }
966  }
967  }
968  }
969 
970  ocean_assert(previousImagePoints.size() == nextImagePoints.size());
971 
972  return true;
973 }
974 
975 template <typename TMetricInteger, typename TMetricFloat>
976 template <unsigned int tSize>
977 bool AdvancedMotion<TMetricInteger, TMetricFloat>::trackPointsBidirectionalSubPixelMirroredBorderWithRoughLocations(const CV::FramePyramid& previousPyramid, const CV::FramePyramid& nextPyramid, const unsigned int coarsestLayerRadius, Vectors2& previousImagePoints, const Vectors2& roughNextImagePoints, Vectors2& nextImagePoints, const Scalar maximalSqrError, Worker* worker, Indices32* validIndices, const unsigned int subPixelIterations)
978 {
979  ocean_assert(previousPyramid && nextPyramid);
980  ocean_assert(previousPyramid.frameType().pixelFormat() == nextPyramid.frameType().pixelFormat() && previousPyramid.frameType().pixelOrigin() == nextPyramid.frameType().pixelOrigin());
981 
982  ocean_assert(!previousImagePoints.empty() && nextImagePoints.empty());
983  ocean_assert(!validIndices || validIndices->empty());
984 
985  ocean_assert(&previousImagePoints != &roughNextImagePoints);
986 
987  if (previousImagePoints.empty())
988  {
989  return true;
990  }
991 
992  if (!previousPyramid || previousPyramid.frameType().pixelFormat() != nextPyramid.frameType().pixelFormat() || previousPyramid.frameType().pixelOrigin() != nextPyramid.frameType().pixelOrigin())
993  {
994  return false;
995  }
996 
997  Vectors2 previousPointCandidates(std::move(previousImagePoints));
998  ocean_assert(previousImagePoints.empty());
999 
1000  // forward point motion
1001  Vectors2 nextPointCandidates(previousPointCandidates.size());
1002  if (!trackPointsSubPixelMirroredBorder<tSize>(previousPyramid, nextPyramid, previousPointCandidates, roughNextImagePoints, nextPointCandidates, coarsestLayerRadius, subPixelIterations, worker))
1003  {
1004  return false;
1005  }
1006 
1007  // backward point motion
1008  Vectors2 backwardsPreviousPointCandidates(previousPointCandidates.size());
1009  if (!trackPointsSubPixelMirroredBorder<tSize>(nextPyramid, previousPyramid, nextPointCandidates, previousPointCandidates, backwardsPreviousPointCandidates, coarsestLayerRadius, subPixelIterations, worker))
1010  {
1011  return false;
1012  }
1013 
1014  previousImagePoints = Vectors2();
1015  previousImagePoints.reserve(previousPointCandidates.size());
1016 
1017  nextImagePoints.clear();
1018  nextImagePoints.reserve(previousPointCandidates.size());
1019 
1020  if (validIndices)
1021  {
1022  validIndices->clear();
1023  validIndices->reserve(previousPointCandidates.size());
1024 
1025  // identify point pairs with almost identical point motion
1026  for (size_t n = 0; n < previousPointCandidates.size(); ++n)
1027  {
1028  const Scalar sqrDistance(previousPointCandidates[n].sqrDistance(backwardsPreviousPointCandidates[n]));
1029  const Vector2 nextImagePoint(nextPointCandidates[n] + (previousPointCandidates[n] - backwardsPreviousPointCandidates[n]) * Scalar(0.5));
1030 
1031  previousImagePoints.push_back(previousPointCandidates[n]);
1032  nextImagePoints.push_back(nextImagePoint);
1033 
1034  if (sqrDistance <= maximalSqrError && nextImagePoint.x() >= 0 && nextImagePoint.y() >= 0 && nextImagePoint.x() < Scalar(nextPyramid.finestWidth()) && nextImagePoint.y() < Scalar(nextPyramid.finestHeight()))
1035  {
1036  validIndices->push_back((unsigned int)(n));
1037  }
1038  }
1039  }
1040  else
1041  {
1042  // identify point pairs with almost identical point motion
1043  for (size_t n = 0; n < previousPointCandidates.size(); ++n)
1044  {
1045  const Scalar sqrDistance(previousPointCandidates[n].sqrDistance(backwardsPreviousPointCandidates[n]));
1046 
1047  if (sqrDistance <= maximalSqrError)
1048  {
1049  const Vector2 nextImagePoint(nextPointCandidates[n] + (previousPointCandidates[n] - backwardsPreviousPointCandidates[n]) * Scalar(0.5));
1050 
1051  if (nextImagePoint.x() >= 0 && nextImagePoint.y() >= 0 && nextImagePoint.x() < Scalar(nextPyramid.finestWidth()) && nextImagePoint.y() < Scalar(nextPyramid.finestHeight()))
1052  {
1053  previousImagePoints.push_back(previousPointCandidates[n]);
1054  nextImagePoints.push_back(nextImagePoint);
1055  }
1056  }
1057  }
1058  }
1059 
1060  ocean_assert(previousImagePoints.size() == nextImagePoints.size());
1061 
1062  return true;
1063 }
1064 
1065 template <typename TMetricInteger, typename TMetricFloat>
1066 template <unsigned int tChannels, unsigned int tSize>
1067 bool AdvancedMotion<TMetricInteger, TMetricFloat>::trackPointsBidirectionalSubPixelMirroredBorderWithRoughLocations(const CV::FramePyramid& previousPyramid, const CV::FramePyramid& nextPyramid, const unsigned int coarsestLayerRadius, Vectors2& previousImagePoints, const Vectors2& roughNextImagePoints, Vectors2& nextImagePoints, const Scalar maximalSqrError, Worker* worker, Indices32* validIndices, const unsigned int subPixelIterations)
1068 {
1069  ocean_assert(previousPyramid && nextPyramid);
1070  ocean_assert(FrameType::arePixelFormatsCompatible(previousPyramid.frameType().pixelFormat(), nextPyramid.frameType().pixelFormat()));
1071  ocean_assert(previousPyramid.frameType().pixelOrigin() == nextPyramid.frameType().pixelOrigin());
1072  ocean_assert(previousPyramid.frameType().channels() == tChannels);
1073  ocean_assert(nextPyramid.frameType().channels() == tChannels);
1074 
1075  ocean_assert(!previousImagePoints.empty() && nextImagePoints.empty());
1076  ocean_assert(!validIndices || validIndices->empty());
1077 
1078  ocean_assert(&previousImagePoints != &roughNextImagePoints);
1079 
1080  if (previousImagePoints.empty())
1081  {
1082  return true;
1083  }
1084 
1085  if (!previousPyramid
1086  || !nextPyramid
1087  || !FrameType::arePixelFormatsCompatible(previousPyramid.frameType().pixelFormat(), nextPyramid.frameType().pixelFormat())
1088  || previousPyramid.frameType().pixelOrigin() != nextPyramid.frameType().pixelOrigin()
1089  || previousPyramid.frameType().channels() != tChannels)
1090  {
1091  return false;
1092  }
1093 
1094  Vectors2 previousPointCandidates(std::move(previousImagePoints));
1095  ocean_assert(previousImagePoints.empty());
1096 
1097  // forward point motion
1098  Vectors2 nextPointCandidates(previousPointCandidates.size());
1099  if (!trackPointsSubPixelMirroredBorder<tChannels, tSize>(previousPyramid, nextPyramid, previousPointCandidates, roughNextImagePoints, nextPointCandidates, coarsestLayerRadius, subPixelIterations, worker))
1100  {
1101  return false;
1102  }
1103 
1104  // backward point motion
1105  Vectors2 backwardsPreviousPointCandidates(previousPointCandidates.size());
1106  if (!trackPointsSubPixelMirroredBorder<tChannels, tSize>(nextPyramid, previousPyramid, nextPointCandidates, previousPointCandidates, backwardsPreviousPointCandidates, coarsestLayerRadius, subPixelIterations, worker))
1107  {
1108  return false;
1109  }
1110 
1111  previousImagePoints = Vectors2();
1112  previousImagePoints.reserve(previousPointCandidates.size());
1113 
1114  nextImagePoints.clear();
1115  nextImagePoints.reserve(previousPointCandidates.size());
1116 
1117  if (validIndices)
1118  {
1119  validIndices->clear();
1120  validIndices->reserve(previousPointCandidates.size());
1121 
1122  // identify point pairs with almost identical point motion
1123  for (size_t n = 0; n < previousPointCandidates.size(); ++n)
1124  {
1125  const Scalar sqrDistance(previousPointCandidates[n].sqrDistance(backwardsPreviousPointCandidates[n]));
1126  const Vector2 nextImagePoint(nextPointCandidates[n] + (previousPointCandidates[n] - backwardsPreviousPointCandidates[n]) * Scalar(0.5));
1127 
1128  previousImagePoints.push_back(previousPointCandidates[n]);
1129  nextImagePoints.push_back(nextImagePoint);
1130 
1131  if (sqrDistance <= maximalSqrError && nextImagePoint.x() >= 0 && nextImagePoint.y() >= 0 && nextImagePoint.x() < Scalar(nextPyramid.finestWidth()) && nextImagePoint.y() < Scalar(nextPyramid.finestHeight()))
1132  {
1133  validIndices->push_back((unsigned int)(n));
1134  }
1135  }
1136  }
1137  else
1138  {
1139  // identify point pairs with almost identical point motion
1140  for (size_t n = 0; n < previousPointCandidates.size(); ++n)
1141  {
1142  const Scalar sqrDistance(previousPointCandidates[n].sqrDistance(backwardsPreviousPointCandidates[n]));
1143 
1144  if (sqrDistance <= maximalSqrError)
1145  {
1146  const Vector2 nextImagePoint(nextPointCandidates[n] + (previousPointCandidates[n] - backwardsPreviousPointCandidates[n]) * Scalar(0.5));
1147 
1148  if (nextImagePoint.x() >= 0 && nextImagePoint.y() >= 0 && nextImagePoint.x() < Scalar(nextPyramid.finestWidth()) && nextImagePoint.y() < Scalar(nextPyramid.finestHeight()))
1149  {
1150  previousImagePoints.push_back(previousPointCandidates[n]);
1151  nextImagePoints.push_back(nextImagePoint);
1152  }
1153  }
1154  }
1155  }
1156 
1157  ocean_assert(previousImagePoints.size() == nextImagePoints.size());
1158 
1159  return true;
1160 }
1161 
1162 template <typename TMetricInteger, typename TMetricFloat>
1163 template <unsigned int tSize>
1164 bool AdvancedMotion<TMetricInteger, TMetricFloat>::trackPointsBidirectionalSubPixelMirroredBorder(const Frame& previousFrame, const Frame& nextFrame, const unsigned int maximalOffset, const unsigned int coarsestLayerRadius, Vectors2& previousImagePoints, Vectors2& nextImagePoints, const Scalar maximalSqrError, const FramePyramid::DownsamplingMode downsamplingMode, Worker* worker, Indices32* validIndices, const unsigned int subPixelIterations)
1165 {
1166  ocean_assert(previousFrame.isValid() && nextFrame.isValid());
1167 
1168  if (!previousFrame || !previousFrame.isPixelFormatCompatible(nextFrame.pixelFormat()) || previousFrame.pixelOrigin() != nextFrame.pixelOrigin())
1169  {
1170  return false;
1171  }
1172 
1173  const unsigned int previousLayers = FramePyramid::idealLayers(previousFrame.width(), previousFrame.height(), (tSize / 2u) * 4u, (tSize / 2u) * 4u, 2u, maximalOffset, coarsestLayerRadius);
1174  const unsigned int nextLayers = FramePyramid::idealLayers(nextFrame.width(), nextFrame.height(), (tSize / 2u) * 4u, (tSize / 2u) * 4u, 2u, maximalOffset, coarsestLayerRadius);
1175  ocean_assert(previousLayers >= 1u && nextLayers >= 1u);
1176 
1177  const unsigned int layers = std::min(previousLayers, nextLayers);
1178 
1179  if (layers == 0u)
1180  {
1181  return false;
1182  }
1183 
1184  const FramePyramid previousPyramid(previousFrame, downsamplingMode, layers, false /*copyFirstLayer*/, worker);
1185  const FramePyramid nextPyramid(nextFrame, downsamplingMode, layers, false /*copyFirstLayer*/, worker);
1186 
1187  return trackPointsBidirectionalSubPixelMirroredBorder<tSize>(previousPyramid, nextPyramid, coarsestLayerRadius, previousImagePoints, nextImagePoints, maximalSqrError, worker, validIndices, subPixelIterations);
1188 }
1189 
1190 template <typename TMetricInteger, typename TMetricFloat>
1191 template <unsigned int tSize>
1192 bool AdvancedMotion<TMetricInteger, TMetricFloat>::trackReliableReferencePoints(const FramePyramid& previousPyramid, const FramePyramid& currentPyramid, Vectors2& previousReferencePoints, Vectors2& currentReferencePoints, const unsigned int horizontalBins, const unsigned int verticalBins, const PixelBoundingBox& boundingBox, const Frame& maskFrame, Worker* worker)
1193 {
1194  ocean_assert(previousReferencePoints.empty());
1195  ocean_assert(currentReferencePoints.empty());
1196 
1197  ocean_assert(&previousReferencePoints != &currentReferencePoints);
1198 
1199  ocean_assert(horizontalBins >= 1u);
1200  ocean_assert(verticalBins >= 1u);
1201 
1202  const Frame& previousFrame = previousPyramid.finestLayer();
1203 
1204  const unsigned int width = previousFrame.width();
1205  const unsigned int height = previousFrame.height();
1206 
1207  const unsigned int areaLeft = boundingBox ? boundingBox.left() : 0u;
1208  const unsigned int areaTop = boundingBox ? boundingBox.top() : 0u;
1209  const unsigned int areaWidth = boundingBox ? boundingBox.width() : width;
1210  const unsigned int areaHeight = boundingBox ? boundingBox.height() : height;
1211 
1212  ocean_assert(areaLeft + areaWidth <= width);
1213  ocean_assert(areaTop + areaHeight <= height);
1214 
1215  ocean_assert(!maskFrame.isValid() || maskFrame.isPixelFormatCompatible(FrameType::FORMAT_Y8));
1216 
1217  Detector::HarrisCorners features;
1218  features.reserve(5000);
1219  Detector::HarrisCornerDetector::detectCorners(previousFrame, areaLeft, areaTop, areaWidth, areaHeight, 1u, true, features, false, worker);
1220  ocean_assert(!features.empty());
1221 
1222  if (features.empty())
1223  {
1224  return false;
1225  }
1226 
1227  std::sort(features.begin(), features.end());
1228  const Vectors2 allPreviousReferencePoints(CV::Detector::HarrisCorner::corners2imagePoints(features));
1229 
1230  const Geometry::SpatialDistribution::DistributionArray distribution(Geometry::SpatialDistribution::distributeToArray(allPreviousReferencePoints.data(), (unsigned int)allPreviousReferencePoints.size(), Scalar(areaLeft), Scalar(areaTop), Scalar(areaWidth), Scalar(areaHeight), horizontalBins, verticalBins));
1231 
1232  const uint8_t* const maskData = maskFrame ? maskFrame.constdata<uint8_t>() : nullptr;
1233  const unsigned int maskStrideElements = maskFrame ? maskFrame.strideElements() : 0u;
1234 
1235  previousReferencePoints.reserve(distribution.bins());
1236  for (unsigned int n = 0u; n < distribution.bins(); ++n)
1237  {
1238  const Indices32& indices = distribution[n];
1239 
1240  if (!indices.empty())
1241  {
1242  ocean_assert(indices.front() < allPreviousReferencePoints.size());
1243  const Vector2& point = allPreviousReferencePoints[indices.front()];
1244 
1245  const unsigned int xPosition = (unsigned int)(point.x());
1246  const unsigned int yPosition = (unsigned int)(point.y());
1247 
1248  ocean_assert(xPosition < width);
1249  ocean_assert(yPosition < height);
1250 
1251  if (maskData == nullptr || maskData[yPosition * maskStrideElements + xPosition] == 0xFFu)
1252  {
1253  previousReferencePoints.push_back(point);
1254  }
1255  }
1256  }
1257 
1258  if (previousReferencePoints.empty())
1259  {
1260  return false;
1261  }
1262 
1263  return trackPointsSubPixelMirroredBorder<tSize>(previousPyramid, currentPyramid, previousReferencePoints, previousReferencePoints, currentReferencePoints, 2u, 4u, worker);
1264 }
1265 
1266 template <typename TMetricInteger, typename TMetricFloat>
1267 template <unsigned int tChannels, unsigned int tPatchSize>
1268 Vector2 AdvancedMotion<TMetricInteger, TMetricFloat>::trackPointSubPixelMirroredBorder(const uint8_t* frame0, const uint8_t* frame1, const unsigned int width0, const unsigned int height0, const unsigned int width1, const unsigned int height1, const unsigned int frame0PaddingElements, const unsigned int frame1PaddingElements, const Vector2& position0, const unsigned int radiusX, const unsigned int radiusY, const Vector2& rough1, const unsigned int subPixelIterations, uint32_t* metricResult, uint32_t* metricIdentityResult)
1269 {
1270  static_assert(tChannels != 0u, "Invalid number of data channels!");
1271  static_assert(tPatchSize % 2u == 1u, "Invalid size of the image patch, must be odd!");
1272 
1273  constexpr unsigned int tPatchSize_2 = tPatchSize / 2u;
1274 
1275  ocean_assert(frame0 != nullptr && frame1 != nullptr);
1276 
1277  ocean_assert(width0 >= tPatchSize && height0 >= tPatchSize);
1278  ocean_assert(width1 >= tPatchSize && height1 >= tPatchSize);
1279 
1280  ocean_assert(position0.x() >= Scalar(0) && position0.x() < Scalar(width0));
1281  ocean_assert(position0.y() >= Scalar(0) && position0.y() < Scalar(height0));
1282 
1284 
1285  const unsigned int leftCenter1 = (unsigned int)(max(0, int(position1.x() - radiusX)));
1286  const unsigned int topCenter1 = (unsigned int)(max(0, int(position1.y() - radiusY)));
1287 
1288  const unsigned int rightCenter1 = min(position1.x() + radiusX, width1 - 1u);
1289  const unsigned int bottomCenter1 = min(position1.y() + radiusY, height1 - 1u);
1290 
1291  // first, we determine a buffer containing the first (interpolated) image patch
1292 
1293  uint8_t buffer0[tPatchSize * tPatchSize * tChannels];
1294  uint8_t buffer1[tPatchSize * tPatchSize * tChannels];
1295 
1296  const unsigned int x0 = (unsigned int)(position0.x());
1297  const unsigned int y0 = (unsigned int)(position0.y());
1298 
1299  if (x0 - tPatchSize_2 < width0 - tPatchSize && y0 - tPatchSize_2 < height0 - tPatchSize)
1300  {
1301  ocean_assert(x0 >= tPatchSize_2 && x0 < width0 - (tPatchSize_2 + 1u) && y0 >= tPatchSize_2 && y0 < height0 - (tPatchSize_2 + 1u));
1302  AdvancedFrameInterpolatorBilinear::interpolateSquarePatch8BitPerChannel<tChannels, tPatchSize, PC_TOP_LEFT>(frame0, width0, frame0PaddingElements, buffer0, position0);
1303  }
1304  else
1305  {
1306  ocean_assert(!(x0 >= tPatchSize_2 && x0 < width0 - (tPatchSize_2 + 1u) && y0 >= tPatchSize_2 && y0 < height0 - (tPatchSize_2 + 1u)));
1307  AdvancedFrameInterpolatorBilinear::interpolateSquareMirroredBorder8BitPerChannel<tChannels, tPatchSize>(frame0, width0, height0, frame0PaddingElements, buffer0, position0);
1308  }
1309 
1310  PixelPosition bestPosition;
1311  uint32_t bestMetric = uint32_t(-1);
1312  unsigned int bestSqrDistance = (unsigned int)(-1);
1313 
1314  for (unsigned int y1 = topCenter1; y1 <= bottomCenter1; ++y1)
1315  {
1316  for (unsigned int x1 = leftCenter1; x1 <= rightCenter1; ++x1)
1317  {
1318  uint32_t candidateMetric;
1319 
1320  if (x1 - tPatchSize_2 < width1 - tPatchSize && y1 - tPatchSize_2 < height1 - tPatchSize)
1321  {
1322  candidateMetric = TMetricInteger::template patchBuffer8BitPerChannel<tChannels, tPatchSize>(frame1, width1, x1, y1, frame1PaddingElements, buffer0);
1323  }
1324  else
1325  {
1326  constexpr unsigned int buffer1PaddingElements = 0u;
1327 
1328  FrameConverter::patchFrameMirroredBorder<uint8_t, tChannels>(frame1, buffer1, width1, height1, x1, y1, tPatchSize, frame1PaddingElements, buffer1PaddingElements); // **TODO** for performance improvements: use patch/patch mirrored border metric
1329 
1330  candidateMetric = TMetricInteger::template buffer8BitPerChannel<tChannels, tPatchSize * tPatchSize>(buffer0, buffer1);
1331  }
1332 
1333  const PixelPosition position(x1, y1);
1334 
1335  if (candidateMetric < bestMetric || (candidateMetric == bestMetric && position1.sqrDistance(position) < bestSqrDistance))
1336  {
1337  bestMetric = candidateMetric;
1338  bestPosition = position;
1339 
1340  bestSqrDistance = position1.sqrDistance(position);
1341  }
1342 
1343  if (metricIdentityResult && x1 == position1.x() && y1 == position1.y())
1344  {
1345  *metricIdentityResult = candidateMetric;
1346  }
1347  }
1348  }
1349 
1350  ocean_assert(bestMetric != (unsigned int)(-1) && bestPosition);
1351 
1352  ocean_assert(abs(int(bestPosition.x()) - int(position1.x())) <= int(radiusX));
1353  ocean_assert(abs(int(bestPosition.y()) - int(position1.y())) <= int(radiusY));
1354 
1355  if (metricResult)
1356  {
1357  *metricResult = bestMetric;
1358  }
1359 
1360  return trackPointBufferSubPixelMirroredBorder<tChannels, tPatchSize>(buffer0, frame1, width1, height1, frame1PaddingElements, Vector2(Scalar(bestPosition.x()), Scalar(bestPosition.y())), subPixelIterations, metricResult);
1361 }
1362 
1363 template <typename TMetricInteger, typename TMetricFloat>
1364 template <unsigned int tPatchSize>
1365 inline Vector2 AdvancedMotion<TMetricInteger, TMetricFloat>::trackPointSubPixelMirroredBorder(const uint8_t* frame0, const uint8_t* frame1, const unsigned int channels, const unsigned int width0, const unsigned int height0, const unsigned int width1, const unsigned int height1, const unsigned int frame0PaddingElements, const unsigned int frame1PaddingElements, const Vector2& position0, const unsigned int radiusX, const unsigned int radiusY, const Vector2& rough1, const unsigned int subPixelIterations, uint32_t* metricResult, uint32_t* metricIdentityResult)
1366 {
1367  ocean_assert(channels >= 1u);
1368 
1369  switch (channels)
1370  {
1371  case 1u:
1372  return trackPointSubPixelMirroredBorder<1u, tPatchSize>(frame0, frame1, width0, height0, width1, height1, frame0PaddingElements, frame1PaddingElements, position0, radiusX, radiusY, rough1, subPixelIterations, metricResult, metricIdentityResult);
1373 
1374  case 2u:
1375  return trackPointSubPixelMirroredBorder<2u, tPatchSize>(frame0, frame1, width0, height0, width1, height1, frame0PaddingElements, frame1PaddingElements, position0, radiusX, radiusY, rough1, subPixelIterations, metricResult, metricIdentityResult);
1376 
1377  case 3u:
1378  return trackPointSubPixelMirroredBorder<3u, tPatchSize>(frame0, frame1, width0, height0, width1, height1, frame0PaddingElements, frame1PaddingElements, position0, radiusX, radiusY, rough1, subPixelIterations, metricResult, metricIdentityResult);
1379 
1380  case 4u:
1381  return trackPointSubPixelMirroredBorder<4u, tPatchSize>(frame0, frame1, width0, height0, width1, height1, frame0PaddingElements, frame1PaddingElements, position0, radiusX, radiusY, rough1, subPixelIterations, metricResult, metricIdentityResult);
1382  }
1383 
1384  ocean_assert(false && "Invalid pixel format!");
1385  return rough1;
1386 }
1387 
1388 template <typename TMetricInteger, typename TMetricFloat>
1389 template <unsigned int tChannels, unsigned int tPatchSize>
1390 Vector2 AdvancedMotion<TMetricInteger, TMetricFloat>::trackPointBufferSubPixelMirroredBorder(const uint8_t* buffer0, const uint8_t* frame1, const unsigned int width1, const unsigned int height1, const unsigned int frame1PaddingElements, const Vector2& roughPosition1, const unsigned int subPixelIterations, uint32_t* metricResult)
1391 {
1392  static_assert(tChannels >= 1u, "Invalid number of data channels!");
1393  static_assert(tPatchSize % 2u == 1u, "Invalid size of the image patch, must be odd!");
1394 
1395  ocean_assert(buffer0 != nullptr && frame1 != nullptr);
1396 
1397  ocean_assert(width1 >= tPatchSize && height1 >= tPatchSize);
1398 
1399  ocean_assert(roughPosition1.x() >= Scalar(0) && roughPosition1.x() < Scalar(width1));
1400  ocean_assert(roughPosition1.y() >= Scalar(0) && roughPosition1.y() < Scalar(height1));
1401 
1402  uint32_t metricBest = uint32_t(-1);
1403 
1404  if (metricResult != nullptr)
1405  {
1406  metricBest = *metricResult;
1407 
1408 #ifdef OCEAN_DEBUG
1409  const bool result = metricBest == TMetricFloat::template patchMirroredBorderBuffer8BitPerChannel<tChannels, tPatchSize>(frame1, width1, height1, roughPosition1.x(), roughPosition1.y(), frame1PaddingElements, buffer0);
1410  ocean_assert_and_suppress_unused(result, result);
1411 #endif
1412  }
1413  else
1414  {
1415  const unsigned int x1 = (unsigned int)(roughPosition1.x());
1416  const unsigned int y1 = (unsigned int)(roughPosition1.y());
1417 
1418  if (x1 - (tPatchSize / 2u) < width1 - tPatchSize && y1 - (tPatchSize / 2u) < height1 - tPatchSize)
1419  {
1420  ocean_assert(x1 >= (tPatchSize / 2u) && y1 >= (tPatchSize / 2u) && x1 < width1 - (tPatchSize / 2u + 1u) && y1 < height1 - (tPatchSize / 2u + 1u));
1421  metricBest = TMetricFloat::template patchBuffer8BitPerChannel<tChannels, tPatchSize>(frame1, width1, roughPosition1.x(), roughPosition1.y(), frame1PaddingElements, buffer0);
1422  }
1423  else
1424  {
1425  ocean_assert(!(x1 >= (tPatchSize / 2u) && y1 >= (tPatchSize / 2u) && x1 < width1 - (tPatchSize / 2u + 1u) && y1 < height1 - (tPatchSize / 2u + 1u)));
1426  metricBest = TMetricFloat::template patchMirroredBorderBuffer8BitPerChannel<tChannels, tPatchSize>(frame1, width1, height1, roughPosition1.x(), roughPosition1.y(), frame1PaddingElements, buffer0);
1427  }
1428  }
1429 
1430  constexpr unsigned int numberSteps = 8u;
1431 
1432  const Vector2 steps[numberSteps] =
1433  {
1434  Vector2(-1, -1),
1435  Vector2(0, -1),
1436  Vector2(1, -1),
1437  Vector2(-1, 0),
1438  Vector2(1, 0),
1439  Vector2(-1, 1),
1440  Vector2(0, 1),
1441  Vector2(1, 1)
1442  };
1443 
1444  Scalar offset = Scalar(0.5);
1445  Vector2 position1 = roughPosition1;
1446 
1447  for (unsigned int n = 0u; n < subPixelIterations; ++n)
1448  {
1449  Vector2 bestPosition1 = position1;
1450 
1451  // make 8 sample calculations
1452 
1453  for (unsigned int i = 0u; i < numberSteps; ++i)
1454  {
1455  const Vector2 candidatePosition1(position1.x() + steps[i].x() * offset, position1.y() + steps[i].y() * offset);
1456 
1457  if (candidatePosition1.x() >= Scalar(0) && candidatePosition1.x() < Scalar(width1) && candidatePosition1.y() >= Scalar(0) && candidatePosition1.y() < Scalar(height1))
1458  {
1459  const unsigned int x1 = (unsigned int)(candidatePosition1.x());
1460  const unsigned int y1 = (unsigned int)(candidatePosition1.y());
1461 
1462  const uint32_t candidateMetric = (x1 - (tPatchSize / 2u) < width1 - tPatchSize && y1 - (tPatchSize / 2u) < height1 - tPatchSize) ?
1463  TMetricFloat::template patchBuffer8BitPerChannel<tChannels, tPatchSize>(frame1, width1, candidatePosition1.x(), candidatePosition1.y(), frame1PaddingElements, buffer0) :
1464  TMetricFloat::template patchMirroredBorderBuffer8BitPerChannel<tChannels, tPatchSize>(frame1, width1, height1, candidatePosition1.x(), candidatePosition1.y(), frame1PaddingElements, buffer0);
1465 
1466  if (candidateMetric < metricBest)
1467  {
1468  metricBest = candidateMetric;
1469  bestPosition1 = candidatePosition1;
1470  }
1471 
1472  }
1473  }
1474 
1475  position1 = bestPosition1;
1476  offset *= Scalar(0.5);
1477  }
1478 
1479  if (metricResult != nullptr)
1480  {
1481  *metricResult = metricBest;
1482  }
1483 
1484  ocean_assert(position1.x() >= 0 && position1.y() >= 0);
1485  ocean_assert(position1.x() < Scalar(width1) && position1.y() < Scalar(height1));
1486 
1487  return position1;
1488 }
1489 
1490 template <typename TMetricInteger, typename TMetricFloat>
1491 template <unsigned int tSize>
1492 void AdvancedMotion<TMetricInteger, TMetricFloat>::trackPointsSubPixelMirroredBorderSubset(const FramePyramid* previousPyramid, const FramePyramid* nextPyramid, const unsigned int numberLayers, const Vectors2* previousPoints, const Vectors2* roughNextPoints, Vectors2* nextPoints, const unsigned int coarsestLayerRadius, const unsigned int subPixelIterations, unsigned int* metricResults, unsigned int* metricIdentityResults, const unsigned int firstPoint, const unsigned int numberPoints)
1493 {
1494  static_assert(tSize % 2u == 1u, "Invalid patch size, must be odd!");
1495 
1496  ocean_assert(previousPyramid != nullptr && nextPyramid != nullptr);
1497  ocean_assert(previousPoints != nullptr && nextPoints != nullptr);
1498 
1499  ocean_assert(previousPyramid->isValid() && nextPyramid->isValid());
1500 
1501  ocean_assert(previousPyramid->frameType().pixelFormat() == nextPyramid->frameType().pixelFormat());
1502  ocean_assert(previousPyramid->frameType().pixelOrigin() == nextPyramid->frameType().pixelOrigin());
1503 
1504  ocean_assert(roughNextPoints == nullptr || previousPoints->size() == roughNextPoints->size());
1505  ocean_assert(nextPoints->size() == previousPoints->size());
1506 
1507  ocean_assert(firstPoint + numberPoints <= previousPoints->size());
1508 
1509  ocean_assert(numberLayers >= 1u);
1510  ocean_assert(numberLayers <= previousPyramid->layers());
1511  ocean_assert(numberLayers <= nextPyramid->layers());
1512 
1513  ocean_assert(previousPyramid->layer(numberLayers - 1u).width() >= tSize / 2u);
1514  ocean_assert(previousPyramid->layer(numberLayers - 1u).height() >= tSize / 2u);
1515 
1516  ShiftVector<Vector2> intermediateRoughNextPoints(firstPoint, numberPoints);
1517 
1518  const Scalar coarsetsWidthNextPyramid = Scalar(nextPyramid->layer(numberLayers - 1u).width());
1519  const Scalar coarsetsHeightNextPyramid = Scalar(nextPyramid->layer(numberLayers - 1u).height());
1520  const Scalar coarsetsLayerFactorNextPyramid = Scalar(1) / Scalar(nextPyramid->sizeFactor(numberLayers - 1u));
1521  ocean_assert(coarsetsWidthNextPyramid >= 1u && coarsetsHeightNextPyramid >= 1u);
1522 
1523  const unsigned int channels = previousPyramid->frameType().channels();
1524  ocean_assert(channels >= 1u && channels <= 4u);
1525 
1526  for (unsigned int n = firstPoint; n < firstPoint + numberPoints; ++n)
1527  {
1528  const Vector2& roughNextPoint = roughNextPoints != nullptr ? (*roughNextPoints)[n] : (*previousPoints)[n];
1529 
1530  const Scalar x = min(roughNextPoint.x() * coarsetsLayerFactorNextPyramid, coarsetsWidthNextPyramid - Scalar(1));
1531  const Scalar y = min(roughNextPoint.y() * coarsetsLayerFactorNextPyramid, coarsetsHeightNextPyramid - Scalar(1));
1532 
1533  intermediateRoughNextPoints[n] = Vector2(x, y);
1534  }
1535 
1536  for (unsigned int layerIndex = numberLayers - 1u; layerIndex < numberLayers; --layerIndex)
1537  {
1538  const Frame& previousLayer = (*previousPyramid)[layerIndex];
1539  const Frame& nextLayer = (*nextPyramid)[layerIndex];
1540 
1541  const uint8_t* const previousLayerData = previousLayer.constdata<uint8_t>();
1542  const uint8_t* const nextLayerData = nextLayer.constdata<uint8_t>();
1543 
1544  const unsigned int previousLayerPaddingElements = previousLayer.paddingElements();
1545  const unsigned int nextLayerPaddingElements = nextLayer.paddingElements();
1546 
1547  const unsigned int previousLayerWidth = previousLayer.width();
1548  const unsigned int previousLayerHeight = previousLayer.height();
1549 
1550  const unsigned int nextLayerWidth = nextLayer.width();
1551  const unsigned int nextLayerHeight = nextLayer.height();
1552 
1553  if (layerIndex == 0u)
1554  {
1555  // we apply a sub-pixel accurate tracking on the finest pyramid layer
1556 
1557  const unsigned int layerRadiusX = numberLayers == 1u ? coarsestLayerRadius : 2u;
1558  const unsigned int layerRadiusY = numberLayers == 1u ? coarsestLayerRadius : 2u;
1559 
1560  for (unsigned int pointIndex = firstPoint; pointIndex < firstPoint + numberPoints; ++pointIndex)
1561  {
1562  const Vector2& previousPosition = (*previousPoints)[pointIndex];
1563  ocean_assert(previousPosition.x() >= Scalar(0) && previousPosition.y() >= Scalar(0));
1564  ocean_assert(previousPosition.x() < Scalar(previousLayerWidth) && previousPosition.y() < Scalar(previousLayerHeight));
1565 
1566  const Vector2& intermediateRoughNextPoint = intermediateRoughNextPoints[pointIndex];
1567  ocean_assert(intermediateRoughNextPoint.x() >= Scalar(0) && intermediateRoughNextPoint.y() >= Scalar(0));
1568  ocean_assert(intermediateRoughNextPoint.x() < Scalar(nextLayerWidth) && intermediateRoughNextPoint.y() < Scalar(nextLayerHeight));
1569 
1570  unsigned int* const metricResult = metricResults ? metricResults + pointIndex : nullptr;
1571  unsigned int* const metricIdentityResult = metricIdentityResults ? metricIdentityResults + pointIndex : nullptr;
1572 
1573  const Vector2 nextPoint = trackPointSubPixelMirroredBorder<tSize>(previousLayerData, nextLayerData, channels, previousLayerWidth, previousLayerHeight, nextLayerWidth, nextLayerHeight, previousLayerPaddingElements, nextLayerPaddingElements, previousPosition, layerRadiusX, layerRadiusY, intermediateRoughNextPoint, subPixelIterations, metricResult, metricIdentityResult);
1574 
1575  ocean_assert(nextPoint.x() >= 0 && nextPoint.x() < Scalar(nextLayerWidth));
1576  ocean_assert(nextPoint.y() >= 0 && nextPoint.y() < Scalar(nextLayerHeight));
1577 
1578  ocean_assert(pointIndex < nextPoints->size());
1579  (*nextPoints)[pointIndex] = nextPoint;
1580  }
1581  }
1582  else // otherwise we apply a pixel accurate determination
1583  {
1584  ocean_assert(layerIndex > 0u);
1585 
1586  const unsigned int layerRadius = (layerIndex == numberLayers - 1u) ? coarsestLayerRadius : 2u;
1587 
1588  const Scalar layerFactor = Scalar(1) / Scalar(1u << layerIndex);
1589 
1590  const Scalar finerNextLayerWidth1 = Scalar((*nextPyramid)[layerIndex - 1u].width()) - Scalar(1);
1591  const Scalar finerNextLayerHeight1 = Scalar((*nextPyramid)[layerIndex - 1u].height()) - Scalar(1);
1592 
1593  for (unsigned int pointIndex = firstPoint; pointIndex < firstPoint + numberPoints; ++pointIndex)
1594  {
1595  ocean_assert(intermediateRoughNextPoints[pointIndex].x() >= Scalar(0) && intermediateRoughNextPoints[pointIndex].y() >= Scalar(0));
1596  ocean_assert(intermediateRoughNextPoints[pointIndex].x() < Scalar(nextLayerWidth) && intermediateRoughNextPoints[pointIndex].y() < Scalar(nextLayerHeight));
1597 
1598  const PixelPosition intermediateRoughNextPoint(Numeric::round32(intermediateRoughNextPoints[pointIndex].x()), Numeric::round32(intermediateRoughNextPoints[pointIndex].y()));
1599  ocean_assert(intermediateRoughNextPoint.x() < nextLayerWidth && intermediateRoughNextPoint.y() < nextLayerHeight);
1600 
1601  unsigned int* const metricResult = metricResults ? metricResults + pointIndex : nullptr;
1602 
1603  const Vector2& previousPointFinestLayer = (*previousPoints)[pointIndex];
1604 
1605  const PixelPosition previousPoint(std::min(Numeric::round32(previousPointFinestLayer.x() * layerFactor), int(previousLayerWidth - 1u)), std::min(Numeric::round32(previousPointFinestLayer.y() * layerFactor), int(previousLayerHeight - 1u)));
1606 
1607  ocean_assert(previousPoint.x() < previousLayerWidth && previousPoint.y() < previousLayerHeight);
1608  if (previousPoint.x() < previousLayerWidth && previousPoint.y() < previousLayerHeight)
1609  {
1610  const PixelPosition nextPoint = Motion<TMetricInteger>::template pointMotionInFrameMirroredBorder<tSize>(previousLayerData, nextLayerData, channels, previousLayerWidth, previousLayerHeight, nextLayerWidth, nextLayerHeight, previousPoint, layerRadius, layerRadius, previousLayerPaddingElements, nextLayerPaddingElements, intermediateRoughNextPoint, metricResult);
1611 
1612  ocean_assert(nextPoint.x() < nextLayerWidth && nextPoint.y() < nextLayerHeight);
1613 
1614  intermediateRoughNextPoints[pointIndex] = Vector2(min(Scalar(nextPoint.x() * 2u), finerNextLayerWidth1), min(Scalar(nextPoint.y() * 2u), finerNextLayerHeight1));
1615 
1616  ocean_assert(intermediateRoughNextPoints[pointIndex].x() >= 0 && intermediateRoughNextPoints[pointIndex].x() <= finerNextLayerWidth1);
1617  ocean_assert(intermediateRoughNextPoints[pointIndex].y() >= 0 && intermediateRoughNextPoints[pointIndex].y() <= finerNextLayerHeight1);
1618  }
1619  else
1620  {
1621  ocean_assert(false && "This should never happen!");
1622 
1623  intermediateRoughNextPoints[pointIndex] = Vector2(previousPointFinestLayer.x() * layerFactor * Scalar(2), previousPointFinestLayer.y() * layerFactor * Scalar(2));
1624 
1625  ocean_assert(intermediateRoughNextPoints[pointIndex].x() >= 0 && intermediateRoughNextPoints[pointIndex].x() < finerNextLayerWidth1);
1626  ocean_assert(intermediateRoughNextPoints[pointIndex].y() >= 0 && intermediateRoughNextPoints[pointIndex].y() < finerNextLayerHeight1);
1627  }
1628  }
1629  }
1630  }
1631 }
1632 
1633 template <typename TMetricInteger, typename TMetricFloat>
1634 template <unsigned int tChannels, unsigned int tSize>
1635 void AdvancedMotion<TMetricInteger, TMetricFloat>::trackPointsSubPixelMirroredBorderSubset(const FramePyramid* previousPyramid, const FramePyramid* currentPyramid, const unsigned int numberLayers, const Vectors2* previousPoints, const Vectors2* roughPoints, Vectors2* currentPoints, const unsigned int coarsestLayerRadius, const unsigned int subPixelIterations, unsigned int* metricResults, unsigned int* metricIdentityResults, const unsigned int firstPoint, const unsigned int numberPoints)
1636 {
1637  static_assert(tSize % 2u == 1u, "Invalid patch size, must be odd!");
1638 
1639  ocean_assert(previousPyramid && currentPyramid);
1640  ocean_assert(previousPoints && roughPoints && currentPoints);
1641 
1642  ocean_assert(*previousPyramid && *currentPyramid);
1643 
1644  ocean_assert(FrameType::arePixelFormatsCompatible(previousPyramid->frameType().pixelFormat(), currentPyramid->frameType().pixelFormat()));
1645  ocean_assert(previousPyramid->frameType().pixelOrigin() == currentPyramid->frameType().pixelOrigin());
1646 
1647  ocean_assert(previousPyramid->frameType().channels() == tChannels);
1648  ocean_assert(currentPyramid->frameType().channels() == tChannels);
1649 
1650  ocean_assert(previousPoints->size() == roughPoints->size());
1651  ocean_assert(currentPoints->size() == previousPoints->size());
1652 
1653  ocean_assert(firstPoint + numberPoints <= previousPoints->size());
1654 
1655  ocean_assert(numberLayers >= 1u);
1656  ocean_assert(numberLayers <= previousPyramid->layers());
1657  ocean_assert(numberLayers <= currentPyramid->layers());
1658 
1659  ocean_assert(previousPyramid->layer(numberLayers - 1u).width() >= tSize / 2u);
1660  ocean_assert(previousPyramid->layer(numberLayers - 1u).height() >= tSize / 2u);
1661 
1662  ShiftVector<Vector2> intermediateRoughPoints(firstPoint, numberPoints);
1663 
1664  const Scalar lowestCurrentWidth = Scalar(currentPyramid->layer(numberLayers - 1u).width());
1665  const Scalar lowestCurrentHeight = Scalar(currentPyramid->layer(numberLayers - 1u).height());
1666  const Scalar lowestLayerFactor = Scalar(1) / Scalar(currentPyramid->sizeFactor(numberLayers - 1u));
1667  ocean_assert(lowestCurrentWidth >= 1u && lowestCurrentHeight >= 1u);
1668 
1669  const unsigned int channels = previousPyramid->frameType().channels();
1670  ocean_assert_and_suppress_unused(channels >= 1u && channels <= 4u, channels);
1671 
1672  for (unsigned int n = firstPoint; n < firstPoint + numberPoints; ++n)
1673  {
1674  const Vector2& roughPoint = (*roughPoints)[n];
1675 
1676  const Scalar x = min(roughPoint.x() * lowestLayerFactor, lowestCurrentWidth - 1);
1677  const Scalar y = min(roughPoint.y() * lowestLayerFactor, lowestCurrentHeight - 1);
1678 
1679  intermediateRoughPoints[n] = Vector2(x, y);
1680  }
1681 
1682  for (int l = int(numberLayers) - 1; l >= 0; --l)
1683  {
1684  const Frame previousFrame = (*previousPyramid)[l];
1685  const Frame currentFrame = (*currentPyramid)[l];
1686 
1687  const uint8_t* const previousFrameData = previousFrame.constdata<uint8_t>();
1688  const uint8_t* const currentFrameData = currentFrame.constdata<uint8_t>();
1689 
1690  const unsigned int previousFramePaddingElements = previousFrame.paddingElements();
1691  const unsigned int currentFramePaddingElements = currentFrame.paddingElements();
1692 
1693  const unsigned int previousWidth = previousFrame.width();
1694  const unsigned int previousHeight = previousFrame.height();
1695 
1696  const unsigned int currentWidth = currentFrame.width();
1697  const unsigned int currentHeight = currentFrame.height();
1698 
1699  // if the finest layer is reached we apply a subpixel accurate determination
1700  if (l == 0)
1701  {
1702  const unsigned int layerRadiusX = numberLayers == 1u ? coarsestLayerRadius : 2u;
1703  const unsigned int layerRadiusY = numberLayers == 1u ? coarsestLayerRadius : 2u;
1704 
1705  for (unsigned int i = firstPoint; i < firstPoint + numberPoints; ++i)
1706  {
1707  ocean_assert(intermediateRoughPoints[i].x() >= Scalar(0) && intermediateRoughPoints[i].y() >= Scalar(0));
1708  ocean_assert(intermediateRoughPoints[i].x() < Scalar(currentWidth) && intermediateRoughPoints[i].y() < Scalar(currentHeight));
1709 
1710  const Vector2& intermediateRoughPoint = intermediateRoughPoints[i];
1711 
1712  unsigned int* const metricResult = metricResults ? metricResults + i : nullptr;
1713  unsigned int* const metricIdentityResult = metricIdentityResults ? metricIdentityResults + i : nullptr;
1714 
1715  const Vector2& previousPosition = (*previousPoints)[i];
1716 
1717  ocean_assert(previousPosition.x() >= Scalar(0) && previousPosition.y() >= Scalar(0));
1718  ocean_assert(previousPosition.x() < Scalar(previousWidth) && previousPosition.y() < Scalar(previousHeight));
1719 
1720  const Vector2 position(trackPointSubPixelMirroredBorder<tChannels, tSize>(previousFrameData, currentFrameData, previousWidth, previousHeight, currentWidth, currentHeight, previousFramePaddingElements, currentFramePaddingElements, previousPosition, layerRadiusX, layerRadiusY, intermediateRoughPoint, subPixelIterations, metricResult, metricIdentityResult));
1721 
1722  ocean_assert(position.x() >= 0 && position.x() < Scalar(currentWidth));
1723  ocean_assert(position.y() >= 0 && position.y() < Scalar(currentHeight));
1724 
1725  ocean_assert(i < currentPoints->size());
1726  (*currentPoints)[i] = position;
1727  }
1728  }
1729  else // otherwise we apply a pixel accurate determination
1730  {
1731  ocean_assert(l > 0);
1732 
1733  const unsigned int layerRadiusX = (l == int(numberLayers) - 1) ? coarsestLayerRadius : 2u;
1734  const unsigned int layerRadiusY = (l == int(numberLayers) - 1) ? coarsestLayerRadius : 2u;
1735 
1736  for (unsigned int i = firstPoint; i < firstPoint + numberPoints; ++i)
1737  {
1738  ocean_assert(intermediateRoughPoints[i].x() >= Scalar(0) && intermediateRoughPoints[i].y() >= Scalar(0));
1739  ocean_assert(intermediateRoughPoints[i].x() < Scalar(currentWidth) && intermediateRoughPoints[i].y() < Scalar(currentHeight));
1740 
1741  const PixelPosition intermediateRoughPoint(Numeric::round32(intermediateRoughPoints[i].x()), Numeric::round32(intermediateRoughPoints[i].y()));
1742 
1743  unsigned int* const metricResult = metricResults ? metricResults + i : nullptr;
1744 
1745  const Scalar layerFactor = Scalar(1) / Scalar((unsigned int)(1 << l));
1746  const PixelPosition previousPosition(min(Numeric::round32((*previousPoints)[i].x() * layerFactor), int(previousWidth) - 1),
1747  min(Numeric::round32((*previousPoints)[i].y() * layerFactor), int(previousHeight) - 1));
1748 
1749  if (previousPosition.x() < previousWidth && previousPosition.y() < previousHeight)
1750  {
1751  const PixelPosition position(Motion<TMetricInteger>::template pointMotionInFrameMirroredBorder<tChannels, tSize>(previousFrameData, currentFrameData, previousWidth, previousHeight, currentWidth, currentHeight, previousPosition, layerRadiusX, layerRadiusY, previousFramePaddingElements, currentFramePaddingElements, intermediateRoughPoint, metricResult));
1752 
1753  ocean_assert(position.x() < currentWidth && position.y() < currentHeight);
1754 
1755  const Scalar higherWidth = Scalar((*currentPyramid)[l - 1].width());
1756  const Scalar higherHeight = Scalar((*currentPyramid)[l - 1].height());
1757 
1758  intermediateRoughPoints[i] = Vector2(min(Scalar(position.x() * 2u), higherWidth - 1), min(Scalar(position.y() * 2u), higherHeight - 1));
1759 
1760  ocean_assert(intermediateRoughPoints[i].x() >= 0 && intermediateRoughPoints[i].x() < Scalar(higherWidth));
1761  ocean_assert(intermediateRoughPoints[i].y() >= 0 && intermediateRoughPoints[i].y() < Scalar(higherHeight));
1762  }
1763  else
1764  {
1765  intermediateRoughPoints[i] = Vector2((*previousPoints)[i].x() * layerFactor * Scalar(2), (*previousPoints)[i].y() * layerFactor * Scalar(2));
1766 
1767  ocean_assert(intermediateRoughPoints[i].x() >= 0 && intermediateRoughPoints[i].x() < Scalar(currentFrame.width() * 2u));
1768  ocean_assert(intermediateRoughPoints[i].y() >= 0 && intermediateRoughPoints[i].y() < Scalar(currentFrame.height() * 2u));
1769  }
1770  }
1771  }
1772  }
1773 }
1774 
1775 }
1776 
1777 }
1778 
1779 }
1780 
1781 #endif // META_OCEAN_CV_ADVANCED_ADVANCED_MOTION_H
bool isValid() const
Returns whether the box holds valid parameters.
Definition: Box2.h:766
bool box2integer(const int constraintLeft, const int constraintTop, const int constraintRight, const int constraintBottom, int &intersectionLeft, int &intersectionTop, unsigned int &intersectionWidth, unsigned int &intersectionHeight) const
Calculates the intersection of this bounding box (with floating point accuracy) and a second bounding...
Definition: Box2.h:928
This class implements advanced motion techniques (mainly with sub-pixel accuracy or binary masks) all...
Definition: AdvancedMotion.h:69
static bool trackPointsBidirectionalSubPixelMirroredBorder(const CV::FramePyramid &previousPyramid, const CV::FramePyramid &nextPyramid, const unsigned int coarsestLayerRadius, Vectors2 &previousImagePoints, Vectors2 &nextImagePoints, const Scalar maximalSqrError=Scalar(0.9 *0.9), Worker *worker=nullptr, Indices32 *validIndices=nullptr, const unsigned int subPixelIterations=4u)
Tracks a set of given points between two frame pyramids with sub-pixel accuracy.
Definition: AdvancedMotion.h:795
static bool trackPointsSubPixelMirroredBorder(const Frame &previousFrame, const Frame &currentFrame, const Vectors2 &previousPoints, const Vectors2 &roughPoints, Vectors2 &currentPoints, const unsigned int maximalOffset, const unsigned int coarsestLayerRadius, const FramePyramid::DownsamplingMode downsamplingMode=FramePyramid::DM_FILTER_14641, const unsigned int subPixelIterations=4u, Worker *worker=nullptr, MetricResults *metricResults=nullptr, MetricResults *metricIdentityResults=nullptr)
Tracks a set of given points between two frames, with sub-pixel accuracy.
Definition: AdvancedMotion.h:500
static Vector2 trackPointSubPixelMirroredBorder(const uint8_t *frame0, const uint8_t *frame1, const unsigned int width0, const unsigned int height0, const unsigned int width1, const unsigned int height1, const unsigned int frame0PaddingElements, const unsigned int frame1PaddingElements, const Vector2 &position0, const unsigned int radiusX, const unsigned int radiusY, const Vector2 &rough1=Vector2(Numeric::maxValue(), Numeric::maxValue()), const unsigned int subPixelIterations=4u, uint32_t *metricResult=nullptr, uint32_t *metricIdentityResult=nullptr)
Tracks the location of one given 2D point from one image to another image with sub-pixel precision by...
Definition: AdvancedMotion.h:1268
std::vector< uint32_t > MetricResults
Definition of a vector holding metric results.
Definition: AdvancedMotion.h:75
static bool trackArbitraryPointsBidirectionalSubPixelMirroredBorder(const CV::FramePyramid &previousPyramid, const CV::FramePyramid &nextPyramid, const unsigned int coarsestLayerRadius, Vectors2 &previousImagePoints, Vectors2 &nextImagePoints, const Scalar maximalSqrError=Scalar(0.9 *0.9), const SubRegion &previousSubRegion=SubRegion(), const unsigned int horizontalBins=0u, const unsigned int verticalBins=0u, const unsigned int strength=30u, Worker *worker=nullptr, const unsigned int trackingLayers=1u)
Tracks a set of arbitrary (unknown) points between two frame pyramids with sub-pixel accuracy.
Definition: AdvancedMotion.h:626
static Vector2 trackPointBufferSubPixelMirroredBorder(const uint8_t *buffer0, const uint8_t *frame1, const unsigned int width1, const unsigned int height1, const unsigned int frame1PaddingElements, const Vector2 &roughPosition1, const unsigned int subPixelIterations, uint32_t *metricResult=nullptr)
Tracks the location of one given 2D point from one image to another image with sub-pixel precision by...
Definition: AdvancedMotion.h:1390
static bool trackPointsBidirectionalSubPixelMirroredBorderWithRoughLocations(const CV::FramePyramid &previousPyramid, const CV::FramePyramid &nextPyramid, const unsigned int coarsestLayerRadius, Vectors2 &previousImagePoints, const Vectors2 &roughNextImagePoints, Vectors2 &nextImagePoints, const Scalar maximalSqrError=Scalar(0.9 *0.9), Worker *worker=nullptr, Indices32 *validIndices=nullptr, const unsigned int subPixelIterations=4u)
Tracks a set of given points between two frame pyramids with sub-pixel accuracy.
Definition: AdvancedMotion.h:977
static bool trackReliableReferencePoints(const FramePyramid &previousPyramid, const FramePyramid &currentPyramid, Vectors2 &previousReferencePoints, Vectors2 &currentReferencePoints, const unsigned int horizontalBins=16u, const unsigned int verticalBins=16u, const PixelBoundingBox &boundingBox=PixelBoundingBox(), const Frame &maskFrame=Frame(), Worker *worker=nullptr)
Detects and tracks reliable arbitrary reference points between two frames.
Definition: AdvancedMotion.h:1192
static void trackPointsSubPixelMirroredBorderSubset(const FramePyramid *previousPyramid, const FramePyramid *nextPyramid, const unsigned int numberLayers, const Vectors2 *previousPoints, const Vectors2 *roughNextPoints, Vectors2 *nextPoints, const unsigned int coarsestLayerRadius, const unsigned int subPixelIterations, unsigned int *metricResults, unsigned int *metricIdentityResults, const unsigned int firstPoint, const unsigned int numberPoints)
Tracks a subset of given points between two frame pyramids with sub-pixel accuracy.
Definition: AdvancedMotion.h:1492
static bool trackPointsSubPixelMask(const FramePyramid &previousPyramid, const FramePyramid &currentPyramid, const FramePyramid &previousMaskPyramid, const FramePyramid &currentMaskPyramid, const Vectors2 &previousPoints, const Vectors2 &roughCurrentPoints, Vectors2 &currentPoints, const unsigned int coarsestLayerRadius, const unsigned int subPixelIterations=4u, Worker *worker=nullptr)
Tracks a set of given points between two frame pyramids with sub-pixel accuracy while each pyramid la...
static bool trackPointsMask(const FramePyramid &previousPyramid, const FramePyramid &currentPyramid, const FramePyramid &previousMaskPyramid, const FramePyramid &currentMaskPyramid, const PixelPositions &previousPoints, const PixelPositions &roughCurrentPoints, PixelPositions &currentPoints, const unsigned int coarsestLayerRadius, Worker *worker=nullptr)
Tracks a set of given points between two frame pyramids with pixel accuracy while each pyramid layer ...
static bool detectCorners(const uint8_t *yFrame, const unsigned int width, const unsigned int height, const unsigned int yFramePaddingElements, const unsigned int threshold, const bool frameIsUndistorted, HarrisCorners &corners, const bool determineExactPosition=false, Worker *worker=nullptr)
Detects Harris corners inside a given 8 bit grayscale image.
Definition: HarrisCornerDetector.h:397
static Geometry::ImagePoints corners2imagePoints(const HarrisCorners &corners)
Converts Harris corners to simple 2D image positions.
Definition: HarrisCorner.h:110
static bool convert(const Frame &source, const FrameType::PixelFormat targetPixelFormat, const FrameType::PixelOrigin targetPixelOrigin, Frame &target, const bool forceCopy=true, Worker *worker=nullptr, const Options &options=Options())
Converts a frame with arbitrary dimension, pixel format and pixel origin into a frame with the same d...
@ CP_AVOID_COPY_IF_POSSIBLE
Tries to avoid copying the frame data whenever possible.
Definition: FrameConverter.h:96
This class implements a frame pyramid.
Definition: FramePyramid.h:37
const Frame & finestLayer() const
Returns the finest layer frame of this pyramid.
Definition: FramePyramid.h:735
static unsigned int idealLayers(const unsigned int width, const unsigned int height, const unsigned int invalidCoarsestWidthOrHeight, unsigned int *coarsestLayerWidth=nullptr, unsigned int *coarsestLayerHeight=nullptr)
Determines the number of layers until an invalid frame size would be reached in the next layer.
unsigned int finestWidth() const
Returns the width of the finest (first) layer.
Definition: FramePyramid.h:778
bool isValid() const
Returns whether this pyramid holds at least one frame layer.
Definition: FramePyramid.h:863
DownsamplingMode
Definition of individual down sampling modes.
Definition: FramePyramid.h:44
@ DM_FILTER_14641
Down sampling is realized by a 5x5 Gaussian filter.
Definition: FramePyramid.h:72
unsigned int layers() const
Returns the number of layers this pyramid holds.
Definition: FramePyramid.h:761
const FrameType & frameType() const
Returns the frame type of the finest layer.
Definition: FramePyramid.h:811
unsigned int finestHeight() const
Returns the height of the finest (first) layer.
Definition: FramePyramid.h:784
static constexpr unsigned int sizeFactor(const unsigned int layer)
Returns the size factor of a specified layer in relation to the finest layer.
Definition: FramePyramid.h:834
const Frame & layer(const unsigned int layer) const
Returns the frame of a specified layer.
Definition: FramePyramid.h:723
This class implements patch-based motion techniques.
Definition: Motion.h:59
T left() const
Returns the left (including) pixel position of this bounding box.
Definition: PixelBoundingBox.h:416
unsigned int width() const
Returns the width (the number of horizontal including pixels) of this bounding box.
Definition: PixelBoundingBox.h:482
T top() const
Returns the top (including) pixel position of this bounding box.
Definition: PixelBoundingBox.h:423
unsigned int height() const
Returns the height (the number of vertical including pixels) of this bounding box.
Definition: PixelBoundingBox.h:489
T y() const
Returns the vertical coordinate position of this object.
Definition: PixelPosition.h:470
unsigned int sqrDistance(const PixelPositionT< T > &position) const
Returns the square difference between two pixel positions.
Definition: PixelPosition.h:489
static PixelPositionT< unsigned int > vector2pixelPosition(const Vector2 &value)
Converts a 2D vector into a pixel position.
T x() const
Returns the horizontal coordinate position of this object.
Definition: PixelPosition.h:458
This class implement a sub-region either defined by 2D triangles or defined by a binary mask.
Definition: SubRegion.h:32
const Box2 & boundingBox() const
Returns the bounding box of this sub-region.
Definition: SubRegion.h:218
bool isEmpty() const
Returns whether this sub-region is empty.
Definition: SubRegion.h:223
bool isInside(const Vector2 &point) const
Returns whether a given point lies inside this sub-region.
static Caller< void > createStatic(typename StaticFunctionPointerMaker< void, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass >::Type function)
Creates a new caller container for a static function with no function parameter.
Definition: Caller.h:2876
This class implements Ocean's image class.
Definition: Frame.h:1792
unsigned int strideElements(const unsigned int planeIndex=0u) const
Returns the number of elements within one row, including optional padding at the end of a row for a s...
Definition: Frame.h:4058
const T * constdata(const unsigned int planeIndex=0u) const
Returns a pointer to the read-only pixel data of a specific plane.
Definition: Frame.h:4168
const FrameType & frameType() const
Returns the frame type of this frame.
Definition: Frame.h:3775
bool isValid() const
Returns whether this frame is valid.
Definition: Frame.h:4448
unsigned int paddingElements(const unsigned int planeIndex=0u) const
Returns the optional number of padding elements at the end of each row for a specific plane.
Definition: Frame.h:4042
@ FORMAT_Y8
Pixel format for grayscale images with byte order Y and 8 bits per pixel.
Definition: Frame.h:594
unsigned int width() const
Returns the width of the frame format in pixel.
Definition: Frame.h:3143
PixelOrigin pixelOrigin() const
Returns the pixel origin of the frame.
Definition: Frame.h:3188
static bool arePixelFormatsCompatible(const PixelFormat pixelFormatA, const PixelFormat pixelFormatB)
Returns whether two given pixel formats are compatible.
PixelFormat pixelFormat() const
Returns the pixel format of the frame.
Definition: Frame.h:3153
unsigned int height() const
Returns the height of the frame in pixel.
Definition: Frame.h:3148
bool isPixelFormatCompatible(const PixelFormat pixelFormat) const
Returns whether the pixel format of this frame type is compatible with a given pixel format.
Definition: Frame.h:3198
unsigned int channels() const
Returns the number of individual channels the frame has.
Definition: Frame.h:3173
unsigned int bins() const
Returns the number of bins this distribution holds.
Definition: SpatialDistribution.h:1089
This class implements a distribution array.
Definition: SpatialDistribution.h:228
static DistributionArray distributeToArray(const ImagePoint *imagePoints, const size_t number, const Scalar left, const Scalar top, const Scalar width, const Scalar height, const unsigned int averagePointsPerBin, const unsigned int maxHorizontalBins, const unsigned int maxVerticalBins, unsigned int &horizontalBins, unsigned int &verticalBins)
Distributes the given 2D image points into a spatial array.
Definition: SpatialDistribution.h:1686
static ImagePoints distributeAndFilter(const ImagePoint *imagePoints, const size_t numberImagePoints, const Scalar left, const Scalar top, const Scalar width, const Scalar height, const unsigned int horizontalBins, const unsigned int verticalBins)
Distributes the given image points into an array of specified size and returns (at most) one point fr...
Definition: SpatialDistribution.h:1714
static constexpr int32_t round32(const T value)
Returns the rounded 32 bit integer value of a given value.
Definition: Numeric.h:2064
static constexpr T maxValue()
Returns the max scalar value.
Definition: Numeric.h:3244
This class implements a vector with shifted elements.
Definition: ShiftVector.h:27
const T & x() const noexcept
Returns the x value.
Definition: Vector2.h:698
const T & y() const noexcept
Returns the y value.
Definition: Vector2.h:710
This class implements a worker able to distribute function calls over different threads.
Definition: Worker.h:33
bool executeFunction(const Function &function, const unsigned int first, const unsigned int size, const unsigned int firstIndex=(unsigned int)(-1), const unsigned int sizeIndex=(unsigned int)(-1), const unsigned int minimalIterations=1u, const unsigned int threadIndex=(unsigned int)(-1))
Executes a callback function separable by two function parameters.
unsigned int sqrDistance(const char first, const char second)
Returns the square distance between two values.
Definition: base/Utilities.h:1089
std::vector< Index32 > Indices32
Definition of a vector holding 32 bit index values.
Definition: Base.h:96
uint32_t Index32
Definition of a 32 bit index value.
Definition: Base.h:84
std::vector< PixelPosition > PixelPositions
Definition of a vector holding pixel positions (with positive coordinate values).
Definition: PixelPosition.h:48
PixelBoundingBoxT< unsigned int > PixelBoundingBox
Definition of the default PixelBoundingBox object with data type allowing only positive coordinate va...
Definition: PixelBoundingBox.h:21
AdvancedMotion< ZeroMeanSumSquareDifferences, AdvancedZeroMeanSumSquareDifferences > AdvancedMotionZeroMeanSSD
Definition of an AdvancedMotion class that applies zero-mean sum square difference calculations as me...
Definition: AdvancedMotion.h:58
AdvancedMotion< SumSquareDifferences, AdvancedSumSquareDifferences > AdvancedMotionSSD
Definition of an AdvancedMotion class that applies sum square difference calculations as metric.
Definition: AdvancedMotion.h:44
std::vector< HarrisCorner > HarrisCorners
Definition of a vector holding Harris corners.
Definition: HarrisCorner.h:24
float Scalar
Definition of a scalar type.
Definition: Math.h:128
std::vector< Vector2 > Vectors2
Definition of a vector holding Vector2 objects.
Definition: Vector2.h:64
VectorT2< Scalar > Vector2
Definition of a 2D vector.
Definition: Vector2.h:21
BoxT2< Scalar > Box2
Definition of the Box2 object, depending on the OCEAN_MATH_USE_SINGLE_PRECISION either with single or...
Definition: Box2.h:22
The namespace covering the entire Ocean framework.
Definition: Accessor.h:15