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