Ocean
Loading...
Searching...
No Matches
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/Worker.h"
18
21#include "ocean/cv/Motion.h"
24#include "ocean/cv/SubRegion.h"
25
27
29
30#include "ocean/math/Box2.h"
31
32namespace Ocean
33{
34
35namespace CV
36{
37
38namespace Advanced
39{
40
41// Forward declaration.
42template <typename TMetricInteger, typename TMetricFloat>
43class AdvancedMotionT;
44
45/**
46 * Definition of an AdvancedMotion class that applies sum square difference calculations as metric.
47 * @see AdvancedMotionZeroMeanSSD, AdvancedMotion.
48 * @ingroup cvadvanced
49 */
51
52/**
53 * Definition of an AdvancedMotion class that applies zero-mean sum square difference calculations as metric.
54 * @see AdvancedMotionSSD, AdvancedMotion.
55 * @ingroup cvadvanced
56 */
58
59class OCEAN_CV_ADVANCED_EXPORT AdvancedMotion
60{
61 public:
62
63 /**
64 * Definition of a vector holding metric results.
65 */
66 using MetricResults = std::vector<uint32_t>;
67
68 /**
69 * This class manages point correspondences for bidirectional tracking across pyramid layers.
70 * The class handles the state management for tracking points from a previous pyramid to a next pyramid, and then back to the previous pyramid for validation.
71 * It manages the progression through pyramid layers (coarse-to-fine) and provides methods to access and update point positions during the tracking process.
72 */
73 class OCEAN_CV_ADVANCED_EXPORT PointCorrespondences
74 {
75 public:
76
77 /**
78 * Creates an invalid point correspondences object.
79 */
81
82 /**
83 * Creates a new point correspondences object with pre-defined predicted next points.
84 * In case no rough estimates for the next image points are known, use a copy of the previous points.
85 * @param previousPoints The previous image points, must be valid
86 * @param predictedNextPoints The predicted next image points (rough estimates), will be updated to the actuall tracked current next points, must be valid
87 * @param validCorrespondences The buffer to store validity flags for each correspondence, must be valid
88 * @param correspondences The number of point correspondences, with range [1, infinity)
89 * @param pyramidLayers The number of pyramid layers to be used for tracking, with range [1, infinity)
90 * @param coarsestLayerRadius The search radius on the coarsest pyramid layer, with range [2, infinity)
91 * @param maximalError Maximal error between forward and backward tracking for a valid point, with range [0, infinity)
92 * @param subPixelIterations Number of sub-pixel iterations that will be applied, with range [1, infinity)
93 */
94 inline PointCorrespondences(const Vector2* previousPoints, Vector2* predictedNextPoints, uint8_t* validCorrespondences, const size_t correspondences, const unsigned int pyramidLayers, const unsigned int coarsestLayerRadius, const Scalar maximalError = Scalar(0.9), const unsigned int subPixelIterations = 4u);
95
96 /**
97 * Initializes the forward tracking from the previous pyramid to the next pyramid.
98 * This method prepares the internal state for tracking points forward through the pyramid layers.
99 * @param previousPyramid The previous frame pyramid, must be valid
100 * @param nextPyramid The next frame pyramid, must be valid
101 */
102 void startForwardTracking(const CV::FramePyramid& previousPyramid, const CV::FramePyramid& nextPyramid);
103
104 /**
105 * Initializes the backward tracking from the next pyramid back to the previous pyramid.
106 * This method prepares the internal state for tracking points backward through the pyramid layers for validation.
107 * @param previousPyramid The previous frame pyramid, must be valid
108 * @param nextPyramid The next frame pyramid, must be valid
109 */
110 void startBackwardTracking(const CV::FramePyramid& /*previousPyramid*/, const CV::FramePyramid& nextPyramid);
111
112 /**
113 * Starts processing a specific pyramid layer.
114 * This method sets up the internal state for tracking on the specified pyramid layer.
115 * @param layerIndex The index of the pyramid layer to process, with range [0, pyramidLayers)
116 * @param previousPyramid The previous frame pyramid, must be valid
117 * @param nextPyramid The next frame pyramid, must be valid
118 * @return True if the layer can be processed; False if the layer index is out of range
119 */
120 bool startLayer(const unsigned int layerIndex, const CV::FramePyramid& previousPyramid, const CV::FramePyramid& nextPyramid);
121
122 /**
123 * Returns the previous image point position at the finest pyramid layer.
124 * This method can only be called when processing the finest layer (layer index 0).
125 * @param pointIndex The index of the point to access, with range [0, size())
126 * @return The previous point position in the finest layer
127 */
128 inline const Vector2& previousPosition(const size_t pointIndex) const;
129
130 /**
131 * Returns the previous image point position downsampled to the current pyramid layer.
132 * This method should be called for pyramid layers other than the finest layer.
133 * @param pointIndex The index of the point to access, with range [0, size())
134 * @return The pixel position of the previous point at the current pyramid layer
135 */
136 inline CV::PixelPosition previousPositionDownsampled(const size_t pointIndex) const;
137
138 /**
139 * Returns the predicted next image point position downsampled to the current pyramid layer.
140 * This method should be called for pyramid layers other than the finest layer.
141 * @param pointIndex The index of the point to access, with range [0, size())
142 * @return The pixel position of the predicted next point at the current pyramid layer
143 */
144 inline CV::PixelPosition predictedNextPositionDownsampled(const size_t pointIndex) const;
145
146 /**
147 * Returns the predicted next image point position at the finest pyramid layer.
148 * This method can only be called when processing the finest layer (layer index 0).
149 * @param pointIndex The index of the point to access, with range [0, size())
150 * @return The predicted next point position in the finest layer
151 */
152 inline const Vector2& predictedNextPosition(const size_t pointIndex) const;
153
154 /**
155 * Propagates the next point position from the current pyramid layer to the finer layer below.
156 * This method upsamples the point and updates the internal state.
157 * During backward tracking, this also performs an early rejection test based on distance.
158 * @param pointIndex The index of the point to propagate, with range [0, size())
159 * @param nextPoint The next point position at the current pyramid layer
160 */
161 void propagateNextPositionDownsampled(const size_t pointIndex, const CV::PixelPosition& nextPoint);
162
163 /**
164 * Propagates the final next point position at the finest pyramid layer.
165 * During forward tracking, this simply stores the final result.
166 * During backward tracking, this validates the bidirectional tracking consistency and computes
167 * the corrected next point position using the forward-backward offset.
168 * @param pointIndex The index of the point to propagate, with range [0, size())
169 * @param nextPoint The next point position at the finest layer
170 */
171 void propagateNextPosition(const size_t pointIndex, const Vector2& nextPoint);
172
173 /**
174 * Returns whether a specific point correspondence is still valid.
175 * Points can be marked invalid during backward tracking if they fail validation criteria.
176 * @param pointIndex The index of the point to check, with range [0, size())
177 * @return True if the point correspondence is valid; False otherwise
178 */
179 inline bool isPointValid(const size_t pointIndex) const;
180
181 /**
182 * Returns the number of pyramid layers to be used for tracking.
183 * @return The number of pyramid layers, with range [1, infinity)
184 */
185 inline unsigned int pyramidLayers() const;
186
187 /**
188 * Returns the search radius for the current pyramid layer.
189 * @return The search radius in pixels, with range [2, infinity)
190 */
191 inline unsigned int layerRadius() const;
192
193 /**
194 * Returns the number of sub-pixel iterations to be applied at the finest pyramid layer.
195 * @return The number of sub-pixel iterations, with range [1, infinity)
196 */
197 inline unsigned int subPixelIterations() const;
198
199 /**
200 * Returns the number of point correspondences.
201 * @return The number of correspondences, with range [0, infinity)
202 */
203 inline size_t size() const;
204
205 /**
206 * Returns whether this object holds valid parameters.
207 * @return True, if so
208 */
209 inline bool isValid() const;
210
211 /**
212 * Determines the coarsest pyramid layer index across multiple correspondence groups.
213 * This static method finds the maximum coarsest layer that can be used based on
214 * the pyramid sizes and the requested layers for all correspondence groups.
215 * @param previousPyramid The previous frame pyramid, must be valid
216 * @param nextPyramid The next frame pyramid, must be valid
217 * @param pointCorrespondenceGroups The array of correspondence groups, must be valid
218 * @param numberCorrespondenceGroups The number of correspondence groups, with range [1, infinity)
219 * @return The coarsest pyramid layer index that can be used, with range [0, min(pyramids.layers()) - 1]
220 */
221 static unsigned int coarsestPyramidLayer(const CV::FramePyramid& previousPyramid, const CV::FramePyramid& nextPyramid, const PointCorrespondences* pointCorrespondenceGroups, const size_t numberCorrespondenceGroups);
222
223 protected:
224
225 /// The image points in the previous pyramid, always at finest layer resolution
226 const Vector2* previousPoints_ = nullptr;
227
228 /// The tracked next image points, progressively refined through pyramid layers (coarsest to finest)
229 Vector2* nextPoints_ = nullptr;
230
231 /// Binary validity flags for each correspondence (0x00 = invalid, 0x01 = valid)
232 uint8_t* validCorrespondences_ = nullptr;
233
234 /// The number of point correspondences to track
235 size_t correspondences_ = 0;
236
237 /// The number of pyramid layers to use for tracking
238 unsigned int pyramidLayers_ = 0u;
239
240 /// The search radius on the coarsest pyramid layer, in pixels
241 unsigned int coarsestLayerRadius_ = 0u;
242
243 /// The squared maximum error threshold for bidirectional tracking validation
244 Scalar maximalSqrError_ = Scalar(-1);
245
246 /// The squared maximum error threshold used during layer processing (includes search radius)
247 Scalar maximalSqrErrorLayer_ = Scalar(-1);
248
249 /// The number of sub-pixel iterations to apply at the finest pyramid layer
250 unsigned int subPixelIterations_ = 0u;
251
252 /// The index of the current pyramid layer being processed, (unsigned int)(-1) if not started
253 unsigned int layerIndex_ = (unsigned int)(-1);
254
255 /// The inverse scale factor for the current pyramid layer (1.0 / sizeFactor)
256 Scalar invLayerFactor_ = Scalar(-1);
257
258 /// The index of the coarsest pyramid layer that will be used for tracking
259 unsigned int coarsestLayerIndex_ = (unsigned int)(-1);
260
261 /// The width of the next pyramid's finest layer, in pixels (used for boundary checking)
262 unsigned int nextPyramidFinestLayerWidth_ = 0u;
263
264 /// The height of the next pyramid's finest layer, in pixels (used for boundary checking)
265 unsigned int nextPyramidFinestLayerHeight_ = 0u;
266
267 /// The width of the current layer being processed in the previous pyramid, in pixels
268 unsigned int previousLayerWidth_ = 0u;
269
270 /// The height of the current layer being processed in the previous pyramid, in pixels
271 unsigned int previousLayerHeight_ = 0u;
272
273 /// The width of the current layer being processed in the next pyramid, in pixels
274 unsigned int nextLayerWidth_ = 0u;
275
276 /// The height of the current layer being processed in the next pyramid, in pixels
277 unsigned int nextLayerHeight_ = 0u;
278
279 /// The search radius for the current pyramid layer being processed, in pixels
280 unsigned int layerRadius_ = 0u;
281
282 /// True during forward tracking (previous->next), False during backward tracking (next->previous)
283 bool forwardTracking_ = true;
284
285 /// Internal storage for backward tracking results used for bidirectional validation
287 };
288
289 /**
290 * This class collects statistics about tracking distances between corresponding 2D feature points.
291 * The class calculates various statistical measures including average, median, percentiles (P95, P99), and maximum tracking distances.<br>
292 * This class can be used to determine suitable tracking parameters for a specific tracking use case.
293 * @see AdvancedMotionT::trackPointsBidirectionalSubPixelMirroredBorder().
294 */
296 {
297 public:
298
299 /**
300 * Creates a new tracking statistic object.
301 * @param width The width of the image frame in pixels, with range [1, infinity)
302 * @param height The height of the image frame in pixels, with range [1, infinity)
303 */
304 TrackingStatistic(const unsigned int width, const unsigned int height);
305
306 /**
307 * Adds a set of point correspondences to the tracking statistics.
308 * @param previousImagePoints The image points from the previous frame, must be valid
309 * @param nextImagePoints The corresponding image points from the next frame, one for each previous image point, must be valid
310 * @param size The number of point correspondences, with range [1, infinity)
311 */
312 void addCorrespondences(const Vector2* previousImagePoints, const Vector2* nextImagePoints, const size_t size);
313
314 /**
315 * Adds a set of point correspondences to the tracking statistics, filtering by validity flags.
316 * Only correspondences marked as valid (non-zero in validCorrespondences) are added to the statistics.
317 * @param previousImagePoints The image points from the previous frame, must be valid
318 * @param nextImagePoints The corresponding image points from the next frame, one for each previous image point, must be valid
319 * @param validCorrespondences The validity flags for each correspondence, non-zero indicates valid, must be valid
320 * @param size The number of point correspondences, with range [1, infinity)
321 */
322 void addCorrespondences(const Vector2* previousImagePoints, const Vector2* nextImagePoints, const uint8_t* validCorrespondences, const size_t size);
323
324 /**
325 * Returns the number of measurements this statistic holds, (the number of calls to addCorrespondences()).
326 * @return The object's measurements
327 */
328 inline size_t measurements() const;
329
330 /**
331 * Returns whether this tracking statistic object is valid.
332 * @return True if so
333 */
334 inline bool isValid() const;
335
336 /**
337 * Returns a string of the tracking statistics.
338 * @return The tracking statistics as string
339 */
340 std::string toString() const;
341
342 protected:
343
344 /// The width of the image frame in pixels
345 unsigned int width_ = 0u;
346
347 /// The height of the image frame in pixels
348 unsigned int height_ = 0u;
349
350 /// The squared Euclidean distances between all added point correspondences
352
353 /// The number of measurements (calls to addCorrespondences())
354 size_t measurements_ = 0;
355 };
356};
357
358/**
359 * 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.
360 * @tparam TMetricInteger The metric that is applied for measurements with pixel accuracy
361 * @tparam TMetricFloat The metric that is applied for measurement with sub-pixel accuracy
362 * @see AdvancedMotionSSD, AdvancedMotionZeroMeanSSD.
363 * @ingroup cvadvanced
364 */
365template <typename TMetricInteger = SumSquareDifferences, typename TMetricFloat = AdvancedSumSquareDifferences>
367{
368 public:
369
370 /**
371 * Tracks multiple groups of point correspondences bidirectionally between two frame pyramids with sub-pixel accuracy.
372 * This function provides fine-grained control over the tracking process by accepting pre-configured PointCorrespondences groups.
373 * Each group can have its own parameters (e.g., different pyramid layers, search radii, or sub-pixel iterations).
374 * The points are tracked from the previous pyramid to the next pyramid (forward tracking), and then from the next pyramid back to the previous pyramid (backward tracking).
375 * Point correspondences with inconsistent bidirectional tracking are marked as invalid.
376 * If a point is near the frame border, a mirrored image patch is applied.
377 * @param previousPyramid The previous frame pyramid, must be valid
378 * @param nextPyramid The next frame pyramid, with same pixel format and pixel origin as the previous pyramid, must be valid
379 * @param pointCorrespondenceGroups The array of point correspondence groups to track, must be valid
380 * @param numberCorrespondenceGroups The number of correspondence groups, with range [1, infinity)
381 * @return True if succeeded; False otherwise
382 * @tparam tChannels The number of channels both frame pyramids have, with range [1, 4]
383 * @tparam tSize The size of the image patch used to determine motion, with range [3, infinity), must be odd
384 */
385 template <unsigned int tChannels, unsigned int tSize>
386 static bool trackPointsBidirectionalSubPixelMirroredBorder(const CV::FramePyramid& previousPyramid, const CV::FramePyramid& nextPyramid, PointCorrespondences* pointCorrespondenceGroups, const size_t numberCorrespondenceGroups);
387
388 /**
389 * Tracks a set of given points between two frames, with sub-pixel accuracy.
390 * Actually, this function simply creates two frame pyramids and invokes the corresponding function needing frame pyramid as parameters.<br>
391 * The motion is determined by application of an image patch centered around the point to be tracked.<br>
392 * The points are tracked unidirectional (from the previous frame to the current frame).<br>
393 * If a point is near the frame border, a mirrored image patch is applied.
394 * @param previousFrame The previous frame in which the previous points are located, must be valid
395 * @param currentFrame The current frame, with same pixel format and pixel orientation as the previous frame, must be valid
396 * @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())
397 * @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())
398 * @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())
399 * @param maximalOffset Maximal expected offset between two corresponding points in pixel, defined in the domain of previous/current frame, with range [1, infinity)
400 * @param coarsestLayerRadius The search radius on the coarsest layer in pixel, with range [2, infinity)
401 * @param downsamplingMode The downsampling mode that is applied to create the pyramid layers
402 * @param subPixelIterations The number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
403 * @param worker Optional worker object to distribute the computation
404 * @param metricResults Optional resulting matching quality of the applied metric, nullptr if the results do not matter
405 * @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
406 * @return True, if succeeded
407 * @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
408 */
409 template <unsigned int tSize>
410 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);
411
412 /**
413 * Tracks a set of given points between two frame pyramids, with sub-pixel accuracy.
414 * The points are tracked unidirectional (from the previous frame to the current frame).<br>
415 * The motion is determined by application of an image patch centered around the point to be tracked.<br>
416 * If a point is near the frame border, a mirrored image patch is applied.<br>
417 * This function can apply a larger search radius on the coarsest pyramid layer than on all other layers.<br>
418 * The search radius on the intermediate layers and on the finest layer is always 2.
419 * @param previousPyramid The frame pyramid of the previous frame in which the previous points are located, must be valid
420 * @param currentPyramid The frame pyramid of the current frame, with same pixel format and pixel origin as the previous frame pyramid, must be valid
421 * @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())
422 * @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())
423 * @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())
424 * @param coarsestLayerRadius The search radius on the coarsest layer in pixel, with range [2, infinity)
425 * @param subPixelIterations Number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
426 * @param worker Optional worker object to distribute the computation
427 * @param metricResults Optional resulting matching quality of the applied metric, nullptr if the results do not matter
428 * @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
429 * @return True, if succeeded
430 * @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
431 */
432 template <unsigned int tSize>
433 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);
434
435 /**
436 * Tracks a set of given points between two frame pyramids, with sub-pixel accuracy.
437 * The points are tracked unidirectional (from the previous frame to the current frame).<br>
438 * The motion is determined by application of an image patch centered around the point to be tracked.<br>
439 * If a point is near the frame border, a mirrored image patch is applied.<br>
440 * This function can apply a larger search radius on the coarsest pyramid layer than on all other layers.<br>
441 * The search radius on the intermediate layers and on the finest layer is always 2.<br>
442 * This function has two template parameters: a) the number of frame channels and b) the patch size, both known at compile time.<br>
443 * @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
444 * @param currentPyramid The frame pyramid of the current frame, with same pixel format and pixel origin as the previous frame pyramid, must be valid
445 * @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())
446 * @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())
447 * @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())
448 * @param coarsestLayerRadius The search radius on the coarsest layer in pixel, with range [2, infinity)
449 * @param subPixelIterations Number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
450 * @param worker Optional worker object to distribute the computation
451 * @param metricResults Optional resulting matching quality of the applied metric, nullptr if the results do not matter
452 * @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
453 * @return True, if succeeded
454 * @tparam tChannels The number of channels the frame pyramids have, with range [1, 4]
455 * @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
456 */
457 template <unsigned int tChannels, unsigned int tSize>
458 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);
459
460 /**
461 * Tracks a set of arbitrary (unknown) points between two frame pyramids with sub-pixel accuracy.
462 * An optional sub-region can be specified shrinking the tracking area.<br>
463 * Further, the arbitrary points can be separated into individual bin arrays allowing to distribute the points over the image area.<br>
464 * 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>
465 * Point correspondences with an inaccurate bidirectional tracking are discarded.<br>
466 * The number of pyramid layers on which points are tracked can be specified. Both pyramids need to contain at least this number of layers.
467 * If a point is near the frame border, a mirrored image patch is applied.
468 * @param previousPyramid Previous frame pyramid
469 * @param nextPyramid Next frame pyramid, with same frame type as the previous frame
470 * @param coarsestLayerRadius The search radius on the coarsest layer, with range [2, infinity)
471 * @param previousImagePoints Resulting set of points that are located in the previous frame
472 * @param nextImagePoints Resulting points in the next frame, each point corresponds to one previous point (by index)
473 * @param maximalSqrError Maximal square distance between forward and backward tracking for a valid point
474 * @param previousSubRegion An optional sub-region in that the points are tracked only
475 * @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)
476 * @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)
477 * @param strength Minimal strength parameter of the tracked feature points
478 * @param worker Optional worker object to distribute the computation
479 * @param trackingLayers Number of pyramid layers on which points are tracked
480 * @return True, if succeeded
481 * @tparam tSize Size of the image patch that is used to determine the motion, must be odd
482 */
483 template <unsigned int tSize>
484 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);
485
486 /**
487 * Tracks a set of arbitrary (unknown) points between two frame pyramids with sub-pixel accuracy.
488 * An optional sub-region can be specified shrinking the tracking area.<br>
489 * Further, the arbitrary points can be separated into individual bin arrays allowing to distribute the points over the image area.<br>
490 * 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>
491 * Point correspondences with an inaccurate bidirectional tracking are discarded.<br>
492 * If a point is near the frame border, a mirrored image patch is applied.<br>
493 * The number of pyramid layers on which points are tracked can be specified.
494 * @param previousFrame Previous frame
495 * @param nextFrame Next frame, with same frame type as the previous frame
496 * @param maximalOffset Maximal offset between two corresponding points, in pixel
497 * @param coarsestLayerRadius The search radius on the coarsest layer, with range [2, infinity)
498 * @param previousImagePoints Resulting set of points that are located in the previous frame
499 * @param nextImagePoints Resulting points in the next frame, each point corresponds to one previous point (by index)
500 * @param maximalSqrError Maximal square distance between forward and backward tracking for a valid point
501 * @param previousSubRegion An optional sub-region in that the points are tracked only
502 * @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)
503 * @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)
504 * @param strength Minimal strength parameter of the tracked feature points
505 * @param downsamplingMode The downsampling mode that is applied to create the pyramid layers
506 * @param worker Optional worker object to distribute the computation
507 * @param trackingLayers Number of pyramid layers on which points are tracked
508 * @return True, if succeeded
509 * @tparam tSize Size of the image patch that is used to determine the motion, must be odd
510 */
511 template <unsigned int tSize>
512 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);
513
514 /**
515 * Tracks a set of given points between two frame pyramids with sub-pixel accuracy.
516 * 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>
517 * Point correspondences with an inaccurate bidirectional tracking are discarded.<br>
518 * If a point is near the frame border, a mirrored image patch is applied.
519 * @param previousPyramid The previous frame pyramid, must be valid
520 * @param nextPyramid The next frame pyramid, with same pixel format and pixel origin as the previous pyramid, must be valid
521 * @param coarsestLayerRadius The search radius on the coarsest layer, with range [2, infinity)
522 * @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)
523 * @param nextImagePoints Resulting points in the next frame, each point corresponds to one previous point
524 * @param maximalSqrError Maximal square distance between forward and backward tracking for a valid point
525 * @param worker Optional worker object to distribute the computation
526 * @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
527 * @param subPixelIterations Number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
528 * @return True, if succeeded
529 * @tparam tSize Size of the image patch that is used to determine the motion, must be odd
530 * @see trackPointsBidirectionalSubPixelMirroredBorderWithRoughLocations().
531 */
532 template <unsigned int tSize>
533 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);
534
535 /**
536 * Tracks a set of given points between two frame pyramids with sub-pixel accuracy.
537 * 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>
538 * Point correspondences with an inaccurate bidirectional tracking are discarded.<br>
539 * If a point is near the frame border, a mirrored image patch is applied.<br>
540 * This function has two template parameters: a) the number of frame channels and b) the patch size, both known at compile time.
541 * @param previousPyramid Previous frame pyramid with pixel format matching with the number of frame channels 'tChannels', must be valid
542 * @param nextPyramid Next frame pyramid, with same frame type as the previous pyramid, must be valid
543 * @param coarsestLayerRadius The search radius on the coarsest layer, with range [2, infinity)
544 * @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)
545 * @param nextImagePoints Resulting points in the next frame, each point corresponds to one previous point
546 * @param maximalSqrError Maximal square distance between forward and backward tracking for a valid point
547 * @param worker Optional worker object to distribute the computation
548 * @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
549 * @param subPixelIterations Number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
550 * @return True, if succeeded
551 * @tparam tChannels The number of channels both frame pyramids have, with range [1, 4]
552 * @tparam tSize Size of the image patch that is used to determine the motion, must be odd
553 * @see trackPointsBidirectionalSubPixelMirroredBorderWithRoughLocations().
554 */
555 template <unsigned int tChannels, unsigned int tSize>
556 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);
557
558 /**
559 * Tracks a set of given points between two frame pyramids with sub-pixel accuracy.
560 * 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>
561 * If a point is near the frame border, a mirrored image patch is applied.
562 * @param previousPyramid The previous frame pyramid, must be valid
563 * @param nextPyramid The next frame pyramid, with same pixel format and pixel origin as the previous pyramid, must be valid
564 * @param coarsestLayerRadius The search radius on the coarsest layer, with range [2, infinity)
565 * @param previousImagePoints The image points located in the previous frame
566 * @param nextImagePoints The resulting points in the next frame, each point corresponds to one previous point
567 * @param validCorrespondences Resulting binary mask for all provided correspondences (0x00 = invalid, 0x01 = valid)
568 * @param maximalSqrError Maximal square distance between forward and backward tracking for a valid point
569 * @param worker Optional worker object to distribute the computation
570 * @param subPixelIterations Number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
571 * @return True, if succeeded
572 * @tparam tSize Size of the image patch that is used to determine the motion, must be odd
573 * @see trackPointsBidirectionalSubPixelMirroredBorderWithRoughLocations().
574 */
575 template <unsigned int tSize>
576 static bool trackPointsBidirectionalSubPixelMirroredBorder(const CV::FramePyramid& previousPyramid, const CV::FramePyramid& nextPyramid, const unsigned int coarsestLayerRadius, const Vectors2& previousImagePoints, Vectors2& nextImagePoints, std::vector<uint8_t>& validCorrespondences, const Scalar maximalSqrError = Scalar(0.9 * 0.9), Worker* worker = nullptr, const unsigned int subPixelIterations = 4u);
577
578 /**
579 * Tracks a set of given points between two frame pyramids with sub-pixel accuracy.
580 * 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>
581 * If a point is near the frame border, a mirrored image patch is applied.<br>
582 * This function has two template parameters: a) the number of frame channels and b) the patch size, both known at compile time.
583 * @param previousPyramid Previous frame pyramid with pixel format matching with the number of frame channels 'tChannels', must be valid
584 * @param nextPyramid Next frame pyramid, with same frame type as the previous pyramid, must be valid
585 * @param coarsestLayerRadius The search radius on the coarsest layer, with range [2, infinity)
586 * @param previousImagePoints The image points located in the previous frame
587 * @param nextImagePoints The resulting points in the next frame, each point corresponds to one previous point
588 * @param validCorrespondences Resulting binary mask for all provided correspondences (0x00 = invalid, 0x01 = valid)
589 * @param maximalSqrError Maximal square distance between forward and backward tracking for a valid point
590 * @param worker Optional worker object to distribute the computation
591 * @param subPixelIterations Number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
592 * @return True, if succeeded
593 * @tparam tChannels The number of channels both frame pyramids have, with range [1, 4]
594 * @tparam tSize Size of the image patch that is used to determine the motion, must be odd
595 * @see trackPointsBidirectionalSubPixelMirroredBorderWithRoughLocations().
596 */
597 template <unsigned int tChannels, unsigned int tSize>
598 static bool trackPointsBidirectionalSubPixelMirroredBorder(const CV::FramePyramid& previousPyramid, const CV::FramePyramid& nextPyramid, const unsigned int coarsestLayerRadius, const Vectors2& previousImagePoints, Vectors2& nextImagePoints, std::vector<uint8_t>& validCorrespondences, const Scalar maximalSqrError = Scalar(0.9 * 0.9), Worker* worker = nullptr, const unsigned int subPixelIterations = 4u);
599
600 /**
601 * Tracks a set of given points between two frame pyramids with sub-pixel accuracy.
602 * 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>
603 * 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>
604 * Point correspondences with an inaccurate bidirectional tracking are discarded.<br>
605 * If a point is near the frame border, a mirrored image patch is applied.
606 * @param previousPyramid Previous frame pyramid, must be valid
607 * @param nextPyramid Next frame pyramid, with same pixel format and pixel orientation as the previous frame, must be valid
608 * @param coarsestLayerRadius The search radius on the coarsest layer, with range [2, infinity)
609 * @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)
610 * @param roughNextImagePoints The rough locations of the previous image points in the current next frame, one for each previous image point
611 * @param nextImagePoints Resulting points in the next frame, each point corresponds to one previous point
612 * @param maximalSqrError Maximal square distance between forward and backward tracking for a valid point
613 * @param worker Optional worker object to distribute the computation
614 * @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
615 * @param subPixelIterations Number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
616 * @return True, if succeeded
617 * @tparam tSize Size of the image patch that is used to determine the motion, must be odd
618 * @see trackPointsBidirectionalSubPixelMirroredBorder().
619 */
620 template <unsigned int tSize>
621 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);
622
623 /**
624 * Tracks a set of given points between two frame pyramids with sub-pixel accuracy.
625 * 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>
626 * 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>
627 * Point correspondences with an inaccurate bidirectional tracking are discarded.<br>
628 * If a point is near the frame border, a mirrored image patch is applied.<br>
629 * This function has two template parameters: a) the number of frame channels and b) the patch size, both known at compile time.
630 * @param previousPyramid Previous frame pyramid with pixel format matching with the number of frame channels 'tChannels', must be valid
631 * @param nextPyramid Next frame pyramid, with same pixel format and pixel orientation as the previous frame, must be valid
632 * @param coarsestLayerRadius The search radius on the coarsest layer, with range [2, infinity)
633 * @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)
634 * @param roughNextImagePoints The rough locations of the previous image points in the current next frame, one for each previous image point
635 * @param nextImagePoints Resulting points in the next frame, each point corresponds to one previous point
636 * @param maximalSqrError Maximal square distance between forward and backward tracking for a valid point
637 * @param worker Optional worker object to distribute the computation
638 * @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
639 * @param subPixelIterations Number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
640 * @return True, if succeeded
641 * @tparam tChannels The number of channels both frame pyramids have, with range [1, 4]
642 * @tparam tSize Size of the image patch that is used to determine the motion, must be odd
643 * @see trackPointsBidirectionalSubPixelMirroredBorder().
644 */
645 template <unsigned int tChannels, unsigned int tSize>
646 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);
647
648 /**
649 * Tracks a set of given points between two frames with sub-pixel accuracy.
650 * 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>
651 * Point correspondences with an inaccurate bidirectional tracking are discarded.<br>
652 * If a point is near the frame border, a mirrored image patch is applied.
653 * @param previousFrame The previous frame, must be valid
654 * @param nextFrame The next frame, with same pixel format and pixel origin as the previous frame, must be valid
655 * @param maximalOffset Maximal offset between two corresponding points, in pixel
656 * @param coarsestLayerRadius The search radius on the coarsest layer, with range [2, infinity)
657 * @param previousImagePoints A set of points that are located in the previous frame, this set may be reduced as some points may be discarded
658 * @param nextImagePoints Resulting points in the next frame, each point corresponds to one previous point
659 * @param maximalSqrError Maximal square distance between forward and backward tracking for a valid point
660 * @param worker Optional worker object to distribute the computation
661 * @param downsamplingMode The downsampling mode that is applied to create the pyramid layers
662 * @param validIndices Optional vector receiving the indices of the valid point correspondences
663 * @param subPixelIterations Number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
664 * @return True, if succeeded
665 * @tparam tSize Size of the image patch that is used to determine the motion, must be odd
666 */
667 template <unsigned int tSize>
668 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);
669
670 /**
671 * Detects and tracks reliable arbitrary reference points between two frames.
672 * The reference points are distributed into an array to receive wide spread points.
673 * @param previousPyramid Frame pyramid of the previous image
674 * @param currentPyramid Frame pyramid of the current image
675 * @param previousReferencePoints Resulting reliable reference points inside the previous frame
676 * @param currentReferencePoints Resulting and tracked reliable reference points inside the current frame
677 * @param horizontalBins Number of horizontal bins to subdivide the specified bounding box
678 * @param verticalBins Number of vertical bins to subdivide the specified bounding box
679 * @param boundingBox Optional bounding box to shrink the search area of reliable tracking points
680 * @param maskFrame Optional mask frame allowing to use only reference points outside a given mask
681 * @param worker Optional worker object to distribute the computation
682 * @return True, if succeeded
683 * @tparam tSize Size of the tracking patch in pixels
684 */
685 template <unsigned int tSize>
686 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);
687
688 /**
689 * 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.
690 * Patch pixels outside the frame are mirrored into the frame before compared.
691 * @param frame0 The first frame, must be valid
692 * @param frame1 The second frame, must be valid
693 * @param width0 Width of the first frame in pixel, with range [tPatchSize / 2u, infinity)
694 * @param height0 Height of the first frame in pixel, with range [tPatchSize / 2u, infinity)
695 * @param width1 Width of the second frame in pixel, with range [tPatchSize / 2u, infinity)
696 * @param height1 Height of the second frame in pixel, with range [tPatchSize / 2u, infinity)
697 * @param frame0PaddingElements The number of padding elements at the end of each first frame row, in elements, with range [0, infinity)
698 * @param frame1PaddingElements The number of padding elements at the end of each second frame row, in elements, with range [0, infinity)
699 * @param position0 Position in the first frame, with range [0, width)x[0, height)
700 * @param radiusX The search radius in horizontal direction, in pixel, with range [0, width - 1]
701 * @param radiusY The search radius in vertical direction, in pixel, with range [0, height - 1]
702 * @param rough1 The optional rough guess of the point in the second frame, (Numeric::maxValue(), Numeric::maxValue()) if unknown
703 * @param subPixelIterations Number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
704 * @param metricResult Optional resulting matching quality of the applied metric, nullptr if the result does not matter
705 * @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
706 * @return Best matching position in the second frame
707 * @tparam tChannels The number of frame channels, with range [1, infinity)
708 * @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
709 */
710 template <unsigned int tChannels, unsigned int tPatchSize>
711 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);
712
713 /**
714 * 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.
715 * Patch pixels outside the frame are mirrored into the frame before compared.
716 * @param frame0 The first frame, must be valid
717 * @param frame1 The second frame, must be valid
718 * @param channels The number of frame channels, with range [1, 4]
719 * @param width0 Width of the first frame in pixel, with range [tPatchSize / 2u, infinity)
720 * @param height0 Height of the first frame in pixel, with range [tPatchSize / 2u, infinity)
721 * @param width1 Width of the second frame in pixel, with range [tPatchSize / 2u, infinity)
722 * @param height1 Height of the second frame in pixel, with range [tPatchSize / 2u, infinity)
723 * @param frame0PaddingElements The number of padding elements at the end of each first frame row, in elements, with range [0, infinity)
724 * @param frame1PaddingElements The number of padding elements at the end of each second frame row, in elements, with range [0, infinity)
725 * @param position0 Position in the first frame, with range [0, width)x[0, height)
726 * @param radiusX The search radius in horizontal direction, in pixel, with range [0, width - 1]
727 * @param radiusY The search radius in vertical direction, in pixel, with range [0, height - 1]
728 * @param rough1 The optional rough guess of the point in the second frame, (Numeric::maxValue(), Numeric::maxValue()) if unknown
729 * @param subPixelIterations Number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
730 * @param metricResult Optional resulting matching quality of the applied metric, nullptr if the result does not matter
731 * @param metricIdentityResult Optional resulting matching quality of the applied metric in both frames at the same previous position, nullptr if the results do not matter
732 * @return Best matching position in the second frame
733 * @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
734 */
735 template <unsigned int tPatchSize>
736 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);
737
738 private:
739
740 /**
741 * 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.
742 * Patch pixels outside the frame are mirrored into the frame before compared.
743 * @param buffer0 The buffer containing the (interpolated) first image patch as one continuous memory block, must be valid
744 * @param frame1 The second image, with same pixel format as the buffer, must be valid
745 * @param width1 Width of the second frame in pixel, with range [tSize, infinity)
746 * @param height1 Height of the second frame in pixel, with range [tSize, infinity)
747 * @param frame1PaddingElements The number of padding elements at the end of each second image row, in elements, with range [0, infinity)
748 * @param roughPosition1 The rough position in the second frame, with range [0, width1)x[0, height1)
749 * @param subPixelIterations Number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
750 * @param metricResult Optional resulting result of the applied metric, nullptr if the result does not matter
751 * @return Best matching position in the second frame
752 * @tparam tChannels The number of data channel each frame has, with range [1, infinity)
753 * @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
754 */
755 template <unsigned int tChannels, unsigned int tPatchSize>
756 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);
757
758 /**
759 * Tracks a subset of given points between two frame pyramids with sub-pixel accuracy.
760 * The points are tracked unidirectional (from the previous frame to the current frame).<br>
761 * If a point is near the frame border, a mirrored image patch is applied.
762 * @param previousPyramid The previous frame pyramid, must be valid
763 * @param nextPyramid The next frame pyramid, with same pixel format and pixel orientation as the previous frame pyramid, must be valid
764 * @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')]
765 * @param previousPoints A set of points that are located in the previous frame
766 * @param roughNextPoints The rough points located in the finest pyramid layer of the next pyramid (if known), nullptr if unknown
767 * @param nextPoints Resulting tracked points located in the finest layer of the next pyramid
768 * @param coarsestLayerRadius The search radius on the coarsest pyramid layer, with range [2, infinity)
769 * @param subPixelIterations Number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
770 * @param metricResults Optional resulting results of the applied metric, nullptr if the results do not matter
771 * @param metricIdentityResults Optional resulting results of the applied metric in both frames at the same previous position, nullptr if the results do not matter
772 * @param firstPoint First point to be applied
773 * @param numberPoints Number of points to be applied
774 * @tparam tSize The size of the applied image patches in pixel, with range [1, infinity), must be odd
775 */
776 template <unsigned int tSize>
777 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);
778
779 /**
780 * Tracks a subset of given points between two frame pyramids with sub-pixel accuracy.
781 * The points are tracked unidirectional (from the previous frame to the current frame).<br>
782 * If a point is near the frame border, a mirrored image patch is applied.<br>
783 * This function has two template parameters: a) the number of frame channels and b) the patch size, both known at compile time.<br>
784 * @param previousPyramid Previous frame pyramid, must be valid
785 * @param currentPyramid Current frame pyramid, with same pixel format and pixel orientation as the previous frame pyramid, must be valid
786 * @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')]
787 * @param previousPoints A set of points that are located in the previous frame
788 * @param roughPoints The rough points in the current frame (if known), otherwise the previousPoints may be provided
789 * @param currentPoints Resulting current points, that have been tracking between the two points
790 * @param coarsestLayerRadius The search radius on the coarsest layer, with range [2, infinity)
791 * @param subPixelIterations Number of sub-pixel iterations that will be applied, each iteration doubles the sub-pixel accuracy, with range [1, infinity)
792 * @param metricResults Optional resulting results of the applied metric, nullptr if the results do not matter
793 * @param metricIdentityResults Optional resulting results of the applied metric in both frames at the same previous position, nullptr if the results do not matter
794 * @param firstPoint First point to be applied
795 * @param numberPoints Number of points to be applied
796 * @tparam tChannels The number of channels both frame pyramids have, with range [1, 4]
797 * @tparam tSize Defines the size of the applied image patches in pixel, must be odd
798 */
799 template <unsigned int tChannels, unsigned int tSize>
800 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);
801};
802
803inline AdvancedMotion::PointCorrespondences::PointCorrespondences(const Vector2* previousPoints, Vector2* predictedNextPoints, uint8_t* validCorrespondences, const size_t correspondences, const unsigned int pyramidLayers, const unsigned int coarsestLayerRadius, const Scalar maximalError, const unsigned int subPixelIterations) :
804 previousPoints_(previousPoints),
805 nextPoints_(predictedNextPoints),
806 validCorrespondences_(validCorrespondences),
807 correspondences_(correspondences),
808 pyramidLayers_(pyramidLayers),
809 coarsestLayerRadius_(coarsestLayerRadius),
810 subPixelIterations_(subPixelIterations)
811{
812 ocean_assert(previousPoints_ != nullptr);
813 ocean_assert(nextPoints_ != nullptr);
814 ocean_assert(validCorrespondences_ != nullptr);
815
816 ocean_assert(previousPoints_ != nextPoints_); // must not point to the same memory
817
818 ocean_assert(pyramidLayers_ != 0u);
819
820 ocean_assert(maximalError >= 0);
821 maximalSqrError_ = Numeric::sqr(maximalError);
822
823 maximalSqrErrorLayer_ = Numeric::sqr(Scalar(2) + maximalError); // search radius 2 plus maximal error
824}
825
826inline const Vector2& AdvancedMotion::PointCorrespondences::previousPosition(const size_t pointIndex) const
827{
828 ocean_assert(layerIndex_ == 0u);
829
830 ocean_assert(pointIndex < correspondences_);
831
832 const Vector2& previousPoint = forwardTracking_ ? previousPoints_[pointIndex] : nextPoints_[pointIndex];
833
834 ocean_assert(previousPoint.x() >= Scalar(0) && previousPoint.x() < Scalar(previousLayerWidth_));
835 ocean_assert(previousPoint.y() >= Scalar(0) && previousPoint.y() < Scalar(previousLayerHeight_));
836
837 return previousPoint;
838}
839
841{
842 ocean_assert(layerIndex_ != (unsigned int)(-1));
843 ocean_assert(layerIndex_ != 0u);
844
845 ocean_assert(pointIndex < correspondences_);
846 ocean_assert(invLayerFactor_ > Scalar(0));
847
848 const Vector2& previousPoint = forwardTracking_ ? previousPoints_[pointIndex] : nextPoints_[pointIndex];
849
850 const int32_t downsampledX = Numeric::round32(previousPoint.x() * invLayerFactor_);
851 const int32_t downsampledY = Numeric::round32(previousPoint.y() * invLayerFactor_);
852
853 const int32_t clampedX = std::min(downsampledX, int32_t(previousLayerWidth_) - 1);
854 const int32_t clampedY = std::min(downsampledY, int32_t(previousLayerHeight_) - 1);
855
856 ocean_assert(clampedX >= 0 && clampedY >= 0);
857
858 return CV::PixelPosition((unsigned int)(clampedX), (unsigned int)(clampedY));
859}
860
862{
863 ocean_assert(layerIndex_ != (unsigned int)(-1));
864 ocean_assert(layerIndex_ != 0u);
865
866 ocean_assert(pointIndex < correspondences_);
867
868 const Vector2& predictedNextPoint = forwardTracking_ ? nextPoints_[pointIndex] : internalBackwardNextPoints_[pointIndex];
869
870 ocean_assert(predictedNextPoint.x() == Scalar(Numeric::round32(predictedNextPoint.x())));
871 ocean_assert(predictedNextPoint.y() == Scalar(Numeric::round32(predictedNextPoint.y())));
872
873 ocean_assert(predictedNextPoint.x() >= Scalar(0) && predictedNextPoint.x() < Scalar(nextLayerWidth_));
874 ocean_assert(predictedNextPoint.y() >= Scalar(0) && predictedNextPoint.y() < Scalar(nextLayerHeight_));
875
876 return CV::PixelPosition((unsigned int)(predictedNextPoint.x()), (unsigned int)(predictedNextPoint.y()));
877}
878
880{
881 ocean_assert(layerIndex_ == 0u);
882
883 ocean_assert(pointIndex < correspondences_);
884
885 if (forwardTracking_)
886 {
887 return nextPoints_[pointIndex];
888 }
889 else
890 {
891 return internalBackwardNextPoints_[pointIndex];
892 }
893}
894
895inline bool AdvancedMotion::PointCorrespondences::isPointValid(const size_t pointIndex) const
896{
897 ocean_assert(layerIndex_ != (unsigned int)(-1));
898
899 ocean_assert(pointIndex < correspondences_);
900
901 ocean_assert(validCorrespondences_ != nullptr);
902 return validCorrespondences_[pointIndex] != 0u;
903}
904
906{
907 ocean_assert(pyramidLayers_ != 0u);
908
909 return pyramidLayers_;
910}
911
913{
914 return layerRadius_;
915}
916
918{
919 ocean_assert(layerIndex_ == 0u);
920
921 return subPixelIterations_;
922}
923
925{
926 return correspondences_;
927}
928
930{
931 return previousPoints_ != nullptr;
932}
933
935{
936 return measurements_;
937}
938
940{
941 return width_ >= 1u && height_ >= 1u;
942}
943
944template <typename TMetricInteger, typename TMetricFloat>
945template <unsigned int tChannels, unsigned int tSize>
946bool AdvancedMotionT<TMetricInteger, TMetricFloat>::trackPointsBidirectionalSubPixelMirroredBorder(const CV::FramePyramid& previousPyramid, const CV::FramePyramid& nextPyramid, PointCorrespondences* pointCorrespondenceGroups, const size_t numberCorrespondenceGroups)
947{
948 static_assert(tChannels >= 1u, "Invalid channel number!");
949 static_assert(tSize >= 1u, "Invalid patch size!");
950
951 ocean_assert(previousPyramid && nextPyramid);
952 ocean_assert(previousPyramid.frameType().channels() == tChannels);
953 ocean_assert(previousPyramid.frameType().numberPlanes() == 1u);
954 ocean_assert(previousPyramid.frameType().isPixelFormatCompatible(nextPyramid.frameType().pixelFormat()));
955 ocean_assert(previousPyramid.frameType().pixelOrigin() == nextPyramid.frameType().pixelOrigin());
956
957 if (!previousPyramid.isValid() || !nextPyramid.isValid())
958 {
959 return false;
960 }
961
962 if (previousPyramid.frameType().channels() != tChannels || previousPyramid.frameType().numberPlanes() != 1u)
963 {
964 return false;
965 }
966
967 if (!previousPyramid.frameType().isPixelFormatCompatible(nextPyramid.frameType().pixelFormat()) || previousPyramid.frameType().pixelOrigin() != nextPyramid.frameType().pixelOrigin())
968 {
969 return false;
970 }
971
972 ocean_assert(pointCorrespondenceGroups != nullptr && numberCorrespondenceGroups >= 1);
973
974 if (pointCorrespondenceGroups == nullptr || numberCorrespondenceGroups == 0)
975 {
976 return false;
977 }
978
979 const unsigned int coarsestPyramidLayerIndex = PointCorrespondences::coarsestPyramidLayer(previousPyramid, nextPyramid, pointCorrespondenceGroups, numberCorrespondenceGroups);
980
981 ocean_assert(coarsestPyramidLayerIndex < previousPyramid.layers());
982 ocean_assert(coarsestPyramidLayerIndex < nextPyramid.layers());
983
984 // forward point tracking
985
986 for (size_t nCorrespondenceGroup = 0; nCorrespondenceGroup < numberCorrespondenceGroups; ++nCorrespondenceGroup)
987 {
988 PointCorrespondences& correspondences = pointCorrespondenceGroups[nCorrespondenceGroup];
989
990 correspondences.startForwardTracking(previousPyramid, nextPyramid);
991 }
992
993 for (unsigned int layerIndex = coarsestPyramidLayerIndex; layerIndex < previousPyramid.layers(); --layerIndex)
994 {
995 for (size_t nCorrespondenceGroup = 0; nCorrespondenceGroup < numberCorrespondenceGroups; ++nCorrespondenceGroup)
996 {
997 PointCorrespondences& correspondences = pointCorrespondenceGroups[nCorrespondenceGroup];
998
999 if (!correspondences.startLayer(layerIndex, previousPyramid, nextPyramid))
1000 {
1001 continue;
1002 }
1003
1004 const Frame& previousFrame = previousPyramid[layerIndex];
1005 const Frame& nextFrame = nextPyramid[layerIndex];
1006
1007 const uint8_t* const previousFrameData = previousFrame.constdata<uint8_t>();
1008 const uint8_t* const nextFrameData = nextFrame.constdata<uint8_t>();
1009
1010 const unsigned int previousFramePaddingElements = previousFrame.paddingElements();
1011 const unsigned int nextFramePaddingElements = nextFrame.paddingElements();
1012
1013 const unsigned int previousWidth = previousFrame.width();
1014 const unsigned int previousHeight = previousFrame.height();
1015
1016 const unsigned int nextWidth = nextFrame.width();
1017 const unsigned int nextHeight = nextFrame.height();
1018
1019 const unsigned int layerRadius = correspondences.layerRadius();
1020
1021 if (layerIndex == 0u)
1022 {
1023 // sub-pixel-precise tracking
1024
1025 const unsigned int subPixelIterations = correspondences.subPixelIterations();
1026
1027 for (size_t nPoint = 0; nPoint < correspondences.size(); ++nPoint)
1028 {
1029 const Vector2& previousPosition = correspondences.previousPosition(nPoint);
1030 Vector2 nextPosition = correspondences.predictedNextPosition(nPoint);
1031
1032 const Vector2 position = trackPointSubPixelMirroredBorder<tChannels, tSize>(previousFrameData, nextFrameData, previousWidth, previousHeight, nextWidth, nextHeight, previousFramePaddingElements, nextFramePaddingElements, previousPosition, layerRadius, layerRadius, nextPosition, subPixelIterations);
1033
1034 correspondences.propagateNextPosition(nPoint, position);
1035 }
1036 }
1037 else
1038 {
1039 // pixel-precise tracking
1040
1041 for (size_t nPoint = 0; nPoint < correspondences.size(); ++nPoint)
1042 {
1043 const CV::PixelPosition previousPosition = correspondences.previousPositionDownsampled(nPoint);
1044 const CV::PixelPosition predictedNextPosition = correspondences.predictedNextPositionDownsampled(nPoint);
1045
1046 const PixelPosition actualNextPoint = MotionT<TMetricInteger>::template pointMotionInFrameMirroredBorder<tChannels, tSize>(previousFrameData, nextFrameData, previousWidth, previousHeight, nextWidth, nextHeight, previousPosition, layerRadius, layerRadius, previousFramePaddingElements, nextFramePaddingElements, predictedNextPosition);
1047
1048 correspondences.propagateNextPositionDownsampled(nPoint, actualNextPoint);
1049 }
1050 }
1051 }
1052 }
1053
1054 // backward point tracking
1055
1056 const CV::FramePyramid& backwardPreviousPyramid = nextPyramid; // an alias for the backward motion
1057 const CV::FramePyramid& backwardNextPyramid = previousPyramid;
1058
1059 for (size_t nCorrespondenceGroup = 0; nCorrespondenceGroup < numberCorrespondenceGroups; ++nCorrespondenceGroup)
1060 {
1061 PointCorrespondences& correspondences = pointCorrespondenceGroups[nCorrespondenceGroup];
1062
1063 correspondences.startBackwardTracking(backwardPreviousPyramid, backwardNextPyramid);
1064 }
1065
1066 for (unsigned int layerIndex = coarsestPyramidLayerIndex; layerIndex < previousPyramid.layers(); --layerIndex)
1067 {
1068 for (size_t nCorrespondenceGroup = 0; nCorrespondenceGroup < numberCorrespondenceGroups; ++nCorrespondenceGroup)
1069 {
1070 PointCorrespondences& correspondences = pointCorrespondenceGroups[nCorrespondenceGroup];
1071
1072 if (!correspondences.startLayer(layerIndex, backwardPreviousPyramid, backwardNextPyramid))
1073 {
1074 continue;
1075 }
1076
1077 const Frame& previousFrame = backwardPreviousPyramid[layerIndex];
1078 const Frame& nextFrame = backwardNextPyramid[layerIndex];
1079
1080 const uint8_t* const previousFrameData = previousFrame.constdata<uint8_t>();
1081 const uint8_t* const nextFrameData = nextFrame.constdata<uint8_t>();
1082
1083 const unsigned int previousFramePaddingElements = previousFrame.paddingElements();
1084 const unsigned int nextFramePaddingElements = nextFrame.paddingElements();
1085
1086 const unsigned int previousWidth = previousFrame.width();
1087 const unsigned int previousHeight = previousFrame.height();
1088
1089 const unsigned int nextWidth = nextFrame.width();
1090 const unsigned int nextHeight = nextFrame.height();
1091
1092 const unsigned int layerRadius = correspondences.layerRadius();
1093
1094 if (layerIndex == 0u)
1095 {
1096 // sub-pixel-precise tracking
1097
1098 const unsigned int subPixelIterations = correspondences.subPixelIterations();
1099
1100 for (size_t nPoint = 0; nPoint < correspondences.size(); ++nPoint)
1101 {
1102 if (correspondences.isPointValid(nPoint))
1103 {
1104 const Vector2& previousPosition = correspondences.previousPosition(nPoint);
1105 Vector2 nextPosition = correspondences.predictedNextPosition(nPoint);
1106
1107 const Vector2 position = trackPointSubPixelMirroredBorder<tChannels, tSize>(previousFrameData, nextFrameData, previousWidth, previousHeight, nextWidth, nextHeight, previousFramePaddingElements, nextFramePaddingElements, previousPosition, layerRadius, layerRadius, nextPosition, subPixelIterations);
1108
1109 correspondences.propagateNextPosition(nPoint, position);
1110 }
1111 }
1112 }
1113 else
1114 {
1115 // pixel-precise tracking
1116
1117 for (size_t nPoint = 0; nPoint < correspondences.size(); ++nPoint)
1118 {
1119 if (correspondences.isPointValid(nPoint))
1120 {
1121 const CV::PixelPosition previousPosition = correspondences.previousPositionDownsampled(nPoint);
1122 const CV::PixelPosition predictedNextPosition = correspondences.predictedNextPositionDownsampled(nPoint);
1123
1124 const PixelPosition actualNextPoint = MotionT<TMetricInteger>::template pointMotionInFrameMirroredBorder<tChannels, tSize>(previousFrameData, nextFrameData, previousWidth, previousHeight, nextWidth, nextHeight, previousPosition, layerRadius, layerRadius, previousFramePaddingElements, nextFramePaddingElements, predictedNextPosition);
1125
1126 correspondences.propagateNextPositionDownsampled(nPoint, actualNextPoint);
1127 }
1128 }
1129 }
1130 }
1131 }
1132
1133 return true;
1134}
1135
1136template <typename TMetricInteger, typename TMetricFloat>
1137template <unsigned int tSize>
1138bool AdvancedMotionT<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)
1139{
1140 static_assert(tSize % 2u == 1u, "Invalid image patch size, must be odd!");
1141 static_assert(tSize >= 3u, "Invalid image patch size, must be larger than 2!");
1142
1143 ocean_assert(previousFrame && currentFrame);
1144
1145 ocean_assert(previousFrame.frameType().pixelFormat() == currentFrame.frameType().pixelFormat());
1146 ocean_assert(previousFrame.frameType().pixelOrigin() == currentFrame.frameType().pixelOrigin());
1147
1148 ocean_assert(previousPoints.size() == roughPoints.size());
1149
1150 ocean_assert(maximalOffset >= 1u);
1151 ocean_assert(subPixelIterations >= 1u);
1152
1153 const unsigned int idealLayers = FramePyramid::idealLayers(previousFrame.width(), previousFrame.height(), (tSize / 2u) * 4u, (tSize / 2u) * 4u, 2u, maximalOffset, coarsestLayerRadius);
1154 ocean_assert(idealLayers >= 1u);
1155
1156 if (idealLayers == 0u)
1157 {
1158 return false;
1159 }
1160
1161 const FramePyramid previousPyramid(previousFrame, downsamplingMode, idealLayers, false /*copyFirstLayer*/, worker);
1162 const FramePyramid currentPyramid(currentFrame, downsamplingMode, idealLayers, false /*copyFirstLayer*/, worker);
1163
1164 return trackPointsSubPixelMirroredBorder<tSize>(previousPyramid, currentPyramid, previousPoints, roughPoints, currentPoints, coarsestLayerRadius, subPixelIterations, worker, metricResults, metricIdentityResults);
1165}
1166
1167template <typename TMetricInteger, typename TMetricFloat>
1168template <unsigned int tSize>
1169inline bool AdvancedMotionT<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)
1170{
1171 static_assert(tSize % 2u == 1u, "Invalid image patch size, must be odd!");
1172 static_assert(tSize >= 3u, "Invalid image patch size, must be larger than 2!");
1173
1174 ocean_assert(previousPyramid.frameType().pixelFormat() == currentPyramid.frameType().pixelFormat());
1175 ocean_assert(previousPyramid.frameType().pixelOrigin() == currentPyramid.frameType().pixelOrigin());
1176
1177 ocean_assert(previousPoints.size() == roughPoints.size());
1178
1179 ocean_assert(subPixelIterations >= 1u);
1180
1181 const unsigned int idealLayers = CV::FramePyramid::idealLayers(previousPyramid.finestWidth(), previousPyramid.finestHeight(), (tSize / 2u) * 4u, (tSize / 2u) * 4u, 2u);
1182 const unsigned int numberLayers = min(min(previousPyramid.layers(), currentPyramid.layers()), idealLayers);
1183
1184 if (numberLayers == 0u)
1185 {
1186 return false;
1187 }
1188
1189 currentPoints.resize(previousPoints.size());
1190
1191 if (metricResults != nullptr)
1192 {
1193 metricResults->resize(previousPoints.size());
1194 }
1195
1196 if (metricIdentityResults != nullptr)
1197 {
1198 metricIdentityResults->resize(previousPoints.size());
1199 }
1200
1201 if (worker != nullptr)
1202 {
1203 worker->executeFunction(Worker::Function::createStatic(&AdvancedMotionT::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()));
1204 }
1205 else
1206 {
1207 trackPointsSubPixelMirroredBorderSubset<tSize>(&previousPyramid, &currentPyramid, numberLayers, &previousPoints, &roughPoints, &currentPoints, coarsestLayerRadius, subPixelIterations, metricResults ? metricResults->data() : nullptr, metricIdentityResults ? metricIdentityResults->data() : nullptr, 0u, (unsigned int)(previousPoints.size()));
1208 }
1209
1210 return true;
1211}
1212
1213template <typename TMetricInteger, typename TMetricFloat>
1214template <unsigned int tChannels, unsigned int tSize>
1215inline bool AdvancedMotionT<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)
1216{
1217 static_assert(tSize % 2u == 1u, "Invalid image patch size, must be odd!");
1218 static_assert(tSize >= 3u, "Invalid image patch size, must be larger than 2!");
1219
1220 ocean_assert(FrameType::arePixelFormatsCompatible(previousPyramid.frameType().pixelFormat(), currentPyramid.frameType().pixelFormat()));
1221 ocean_assert(previousPyramid.frameType().pixelOrigin() == currentPyramid.frameType().pixelOrigin());
1222
1223 ocean_assert(previousPyramid.frameType().channels() == tChannels);
1224 ocean_assert(currentPyramid.frameType().channels() == tChannels);
1225
1226 ocean_assert(previousPoints.size() == roughPoints.size());
1227
1228 ocean_assert(subPixelIterations >= 1u);
1229
1230 const unsigned int idealLayers = CV::FramePyramid::idealLayers(previousPyramid.finestWidth(), previousPyramid.finestHeight(), (tSize / 2u) * 4u, (tSize / 2u) * 4u, 2u);
1231 const unsigned int numberLayers = min(min(previousPyramid.layers(), currentPyramid.layers()), idealLayers);
1232
1233 if (numberLayers == 0u)
1234 {
1235 return false;
1236 }
1237
1238 currentPoints.resize(previousPoints.size());
1239
1240 if (metricResults != nullptr)
1241 {
1242 metricResults->resize(previousPoints.size());
1243 }
1244
1245 if (metricIdentityResults != nullptr)
1246 {
1247 metricIdentityResults->resize(previousPoints.size());
1248 }
1249
1250 if (worker != nullptr)
1251 {
1252 worker->executeFunction(Worker::Function::createStatic(&AdvancedMotionT::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()));
1253 }
1254 else
1255 {
1256 trackPointsSubPixelMirroredBorderSubset<tChannels, tSize>(&previousPyramid, &currentPyramid, numberLayers, &previousPoints, &roughPoints, &currentPoints, coarsestLayerRadius, subPixelIterations, metricResults ? metricResults->data() : nullptr, metricIdentityResults ? metricIdentityResults->data() : nullptr, 0u, (unsigned int)(previousPoints.size()));
1257 }
1258
1259 return true;
1260}
1261
1262template <typename TMetricInteger, typename TMetricFloat>
1263template <unsigned int tSize>
1264bool AdvancedMotionT<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)
1265{
1266 ocean_assert(previousPyramid && nextPyramid);
1267
1268 ocean_assert(previousImagePoints.empty() && nextImagePoints.empty());
1269
1270 if (!previousPyramid || previousPyramid.frameType() != nextPyramid.frameType())
1271 {
1272 return false;
1273 }
1274
1275 const unsigned int maximalTrackingLayers = min(trackingLayers, min(previousPyramid.layers(), nextPyramid.layers()));
1276
1277 for (unsigned int n = 0u; n < maximalTrackingLayers; ++n)
1278 {
1279 const Frame& previousLayer = previousPyramid[n];
1280
1281 if (previousLayer.width() < tSize || previousLayer.height() < tSize)
1282 {
1283 break;
1284 }
1285
1286 Frame yFrame;
1288 {
1289 return false;
1290 }
1291
1292 if (yFrame.width() == 0u || yFrame.height() == 0u)
1293 {
1294 return false;
1295 }
1296
1297 const Scalar layerFactor = Scalar((unsigned int)(1 << n));
1298 const Scalar invLayerFactor = Scalar(1) / layerFactor;
1299
1300 // Calculate bounding box:
1301 Box2 boundingBox = previousSubRegion.boundingBox().isValid() ? previousSubRegion.boundingBox() * invLayerFactor : Box2();
1302 if (!boundingBox.isValid())
1303 {
1304 boundingBox = Box2(0, 0, Scalar(yFrame.width()), Scalar(yFrame.height()));
1305 }
1306
1307 // Calculate clip window by intersecting bounding box with image borders:
1308 unsigned int windowLeft, windowTop, windowWidth, windowHeight;
1309 if (!boundingBox.box2integer(yFrame.width(), yFrame.height(), windowLeft, windowTop, windowWidth, windowHeight))
1310 {
1311 continue;
1312 }
1313
1314 ocean_assert(windowWidth >= 1u && windowWidth <= yFrame.width());
1315 ocean_assert(windowHeight >= 1u && windowHeight <= yFrame.height());
1316
1318 if (!Detector::HarrisCornerDetector::detectCorners(yFrame, windowLeft, windowTop, windowWidth, windowHeight, strength, true, corners, true, worker))
1319 {
1320 return false;
1321 }
1322
1323 // If first run went bad, we try again with lowered expectations:
1324 if (n == 0u && corners.size() < 50)
1325 {
1326 corners.clear();
1327 if (!Detector::HarrisCornerDetector::detectCorners(yFrame, windowLeft, windowTop, windowWidth, windowHeight, strength / 2u, true, corners, true, worker))
1328 {
1329 return false;
1330 }
1331 }
1332
1333 // If second run went bad, we try once more with even lower expectations:
1334 if (n == 0u && corners.size() < 20)
1335 {
1336 corners.clear();
1337 if (!Detector::HarrisCornerDetector::detectCorners(yFrame, windowLeft, windowTop, windowWidth, windowHeight, strength / 4u, true, corners, true, worker))
1338 {
1339 return false;
1340 }
1341 }
1342
1343 if (corners.empty())
1344 {
1345 continue;
1346 }
1347
1348 // Restrict corners to those lying inside sub regions:
1349 Detector::HarrisCorners cornersSubRegion;
1350
1351 // if no sub-area filter is provided, use all corners
1352 if (previousSubRegion.isEmpty())
1353 {
1354 cornersSubRegion = std::move(corners);
1355 }
1356 else
1357 {
1358 cornersSubRegion.reserve(corners.size());
1359
1360 for (Detector::HarrisCorners::const_iterator i = corners.begin(); i != corners.end(); ++i)
1361 {
1362 if (previousSubRegion.isInside(i->observation() * layerFactor))
1363 {
1364 cornersSubRegion.push_back(*i);
1365 }
1366 }
1367 }
1368
1369 if (cornersSubRegion.empty())
1370 {
1371 continue;
1372 }
1373
1374 std::sort(cornersSubRegion.begin(), cornersSubRegion.end()); // Sort by corner strength in descending order
1375 Vectors2 smallPreviousImagePoints = Detector::HarrisCorner::corners2imagePoints(cornersSubRegion);
1376
1377 if (!smallPreviousImagePoints.empty() && horizontalBins != 0u && verticalBins != 0u)
1378 {
1379 smallPreviousImagePoints = Geometry::SpatialDistribution::distributeAndFilter(smallPreviousImagePoints.data(), smallPreviousImagePoints.size(), Scalar(windowLeft), Scalar(windowTop), Scalar(windowWidth), Scalar(windowHeight), horizontalBins, verticalBins);
1380 }
1381
1382 if (smallPreviousImagePoints.empty())
1383 {
1384 continue;
1385 }
1386
1387 // Create sub pyramid using fast frame data referencing:
1388 const FramePyramid previousSmall = FramePyramid(previousPyramid, n, previousPyramid.layers() - n, false);
1389 const FramePyramid nextSmall = FramePyramid(nextPyramid, n, nextPyramid.layers() - n, false);
1390
1391 Vectors2 smallNextImagePoints;
1392
1393 // Find corresponding points in next frame:
1394 if (trackPointsBidirectionalSubPixelMirroredBorder<tSize>(previousSmall, nextSmall, coarsestLayerRadius, smallPreviousImagePoints, smallNextImagePoints, maximalSqrError, worker))
1395 {
1396 for (unsigned int i = 0u; i < smallPreviousImagePoints.size(); ++i)
1397 {
1398 previousImagePoints.push_back(smallPreviousImagePoints[i] * layerFactor);
1399 nextImagePoints.push_back(smallNextImagePoints[i] * layerFactor);
1400 }
1401 }
1402 }
1403
1404 return true;
1405}
1406
1407template <typename TMetricInteger, typename TMetricFloat>
1408template <unsigned int tSize>
1409bool AdvancedMotionT<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)
1410{
1411 ocean_assert(previousFrame && nextFrame);
1412
1413 if (!previousFrame || previousFrame.frameType() != nextFrame.frameType())
1414 {
1415 return false;
1416 }
1417
1418 const unsigned int layers = min(trackingLayers, FramePyramid::idealLayers(previousFrame.width(), previousFrame.height(), (tSize / 2u) * 4u, (tSize / 2u) * 4u, 2u, maximalOffset, coarsestLayerRadius));
1419
1420 if (layers == 0u)
1421 {
1422 return false;
1423 }
1424
1425 const FramePyramid previousPyramid(previousFrame, downsamplingMode, layers, false /*copyFirstLayer*/, worker);
1426 const FramePyramid nextPyramid(nextFrame, downsamplingMode, layers, false /*copyFirstLayer*/, worker);
1427
1428 return trackArbitraryPointsBidirectionalSubPixelMirroredBorder<tSize>(previousPyramid, nextPyramid, coarsestLayerRadius, previousImagePoints, nextImagePoints, maximalSqrError, previousSubRegion, horizontalBins, verticalBins, strength, worker, trackingLayers);
1429}
1430
1431template <typename TMetricInteger, typename TMetricFloat>
1432template <unsigned int tSize>
1433bool AdvancedMotionT<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)
1434{
1435 ocean_assert(previousPyramid && nextPyramid);
1436
1437 ocean_assert(!previousImagePoints.empty() && nextImagePoints.empty());
1438 ocean_assert(validIndices == nullptr || validIndices->empty());
1439
1440 if (previousImagePoints.empty())
1441 {
1442 return true;
1443 }
1444
1445 if (!previousPyramid || !previousPyramid.frameType().isPixelFormatCompatible(nextPyramid.frameType().pixelFormat()) || previousPyramid.frameType().pixelOrigin() != nextPyramid.frameType().pixelOrigin())
1446 {
1447 return false;
1448 }
1449
1450 Vectors2 previousPointCandidates(std::move(previousImagePoints));
1451 ocean_assert(previousImagePoints.empty());
1452
1453 // forward point motion
1454 Vectors2 nextPointCandidates(previousPointCandidates.size());
1455 if (!trackPointsSubPixelMirroredBorder<tSize>(previousPyramid, nextPyramid, previousPointCandidates, previousPointCandidates, nextPointCandidates, coarsestLayerRadius, subPixelIterations, worker))
1456 {
1457 return false;
1458 }
1459
1460 // backward point motion
1461 Vectors2 backwardsPreviousPointCandidates(previousPointCandidates.size());
1462 if (!trackPointsSubPixelMirroredBorder<tSize>(nextPyramid, previousPyramid, nextPointCandidates, nextPointCandidates, backwardsPreviousPointCandidates, coarsestLayerRadius, subPixelIterations, worker))
1463 {
1464 return false;
1465 }
1466
1467 ocean_assert(previousPointCandidates.size() == nextPointCandidates.size());
1468 ocean_assert(previousPointCandidates.size() == backwardsPreviousPointCandidates.size());
1469
1470 previousImagePoints = Vectors2();
1471 previousImagePoints.reserve(previousPointCandidates.size());
1472
1473 nextImagePoints.clear();
1474 nextImagePoints.reserve(previousPointCandidates.size());
1475
1476 if (validIndices != nullptr)
1477 {
1478 validIndices->clear();
1479 validIndices->reserve(previousPointCandidates.size());
1480
1481 for (size_t n = 0; n < previousPointCandidates.size(); ++n)
1482 {
1483 const Scalar sqrDistance(previousPointCandidates[n].sqrDistance(backwardsPreviousPointCandidates[n]));
1484 const Vector2 nextImagePoint(nextPointCandidates[n] + (previousPointCandidates[n] - backwardsPreviousPointCandidates[n]) * Scalar(0.5));
1485
1486 previousImagePoints.push_back(previousPointCandidates[n]);
1487 nextImagePoints.push_back(nextImagePoint);
1488
1489 // identify point pairs with almost identical point motion
1490
1491 if (sqrDistance <= maximalSqrError && nextImagePoint.x() >= 0 && nextImagePoint.y() >= 0 && nextImagePoint.x() < Scalar(nextPyramid.finestWidth()) && nextImagePoint.y() < Scalar(nextPyramid.finestHeight()))
1492 {
1493 validIndices->push_back(Index32(n));
1494 }
1495 }
1496 }
1497 else
1498 {
1499 // identify point pairs with almost identical point motion
1500 for (size_t n = 0; n < previousPointCandidates.size(); ++n)
1501 {
1502 const Scalar sqrDistance(previousPointCandidates[n].sqrDistance(backwardsPreviousPointCandidates[n]));
1503
1504 if (sqrDistance <= maximalSqrError)
1505 {
1506 const Vector2 nextImagePoint(nextPointCandidates[n] + (previousPointCandidates[n] - backwardsPreviousPointCandidates[n]) * Scalar(0.5));
1507
1508 if (nextImagePoint.x() >= Scalar(0) && nextImagePoint.y() >= Scalar(0) && nextImagePoint.x() < Scalar(nextPyramid.finestWidth()) && nextImagePoint.y() < Scalar(nextPyramid.finestHeight()))
1509 {
1510 previousImagePoints.push_back(previousPointCandidates[n]);
1511 nextImagePoints.push_back(nextImagePoint);
1512 }
1513 }
1514 }
1515 }
1516
1517 ocean_assert(previousImagePoints.size() == nextImagePoints.size());
1518
1519 return true;
1520}
1521
1522template <typename TMetricInteger, typename TMetricFloat>
1523template <unsigned int tChannels, unsigned int tSize>
1524bool AdvancedMotionT<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)
1525{
1526 ocean_assert(previousPyramid && nextPyramid);
1527 ocean_assert(previousPyramid.frameType().channels() == tChannels);
1528 ocean_assert(nextPyramid.frameType().channels() == tChannels);
1529
1530 ocean_assert(!previousImagePoints.empty() && nextImagePoints.empty());
1531 ocean_assert(validIndices == nullptr || validIndices->empty());
1532
1533 if (previousImagePoints.empty())
1534 {
1535 return true;
1536 }
1537
1538 if (!previousPyramid || previousPyramid.frameType() != nextPyramid.frameType())
1539 {
1540 return false;
1541 }
1542
1543 Vectors2 previousPointCandidates(std::move(previousImagePoints));
1544 ocean_assert(previousImagePoints.empty());
1545
1546 // forward point motion
1547 Vectors2 nextPointCandidates(previousPointCandidates.size());
1548 if (!trackPointsSubPixelMirroredBorder<tChannels, tSize>(previousPyramid, nextPyramid, previousPointCandidates, previousPointCandidates, nextPointCandidates, coarsestLayerRadius, subPixelIterations, worker))
1549 {
1550 return false;
1551 }
1552
1553 // backward point motion
1554 Vectors2 backwardsPreviousPointCandidates(previousPointCandidates.size());
1555 if (!trackPointsSubPixelMirroredBorder<tChannels, tSize>(nextPyramid, previousPyramid, nextPointCandidates, nextPointCandidates, backwardsPreviousPointCandidates, coarsestLayerRadius, subPixelIterations, worker))
1556 {
1557 return false;
1558 }
1559
1560 ocean_assert(previousPointCandidates.size() == nextPointCandidates.size());
1561 ocean_assert(previousPointCandidates.size() == backwardsPreviousPointCandidates.size());
1562
1563 previousImagePoints = Vectors2();
1564 previousImagePoints.reserve(previousPointCandidates.size());
1565
1566 nextImagePoints.clear();
1567 nextImagePoints.reserve(previousPointCandidates.size());
1568
1569 if (validIndices != nullptr)
1570 {
1571 validIndices->clear();
1572 validIndices->reserve(previousPointCandidates.size());
1573
1574 for (size_t n = 0; n < previousPointCandidates.size(); ++n)
1575 {
1576 const Scalar sqrDistance(previousPointCandidates[n].sqrDistance(backwardsPreviousPointCandidates[n]));
1577 const Vector2 nextImagePoint(nextPointCandidates[n] + (previousPointCandidates[n] - backwardsPreviousPointCandidates[n]) * Scalar(0.5));
1578
1579 previousImagePoints.push_back(previousPointCandidates[n]);
1580 nextImagePoints.push_back(nextImagePoint);
1581
1582 // identify point pairs with almost identical point motion
1583 if (sqrDistance <= maximalSqrError && nextImagePoint.x() >= 0 && nextImagePoint.y() >= 0 && nextImagePoint.x() < Scalar(nextPyramid.finestWidth()) && nextImagePoint.y() < Scalar(nextPyramid.finestHeight()))
1584 {
1585 validIndices->push_back(Index32(n));
1586 }
1587 }
1588 }
1589 else
1590 {
1591 // identify point pairs with almost identical point motion
1592 for (size_t n = 0; n < previousPointCandidates.size(); ++n)
1593 {
1594 const Scalar sqrDistance(previousPointCandidates[n].sqrDistance(backwardsPreviousPointCandidates[n]));
1595
1596 if (sqrDistance <= maximalSqrError)
1597 {
1598 const Vector2 nextImagePoint(nextPointCandidates[n] + (previousPointCandidates[n] - backwardsPreviousPointCandidates[n]) * Scalar(0.5));
1599
1600 if (nextImagePoint.x() >= 0 && nextImagePoint.y() >= 0 && nextImagePoint.x() < Scalar(nextPyramid.finestWidth()) && nextImagePoint.y() < Scalar(nextPyramid.finestHeight()))
1601 {
1602 previousImagePoints.push_back(previousPointCandidates[n]);
1603 nextImagePoints.push_back(nextImagePoint);
1604 }
1605 }
1606 }
1607 }
1608
1609 ocean_assert(previousImagePoints.size() == nextImagePoints.size());
1610
1611 return true;
1612}
1613
1614template <typename TMetricInteger, typename TMetricFloat>
1615template <unsigned int tSize>
1616bool AdvancedMotionT<TMetricInteger, TMetricFloat>::trackPointsBidirectionalSubPixelMirroredBorder(const CV::FramePyramid& previousPyramid, const CV::FramePyramid& nextPyramid, const unsigned int coarsestLayerRadius, const Vectors2& previousImagePoints, Vectors2& nextImagePoints, std::vector<uint8_t>& validCorrespondences, const Scalar maximalSqrError, Worker* worker, const unsigned int subPixelIterations)
1617{
1618 ocean_assert(previousPyramid && nextPyramid);
1619
1620 ocean_assert(!previousImagePoints.empty() && nextImagePoints.empty());
1621
1622 if (previousImagePoints.empty())
1623 {
1624 return true;
1625 }
1626
1627 if (!previousPyramid || !previousPyramid.frameType().isPixelFormatCompatible(nextPyramid.frameType().pixelFormat()) || previousPyramid.frameType().pixelOrigin() != nextPyramid.frameType().pixelOrigin())
1628 {
1629 return false;
1630 }
1631
1632 // forward point motion
1633 if (!trackPointsSubPixelMirroredBorder<tSize>(previousPyramid, nextPyramid, previousImagePoints, previousImagePoints, nextImagePoints, coarsestLayerRadius, subPixelIterations, worker))
1634 {
1635 return false;
1636 }
1637
1638 // backward point motion
1639 Vectors2 backwardsPreviousImagePoints(previousImagePoints.size());
1640 if (!trackPointsSubPixelMirroredBorder<tSize>(nextPyramid, previousPyramid, nextImagePoints, nextImagePoints, backwardsPreviousImagePoints, coarsestLayerRadius, subPixelIterations, worker))
1641 {
1642 return false;
1643 }
1644
1645 validCorrespondences.assign(previousImagePoints.size(), uint8_t(0));
1646
1647 for (size_t n = 0; n < previousImagePoints.size(); ++n)
1648 {
1649 const Vector2 forwardBackwardOffset = previousImagePoints[n] - backwardsPreviousImagePoints[n];
1650
1651 const Scalar sqrDistance = forwardBackwardOffset.sqr();
1652
1653 // let's check whether forward and backward motion is almost identical
1654
1655 if (sqrDistance <= maximalSqrError)
1656 {
1657 const Vector2 nextImagePoint(nextImagePoints[n] + forwardBackwardOffset * Scalar(0.5));
1658
1659 if (nextImagePoint.x() >= 0 && nextImagePoint.y() >= 0 && nextImagePoint.x() < Scalar(nextPyramid.finestWidth()) && nextImagePoint.y() < Scalar(nextPyramid.finestHeight()))
1660 {
1661 nextImagePoints[n] = nextImagePoint;
1662
1663 validCorrespondences[n] = uint8_t(1);
1664 }
1665 }
1666 }
1667
1668 ocean_assert(previousImagePoints.size() == nextImagePoints.size());
1669
1670 return true;
1671}
1672
1673template <typename TMetricInteger, typename TMetricFloat>
1674template <unsigned int tChannels, unsigned int tSize>
1675bool AdvancedMotionT<TMetricInteger, TMetricFloat>::trackPointsBidirectionalSubPixelMirroredBorder(const CV::FramePyramid& previousPyramid, const CV::FramePyramid& nextPyramid, const unsigned int coarsestLayerRadius, const Vectors2& previousImagePoints, Vectors2& nextImagePoints, std::vector<uint8_t>& validCorrespondences, const Scalar maximalSqrError, Worker* worker, const unsigned int subPixelIterations)
1676{
1677 ocean_assert(previousPyramid && nextPyramid);
1678 ocean_assert(previousPyramid.frameType().channels() == tChannels);
1679 ocean_assert(nextPyramid.frameType().channels() == tChannels);
1680
1681 ocean_assert(!previousImagePoints.empty() && nextImagePoints.empty());
1682
1683 if (previousImagePoints.empty())
1684 {
1685 return true;
1686 }
1687
1688 if (!previousPyramid || previousPyramid.frameType() != nextPyramid.frameType())
1689 {
1690 return false;
1691 }
1692
1693 // forward point motion
1694 nextImagePoints.reserve(previousImagePoints.size());
1695 if (!trackPointsSubPixelMirroredBorder<tChannels, tSize>(previousPyramid, nextPyramid, previousImagePoints, previousImagePoints, nextImagePoints, coarsestLayerRadius, subPixelIterations, worker))
1696 {
1697 return false;
1698 }
1699
1700 // backward point motion
1701 Vectors2 backwardsPreviousImagePoints(previousImagePoints.size());
1702 if (!trackPointsSubPixelMirroredBorder<tChannels, tSize>(nextPyramid, previousPyramid, nextImagePoints, nextImagePoints, backwardsPreviousImagePoints, coarsestLayerRadius, subPixelIterations, worker))
1703 {
1704 return false;
1705 }
1706
1707 validCorrespondences.assign(previousImagePoints.size(), uint8_t(0));
1708
1709 for (size_t n = 0; n < previousImagePoints.size(); ++n)
1710 {
1711 const Vector2 forwardBackwardOffset = previousImagePoints[n] - backwardsPreviousImagePoints[n];
1712
1713 const Scalar sqrDistance = forwardBackwardOffset.sqr();
1714
1715 // let's check whether forward and backward motion is almost identical
1716
1717 if (sqrDistance <= maximalSqrError)
1718 {
1719 const Vector2 nextImagePoint(nextImagePoints[n] + forwardBackwardOffset * Scalar(0.5));
1720
1721 if (nextImagePoint.x() >= 0 && nextImagePoint.y() >= 0 && nextImagePoint.x() < Scalar(nextPyramid.finestWidth()) && nextImagePoint.y() < Scalar(nextPyramid.finestHeight()))
1722 {
1723 nextImagePoints[n] = nextImagePoint;
1724
1725 validCorrespondences[n] = uint8_t(1);
1726 }
1727 }
1728 }
1729
1730 ocean_assert(previousImagePoints.size() == nextImagePoints.size());
1731
1732 return true;
1733}
1734
1735template <typename TMetricInteger, typename TMetricFloat>
1736template <unsigned int tSize>
1737bool AdvancedMotionT<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)
1738{
1739 ocean_assert(previousPyramid && nextPyramid);
1740 ocean_assert(previousPyramid.frameType().pixelFormat() == nextPyramid.frameType().pixelFormat() && previousPyramid.frameType().pixelOrigin() == nextPyramid.frameType().pixelOrigin());
1741
1742 ocean_assert(!previousImagePoints.empty() && nextImagePoints.empty());
1743 ocean_assert(validIndices == nullptr || validIndices->empty());
1744
1745 ocean_assert(&previousImagePoints != &roughNextImagePoints);
1746
1747 if (previousImagePoints.empty())
1748 {
1749 return true;
1750 }
1751
1752 if (!previousPyramid || previousPyramid.frameType().pixelFormat() != nextPyramid.frameType().pixelFormat() || previousPyramid.frameType().pixelOrigin() != nextPyramid.frameType().pixelOrigin())
1753 {
1754 return false;
1755 }
1756
1757 Vectors2 previousPointCandidates(std::move(previousImagePoints));
1758 ocean_assert(previousImagePoints.empty());
1759
1760 // forward point motion
1761 Vectors2 nextPointCandidates(previousPointCandidates.size());
1762 if (!trackPointsSubPixelMirroredBorder<tSize>(previousPyramid, nextPyramid, previousPointCandidates, roughNextImagePoints, nextPointCandidates, coarsestLayerRadius, subPixelIterations, worker))
1763 {
1764 return false;
1765 }
1766
1767 // backward point motion
1768 Vectors2 backwardsPreviousPointCandidates(previousPointCandidates.size());
1769 if (!trackPointsSubPixelMirroredBorder<tSize>(nextPyramid, previousPyramid, nextPointCandidates, previousPointCandidates, backwardsPreviousPointCandidates, coarsestLayerRadius, subPixelIterations, worker))
1770 {
1771 return false;
1772 }
1773
1774 previousImagePoints = Vectors2();
1775 previousImagePoints.reserve(previousPointCandidates.size());
1776
1777 nextImagePoints.clear();
1778 nextImagePoints.reserve(previousPointCandidates.size());
1779
1780 if (validIndices != nullptr)
1781 {
1782 validIndices->clear();
1783 validIndices->reserve(previousPointCandidates.size());
1784
1785 // identify point pairs with almost identical point motion
1786 for (size_t n = 0; n < previousPointCandidates.size(); ++n)
1787 {
1788 const Scalar sqrDistance(previousPointCandidates[n].sqrDistance(backwardsPreviousPointCandidates[n]));
1789 const Vector2 nextImagePoint(nextPointCandidates[n] + (previousPointCandidates[n] - backwardsPreviousPointCandidates[n]) * Scalar(0.5));
1790
1791 previousImagePoints.push_back(previousPointCandidates[n]);
1792 nextImagePoints.push_back(nextImagePoint);
1793
1794 if (sqrDistance <= maximalSqrError && nextImagePoint.x() >= 0 && nextImagePoint.y() >= 0 && nextImagePoint.x() < Scalar(nextPyramid.finestWidth()) && nextImagePoint.y() < Scalar(nextPyramid.finestHeight()))
1795 {
1796 validIndices->push_back((unsigned int)(n));
1797 }
1798 }
1799 }
1800 else
1801 {
1802 // identify point pairs with almost identical point motion
1803 for (size_t n = 0; n < previousPointCandidates.size(); ++n)
1804 {
1805 const Scalar sqrDistance(previousPointCandidates[n].sqrDistance(backwardsPreviousPointCandidates[n]));
1806
1807 if (sqrDistance <= maximalSqrError)
1808 {
1809 const Vector2 nextImagePoint(nextPointCandidates[n] + (previousPointCandidates[n] - backwardsPreviousPointCandidates[n]) * Scalar(0.5));
1810
1811 if (nextImagePoint.x() >= 0 && nextImagePoint.y() >= 0 && nextImagePoint.x() < Scalar(nextPyramid.finestWidth()) && nextImagePoint.y() < Scalar(nextPyramid.finestHeight()))
1812 {
1813 previousImagePoints.push_back(previousPointCandidates[n]);
1814 nextImagePoints.push_back(nextImagePoint);
1815 }
1816 }
1817 }
1818 }
1819
1820 ocean_assert(previousImagePoints.size() == nextImagePoints.size());
1821
1822 return true;
1823}
1824
1825template <typename TMetricInteger, typename TMetricFloat>
1826template <unsigned int tChannels, unsigned int tSize>
1827bool AdvancedMotionT<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)
1828{
1829 ocean_assert(previousPyramid && nextPyramid);
1830 ocean_assert(FrameType::arePixelFormatsCompatible(previousPyramid.frameType().pixelFormat(), nextPyramid.frameType().pixelFormat()));
1831 ocean_assert(previousPyramid.frameType().pixelOrigin() == nextPyramid.frameType().pixelOrigin());
1832 ocean_assert(previousPyramid.frameType().channels() == tChannels);
1833 ocean_assert(nextPyramid.frameType().channels() == tChannels);
1834
1835 ocean_assert(!previousImagePoints.empty() && nextImagePoints.empty());
1836 ocean_assert(validIndices == nullptr || validIndices->empty());
1837
1838 ocean_assert(&previousImagePoints != &roughNextImagePoints);
1839
1840 if (previousImagePoints.empty())
1841 {
1842 return true;
1843 }
1844
1845 if (!previousPyramid
1846 || !nextPyramid
1847 || !FrameType::arePixelFormatsCompatible(previousPyramid.frameType().pixelFormat(), nextPyramid.frameType().pixelFormat())
1848 || previousPyramid.frameType().pixelOrigin() != nextPyramid.frameType().pixelOrigin()
1849 || previousPyramid.frameType().channels() != tChannels)
1850 {
1851 return false;
1852 }
1853
1854 Vectors2 previousPointCandidates(std::move(previousImagePoints));
1855 ocean_assert(previousImagePoints.empty());
1856
1857 // forward point motion
1858 Vectors2 nextPointCandidates(previousPointCandidates.size());
1859 if (!trackPointsSubPixelMirroredBorder<tChannels, tSize>(previousPyramid, nextPyramid, previousPointCandidates, roughNextImagePoints, nextPointCandidates, coarsestLayerRadius, subPixelIterations, worker))
1860 {
1861 return false;
1862 }
1863
1864 // backward point motion
1865 Vectors2 backwardsPreviousPointCandidates(previousPointCandidates.size());
1866 if (!trackPointsSubPixelMirroredBorder<tChannels, tSize>(nextPyramid, previousPyramid, nextPointCandidates, previousPointCandidates, backwardsPreviousPointCandidates, coarsestLayerRadius, subPixelIterations, worker))
1867 {
1868 return false;
1869 }
1870
1871 previousImagePoints = Vectors2();
1872 previousImagePoints.reserve(previousPointCandidates.size());
1873
1874 nextImagePoints.clear();
1875 nextImagePoints.reserve(previousPointCandidates.size());
1876
1877 if (validIndices != nullptr)
1878 {
1879 validIndices->clear();
1880 validIndices->reserve(previousPointCandidates.size());
1881
1882 // identify point pairs with almost identical point motion
1883 for (size_t n = 0; n < previousPointCandidates.size(); ++n)
1884 {
1885 const Scalar sqrDistance(previousPointCandidates[n].sqrDistance(backwardsPreviousPointCandidates[n]));
1886 const Vector2 nextImagePoint(nextPointCandidates[n] + (previousPointCandidates[n] - backwardsPreviousPointCandidates[n]) * Scalar(0.5));
1887
1888 previousImagePoints.push_back(previousPointCandidates[n]);
1889 nextImagePoints.push_back(nextImagePoint);
1890
1891 if (sqrDistance <= maximalSqrError && nextImagePoint.x() >= 0 && nextImagePoint.y() >= 0 && nextImagePoint.x() < Scalar(nextPyramid.finestWidth()) && nextImagePoint.y() < Scalar(nextPyramid.finestHeight()))
1892 {
1893 validIndices->push_back((unsigned int)(n));
1894 }
1895 }
1896 }
1897 else
1898 {
1899 // identify point pairs with almost identical point motion
1900 for (size_t n = 0; n < previousPointCandidates.size(); ++n)
1901 {
1902 const Scalar sqrDistance(previousPointCandidates[n].sqrDistance(backwardsPreviousPointCandidates[n]));
1903
1904 if (sqrDistance <= maximalSqrError)
1905 {
1906 const Vector2 nextImagePoint(nextPointCandidates[n] + (previousPointCandidates[n] - backwardsPreviousPointCandidates[n]) * Scalar(0.5));
1907
1908 if (nextImagePoint.x() >= 0 && nextImagePoint.y() >= 0 && nextImagePoint.x() < Scalar(nextPyramid.finestWidth()) && nextImagePoint.y() < Scalar(nextPyramid.finestHeight()))
1909 {
1910 previousImagePoints.push_back(previousPointCandidates[n]);
1911 nextImagePoints.push_back(nextImagePoint);
1912 }
1913 }
1914 }
1915 }
1916
1917 ocean_assert(previousImagePoints.size() == nextImagePoints.size());
1918
1919 return true;
1920}
1921
1922template <typename TMetricInteger, typename TMetricFloat>
1923template <unsigned int tSize>
1924bool AdvancedMotionT<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)
1925{
1926 ocean_assert(previousFrame.isValid() && nextFrame.isValid());
1927
1928 if (!previousFrame || !previousFrame.isPixelFormatCompatible(nextFrame.pixelFormat()) || previousFrame.pixelOrigin() != nextFrame.pixelOrigin())
1929 {
1930 return false;
1931 }
1932
1933 const unsigned int previousLayers = FramePyramid::idealLayers(previousFrame.width(), previousFrame.height(), (tSize / 2u) * 4u, (tSize / 2u) * 4u, 2u, maximalOffset, coarsestLayerRadius);
1934 const unsigned int nextLayers = FramePyramid::idealLayers(nextFrame.width(), nextFrame.height(), (tSize / 2u) * 4u, (tSize / 2u) * 4u, 2u, maximalOffset, coarsestLayerRadius);
1935 ocean_assert(previousLayers >= 1u && nextLayers >= 1u);
1936
1937 const unsigned int layers = std::min(previousLayers, nextLayers);
1938
1939 if (layers == 0u)
1940 {
1941 return false;
1942 }
1943
1944 const FramePyramid previousPyramid(previousFrame, downsamplingMode, layers, false /*copyFirstLayer*/, worker);
1945 const FramePyramid nextPyramid(nextFrame, downsamplingMode, layers, false /*copyFirstLayer*/, worker);
1946
1947 return trackPointsBidirectionalSubPixelMirroredBorder<tSize>(previousPyramid, nextPyramid, coarsestLayerRadius, previousImagePoints, nextImagePoints, maximalSqrError, worker, validIndices, subPixelIterations);
1948}
1949
1950template <typename TMetricInteger, typename TMetricFloat>
1951template <unsigned int tSize>
1952bool AdvancedMotionT<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)
1953{
1954 ocean_assert(previousReferencePoints.empty());
1955 ocean_assert(currentReferencePoints.empty());
1956
1957 ocean_assert(&previousReferencePoints != &currentReferencePoints);
1958
1959 ocean_assert(horizontalBins >= 1u);
1960 ocean_assert(verticalBins >= 1u);
1961
1962 const Frame& previousFrame = previousPyramid.finestLayer();
1963
1964 const unsigned int width = previousFrame.width();
1965 const unsigned int height = previousFrame.height();
1966
1967 const unsigned int areaLeft = boundingBox ? boundingBox.left() : 0u;
1968 const unsigned int areaTop = boundingBox ? boundingBox.top() : 0u;
1969 const unsigned int areaWidth = boundingBox ? boundingBox.width() : width;
1970 const unsigned int areaHeight = boundingBox ? boundingBox.height() : height;
1971
1972 ocean_assert(areaLeft + areaWidth <= width);
1973 ocean_assert(areaTop + areaHeight <= height);
1974
1975 ocean_assert(!maskFrame.isValid() || maskFrame.isPixelFormatCompatible(FrameType::genericPixelFormat<uint8_t, 1u>()));
1976
1977 Detector::HarrisCorners features;
1978 features.reserve(5000);
1979 Detector::HarrisCornerDetector::detectCorners(previousFrame, areaLeft, areaTop, areaWidth, areaHeight, 1u, true, features, false, worker);
1980 ocean_assert(!features.empty());
1981
1982 if (features.empty())
1983 {
1984 return false;
1985 }
1986
1987 std::sort(features.begin(), features.end());
1988 const Vectors2 allPreviousReferencePoints(CV::Detector::HarrisCorner::corners2imagePoints(features));
1989
1990 const Geometry::SpatialDistribution::DistributionArray distribution(Geometry::SpatialDistribution::distributeToArray(allPreviousReferencePoints.data(), (unsigned int)allPreviousReferencePoints.size(), Scalar(areaLeft), Scalar(areaTop), Scalar(areaWidth), Scalar(areaHeight), horizontalBins, verticalBins));
1991
1992 const uint8_t* const maskData = maskFrame ? maskFrame.constdata<uint8_t>() : nullptr;
1993 const unsigned int maskStrideElements = maskFrame ? maskFrame.strideElements() : 0u;
1994
1995 previousReferencePoints.reserve(distribution.bins());
1996 for (unsigned int n = 0u; n < distribution.bins(); ++n)
1997 {
1998 const Indices32& indices = distribution[n];
1999
2000 if (!indices.empty())
2001 {
2002 ocean_assert(indices.front() < allPreviousReferencePoints.size());
2003 const Vector2& point = allPreviousReferencePoints[indices.front()];
2004
2005 const unsigned int xPosition = (unsigned int)(point.x());
2006 const unsigned int yPosition = (unsigned int)(point.y());
2007
2008 ocean_assert(xPosition < width);
2009 ocean_assert(yPosition < height);
2010
2011 if (maskData == nullptr || maskData[yPosition * maskStrideElements + xPosition] == 0xFFu)
2012 {
2013 previousReferencePoints.push_back(point);
2014 }
2015 }
2016 }
2017
2018 if (previousReferencePoints.empty())
2019 {
2020 return false;
2021 }
2022
2023 return trackPointsSubPixelMirroredBorder<tSize>(previousPyramid, currentPyramid, previousReferencePoints, previousReferencePoints, currentReferencePoints, 2u, 4u, worker);
2024}
2025
2026template <typename TMetricInteger, typename TMetricFloat>
2027template <unsigned int tChannels, unsigned int tPatchSize>
2028Vector2 AdvancedMotionT<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)
2029{
2030 static_assert(tChannels != 0u, "Invalid number of data channels!");
2031 static_assert(tPatchSize % 2u == 1u, "Invalid size of the image patch, must be odd!");
2032
2033 constexpr unsigned int tPatchSize_2 = tPatchSize / 2u;
2034
2035 ocean_assert(frame0 != nullptr && frame1 != nullptr);
2036
2037 ocean_assert(width0 >= tPatchSize && height0 >= tPatchSize);
2038 ocean_assert(width1 >= tPatchSize && height1 >= tPatchSize);
2039
2040 ocean_assert(position0.x() >= Scalar(0) && position0.x() < Scalar(width0));
2041 ocean_assert(position0.y() >= Scalar(0) && position0.y() < Scalar(height0));
2042
2044
2045 const unsigned int leftCenter1 = (unsigned int)(max(0, int(position1.x() - radiusX)));
2046 const unsigned int topCenter1 = (unsigned int)(max(0, int(position1.y() - radiusY)));
2047
2048 const unsigned int rightCenter1 = min(position1.x() + radiusX, width1 - 1u);
2049 const unsigned int bottomCenter1 = min(position1.y() + radiusY, height1 - 1u);
2050
2051 // first, we determine a buffer containing the first (interpolated) image patch
2052
2053 uint8_t buffer0[tPatchSize * tPatchSize * tChannels];
2054 uint8_t buffer1[tPatchSize * tPatchSize * tChannels];
2055
2056 const unsigned int x0 = (unsigned int)(position0.x());
2057 const unsigned int y0 = (unsigned int)(position0.y());
2058
2059 if (x0 - tPatchSize_2 < width0 - tPatchSize && y0 - tPatchSize_2 < height0 - tPatchSize)
2060 {
2061 ocean_assert(x0 >= tPatchSize_2 && x0 < width0 - (tPatchSize_2 + 1u) && y0 >= tPatchSize_2 && y0 < height0 - (tPatchSize_2 + 1u));
2062 AdvancedFrameInterpolatorBilinear::interpolateSquarePatch8BitPerChannel<tChannels, tPatchSize, PC_TOP_LEFT>(frame0, width0, frame0PaddingElements, buffer0, position0);
2063 }
2064 else
2065 {
2066 ocean_assert(!(x0 >= tPatchSize_2 && x0 < width0 - (tPatchSize_2 + 1u) && y0 >= tPatchSize_2 && y0 < height0 - (tPatchSize_2 + 1u)));
2067 AdvancedFrameInterpolatorBilinear::interpolateSquareMirroredBorder8BitPerChannel<tChannels, tPatchSize>(frame0, width0, height0, frame0PaddingElements, buffer0, position0);
2068 }
2069
2070 PixelPosition bestPosition;
2071 uint32_t bestMetric = uint32_t(-1);
2072 unsigned int bestSqrDistance = (unsigned int)(-1);
2073
2074 for (unsigned int y1 = topCenter1; y1 <= bottomCenter1; ++y1)
2075 {
2076 for (unsigned int x1 = leftCenter1; x1 <= rightCenter1; ++x1)
2077 {
2078 uint32_t candidateMetric;
2079
2080 if (x1 - tPatchSize_2 < width1 - tPatchSize && y1 - tPatchSize_2 < height1 - tPatchSize)
2081 {
2082 candidateMetric = TMetricInteger::template patchBuffer8BitPerChannel<tChannels, tPatchSize>(frame1, width1, x1, y1, frame1PaddingElements, buffer0);
2083 }
2084 else
2085 {
2086 constexpr unsigned int buffer1PaddingElements = 0u;
2087
2088 FrameConverter::patchFrameMirroredBorder<uint8_t, tChannels>(frame1, buffer1, width1, height1, x1, y1, tPatchSize, frame1PaddingElements, buffer1PaddingElements); // **TODO** for performance improvements: use patch/patch mirrored border metric
2089
2090 candidateMetric = TMetricInteger::template buffer8BitPerChannel<tChannels, tPatchSize * tPatchSize>(buffer0, buffer1);
2091 }
2092
2093 const PixelPosition position(x1, y1);
2094
2095 if (candidateMetric < bestMetric || (candidateMetric == bestMetric && position1.sqrDistance(position) < bestSqrDistance))
2096 {
2097 bestMetric = candidateMetric;
2098 bestPosition = position;
2099
2100 bestSqrDistance = position1.sqrDistance(position);
2101 }
2102
2103 if (metricIdentityResult && x1 == position1.x() && y1 == position1.y())
2104 {
2105 *metricIdentityResult = candidateMetric;
2106 }
2107 }
2108 }
2109
2110 ocean_assert(bestMetric != (unsigned int)(-1) && bestPosition);
2111
2112 ocean_assert(abs(int(bestPosition.x()) - int(position1.x())) <= int(radiusX));
2113 ocean_assert(abs(int(bestPosition.y()) - int(position1.y())) <= int(radiusY));
2114
2115 if (metricResult)
2116 {
2117 *metricResult = bestMetric;
2118 }
2119
2120 return trackPointBufferSubPixelMirroredBorder<tChannels, tPatchSize>(buffer0, frame1, width1, height1, frame1PaddingElements, Vector2(Scalar(bestPosition.x()), Scalar(bestPosition.y())), subPixelIterations, metricResult);
2121}
2122
2123template <typename TMetricInteger, typename TMetricFloat>
2124template <unsigned int tPatchSize>
2125inline Vector2 AdvancedMotionT<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)
2126{
2127 ocean_assert(channels >= 1u);
2128
2129 switch (channels)
2130 {
2131 case 1u:
2132 return trackPointSubPixelMirroredBorder<1u, tPatchSize>(frame0, frame1, width0, height0, width1, height1, frame0PaddingElements, frame1PaddingElements, position0, radiusX, radiusY, rough1, subPixelIterations, metricResult, metricIdentityResult);
2133
2134 case 2u:
2135 return trackPointSubPixelMirroredBorder<2u, tPatchSize>(frame0, frame1, width0, height0, width1, height1, frame0PaddingElements, frame1PaddingElements, position0, radiusX, radiusY, rough1, subPixelIterations, metricResult, metricIdentityResult);
2136
2137 case 3u:
2138 return trackPointSubPixelMirroredBorder<3u, tPatchSize>(frame0, frame1, width0, height0, width1, height1, frame0PaddingElements, frame1PaddingElements, position0, radiusX, radiusY, rough1, subPixelIterations, metricResult, metricIdentityResult);
2139
2140 case 4u:
2141 return trackPointSubPixelMirroredBorder<4u, tPatchSize>(frame0, frame1, width0, height0, width1, height1, frame0PaddingElements, frame1PaddingElements, position0, radiusX, radiusY, rough1, subPixelIterations, metricResult, metricIdentityResult);
2142 }
2143
2144 ocean_assert(false && "Invalid pixel format!");
2145 return rough1;
2146}
2147
2148template <typename TMetricInteger, typename TMetricFloat>
2149template <unsigned int tChannels, unsigned int tPatchSize>
2150Vector2 AdvancedMotionT<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)
2151{
2152 static_assert(tChannels >= 1u, "Invalid number of data channels!");
2153 static_assert(tPatchSize % 2u == 1u, "Invalid size of the image patch, must be odd!");
2154
2155 ocean_assert(buffer0 != nullptr && frame1 != nullptr);
2156
2157 ocean_assert(width1 >= tPatchSize && height1 >= tPatchSize);
2158
2159 ocean_assert(roughPosition1.x() >= Scalar(0) && roughPosition1.x() < Scalar(width1));
2160 ocean_assert(roughPosition1.y() >= Scalar(0) && roughPosition1.y() < Scalar(height1));
2161
2162 uint32_t metricBest = uint32_t(-1);
2163
2164 if (metricResult != nullptr)
2165 {
2166 metricBest = *metricResult;
2167
2168#ifdef OCEAN_DEBUG
2169 const bool result = metricBest == TMetricFloat::template patchMirroredBorderBuffer8BitPerChannel<tChannels, tPatchSize>(frame1, width1, height1, roughPosition1.x(), roughPosition1.y(), frame1PaddingElements, buffer0);
2170 ocean_assert_and_suppress_unused(result, result);
2171#endif
2172 }
2173 else
2174 {
2175 const unsigned int x1 = (unsigned int)(roughPosition1.x());
2176 const unsigned int y1 = (unsigned int)(roughPosition1.y());
2177
2178 if (x1 - (tPatchSize / 2u) < width1 - tPatchSize && y1 - (tPatchSize / 2u) < height1 - tPatchSize)
2179 {
2180 ocean_assert(x1 >= (tPatchSize / 2u) && y1 >= (tPatchSize / 2u) && x1 < width1 - (tPatchSize / 2u + 1u) && y1 < height1 - (tPatchSize / 2u + 1u));
2181 metricBest = TMetricFloat::template patchBuffer8BitPerChannel<tChannels, tPatchSize>(frame1, width1, roughPosition1.x(), roughPosition1.y(), frame1PaddingElements, buffer0);
2182 }
2183 else
2184 {
2185 ocean_assert(!(x1 >= (tPatchSize / 2u) && y1 >= (tPatchSize / 2u) && x1 < width1 - (tPatchSize / 2u + 1u) && y1 < height1 - (tPatchSize / 2u + 1u)));
2186 metricBest = TMetricFloat::template patchMirroredBorderBuffer8BitPerChannel<tChannels, tPatchSize>(frame1, width1, height1, roughPosition1.x(), roughPosition1.y(), frame1PaddingElements, buffer0);
2187 }
2188 }
2189
2190 constexpr unsigned int numberSteps = 8u;
2191
2192 const Vector2 steps[numberSteps] =
2193 {
2194 Vector2(-1, -1),
2195 Vector2(0, -1),
2196 Vector2(1, -1),
2197 Vector2(-1, 0),
2198 Vector2(1, 0),
2199 Vector2(-1, 1),
2200 Vector2(0, 1),
2201 Vector2(1, 1)
2202 };
2203
2204 Scalar offset = Scalar(0.5);
2205 Vector2 position1 = roughPosition1;
2206
2207 for (unsigned int n = 0u; n < subPixelIterations; ++n)
2208 {
2209 Vector2 bestPosition1 = position1;
2210
2211 // make 8 sample calculations
2212
2213 for (unsigned int i = 0u; i < numberSteps; ++i)
2214 {
2215 const Vector2 candidatePosition1(position1.x() + steps[i].x() * offset, position1.y() + steps[i].y() * offset);
2216
2217 if (candidatePosition1.x() >= Scalar(0) && candidatePosition1.x() < Scalar(width1) && candidatePosition1.y() >= Scalar(0) && candidatePosition1.y() < Scalar(height1))
2218 {
2219 const unsigned int x1 = (unsigned int)(candidatePosition1.x());
2220 const unsigned int y1 = (unsigned int)(candidatePosition1.y());
2221
2222 const uint32_t candidateMetric = (x1 - (tPatchSize / 2u) < width1 - tPatchSize && y1 - (tPatchSize / 2u) < height1 - tPatchSize) ?
2223 TMetricFloat::template patchBuffer8BitPerChannel<tChannels, tPatchSize>(frame1, width1, candidatePosition1.x(), candidatePosition1.y(), frame1PaddingElements, buffer0) :
2224 TMetricFloat::template patchMirroredBorderBuffer8BitPerChannel<tChannels, tPatchSize>(frame1, width1, height1, candidatePosition1.x(), candidatePosition1.y(), frame1PaddingElements, buffer0);
2225
2226 if (candidateMetric < metricBest)
2227 {
2228 metricBest = candidateMetric;
2229 bestPosition1 = candidatePosition1;
2230 }
2231 }
2232 }
2233
2234 position1 = bestPosition1;
2235 offset *= Scalar(0.5);
2236 }
2237
2238 if (metricResult != nullptr)
2239 {
2240 *metricResult = metricBest;
2241 }
2242
2243 ocean_assert(position1.x() >= 0 && position1.y() >= 0);
2244 ocean_assert(position1.x() < Scalar(width1) && position1.y() < Scalar(height1));
2245
2246 return position1;
2247}
2248
2249template <typename TMetricInteger, typename TMetricFloat>
2250template <unsigned int tSize>
2251void AdvancedMotionT<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)
2252{
2253 static_assert(tSize % 2u == 1u, "Invalid patch size, must be odd!");
2254
2255 ocean_assert(previousPyramid != nullptr && nextPyramid != nullptr);
2256 ocean_assert(previousPoints != nullptr && nextPoints != nullptr);
2257
2258 ocean_assert(previousPyramid->isValid() && nextPyramid->isValid());
2259
2260 ocean_assert(previousPyramid->frameType().pixelFormat() == nextPyramid->frameType().pixelFormat());
2261 ocean_assert(previousPyramid->frameType().pixelOrigin() == nextPyramid->frameType().pixelOrigin());
2262
2263 ocean_assert(roughNextPoints == nullptr || previousPoints->size() == roughNextPoints->size());
2264 ocean_assert(nextPoints->size() == previousPoints->size());
2265
2266 ocean_assert(firstPoint + numberPoints <= previousPoints->size());
2267
2268 ocean_assert(numberLayers >= 1u);
2269 ocean_assert(numberLayers <= previousPyramid->layers());
2270 ocean_assert(numberLayers <= nextPyramid->layers());
2271
2272 ocean_assert(previousPyramid->layer(numberLayers - 1u).width() >= tSize / 2u);
2273 ocean_assert(previousPyramid->layer(numberLayers - 1u).height() >= tSize / 2u);
2274
2275 ShiftVector<Vector2> intermediateRoughNextPoints(firstPoint, numberPoints);
2276
2277 const Scalar coarsestWidthNextPyramid = Scalar(nextPyramid->layer(numberLayers - 1u).width());
2278 const Scalar coarsestHeightNextPyramid = Scalar(nextPyramid->layer(numberLayers - 1u).height());
2279 const Scalar coarsestLayerFactorNextPyramid = Scalar(1) / Scalar(nextPyramid->sizeFactor(numberLayers - 1u));
2280 ocean_assert(coarsestWidthNextPyramid >= 1u && coarsestHeightNextPyramid >= 1u);
2281
2282 const unsigned int channels = previousPyramid->frameType().channels();
2283 ocean_assert(channels >= 1u && channels <= 4u);
2284
2285 for (unsigned int n = firstPoint; n < firstPoint + numberPoints; ++n)
2286 {
2287 const Vector2& roughNextPoint = roughNextPoints != nullptr ? (*roughNextPoints)[n] : (*previousPoints)[n];
2288
2289 const Scalar x = min(roughNextPoint.x() * coarsestLayerFactorNextPyramid, coarsestWidthNextPyramid - Scalar(1));
2290 const Scalar y = min(roughNextPoint.y() * coarsestLayerFactorNextPyramid, coarsestHeightNextPyramid - Scalar(1));
2291
2292 intermediateRoughNextPoints[n] = Vector2(x, y);
2293 }
2294
2295 for (unsigned int layerIndex = numberLayers - 1u; layerIndex < numberLayers; --layerIndex)
2296 {
2297 const Frame& previousLayer = (*previousPyramid)[layerIndex];
2298 const Frame& nextLayer = (*nextPyramid)[layerIndex];
2299
2300 const uint8_t* const previousLayerData = previousLayer.constdata<uint8_t>();
2301 const uint8_t* const nextLayerData = nextLayer.constdata<uint8_t>();
2302
2303 const unsigned int previousLayerPaddingElements = previousLayer.paddingElements();
2304 const unsigned int nextLayerPaddingElements = nextLayer.paddingElements();
2305
2306 const unsigned int previousLayerWidth = previousLayer.width();
2307 const unsigned int previousLayerHeight = previousLayer.height();
2308
2309 const unsigned int nextLayerWidth = nextLayer.width();
2310 const unsigned int nextLayerHeight = nextLayer.height();
2311
2312 if (layerIndex == 0u)
2313 {
2314 // we apply a sub-pixel accurate tracking on the finest pyramid layer
2315
2316 const unsigned int layerRadiusX = numberLayers == 1u ? coarsestLayerRadius : 2u;
2317 const unsigned int layerRadiusY = numberLayers == 1u ? coarsestLayerRadius : 2u;
2318
2319 for (unsigned int pointIndex = firstPoint; pointIndex < firstPoint + numberPoints; ++pointIndex)
2320 {
2321 const Vector2& previousPosition = (*previousPoints)[pointIndex];
2322 ocean_assert(previousPosition.x() >= Scalar(0) && previousPosition.y() >= Scalar(0));
2323 ocean_assert(previousPosition.x() < Scalar(previousLayerWidth) && previousPosition.y() < Scalar(previousLayerHeight));
2324
2325 const Vector2& intermediateRoughNextPoint = intermediateRoughNextPoints[pointIndex];
2326 ocean_assert(intermediateRoughNextPoint.x() >= Scalar(0) && intermediateRoughNextPoint.y() >= Scalar(0));
2327 ocean_assert(intermediateRoughNextPoint.x() < Scalar(nextLayerWidth) && intermediateRoughNextPoint.y() < Scalar(nextLayerHeight));
2328
2329 unsigned int* const metricResult = metricResults ? metricResults + pointIndex : nullptr;
2330 unsigned int* const metricIdentityResult = metricIdentityResults ? metricIdentityResults + pointIndex : nullptr;
2331
2332 const Vector2 nextPoint = trackPointSubPixelMirroredBorder<tSize>(previousLayerData, nextLayerData, channels, previousLayerWidth, previousLayerHeight, nextLayerWidth, nextLayerHeight, previousLayerPaddingElements, nextLayerPaddingElements, previousPosition, layerRadiusX, layerRadiusY, intermediateRoughNextPoint, subPixelIterations, metricResult, metricIdentityResult);
2333
2334 ocean_assert(nextPoint.x() >= 0 && nextPoint.x() < Scalar(nextLayerWidth));
2335 ocean_assert(nextPoint.y() >= 0 && nextPoint.y() < Scalar(nextLayerHeight));
2336
2337 ocean_assert(pointIndex < nextPoints->size());
2338 (*nextPoints)[pointIndex] = nextPoint;
2339 }
2340 }
2341 else // otherwise we apply a pixel accurate determination
2342 {
2343 ocean_assert(layerIndex > 0u);
2344
2345 const unsigned int layerRadius = (layerIndex == numberLayers - 1u) ? coarsestLayerRadius : 2u;
2346
2347 const Scalar layerFactor = Scalar(1) / Scalar(1u << layerIndex);
2348
2349 const Scalar finerNextLayerWidth1 = Scalar((*nextPyramid)[layerIndex - 1u].width()) - Scalar(1);
2350 const Scalar finerNextLayerHeight1 = Scalar((*nextPyramid)[layerIndex - 1u].height()) - Scalar(1);
2351
2352 for (unsigned int pointIndex = firstPoint; pointIndex < firstPoint + numberPoints; ++pointIndex)
2353 {
2354 ocean_assert(intermediateRoughNextPoints[pointIndex].x() >= Scalar(0) && intermediateRoughNextPoints[pointIndex].y() >= Scalar(0));
2355 ocean_assert(intermediateRoughNextPoints[pointIndex].x() < Scalar(nextLayerWidth) && intermediateRoughNextPoints[pointIndex].y() < Scalar(nextLayerHeight));
2356
2357 const PixelPosition intermediateRoughNextPoint(Numeric::round32(intermediateRoughNextPoints[pointIndex].x()), Numeric::round32(intermediateRoughNextPoints[pointIndex].y()));
2358 ocean_assert(intermediateRoughNextPoint.x() < nextLayerWidth && intermediateRoughNextPoint.y() < nextLayerHeight);
2359
2360 unsigned int* const metricResult = metricResults ? metricResults + pointIndex : nullptr;
2361
2362 const Vector2& previousPointFinestLayer = (*previousPoints)[pointIndex];
2363
2364 const PixelPosition previousPoint(std::min(Numeric::round32(previousPointFinestLayer.x() * layerFactor), int(previousLayerWidth - 1u)), std::min(Numeric::round32(previousPointFinestLayer.y() * layerFactor), int(previousLayerHeight - 1u)));
2365
2366 ocean_assert(previousPoint.x() < previousLayerWidth && previousPoint.y() < previousLayerHeight);
2367 if (previousPoint.x() < previousLayerWidth && previousPoint.y() < previousLayerHeight)
2368 {
2369 const PixelPosition nextPoint = MotionT<TMetricInteger>::template pointMotionInFrameMirroredBorder<tSize>(previousLayerData, nextLayerData, channels, previousLayerWidth, previousLayerHeight, nextLayerWidth, nextLayerHeight, previousPoint, layerRadius, layerRadius, previousLayerPaddingElements, nextLayerPaddingElements, intermediateRoughNextPoint, metricResult);
2370
2371 ocean_assert(nextPoint.x() < nextLayerWidth && nextPoint.y() < nextLayerHeight);
2372
2373 intermediateRoughNextPoints[pointIndex] = Vector2(min(Scalar(nextPoint.x() * 2u), finerNextLayerWidth1), min(Scalar(nextPoint.y() * 2u), finerNextLayerHeight1));
2374
2375 ocean_assert(intermediateRoughNextPoints[pointIndex].x() >= 0 && intermediateRoughNextPoints[pointIndex].x() <= finerNextLayerWidth1);
2376 ocean_assert(intermediateRoughNextPoints[pointIndex].y() >= 0 && intermediateRoughNextPoints[pointIndex].y() <= finerNextLayerHeight1);
2377 }
2378 else
2379 {
2380 ocean_assert(false && "This should never happen!");
2381
2382 intermediateRoughNextPoints[pointIndex] = Vector2(previousPointFinestLayer.x() * layerFactor * Scalar(2), previousPointFinestLayer.y() * layerFactor * Scalar(2));
2383
2384 ocean_assert(intermediateRoughNextPoints[pointIndex].x() >= 0 && intermediateRoughNextPoints[pointIndex].x() < finerNextLayerWidth1);
2385 ocean_assert(intermediateRoughNextPoints[pointIndex].y() >= 0 && intermediateRoughNextPoints[pointIndex].y() < finerNextLayerHeight1);
2386 }
2387 }
2388 }
2389 }
2390}
2391
2392template <typename TMetricInteger, typename TMetricFloat>
2393template <unsigned int tChannels, unsigned int tSize>
2394void AdvancedMotionT<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)
2395{
2396 static_assert(tSize % 2u == 1u, "Invalid patch size, must be odd!");
2397
2398 ocean_assert(previousPyramid && currentPyramid);
2399 ocean_assert(previousPoints && roughPoints && currentPoints);
2400
2401 ocean_assert(*previousPyramid && *currentPyramid);
2402
2403 ocean_assert(FrameType::arePixelFormatsCompatible(previousPyramid->frameType().pixelFormat(), currentPyramid->frameType().pixelFormat()));
2404 ocean_assert(previousPyramid->frameType().pixelOrigin() == currentPyramid->frameType().pixelOrigin());
2405
2406 ocean_assert(previousPyramid->frameType().channels() == tChannels);
2407 ocean_assert(currentPyramid->frameType().channels() == tChannels);
2408
2409 ocean_assert(previousPoints->size() == roughPoints->size());
2410 ocean_assert(currentPoints->size() == previousPoints->size());
2411
2412 ocean_assert(firstPoint + numberPoints <= previousPoints->size());
2413
2414 ocean_assert(numberLayers >= 1u);
2415 ocean_assert(numberLayers <= previousPyramid->layers());
2416 ocean_assert(numberLayers <= currentPyramid->layers());
2417
2418 ocean_assert(previousPyramid->layer(numberLayers - 1u).width() >= tSize / 2u);
2419 ocean_assert(previousPyramid->layer(numberLayers - 1u).height() >= tSize / 2u);
2420
2421 ShiftVector<Vector2> intermediateRoughPoints(firstPoint, numberPoints);
2422
2423 const Scalar lowestCurrentWidth = Scalar(currentPyramid->layer(numberLayers - 1u).width());
2424 const Scalar lowestCurrentHeight = Scalar(currentPyramid->layer(numberLayers - 1u).height());
2425 const Scalar lowestLayerFactor = Scalar(1) / Scalar(currentPyramid->sizeFactor(numberLayers - 1u));
2426 ocean_assert(lowestCurrentWidth >= 1u && lowestCurrentHeight >= 1u);
2427
2428 const unsigned int channels = previousPyramid->frameType().channels();
2429 ocean_assert_and_suppress_unused(channels >= 1u && channels <= 4u, channels);
2430
2431 for (unsigned int n = firstPoint; n < firstPoint + numberPoints; ++n)
2432 {
2433 const Vector2& roughPoint = (*roughPoints)[n];
2434
2435 const Scalar x = min(roughPoint.x() * lowestLayerFactor, lowestCurrentWidth - 1);
2436 const Scalar y = min(roughPoint.y() * lowestLayerFactor, lowestCurrentHeight - 1);
2437
2438 intermediateRoughPoints[n] = Vector2(x, y);
2439 }
2440
2441 for (int layerIndex = int(numberLayers) - 1; layerIndex >= 0; --layerIndex)
2442 {
2443 const Frame& previousFrame = (*previousPyramid)[layerIndex];
2444 const Frame& currentFrame = (*currentPyramid)[layerIndex];
2445
2446 const uint8_t* const previousFrameData = previousFrame.constdata<uint8_t>();
2447 const uint8_t* const currentFrameData = currentFrame.constdata<uint8_t>();
2448
2449 const unsigned int previousFramePaddingElements = previousFrame.paddingElements();
2450 const unsigned int currentFramePaddingElements = currentFrame.paddingElements();
2451
2452 const unsigned int previousWidth = previousFrame.width();
2453 const unsigned int previousHeight = previousFrame.height();
2454
2455 const unsigned int currentWidth = currentFrame.width();
2456 const unsigned int currentHeight = currentFrame.height();
2457
2458 // if the finest layer is reached we apply a subpixel accurate determination
2459 if (layerIndex == 0)
2460 {
2461 const unsigned int layerRadiusX = numberLayers == 1u ? coarsestLayerRadius : 2u;
2462 const unsigned int layerRadiusY = numberLayers == 1u ? coarsestLayerRadius : 2u;
2463
2464 for (unsigned int i = firstPoint; i < firstPoint + numberPoints; ++i)
2465 {
2466 ocean_assert(intermediateRoughPoints[i].x() >= Scalar(0) && intermediateRoughPoints[i].y() >= Scalar(0));
2467 ocean_assert(intermediateRoughPoints[i].x() < Scalar(currentWidth) && intermediateRoughPoints[i].y() < Scalar(currentHeight));
2468
2469 const Vector2& intermediateRoughPoint = intermediateRoughPoints[i];
2470
2471 unsigned int* const metricResult = metricResults ? metricResults + i : nullptr;
2472 unsigned int* const metricIdentityResult = metricIdentityResults ? metricIdentityResults + i : nullptr;
2473
2474 const Vector2& previousPosition = (*previousPoints)[i];
2475
2476 ocean_assert(previousPosition.x() >= Scalar(0) && previousPosition.y() >= Scalar(0));
2477 ocean_assert(previousPosition.x() < Scalar(previousWidth) && previousPosition.y() < Scalar(previousHeight));
2478
2479 const Vector2 position(trackPointSubPixelMirroredBorder<tChannels, tSize>(previousFrameData, currentFrameData, previousWidth, previousHeight, currentWidth, currentHeight, previousFramePaddingElements, currentFramePaddingElements, previousPosition, layerRadiusX, layerRadiusY, intermediateRoughPoint, subPixelIterations, metricResult, metricIdentityResult));
2480
2481 ocean_assert(position.x() >= 0 && position.x() < Scalar(currentWidth));
2482 ocean_assert(position.y() >= 0 && position.y() < Scalar(currentHeight));
2483
2484 ocean_assert(i < currentPoints->size());
2485 (*currentPoints)[i] = position;
2486 }
2487 }
2488 else // otherwise we apply a pixel accurate determination
2489 {
2490 ocean_assert(layerIndex > 0);
2491
2492 const unsigned int layerRadiusX = (layerIndex == int(numberLayers) - 1) ? coarsestLayerRadius : 2u;
2493 const unsigned int layerRadiusY = (layerIndex == int(numberLayers) - 1) ? coarsestLayerRadius : 2u;
2494
2495 for (unsigned int i = firstPoint; i < firstPoint + numberPoints; ++i)
2496 {
2497 ocean_assert(intermediateRoughPoints[i].x() >= Scalar(0) && intermediateRoughPoints[i].y() >= Scalar(0));
2498 ocean_assert(intermediateRoughPoints[i].x() < Scalar(currentWidth) && intermediateRoughPoints[i].y() < Scalar(currentHeight));
2499
2500 const PixelPosition intermediateRoughPoint(Numeric::round32(intermediateRoughPoints[i].x()), Numeric::round32(intermediateRoughPoints[i].y()));
2501
2502 unsigned int* const metricResult = metricResults ? metricResults + i : nullptr;
2503
2504 const Scalar layerFactor = Scalar(1) / Scalar((unsigned int)(1 << layerIndex));
2505 const PixelPosition previousPosition(min(Numeric::round32((*previousPoints)[i].x() * layerFactor), int(previousWidth) - 1),
2506 min(Numeric::round32((*previousPoints)[i].y() * layerFactor), int(previousHeight) - 1));
2507
2508 if (previousPosition.x() < previousWidth && previousPosition.y() < previousHeight)
2509 {
2510 const PixelPosition position(MotionT<TMetricInteger>::template pointMotionInFrameMirroredBorder<tChannels, tSize>(previousFrameData, currentFrameData, previousWidth, previousHeight, currentWidth, currentHeight, previousPosition, layerRadiusX, layerRadiusY, previousFramePaddingElements, currentFramePaddingElements, intermediateRoughPoint, metricResult));
2511
2512 ocean_assert(position.x() < currentWidth && position.y() < currentHeight);
2513
2514 const Scalar higherWidth = Scalar((*currentPyramid)[layerIndex - 1].width());
2515 const Scalar higherHeight = Scalar((*currentPyramid)[layerIndex - 1].height());
2516
2517 intermediateRoughPoints[i] = Vector2(min(Scalar(position.x() * 2u), higherWidth - 1), min(Scalar(position.y() * 2u), higherHeight - 1));
2518
2519 ocean_assert(intermediateRoughPoints[i].x() >= 0 && intermediateRoughPoints[i].x() < Scalar(higherWidth));
2520 ocean_assert(intermediateRoughPoints[i].y() >= 0 && intermediateRoughPoints[i].y() < Scalar(higherHeight));
2521 }
2522 else
2523 {
2524 intermediateRoughPoints[i] = Vector2((*previousPoints)[i].x() * layerFactor * Scalar(2), (*previousPoints)[i].y() * layerFactor * Scalar(2));
2525
2526 ocean_assert(intermediateRoughPoints[i].x() >= 0 && intermediateRoughPoints[i].x() < Scalar(currentFrame.width() * 2u));
2527 ocean_assert(intermediateRoughPoints[i].y() >= 0 && intermediateRoughPoints[i].y() < Scalar(currentFrame.height() * 2u));
2528 }
2529 }
2530 }
2531 }
2532}
2533
2534}
2535
2536}
2537
2538}
2539
2540#endif // META_OCEAN_CV_ADVANCED_ADVANCED_MOTION_H
bool isValid() const
Returns whether the box holds valid parameters.
Definition Box2.h:761
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:923
This class manages point correspondences for bidirectional tracking across pyramid layers.
Definition AdvancedMotion.h:74
unsigned int subPixelIterations() const
Returns the number of sub-pixel iterations to be applied at the finest pyramid layer.
Definition AdvancedMotion.h:917
bool isPointValid(const size_t pointIndex) const
Returns whether a specific point correspondence is still valid.
Definition AdvancedMotion.h:895
CV::PixelPosition previousPositionDownsampled(const size_t pointIndex) const
Returns the previous image point position downsampled to the current pyramid layer.
Definition AdvancedMotion.h:840
PointCorrespondences()=default
Creates an invalid point correspondences object.
Scalar maximalSqrError_
The squared maximum error threshold for bidirectional tracking validation.
Definition AdvancedMotion.h:244
unsigned int pyramidLayers() const
Returns the number of pyramid layers to be used for tracking.
Definition AdvancedMotion.h:905
size_t size() const
Returns the number of point correspondences.
Definition AdvancedMotion.h:924
const Vector2 & predictedNextPosition(const size_t pointIndex) const
Returns the predicted next image point position at the finest pyramid layer.
Definition AdvancedMotion.h:879
void propagateNextPositionDownsampled(const size_t pointIndex, const CV::PixelPosition &nextPoint)
Propagates the next point position from the current pyramid layer to the finer layer below.
void startForwardTracking(const CV::FramePyramid &previousPyramid, const CV::FramePyramid &nextPyramid)
Initializes the forward tracking from the previous pyramid to the next pyramid.
unsigned int pyramidLayers_
The number of pyramid layers to use for tracking.
Definition AdvancedMotion.h:238
const Vector2 * previousPoints_
The image points in the previous pyramid, always at finest layer resolution.
Definition AdvancedMotion.h:226
Scalar maximalSqrErrorLayer_
The squared maximum error threshold used during layer processing (includes search radius)
Definition AdvancedMotion.h:247
uint8_t * validCorrespondences_
Binary validity flags for each correspondence (0x00 = invalid, 0x01 = valid)
Definition AdvancedMotion.h:232
void startBackwardTracking(const CV::FramePyramid &, const CV::FramePyramid &nextPyramid)
Initializes the backward tracking from the next pyramid back to the previous pyramid.
CV::PixelPosition predictedNextPositionDownsampled(const size_t pointIndex) const
Returns the predicted next image point position downsampled to the current pyramid layer.
Definition AdvancedMotion.h:861
static unsigned int coarsestPyramidLayer(const CV::FramePyramid &previousPyramid, const CV::FramePyramid &nextPyramid, const PointCorrespondences *pointCorrespondenceGroups, const size_t numberCorrespondenceGroups)
Determines the coarsest pyramid layer index across multiple correspondence groups.
const Vector2 & previousPosition(const size_t pointIndex) const
Returns the previous image point position at the finest pyramid layer.
Definition AdvancedMotion.h:826
Vectors2 internalBackwardNextPoints_
Internal storage for backward tracking results used for bidirectional validation.
Definition AdvancedMotion.h:286
void propagateNextPosition(const size_t pointIndex, const Vector2 &nextPoint)
Propagates the final next point position at the finest pyramid layer.
bool isValid() const
Returns whether this object holds valid parameters.
Definition AdvancedMotion.h:929
Vector2 * nextPoints_
The tracked next image points, progressively refined through pyramid layers (coarsest to finest)
Definition AdvancedMotion.h:229
unsigned int layerRadius() const
Returns the search radius for the current pyramid layer.
Definition AdvancedMotion.h:912
bool startLayer(const unsigned int layerIndex, const CV::FramePyramid &previousPyramid, const CV::FramePyramid &nextPyramid)
Starts processing a specific pyramid layer.
This class collects statistics about tracking distances between corresponding 2D feature points.
Definition AdvancedMotion.h:296
std::string toString() const
Returns a string of the tracking statistics.
size_t measurements() const
Returns the number of measurements this statistic holds, (the number of calls to addCorrespondences()...
Definition AdvancedMotion.h:934
Scalars sqrDistances_
The squared Euclidean distances between all added point correspondences.
Definition AdvancedMotion.h:351
void addCorrespondences(const Vector2 *previousImagePoints, const Vector2 *nextImagePoints, const uint8_t *validCorrespondences, const size_t size)
Adds a set of point correspondences to the tracking statistics, filtering by validity flags.
void addCorrespondences(const Vector2 *previousImagePoints, const Vector2 *nextImagePoints, const size_t size)
Adds a set of point correspondences to the tracking statistics.
bool isValid() const
Returns whether this tracking statistic object is valid.
Definition AdvancedMotion.h:939
TrackingStatistic(const unsigned int width, const unsigned int height)
Creates a new tracking statistic object.
Definition AdvancedMotion.h:60
std::vector< uint32_t > MetricResults
Definition of a vector holding metric results.
Definition AdvancedMotion.h:66
This class implements advanced motion techniques (mainly with sub-pixel accuracy or binary masks) all...
Definition AdvancedMotion.h:367
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:1264
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:2028
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:2150
static bool trackPointsBidirectionalSubPixelMirroredBorder(const CV::FramePyramid &previousPyramid, const CV::FramePyramid &nextPyramid, PointCorrespondences *pointCorrespondenceGroups, const size_t numberCorrespondenceGroups)
Tracks multiple groups of point correspondences bidirectionally between two frame pyramids with sub-p...
Definition AdvancedMotion.h:946
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:2251
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:1138
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:1737
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:1952
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:46
const Frame & finestLayer() const
Returns the finest layer frame of this pyramid.
Definition FramePyramid.h:785
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:828
bool isValid() const
Returns whether this pyramid holds at least one frame layer.
Definition FramePyramid.h:913
DownsamplingMode
Definition of individual down sampling modes.
Definition FramePyramid.h:53
@ DM_FILTER_14641
Down sampling is realized by a 5x5 Gaussian filter.
Definition FramePyramid.h:81
static constexpr unsigned int sizeFactor(const unsigned int layerIndex)
Returns the size factor of a specified layer in relation to the finest layer.
Definition FramePyramid.h:884
unsigned int layers() const
Returns the number of layers this pyramid holds.
Definition FramePyramid.h:811
const FrameType & frameType() const
Returns the frame type of the finest layer.
Definition FramePyramid.h:861
unsigned int finestHeight() const
Returns the height of the finest (first) layer.
Definition FramePyramid.h:834
const Frame & layer(const unsigned int layer) const
Returns the frame of a specified layer.
Definition FramePyramid.h:773
This class implements patch-based motion techniques.
Definition Motion.h:60
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
static PixelPositionT< unsigned int > vector2pixelPosition(const Vector2 &value)
Converts a 2D vector into a pixel position.
T y() const
Returns the vertical coordinate position of this object.
Definition PixelPosition.h:468
unsigned int sqrDistance(const PixelPositionT< T > &position) const
Returns the square difference between two pixel positions.
Definition PixelPosition.h:487
T x() const
Returns the horizontal coordinate position of this object.
Definition PixelPosition.h:456
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:2877
This class implements Ocean's image class.
Definition Frame.h:1879
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:4222
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:4332
const FrameType & frameType() const
Returns the frame type of this frame.
Definition Frame.h:3936
bool isValid() const
Returns whether this frame is valid.
Definition Frame.h:4612
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:4206
@ 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:3241
PixelOrigin pixelOrigin() const
Returns the pixel origin of the frame.
Definition Frame.h:3286
uint32_t numberPlanes() const
Returns the number of planes of the pixel format of this frame.
Definition Frame.h:3281
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:3251
unsigned int height() const
Returns the height of the frame in pixel.
Definition Frame.h:3246
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:3298
unsigned int channels() const
Returns the number of individual channels the frame has.
Definition Frame.h:3271
unsigned int bins() const
Returns the number of bins this distribution holds.
Definition SpatialDistribution.h:1088
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:1685
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:1713
static constexpr int32_t round32(const T value)
Returns the rounded 32 bit integer value of a given value.
Definition Numeric.h:2073
static constexpr T sqr(const T value)
Returns the square of a given value.
Definition Numeric.h:1499
static constexpr T maxValue()
Returns the max scalar value.
Definition Numeric.h:3253
This class implements a vector with shifted elements.
Definition ShiftVector.h:27
const T & x() const noexcept
Returns the x value.
Definition Vector2.h:710
const T & y() const noexcept
Returns the y value.
Definition Vector2.h:722
T sqr() const
Returns the square of the vector length.
Definition Vector2.h:633
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:1159
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
PixelBoundingBoxT< unsigned int > PixelBoundingBox
Definition of the default PixelBoundingBox object with data type allowing only positive coordinate va...
Definition PixelBoundingBox.h:28
PixelPositionT< unsigned int > PixelPosition
Definition of the default PixelPosition object with a data type allowing only positive coordinate val...
Definition PixelPosition.h:32
std::vector< HarrisCorner > HarrisCorners
Definition of a vector holding Harris corners.
Definition HarrisCorner.h:30
BoxT2< Scalar > Box2
Definition of the Box2 object, depending on the OCEAN_MATH_USE_SINGLE_PRECISION either with single or...
Definition Box2.h:29
std::vector< Vector2 > Vectors2
Definition of a vector holding Vector2 objects.
Definition Vector2.h:64
float Scalar
Definition of a scalar type.
Definition Math.h:129
std::vector< Scalar > Scalars
Definition of a vector holding Scalar objects.
Definition Math.h:145
VectorT2< Scalar > Vector2
Definition of a 2D vector.
Definition Vector2.h:28
The namespace covering the entire Ocean framework.
Definition Accessor.h:15