Ocean
Motion.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_MOTION_H
9 #define META_OCEAN_CV_MOTION_H
10 
11 #include "ocean/cv/CV.h"
12 #include "ocean/cv/FramePyramid.h"
13 #include "ocean/cv/PixelPosition.h"
17 
18 #include "ocean/base/ShiftVector.h"
19 #include "ocean/base/Worker.h"
20 
21 namespace Ocean
22 {
23 
24 namespace CV
25 {
26 
27 // Forward declaration.
28 template <typename TMetric> class Motion;
29 
30 /**
31  * Definition of a Motion class that applies sum absolute difference calculations as metric.
32  * @see MotionSSD, MotionZeroMeanSSD, Motion.
33  * @ingroup cv
34  */
36 
37 /**
38  * Definition of a Motion class that applies sum square difference calculations as metric.
39  * @see MotionSAD, MotionZeroMeanSSD, Motion.
40  * @ingroup cv
41  */
43 
44 /**
45  * Definition of a Motion class that applies zero-mean sum square difference calculations as metric.
46  * @see MotionSAD, MotionSSD, Motion.
47  * @ingroup cv
48  */
50 
51 /**
52  * This class implements patch-based motion techniques.
53  * @tparam TMetric The metric that is applied for measurements with pixel accuracy
54  * @see MotionSAD, MotionSSD, MotionZeroMeanSSD.
55  * @ingroup cv
56  */
57 template <typename TMetric = SumSquareDifferences>
58 class Motion
59 {
60  public:
61 
62  /**
63  * Tracks a set of given points between two frames with pixel accuracy.
64  * Actually, this function simply creates two frame pyramids and invokes the corresponding function needing frame pyramid as parameters.<br>
65  * The motion is determined by application of an image patch centered around the point to be tracked.<br>
66  * The points are tracked unidirectional (from the previous frame to the current frame).<br>
67  * If a point is near the frame border, a mirrored image patch is applied
68  * @param previousFrame The previous frame in which the previous points are located, must be valid
69  * @param currentFrame The current frame, with same pixel format and pixel orientation as the previous frame, must be valid
70  * @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())
71  * @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())
72  * @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())
73  * @param maximalOffset Maximal expected offset between two corresponding points in pixel, defined in the domain of previous/current frame, with range [1, infinity)
74  * @param coarsestLayerRadiusX The search radius on the coarsest pyramid layer in horizontal direction, in pixel, with range [0, infinity)
75  * @param coarsestLayerRadiusY The search radius on the coarsest pyramid layer in vertical direction, in pixel, with range [0, infinity)
76  * @param downsamplingMode The down sampling mode that is applied to create the pyramid layers
77  * @param worker Optional worker object to distribute the computation
78  * @param metricResults Optional resulting matching quality of the applied metric, nullptr if the results do not matter
79  * @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
80  * @return True, if succeeded
81  * @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
82  */
83  template <unsigned int tPatchSize>
84  static bool trackPointsInPyramidMirroredBorder(const Frame& previousFrame, const Frame& currentFrame, const PixelPositions& previousPoints, const PixelPositions& roughPoints, PixelPositions& currentPoints, const unsigned int maximalOffset, const unsigned int coarsestLayerRadiusX, const unsigned int coarsestLayerRadiusY, const FramePyramid::DownsamplingMode downsamplingMode = FramePyramid::DM_FILTER_14641, Worker* worker = nullptr, std::vector<uint32_t>* metricResults = nullptr, std::vector<uint32_t>* metricIdentityResults = nullptr);
85 
86  /**
87  * Tracks a set of given points between two frame pyramids, with pixel accuracy.
88  * The points are tracked unidirectional (from the previous frame to the current frame).<br>
89  * The motion is determined by application of an image patch centered around the point to be tracked.<br>
90  * If a point is near the frame border, a mirrored image patch is applied.
91  * @param previousPyramid The frame pyramid of the previous frame in which the previous points are located, must be valid
92  * @param currentPyramid The frame pyramid of the current frame, with same pixel format and pixel origin as the previous frame pyramid, must be valid
93  * @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())
94  * @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())
95  * @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())
96  * @param coarsestLayerRadiusX The search radius on the coarsest pyramid layer in horizontal direction, in pixel, with range [0, infinity)
97  * @param coarsestLayerRadiusY The search radius on the coarsest pyramid layer in vertical direction, in pixel, with range [0, infinity)
98  * @param worker Optional worker object to distribute the computation
99  * @param metricResults Optional resulting matching quality of the applied metric, nullptr if the results do not matter
100  * @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
101  * @return True, if succeeded
102  * @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
103  */
104  template <unsigned int tPatchSize>
105  static bool trackPointsInPyramidMirroredBorder(const FramePyramid& previousPyramid, const FramePyramid& currentPyramid, const PixelPositions& previousPoints, const PixelPositions& roughPoints, PixelPositions& currentPoints, const unsigned int coarsestLayerRadiusX, const unsigned int coarsestLayerRadiusY, Worker* worker = nullptr, std::vector<uint32_t>* metricResults = nullptr, std::vector<uint32_t>* metricIdentityResults = nullptr);
106 
107  /**
108  * Determines the motion for one given point between two frames by application of an image patch.
109  * Patch pixels outside the frame are mirrored into the frame before comparison.
110  * @param frame0 The first frame, must be valid
111  * @param frame1 The second frame, must be valid
112  * @param width0 Width of the first frame in pixel, with range [tPatchSize/2, infinity)
113  * @param height0 Height of the first frame in pixel, with range [tPatchSize/2, infinity)
114  * @param width1 Width of the second frame in pixel, with range [tPatchSize/2, infinity)
115  * @param height1 Height of the second frame in pixel, with range [tPatchSize/2, infinity)
116  * @param position0 The position in the first frame, with range [0, width - 1]x[0, height - 1]
117  * @param radiusX The search radius in horizontal direction, in pixel, with range [0, width - 1]
118  * @param radiusY The search radius in vertical direction, in pixel, with range [0, height - 1]
119  * @param frame0PaddingElements The number of padding elements at the end of each row of the first frame, in elements, with range [0, infinity)
120  * @param frame1PaddingElements The number of padding elements at the end of each row of the second frame, in elements, with range [0, infinity)
121  * @param rough1 The optional rough guess of the point in the second frame, an invalid position if unknown
122  * @param metricResult Optional resulting matching quality of the applied metric, nullptr if the result does not matter
123  * @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
124  * @return Best matching position in the second frame
125  * @tparam tChannels The number of frame channels, with range [1u, infinity)
126  * @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
127  */
128  template <unsigned int tChannels, unsigned int tPatchSize>
129  static PixelPosition pointMotionInFrameMirroredBorder(const uint8_t* const frame0, const uint8_t* const frame1, const unsigned int width0, const unsigned int height0, const unsigned int width1, const unsigned int height1, const PixelPosition& position0, const unsigned int radiusX, const unsigned int radiusY, const unsigned int frame0PaddingElements, const unsigned int frame1PaddingElements, const PixelPosition& rough1 = PixelPosition(), uint32_t* const metricResult = nullptr, uint32_t* const metricIdentityResult = nullptr);
130 
131  /**
132  * Determines the motion for one given point between two frames by application of an image patch.
133  * Patch pixels outside the frame are mirrored into the frame before comparison.
134  * @param frame0 The first frame, must be valid
135  * @param frame1 The second frame, must be valid
136  * @param channels The number of frame channels, with range [1, 4]
137  * @param width0 Width of the first frame in pixel, with range [tPatchSize/2, infinity)
138  * @param height0 Height of the first frame in pixel, with range [tPatchSize/2, infinity)
139  * @param width1 Width of the second frame in pixel, with range [tPatchSize/2, infinity)
140  * @param height1 Height of the second frame in pixel, with range [tPatchSize/2, infinity)
141  * @param position0 The position in the first frame, with range [0, width - 1]x[0, height - 1]
142  * @param radiusX The search radius in horizontal direction, in pixel, with range [0, width - 1]
143  * @param radiusY The search radius in vertical direction, in pixel, with range [0, height - 1]
144  * @param frame0PaddingElements The number of padding elements at the end of each row of the first frame, in elements, with range [0, infinity)
145  * @param frame1PaddingElements The number of padding elements at the end of each row of the second frame, in elements, with range [0, infinity)
146  * @param rough1 The optional rough guess of the point in the second frame, an invalid position if unknown
147  * @param metricResult Optional resulting matching quality of the applied metric, nullptr if the result does not matter
148  * @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
149  * @return Best matching position in the second frame
150  * @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
151  */
152  template <unsigned int tPatchSize>
153  static inline PixelPosition pointMotionInFrameMirroredBorder(const uint8_t* const frame0, const uint8_t* const frame1, const unsigned int channels, const unsigned int width0, const unsigned int height0, const unsigned int width1, const unsigned int height1, const PixelPosition& position0, const unsigned int radiusX, const unsigned int radiusY, const unsigned int frame0PaddingElements, const unsigned int frame1PaddingElements, const PixelPosition& rough1 = PixelPosition(), uint32_t* const metricResult = nullptr, uint32_t* const metricIdentityResult = nullptr);
154 
155  protected:
156 
157  /**
158  * Tracks a subset of given points between two frame pyramids.
159  * The points are tracked unidirectional (from the previous frame to the current frame).<br>
160  * If a point is near the frame border, a mirrored image patch is applied.<br>
161  * @param previousPyramid Previous frame pyramid
162  * @param currentPyramid Current frame pyramid, with same frame type as the previous frame
163  * @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)]
164  * @param previousPoints A set of points that are located in the previous frame
165  * @param roughPoints The rough points in the current frame (if known), otherwise the prevousPoints may be provided
166  * @param currentPoints Resulting current points, that have been tracking between the two points
167  * @param coarsestLayerRadiusX The search radius on the coarsest pyramid layer in horizontal direction, in pixel, with range [0, infinity)
168  * @param coarsestLayerRadiusY The search radius on the coarsest pyramid layer in vertical direction, in pixel, with range [0, infinity)
169  * @param metricResults Optional resulting results of the applied metric, nullptr if the results do not matter
170  * @param metricIdentityResults Optional resulting results of the applied metric in both frames at the same previous position, nullptr if the results do not matter
171  * @param firstPoint The first point to be handled, with range [0, numberPoints - 1]
172  * @param numberPoints The number of points to handled, with range [1, infinity)
173  * @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
174  */
175  template <unsigned int tPatchSize>
176  static void trackPointsInPyramidMirroredBorderSubset(const FramePyramid* previousPyramid, const FramePyramid* currentPyramid, const unsigned int numberLayers, const PixelPositions* previousPoints, const PixelPositions* roughPoints, PixelPositions* currentPoints, const unsigned int coarsestLayerRadiusX, const unsigned int coarsestLayerRadiusY, uint32_t* metricResults, uint32_t* metricIdentityResults, const unsigned int firstPoint, const unsigned int numberPoints);
177 };
178 
179 template <typename TMetric>
180 template <unsigned int tPatchSize>
181 bool Motion<TMetric>::trackPointsInPyramidMirroredBorder(const Frame& previousFrame, const Frame& currentFrame, const PixelPositions& previousPoints, const PixelPositions& roughPoints, PixelPositions& currentPoints, const unsigned int maximalOffset, const unsigned int coarsestLayerRadiusX, const unsigned int coarsestLayerRadiusY, const FramePyramid::DownsamplingMode downsamplingMode, Worker* worker, std::vector<uint32_t>* metricResults, std::vector<uint32_t>* metricIdentityResults)
182 {
183  static_assert(tPatchSize % 2u == 1u, "Invalid image patch size, must be odd!");
184  static_assert(tPatchSize >= 3u, "Invalid image patch size!");
185 
186  ocean_assert(previousFrame && currentFrame);
187 
188  ocean_assert(previousFrame.frameType().pixelFormat() == currentFrame.frameType().pixelFormat());
189  ocean_assert(previousFrame.frameType().pixelOrigin() == currentFrame.frameType().pixelOrigin());
190 
191  ocean_assert(previousPoints.size() == roughPoints.size());
192 
193  const unsigned int idealLayers = FramePyramid::idealLayers(previousFrame.width(), previousFrame.height(), (tPatchSize / 2u) * 4u, (tPatchSize / 2u) * 4u, 2u, maximalOffset);
194 
195  if (idealLayers == 0u)
196  {
197  return false;
198  }
199 
200  const FramePyramid previousPyramid(previousFrame, downsamplingMode, idealLayers, false /*copyFirstLayer*/, worker);
201  const FramePyramid currentPyramid(currentFrame, downsamplingMode, idealLayers, false /*copyFirstLayer*/, worker);
202 
203  return trackPointsInPyramidMirroredBorder<tPatchSize>(previousPyramid, currentPyramid, previousPoints, roughPoints, currentPoints, coarsestLayerRadiusX, coarsestLayerRadiusY, worker, metricResults, metricIdentityResults);
204 }
205 
206 template <typename TMetric>
207 template <unsigned int tPatchSize>
208 bool Motion<TMetric>::trackPointsInPyramidMirroredBorder(const FramePyramid& previousPyramid, const FramePyramid& currentPyramid, const PixelPositions& previousPoints, const PixelPositions& roughPoints, PixelPositions& currentPoints, const unsigned int coarsestLayerRadiusX, const unsigned int coarsestLayerRadiusY, Worker* worker, std::vector<uint32_t>* metricResults, std::vector<uint32_t>* metricIdentityResults)
209 {
210  static_assert(tPatchSize % 2u == 1u, "Invalid image patch size, must be odd!");
211  static_assert(tPatchSize >= 3u, "Invalid image patch size, must be larger than 2!");
212 
213  ocean_assert(previousPyramid.frameType().pixelFormat() == currentPyramid.frameType().pixelFormat());
214  ocean_assert(previousPyramid.frameType().pixelOrigin() == currentPyramid.frameType().pixelOrigin());
215 
216  const unsigned int idealLayers = CV::FramePyramid::idealLayers(previousPyramid.finestWidth(), previousPyramid.finestHeight(), (tPatchSize / 2u) * 4u, (tPatchSize / 2u) * 4u, 2u);
217  const unsigned int numberLayers = std::min(std::min(previousPyramid.layers(), currentPyramid.layers()), idealLayers);
218 
219  if (numberLayers == 0u)
220  {
221  return false;
222  }
223 
224  currentPoints.resize(previousPoints.size());
225 
226  if (metricResults)
227  {
228  metricResults->resize(previousPoints.size());
229  }
230 
231  if (metricIdentityResults)
232  {
233  metricIdentityResults->resize(previousPoints.size());
234  }
235 
236  if (worker)
237  {
238  worker->executeFunction(Worker::Function::createStatic(&Motion::trackPointsInPyramidMirroredBorderSubset<tPatchSize>, &previousPyramid, &currentPyramid, numberLayers, &previousPoints, &roughPoints, &currentPoints, coarsestLayerRadiusX, coarsestLayerRadiusY, metricResults ? metricResults->data() : nullptr, metricIdentityResults ? metricIdentityResults->data() : nullptr, 0u, 0u), 0u, (unsigned int)(previousPoints.size()));
239  }
240  else
241  {
242  trackPointsInPyramidMirroredBorderSubset<tPatchSize>(&previousPyramid, &currentPyramid, numberLayers, &previousPoints, &roughPoints, &currentPoints, coarsestLayerRadiusX, coarsestLayerRadiusY, metricResults ? metricResults->data() : nullptr, metricIdentityResults ? metricIdentityResults->data() : nullptr, 0u, (unsigned int)(previousPoints.size()));
243  }
244 
245  return true;
246 }
247 
248 template <typename TMetric>
249 template <unsigned int tChannels, unsigned int tPatchSize>
250 PixelPosition Motion<TMetric>::pointMotionInFrameMirroredBorder(const uint8_t* const frame0, const uint8_t* const frame1, const unsigned int width0, const unsigned int height0, const unsigned int width1, const unsigned int height1, const PixelPosition& position0, const unsigned int radiusX, const unsigned int radiusY, const unsigned int frame0PaddingElements, const unsigned int frame1PaddingElements, const PixelPosition& rough1, uint32_t* const metricResult, uint32_t* const metricIdentityResult)
251 {
252  static_assert(tChannels != 0u, "Invalid number of data channels!");
253  static_assert(tPatchSize % 2u == 1u, "Invalid size of the image patch, must be odd!");
254 
255  constexpr unsigned int tPatchSize_2 = tPatchSize / 2u;
256 
257  ocean_assert(frame0 != nullptr && frame1 != nullptr);
258  ocean_assert(radiusX != 0u || radiusY != 0u);
259 
260  ocean_assert(width0 >= tPatchSize_2 && height0 >= tPatchSize_2);
261  ocean_assert(width1 >= tPatchSize_2 && height1 >= tPatchSize_2);
262 
263  ocean_assert(position0.x() < width0);
264  ocean_assert(position0.y() < height0);
265 
266  const PixelPosition position1((rough1 && rough1.x() < width1 && rough1.y() < height1) ? rough1 : position0);
267  ocean_assert(position1.x() < width1);
268  ocean_assert(position1.y() < height1);
269 
270  const unsigned int leftCenter1 = (unsigned int)(max(0, int(position1.x() - radiusX)));
271  const unsigned int topCenter1 = (unsigned int)(max(0, int(position1.y() - radiusY)));
272 
273  const unsigned int rightCenter1 = min(position1.x() + radiusX, width1 - 1u);
274  const unsigned int bottomCenter1 = min(position1.y() + radiusY, height1 - 1u);
275 
276  ocean_assert(leftCenter1 < width1 && leftCenter1 <= rightCenter1 && rightCenter1 < width1);
277  ocean_assert(topCenter1 < height1 && topCenter1 <= bottomCenter1 && bottomCenter1 < height1);
278 
279  PixelPosition bestPosition;
280  uint32_t bestMetric = uint32_t(-1);
281  uint32_t bestSqrDistance = uint32_t(-1);
282 
283  // check whether position0 is entirely inside the frame (including the patch with size tPatchSize x tPatchSize)
284  if ((position0.x() >= tPatchSize_2 && position0.y() >= tPatchSize_2 && position0.x() + tPatchSize_2 < width0 && position0.y() + tPatchSize_2 < height0))
285  {
286  for (unsigned int y1 = topCenter1; y1 <= bottomCenter1; ++y1)
287  {
288  for (unsigned int x1 = leftCenter1; x1 <= rightCenter1; ++x1)
289  {
290  // check whether we can use the fast metric function
291 
292  const uint32_t metric = (x1 - tPatchSize_2 < width1 - (tPatchSize - 1u) && y1 - tPatchSize_2 < height1 - (tPatchSize - 1u)) ?
293  TMetric::template patch8BitPerChannel<tChannels, tPatchSize>(frame0, frame1, width0, width1, position0.x(), position0.y(), x1, y1, frame0PaddingElements, frame1PaddingElements) :
294  TMetric::template patchMirroredBorder8BitPerChannel<tChannels, tPatchSize>(frame0, frame1, width0, height0, width1, height1, position0.x(), position0.y(), x1, y1, frame0PaddingElements, frame1PaddingElements);
295 
296  const PixelPosition position(x1, y1);
297 
298  if (metric < bestMetric || (metric == bestMetric && position1.sqrDistance(position) < bestSqrDistance))
299  {
300  bestMetric = metric;
301  bestPosition = position;
302 
303  bestSqrDistance = position1.sqrDistance(position);
304  }
305 
306  if (metricIdentityResult && x1 == position1.x() && y1 == position1.y())
307  {
308  *metricIdentityResult = metric;
309  }
310  }
311  }
312  }
313  else
314  {
315  for (unsigned int y1 = topCenter1; y1 <= bottomCenter1; ++y1)
316  {
317  for (unsigned int x1 = leftCenter1; x1 <= rightCenter1; ++x1)
318  {
319  const uint32_t metric = TMetric::template patchMirroredBorder8BitPerChannel<tChannels, tPatchSize>(frame0, frame1, width0, height0, width1, height1, position0.x(), position0.y(), x1, y1, frame0PaddingElements, frame1PaddingElements);
320 
321  const PixelPosition position(x1, y1);
322 
323  if (metric < bestMetric || (metric == bestMetric && position1.sqrDistance(position) < bestSqrDistance))
324  {
325  bestMetric = metric;
326  bestPosition = position;
327 
328  bestSqrDistance = position1.sqrDistance(position);
329  }
330 
331  if (metricIdentityResult && x1 == position1.x() && y1 == position1.y())
332  {
333  *metricIdentityResult = metric;
334  }
335  }
336  }
337  }
338 
339  ocean_assert(bestMetric != uint32_t(-1) && bestPosition.isValid());
340 
341  if (metricResult)
342  {
343  *metricResult = bestMetric;
344  }
345 
346  ocean_assert(abs(int(bestPosition.x()) - int(position1.x())) <= int(radiusX));
347  ocean_assert(abs(int(bestPosition.y()) - int(position1.y())) <= int(radiusY));
348 
349  return bestPosition;
350 }
351 
352 template <typename TMetric>
353 template <unsigned int tPatchSize>
354 inline PixelPosition Motion<TMetric>::pointMotionInFrameMirroredBorder(const uint8_t* const frame0, const uint8_t* const frame1, const unsigned int channels, const unsigned int width0, const unsigned int height0, const unsigned int width1, const unsigned int height1, const PixelPosition& position0, const unsigned int radiusX, const unsigned int radiusY, const unsigned int frame0PaddingElements, const unsigned int frame1PaddingElements, const PixelPosition& rough1, uint32_t* const metricResult, uint32_t* const metricIdentityResult)
355 {
356  ocean_assert(channels >= 1u);
357 
358  switch (channels)
359  {
360  case 1u:
361  return pointMotionInFrameMirroredBorder<1u, tPatchSize>(frame0, frame1, width0, height0, width1, height1, position0, radiusX, radiusY, frame0PaddingElements, frame1PaddingElements, rough1, metricResult, metricIdentityResult);
362 
363  case 2u:
364  return pointMotionInFrameMirroredBorder<2u, tPatchSize>(frame0, frame1, width0, height0, width1, height1, position0, radiusX, radiusY, frame0PaddingElements, frame1PaddingElements, rough1, metricResult, metricIdentityResult);
365 
366  case 3u:
367  return pointMotionInFrameMirroredBorder<3u, tPatchSize>(frame0, frame1, width0, height0, width1, height1, position0, radiusX, radiusY, frame0PaddingElements, frame1PaddingElements, rough1, metricResult, metricIdentityResult);
368 
369  case 4u:
370  return pointMotionInFrameMirroredBorder<4u, tPatchSize>(frame0, frame1, width0, height0, width1, height1, position0, radiusX, radiusY, frame0PaddingElements, frame1PaddingElements, rough1, metricResult, metricIdentityResult);
371  }
372 
373  ocean_assert(false && "Invalid pixel format!");
374  return rough1;
375 }
376 
377 template <typename TMetric>
378 template <unsigned int tPatchSize>
379 void Motion<TMetric>::trackPointsInPyramidMirroredBorderSubset(const FramePyramid* previousPyramid, const FramePyramid* currentPyramid, const unsigned int numberLayers, const PixelPositions* previousPoints, const PixelPositions* roughPoints, PixelPositions* currentPoints, const unsigned int coarsestLayerRadiusX, const unsigned int coarsestLayerRadiusY, uint32_t* metricResults, uint32_t* metricIdentityResults, const unsigned int firstPoint, const unsigned int numberPoints)
380 {
381  static_assert(tPatchSize % 2u == 1u, "Invalid image patch size!");
382  static_assert(tPatchSize >= 3u, "Invalid image patch size!");
383 
384  ocean_assert(previousPyramid && currentPyramid);
385  ocean_assert(previousPoints && roughPoints && currentPoints);
386 
387  ocean_assert(*previousPyramid && *currentPyramid);
388 
389  ocean_assert(previousPyramid->frameType().pixelFormat() == currentPyramid->frameType().pixelFormat());
390  ocean_assert(previousPyramid->frameType().pixelOrigin() == currentPyramid->frameType().pixelOrigin());
391 
392  ocean_assert(previousPyramid->layers() >= 1u && currentPyramid->layers() >= 1u);
393 
394  ocean_assert(previousPoints->size() == roughPoints->size());
395  ocean_assert(currentPoints->size() == previousPoints->size());
396 
397  ocean_assert(coarsestLayerRadiusX != 0u || coarsestLayerRadiusY != 0u);
398 
399  ocean_assert(firstPoint + numberPoints <= previousPoints->size());
400 
401  ocean_assert(numberLayers >= 1u);
402  ocean_assert(numberLayers <= previousPyramid->layers());
403  ocean_assert(numberLayers <= currentPyramid->layers());
404 
405  ocean_assert(previousPyramid->layer(numberLayers - 1u).width() >= tPatchSize / 2u);
406  ocean_assert(previousPyramid->layer(numberLayers - 1u).height() >= tPatchSize / 2u);
407 
408  ShiftVector<PixelPosition> intermediateRoughPoints(firstPoint, numberPoints);
409 
410  const unsigned int lowestCurrentWidth = currentPyramid->layer(numberLayers - 1u).width();
411  const unsigned int lowestCurrentHeight = currentPyramid->layer(numberLayers - 1u).height();
412  const unsigned int lowestLayerFactor = currentPyramid->sizeFactor(numberLayers - 1u);
413  ocean_assert(lowestCurrentWidth >= 1u && lowestCurrentHeight >= 1u);
414 
415  const unsigned int channels = previousPyramid->frameType().channels();
416  ocean_assert(channels >= 1u && channels <= 4u);
417 
418  for (unsigned int n = firstPoint; n < firstPoint + numberPoints; ++n)
419  {
420  const PixelPosition& roughPoint = (*roughPoints)[n];
421 
422  const unsigned int x = min((roughPoint.x() + lowestLayerFactor / 2u) / lowestLayerFactor, lowestCurrentWidth - 1u);
423  const unsigned int y = min((roughPoint.y() + lowestLayerFactor / 2u) / lowestLayerFactor, lowestCurrentHeight - 1u);
424 
425  intermediateRoughPoints[n] = PixelPosition(x, y);
426  }
427 
428  unsigned int layerRadiusX = coarsestLayerRadiusX;
429  unsigned int layerRadiusY = coarsestLayerRadiusY;
430 
431  for (unsigned int layerIndex = numberLayers - 1u; layerIndex < numberLayers; --layerIndex)
432  {
433  const Frame& previousFrame = (*previousPyramid)[layerIndex];
434  const Frame& currentFrame = (*currentPyramid)[layerIndex];
435 
436  const unsigned int previousWidth = previousFrame.width();
437  const unsigned int previousHeight = previousFrame.height();
438 
439  const unsigned int currentWidth = currentFrame.width();
440  const unsigned int currentHeight = currentFrame.height();
441 
442  for (unsigned int i = firstPoint; i < firstPoint + numberPoints; ++i)
443  {
444  ocean_assert(intermediateRoughPoints[i].x() < currentWidth && intermediateRoughPoints[i].y() < currentHeight);
445 
446  PixelPosition& intermediateRoughPoint = intermediateRoughPoints[i];
447 
448  uint32_t* const metricResult = metricResults ? metricResults + i : nullptr;
449  uint32_t* const metricIdentityResult = (layerIndex == 0u && metricIdentityResults) ? metricIdentityResults + i : nullptr;
450 
451  ocean_assert(layerIndex < 31u);
452  const unsigned int layerFactor = 1u << layerIndex;
453 
454  const PixelPosition previousPosition(min(((*previousPoints)[i].x() + layerFactor / 2u) / layerFactor, previousWidth - 1u), min(((*previousPoints)[i].y() + layerFactor / 2u) / layerFactor, previousHeight - 1u));
455 
456  if (previousPosition.x() < previousWidth && previousPosition.y() < previousHeight)
457  {
458  const PixelPosition position(pointMotionInFrameMirroredBorder<tPatchSize>(previousFrame.constdata<uint8_t>(), currentFrame.constdata<uint8_t>(), channels, previousWidth, previousHeight, currentWidth, currentHeight, previousPosition, layerRadiusX, layerRadiusY, previousFrame.paddingElements(), currentFrame.paddingElements(), intermediateRoughPoint, metricResult, metricIdentityResult));
459 
460  ocean_assert(position.x() < currentWidth && position.y() < currentHeight);
461 
462  // if we are back on the finest layer, than we store the tracked point as final result
463  if (layerIndex == 0u)
464  {
465  ocean_assert(i < currentPoints->size());
466  (*currentPoints)[i] = position;
467  }
468  else
469  {
470  // we store the tracked point as rough intermediate point for the next finer layer
471  const unsigned int higherWidth = currentPyramid->layer(layerIndex - 1u).width();
472  const unsigned int higherHeight = currentPyramid->layer(layerIndex - 1u).height();
473 
474  intermediateRoughPoint = PixelPosition(min(position.x() * 2u, higherWidth - 1u), min(position.y() * 2u, higherHeight - 1u));
475  }
476  }
477  else
478  {
479  // if we are back on the finest layer, than we store the rough point as final result, otherwise we guess the rough intermediate point for the next layer
480  if (layerIndex == 0u)
481  {
482  ocean_assert(i < currentPoints->size());
483  (*currentPoints)[i] = intermediateRoughPoint;
484  }
485  else
486  {
487  intermediateRoughPoint = PixelPosition((intermediateRoughPoint.x() * 2u + layerFactor / 2u) / layerFactor, (intermediateRoughPoint.y() * 2u + layerFactor / 2u) / layerFactor);
488  }
489  }
490  }
491 
492  // all layers expect the coarsest layer will apply a search radius of 2 pixels (a search region of 5x5)
493 
494  layerRadiusX = 2u;
495  layerRadiusY = 2u;
496  }
497 }
498 
499 }
500 
501 }
502 
503 #endif // META_OCEAN_CV_MOTION_H
This class implements a frame pyramid.
Definition: FramePyramid.h:37
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
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
static PixelPosition pointMotionInFrameMirroredBorder(const uint8_t *const frame0, const uint8_t *const frame1, const unsigned int width0, const unsigned int height0, const unsigned int width1, const unsigned int height1, const PixelPosition &position0, const unsigned int radiusX, const unsigned int radiusY, const unsigned int frame0PaddingElements, const unsigned int frame1PaddingElements, const PixelPosition &rough1=PixelPosition(), uint32_t *const metricResult=nullptr, uint32_t *const metricIdentityResult=nullptr)
Determines the motion for one given point between two frames by application of an image patch.
Definition: Motion.h:250
static void trackPointsInPyramidMirroredBorderSubset(const FramePyramid *previousPyramid, const FramePyramid *currentPyramid, const unsigned int numberLayers, const PixelPositions *previousPoints, const PixelPositions *roughPoints, PixelPositions *currentPoints, const unsigned int coarsestLayerRadiusX, const unsigned int coarsestLayerRadiusY, uint32_t *metricResults, uint32_t *metricIdentityResults, const unsigned int firstPoint, const unsigned int numberPoints)
Tracks a subset of given points between two frame pyramids.
Definition: Motion.h:379
static bool trackPointsInPyramidMirroredBorder(const Frame &previousFrame, const Frame &currentFrame, const PixelPositions &previousPoints, const PixelPositions &roughPoints, PixelPositions &currentPoints, const unsigned int maximalOffset, const unsigned int coarsestLayerRadiusX, const unsigned int coarsestLayerRadiusY, const FramePyramid::DownsamplingMode downsamplingMode=FramePyramid::DM_FILTER_14641, Worker *worker=nullptr, std::vector< uint32_t > *metricResults=nullptr, std::vector< uint32_t > *metricIdentityResults=nullptr)
Tracks a set of given points between two frames with pixel accuracy.
Definition: Motion.h:181
bool isValid() const
Returns whether this pixel position object holds two valid parameters.
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
static Caller< void > createStatic(typename StaticFunctionPointerMaker< void, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass >::Type function)
Creates a new caller container for a static function with no function parameter.
Definition: Caller.h:2876
This class implements Ocean's image class.
Definition: Frame.h:1792
const T * constdata(const unsigned int planeIndex=0u) const
Returns a pointer to the read-only pixel data of a specific plane.
Definition: Frame.h:4168
const FrameType & frameType() const
Returns the frame type of this frame.
Definition: Frame.h:3775
unsigned int paddingElements(const unsigned int planeIndex=0u) const
Returns the optional number of padding elements at the end of each row for a specific plane.
Definition: Frame.h:4042
unsigned int width() const
Returns the width of the frame format in pixel.
Definition: Frame.h:3143
PixelOrigin pixelOrigin() const
Returns the pixel origin of the frame.
Definition: Frame.h:3188
PixelFormat pixelFormat() const
Returns the pixel format of the frame.
Definition: Frame.h:3153
unsigned int height() const
Returns the height of the frame in pixel.
Definition: Frame.h:3148
unsigned int channels() const
Returns the number of individual channels the frame has.
Definition: Frame.h:3173
This class implements a vector with shifted elements.
Definition: ShiftVector.h:27
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.
Motion< ZeroMeanSumSquareDifferences > MotionZeroMeanSSD
Definition of a Motion class that applies zero-mean sum square difference calculations as metric.
Definition: Motion.h:49
std::vector< PixelPosition > PixelPositions
Definition of a vector holding pixel positions (with positive coordinate values).
Definition: PixelPosition.h:48
Motion< SumAbsoluteDifferences > MotionSAD
Definition of a Motion class that applies sum absolute difference calculations as metric.
Definition: Motion.h:28
Motion< SumSquareDifferences > MotionSSD
Definition of a Motion class that applies sum square difference calculations as metric.
Definition: Motion.h:42
PixelPositionT< unsigned int > PixelPosition
Definition of the default PixelPosition object with a data type allowing only positive coordinate val...
Definition: PixelPosition.h:27
The namespace covering the entire Ocean framework.
Definition: Accessor.h:15