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