Ocean
Loading...
Searching...
No Matches
FREAKDescriptor.h
Go to the documentation of this file.
1/*
2 * Portions 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/*
9 * This file contains derived work from
10 *
11 * https://github.com/opencv/opencv_contrib/blob/70b5d209a85b33096312bc6498c4553c1b9112dc/modules/xfeatures2d/src/freak.cpp
12 *
13 * Copyright (C) 2011-2012 Signal processing laboratory 2, EPFL,
14 * Kirell Benzi (kirell.benzi@epfl.ch),
15 * Raphael Ortiz (raphael.ortiz@a3.epfl.ch)
16 * Alexandre Alahi (alexandre.alahi@epfl.ch)
17 * and Pierre Vandergheynst (pierre.vandergheynst@epfl.ch)
18 *
19 * Licensed under the Apache License, Version 2.0 (the "License");
20 * you may not use this file except in compliance with the License.
21 * You may obtain a copy of the License at
22 *
23 * https://github.com/opencv/opencv_contrib/blob/70b5d209a85b33096312bc6498c4553c1b9112dc/LICENSE
24 *
25 * Unless required by applicable law or agreed to in writing, software
26 * distributed under the License is distributed on an "AS IS" BASIS,
27 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
28 * See the License for the specific language governing permissions and
29 * limitations under the License.
30 */
31
32#ifndef META_OCEAN_CV_DETECTOR_FREAK_DESCRIPTOR_H
33#define META_OCEAN_CV_DETECTOR_FREAK_DESCRIPTOR_H
34
39
43
46
48
49#include <Eigen/Core>
50#include <Eigen/Dense>
51
52namespace Ocean
53{
54
55namespace CV
56{
57
58namespace Detector
59{
60
61/// Forward-declaration of the descriptor class.
62template <size_t tSize>
63class FREAKDescriptorT;
64
65/// Typedef for the 32-bytes long FREAK descriptor
67
68/// Vector of 32-bytes long FREAK descriptors
69using FREAKDescriptors32 = std::vector<FREAKDescriptor32>;
70
71/// Typedef for the 64-bytes long FREAK descriptor
73
74/// Vector of 64-bytes long FREAK descriptors
75using FREAKDescriptors64 = std::vector<FREAKDescriptor64>;
76
77/**
78 * Implementation the template-free base class for the FREAK descriptors.
79 * @ingroup cvdetector
80 */
82{
83 public:
84
85 /// Typedef for the selected pixel type. This might be turned into a template parameter at some point.
86 using PixelType = std::uint8_t;
87
88 /// The Jacobian of the projection matrix at a specific 3D location (ray from projection center to pixel in image plane)
89 using PointJacobianMatrix2x3 = Eigen::Matrix<float, 2, 3>;
90
91 /**
92 * The camera data that is required to compute the FREAK descriptor of an image point
93 */
95 {
96 /// The normalized ray that points from projection center to a 2D pixel location in the image plane of camera (this is in inverted-flipped coordinates)
97 Eigen::Vector3f unprojectRayIF;
98
99 /// The 2-by-3 Jacobian matrix of a projection matrix wrt. to the above 2D pixel location in the image plane of a camera (this is in inverted-flipped coordinates)
101 };
102
103 /**
104 * Base class to compute the Jacobian of the camera projection matrix wrt. to a 2D point and the corresponding unprojection ray of an arbitrary camera model
105 */
107 {
108 public:
109
110 /**
111 * Default destructor
112 */
113 virtual ~CameraDerivativeFunctor() = default;
114
115 /**
116 * Purely virtual function to compute the camera derivative data; has to be implemented in any derived class
117 * @param point A 2D point in the image plane of a camera
118 * @param pointPyramidLevel Level of the frame pyramid at which the input point is located, range: [0, supportedPyramidLevels())
119 * @param inverseFocalLength The resulting inverse focal length of the camera associated with the specified pyramid level
120 * @return The 2x3 Jacobian matrix of the projection matrix and the unprojection ray (normalized to length 1)
121 */
122 virtual CameraDerivativeData computeCameraDerivativeData(const Eigen::Vector2f& point, const unsigned int pointPyramidLevel, float& inverseFocalLength) const = 0;
123
124 /**
125 * Returns the maximum number of pyramid levels for which camera derivative data can be computed
126 * @return Number of pyramid levels for which camera derivative data can be computed, range: [1, infinity)
127 */
128 virtual unsigned int supportedPyramidLevels() const = 0;
129 };
130
131 /**
132 * Deprecated.
133 *
134 * Functor that can be used to obtain the 2x3 Jacobian of the camera projection matrix wrt. to a 2D point and the corresponding unprojection ray of a pinhole camera.
135 */
137 {
138 public:
139
140 /**
141 * Constructs a valid functor to compute pinhole camera derivative data
142 * @param pinholeCamera A pinhole camera that is defined at the finest layer of an image pyramid, must be valid
143 * @param pyramidLevels Number of pyramid levels that this functor instance will be prepared for, range: [1, infinity), note: actual supported number may be lower depending on the image resolution
144 */
145 PinholeCameraDerivativeFunctor(const PinholeCamera& pinholeCamera, const unsigned int pyramidLevels = 1u);
146
147 /**
148 * Computes the point Jacobian of the projection matrix and unprojection ray for a specified point
149 * @see CameraDerivativeFunctor::computeCameraDerivativeData().
150 */
151 CameraDerivativeData computeCameraDerivativeData(const Eigen::Vector2f& point, const unsigned int pointPyramidLevel, float& inverseFocalLength) const override;
152
153 /**
154 * Returns the maximum number of pyramid levels for which camera derivative data can be computed
155 * @see CameraDerivativeFunctor::supportedPyramidLevels().
156 */
157 unsigned int supportedPyramidLevels() const override;
158
159 /**
160 * Computes the point Jacobian of the projection matrix and unprojection ray for a specified point
161 * @see CameraDerivativeFunctor::computeCameraDerivativeData().
162 */
163 static CameraDerivativeData computeCameraDerivativeData(const PinholeCamera& pinholeCamera, const Eigen::Vector2f& point);
164
165 protected:
166
167 /// The camera instance used to compute the Jacobian matrix and unprojection ray at the finest layer of an image pyramid
169 };
170
171 /**
172 * Functor that can be used to obtain the 2x3 Jacobian of the camera projection matrix wrt. to a 2D point and the corresponding unprojection ray of a camera
173 */
175 {
176 public:
177
178 /**
179 * Constructs a valid functor to compute pinhole camera derivative data
180 * @param camera A pinhole camera that is defined at the finest layer of an image pyramid, must be valid
181 * @param pyramidLevels Number of pyramid levels that this functor instance will be prepared for, range: [1, infinity), note: actual supported number may be lower depending on the image resolution
182 */
183 AnyCameraDerivativeFunctor(const SharedAnyCamera& camera, const unsigned int pyramidLevels = 1u);
184
185 /**
186 * Computes the point Jacobian of the projection matrix and unprojection ray for a specified point
187 * @see CameraDerivativeFunctor::computeCameraDerivativeData().
188 */
189 CameraDerivativeData computeCameraDerivativeData(const Eigen::Vector2f& point, const unsigned int pointPyramidLevel, float& inverseFocalLength) const override;
190
191 /**
192 * Returns the maximum number of pyramid levels for which camera derivative data can be computed
193 * @see CameraDerivativeFunctor::supportedPyramidLevels().
194 */
195 unsigned int supportedPyramidLevels() const override;
196
197 /**
198 * Computes the point Jacobian of the projection matrix and unprojection ray for a specified point
199 * @see CameraDerivativeFunctor::computeCameraDerivativeData().
200 */
201 static CameraDerivativeData computeCameraDerivativeData(const AnyCamera& camera, const Eigen::Vector2f& point);
202
203 protected:
204
205 /// The camera instance used to compute the Jacobian matrix and unprojection ray at the finest layer of an image pyramid
207
208 /// The inverse focal length of the cameras, one for each pyramid level
209 std::vector<float> inverseFocalLengths_;
210 };
211};
212
213/**
214 * Implementation of the Fast Retina Keypoint descriptors (FREAK).
215 * @tparam tSize The length of the FREAK descriptor in bytes. Set of valid values: {32, 64}
216 * @ingroup cvdetector
217 */
218template <size_t tSize>
220{
221 static_assert(tSize == 32 || tSize == 64, "Invalid size!");
222
223 public:
224
225 /// Single-level FREAK descriptor.
226 using SinglelevelDescriptorData = std::array<PixelType, tSize>;
227
228 /// Multi-level FREAK descriptor data; if possible, this implementation computes the descriptor at three different scales: 1.0, 1.2599, and 1.5874, cf. 'descriptorLevels()'
229 using MultilevelDescriptorData = std::array<SinglelevelDescriptorData, 3>;
230
231 public:
232
233 /**
234 * Creates a new and invalid FREAK descriptor object
235 */
236 FREAKDescriptorT() = default;
237
238 /**
239 * Creates a new FREAK descriptor object by copying from an existing one
240 */
242
243 /**
244 * Creates a new FREAK descriptor object that will be initialized to all zeros
245 * @param data The data of this descriptor, will be moved, must be valid
246 * @param levels The number of valid levels in the descriptor data, range: [1, 3]
247 * @param orientation The orientation of the descriptor in Radian, range: (-pi, pi]
248 */
249 inline FREAKDescriptorT(MultilevelDescriptorData&& data, const unsigned int levels, const float orientation) noexcept;
250
251 /**
252 * Returns the orientation of the descriptor in Radian
253 * @return The orientation of the descriptor in Radian, range: (-pi, pi]
254 */
255 inline float orientation() const;
256
257 /**
258 * Returns the descriptor data (writable)
259 * @return A non-const reference to the descriptor data of this instance
260 */
262
263 /**
264 * Returns the descriptor data
265 * @return A const reference to the descriptor data of this instance
266 */
267 inline const MultilevelDescriptorData& data() const;
268
269 /**
270 * Returns the number of levels stored in the multi-level descriptor
271 * @return The number of valid descriptor levels, range: [0, 3]
272 */
273 inline unsigned int descriptorLevels() const;
274
275 /**
276 * Returns the distance between this descriptor and a second descriptor.
277 * The resulting distance is the minimal distance between all existing level/single descriptors.
278 * @param descriptor The second descriptor, must be valid
279 * @return The distance between both descriptors (the hamming distance), with range [0, tSize * 8]
280 */
281 OCEAN_FORCE_INLINE unsigned int distance(const FREAKDescriptorT<tSize>& descriptor) const;
282
283 /**
284 * Returns true if this is a valid descriptor
285 * @return True if this is a valid descriptor, otherwise false
286 */
287 inline bool isValid() const;
288
289 /**
290 * Returns the length of this descriptor in bytes.
291 * @return The descriptor's length in bytes
292 */
293 static constexpr size_t size();
294
295 /**
296 * Copy assignment operator, needs to be defined since there is a custom copy constructor.
297 * @return Reference to this object
298 */
299 inline FREAKDescriptorT& operator=(const FREAKDescriptorT<tSize>&) noexcept = default;
300
301 /**
302 * Compute a FREAK descriptor for a single image point.
303 * @param framePyramid Frame pyramid in which the location 'point' has been defined, must be valid
304 * @param point Point defined at level 'pointPyramidLevel' in 'framePyramid' for which a descriptor will be computed, must be valid
305 * @param pointPyramidLevel Level of the frame pyramid at which the input point is located, range: [0, framePyramid.layers() - 1)
306 * @param freakDescriptor The FREAK descriptor that will be computed for the input point, will be valid only if this function returns true
307 * @param unprojectRayIF This is the 3D vector that connects the projection center of the camera with image point 'point' in the image plane, must be valid
308 * @param inverseFocalLength The inverse focal length (assumes identical vertical and horizontal focal lengths), defined at pyramid level 'pointPyramidLevel', with range (0, infinity)
309 * @param pointJacobianMatrix2x3 The 2-by-3 Jacobian of the camera projection matrix, cf. 'Geometry::Jacobian::calculatePointJacobian2x3()', must be valid
310 * @return True if the descriptor was successfully computed, otherwise false
311 */
312 static bool computeDescriptor(const FramePyramid& framePyramid, const Eigen::Vector2f& point, const unsigned int pointPyramidLevel, FREAKDescriptorT<tSize>& freakDescriptor, const Eigen::Vector3f& unprojectRayIF, const float inverseFocalLength, const PointJacobianMatrix2x3& pointJacobianMatrix2x3);
313
314 /**
315 * Compute FREAK descriptors for multiple image points.
316 * @param camera The camera profile defining the projection (of the finest pyramid layer), must be valid
317 * @param framePyramid Frame pyramid in which the location 'points' has been defined, must be valid
318 * @param points The 2D image points which are defined at level 'pointsPyramidLevel' in 'framePyramid' for which descriptors will be computed, must be valid
319 * @param pointsSize The number of elements in 'points', range: [0, infinity)
320 * @param pointsPyramidLevel Level of the frame pyramid at which the input points are located, range: [0, framePyramid.layers() - 1)
321 * @param freakDescriptors Pointer to the FREAK descriptors that will be computed for the input points, must be valid and have 'pointsSize' elements. Final descriptors can be invalid, e.g., if they are too close to the image border
322 * @param worker Optional worker instance for parallelization
323 * @tparam TImagePoint The data type of the image points, with defined TImagePoint::x() and TImagePoint::y()
324 */
325 template <typename TImagePoint>
326 static inline void computeDescriptors(const SharedAnyCamera& camera, const FramePyramid& framePyramid, const TImagePoint* points, const size_t pointsSize, const unsigned int pointsPyramidLevel, FREAKDescriptorT<tSize>* freakDescriptors, Worker* worker = nullptr);
327
328 /**
329 * Compute FREAK descriptors for multiple image points.
330 * This function requires a callback function which is used internally to determine the (normalized) ray from the
331 * camera projection center to a 2D image location in the image plane and the corresponding 2-by-3 Jacobian matrix of the projection matrix wrt. to the 2D image location.
332 *
333 * Example usage:
334 *
335 * @code
336 * class YourCameraDerivativeFunctor : public CameraDerivativeFunctor
337 * {
338 * typename FREAKDescriptorT<tSize>::CameraDerivativeData computeCameraDerivativeData(const Eigen::Vector2f& point, const unsigned int pointPyramidLevel, float& inverseFocalLength) override
339 * {
340 * FREAKDescriptorT<tSize>::CameraDerivativeData data;
341 *
342 * inverseFocalLength = ... // Compute inverse focal length for the camera at pointPyramidLevel
343 * data.pointJacobianMatrixIF = ... // Add your computation here
344 * data.unprojectRayIF = ... // Add your computation here
345 *
346 * return data;
347 * }
348 * };
349 *
350 * YourCameraDerivativeFunctor YourCameraDerivativeFunctor;
351 * FREAKDescriptorT<tSize>::computeDescriptors(yFramePyramid, points.data(), points.size(), level, oceanFreakDescriptorsMulticore.data(), yourCameraDerivativeFunctor, &worker);
352 * @endcode
353 *
354 * @param framePyramid Frame pyramid in which the location 'points' has been defined, must be valid
355 * @param points A pointer to the 2D image points which are defined at level 'pointsPyramidLevel' in 'framePyramid' for which descriptors will be computed, must be valid
356 * @param pointsSize The number of elements in 'points', range: [0, infinity)
357 * @param pointsPyramidLevel Level of the frame pyramid at which the input points are located, range: [0, framePyramid.layers() - 1)
358 * @param freakDescriptors Pointer to the FREAK descriptors that will be computed for the input points, must be valid and have 'pointsSize' elements. Final descriptors can be invalid, e.g., if they are too close to the image border
359 * @param cameraDerivativeFunctor A functor that is called for each input point and which must return its corresponding 2x3 Jacobian of the projection matrix and normalized unprojection ray.
360 * @param worker Optional worker instance for parallelization
361 * @tparam TImagePoint The data type of the image points, with defined TImagePoint::x() and TImagePoint::y()
362 */
363 template <typename TImagePoint>
364 static inline void computeDescriptors(const FramePyramid& framePyramid, const TImagePoint* points, const size_t pointsSize, const unsigned int pointsPyramidLevel, FREAKDescriptorT<tSize>* freakDescriptors, const CameraDerivativeFunctor& cameraDerivativeFunctor, Worker* worker = nullptr);
365
366 /**
367 * Extract Harris corners from an image pyramid and compute FREAK descriptors
368 * @param yFrame The 8-bit grayscale image for which Harris corners and FREAK descriptors will be computed, must be valid
369 * @param maxFrameArea This value determines the first layer of the frame pyramid for which corners and descriptors will be computed, range: (minFrameArea, infinity)
370 * @param minFrameArea This value determines the last layer of the frame pyramid for which corners and descriptors will be computed, range: [0, maxFrameArea)
371 * @param expectedHarrisCorners640x480 Expected number of Harris corners if the resolution of the image were 640 x 480 pixels. The actual number of expected corners is scaled to the size of the first layer in the image pyramid that is used for the extraction and then distributed over the range of pyramid layers that is used, range: [1, infinity)
372 * @param harrisCornersReductionScale Scale factor that determines the rate with which the number of corners is reduced as the function climbs through the image pyramid, range: (0, 1)
373 * @param harrisCornerThreshold Threshold value for the Harris corner detector, range: [0, 512]
374 * @param cameraDerivativeFunctor A functor that is called for each input point and which must return its corresponding 2x3 Jacobian of the projection matrix and normalized unprojection ray
375 * @param corners The Harris corners that have been extracted from the frame pyramid, will be initialized by this function, will have the same size as 'cornerPyramidLevels' and 'descriptors'
376 * @param cornerPyramidLevels Will hold for each Harris corner the level index of the pyramid level where it was extracted, will have the same size as 'corners' and 'descriptors'
377 * @param descriptors Will hold the FREAK descriptors of each Harris corner. Descriptors may be invalid. Will have the same size as 'corners' and 'cornerPyramidLevels'
378 * @param removeInvalid If true, all invalid descriptors (and corresponding corners and entries of pyramid levels) will be removed, otherwise all results will remain as-is
379 * @param border Minimum distance in pixels from the image border (same value on all levels of the pyramid) that all Harris corners must have in order to be accepted, otherwise they will be discarded, range: [0, min(yFrame.width(), yFrame.height())/2)
380 * @param determineExactHarrisCornerPositions If true, force the subpixel interpolation to determine the exact position of the extracted Harris corners
381 * @param yFrameIsUndistorted If true the original input frame is undistorted and all extracted 2D feature positions will be marked as undistorted, too
382 * @param worker Optional worker instance for parallelization
383 */
384 static bool extractHarrisCornersAndComputeDescriptors(const Frame& yFrame, const unsigned int maxFrameArea, const unsigned int minFrameArea, const unsigned int expectedHarrisCorners640x480, const Scalar harrisCornersReductionScale, const unsigned int harrisCornerThreshold, const CameraDerivativeFunctor& cameraDerivativeFunctor, HarrisCorners& corners, Indices32& cornerPyramidLevels, std::vector<FREAKDescriptorT<tSize>>& descriptors, const bool removeInvalid = false, const Scalar border = Scalar(20), const bool determineExactHarrisCornerPositions = false, const bool yFrameIsUndistorted = true, Worker* worker = nullptr);
385
386 protected:
387
388 /**
389 * Compute FREAK descriptors for a subset of points
390 * @param framePyramid Frame pyramid in which the location 'points' has been defined, must be valid
391 * @param points A pointer to the 2D image points which are defined at level 'pointPyramidLevel' in 'framePyramid' for which descriptors will be computed, must be valid
392 * @param pointsSize The number of elements in 'points', range: [1, infinity)
393 * @param pointPyramidLevel Level of the frame pyramid at which the input points are located, range: [0, framePyramid.layers() - 1)
394 * @param freakDescriptors Pointer to the FREAK descriptors that will be computed for the input points, must be valid and have 'pointsSize' elements. Final descriptors can be invalid, e.g., if they are too close to the image border
395 * @param cameraDerivativeFunctor A callback function that is called for each input point and which must return its corresponding 2x3 Jacobian of the projection matrix and normalized unprojection ray
396 * @param firstPoint The index of the first point that will be processed by this function, range: [0, pointsSize)
397 * @param numberOfPoints Number of points that should be processed in this function starting at 'firstIndex', range: [1, pointsSize - firstIndex]
398 * @tparam TImagePoint The data type of the image points, with defined TImagePoint::x() and TImagePoint::y()
399 */
400 template <typename TImagePoint>
401 static void computeDescriptorsSubset(const FramePyramid* framePyramid, const TImagePoint* points, const size_t pointsSize, const unsigned int pointPyramidLevel, FREAKDescriptorT<tSize>* freakDescriptors, const CameraDerivativeFunctor* cameraDerivativeFunctor, const unsigned int firstPoint, const unsigned int numberOfPoints);
402
403 /**
404 * Computes the transformation to deform receptive fields and the orientation of the descriptor
405 * @param framePyramid Frame pyramid in which the location 'point' has been defined, must be valid
406 * @param point The point defined at level 'pointPyramidLevel' in 'framePyramid' for which a descriptor will be computed, must be valid
407 * @param pointPyramidLevel Level of the frame pyramid at which the input point is located, range: [0, framePyramid.layers() - 1)
408 * @param unprojectRayIF This is the 3D vector that connects the projection center of the camera with image point 'point' in the image plane, must be valid and inside the image
409 * @param inverseFocalLength The inverse focal length (assumes identical vertical and horizontal focal lengths), defined at pyramid level 'pointPyramidLevel', with range (0, infinity)
410 * @param pointJacobianMatrix2x3 The 2-by-3 Jacobian of the camera projection matrix, cf. 'Geometry::Jacobian::calculatePointJacobian2x3()', must be valid
411 * @param deformationMatrix The deformation transformation is a 2-by-2 matrix that is computed from the Jacobian of the camera projection matrix, the unprojection ray and the (inverse of the) focal length
412 * @param orientation The orientation of this descriptor in radian, range: [-pi, pi]
413 * @return True if this is a valid descriptor, otherwise false
414 */
415 static bool computeLocalDeformationMatrixAndOrientation(const FramePyramid& framePyramid, const Eigen::Vector2f& point, const unsigned int pointPyramidLevel, const Eigen::Vector3f& unprojectRayIF, const float inverseFocalLength, const PointJacobianMatrix2x3& pointJacobianMatrix2x3, Eigen::Matrix<float, 2, 2>& deformationMatrix, float& orientation);
416
417 /**
418 * Computes the average intensity of a cell
419 * @param framePyramidLayer Layer of a frame pyramid in which the location '(x, y)' has been defined, must be valid
420 * @param x The pixel-accurate horizontal coordinate of the cell, range: [0 + u, width - u), u = kernel_radius / 2 if 'tEnableBorderChecks == true', otherwise u = kernel_radius
421 * @param y The pixel-accurate vertical coordinate of the cell, range: [0 + v, height - v), v = kernel_radius / 2 if 'tEnableBorderChecks == true', otherwise v = kernel_radius
422 * @param kernelX Pointer to the horizontal offsets of the kernel elements (relative to 'x'), must be valid and have 'kernelElements' elements
423 * @param kernelY Pointer to the vertical offsets of the kernel elements (relative to 'y'), must be valid and have 'kernelElements' elements
424 * @param kernelElements Number of elements in the kernel, range: [1, infinity]
425 * @param averageIntensity The average intensity of the selected cell
426 * @tparam tEnableBorderChecks If true, only kernel values inside the input image will be added, otherwise the check will be disabled and the entire kernel is assumed to fit inside the image (for performance)
427 * @return True if the average intensity was successfully computed, otherwise false (e.g. location (x, y) too close to the image border)
428 */
429 template <bool tEnableBorderChecks>
430 static bool computeAverageCellIntensity(const Frame& framePyramidLayer, int x, int y, const int* kernelX, const int* kernelY, const size_t kernelElements, PixelType& averageIntensity);
431
432 /**
433 * Creates a new pyramid frame for a specific pixel format (a specific number of channels) and applies a Gaussian blur before each down-size step.
434 * @param frame The frame pyramid will be built using this frame, must be valid and use 8 bits per channel
435 * @param kernelWidth Width of the Gaussian kernel that is applied before a down-size step, range: [1, infinity), value must be odd
436 * @param kernelHeight Height of the Gaussian kernel that is applied before a down-size step, range: [1, infinity), value must be odd
437 * @param layers The number of pyramid layers to be created, with range [1, infinity)
438 * @param worker Optional worker object to distribute the computation
439 * @return The created frame pyramid (will be invalid in case of a failure)
440 */
441 static FramePyramid createFramePyramidWithBlur8BitsPerChannel(const Frame& frame, const unsigned int kernelWidth, const unsigned int kernelHeight, const unsigned int layers, Worker* worker = nullptr);
442
443 /**
444 * Downsamples a frame by two applying a 1-1 filter after applying a Gaussian blur to the source layer.
445 * @param finerLayer The finer pyramid layer, must be valid
446 * @param coarserLayer The coarser pyramid layer, must be valid
447 * @param worker The optional worker to distribute the computation
448 * @param kernelWidth The width of the Gaussian kernel, in pixel, with range [1, infinity), must odd
449 * @param kernelHeight The height of the Gaussian kernel, in pixel, with range [1, infinity), must odd
450 * @param reusableFrame A reusable frame which can be used internally
451 * @return True, if succeeded
452 */
453 static bool blurAndDownsampleByTwo11(const Frame& finerLayer, Frame& coarserLayer, Worker* worker, const unsigned int kernelWidth, const unsigned int kernelHeight, Frame& reusableFrame);
454
455 private:
456
457 /// The number of cells per keypoint that this implementation is using
458 static constexpr size_t numberOfCells = 43;
459
460 /// The pre-defined horizontal coordinates of the cells
461 static const float cellsX[numberOfCells];
462
463 /// The pre-defined vertical coordinates of the cells
464 static const float cellsY[numberOfCells];
465
466 /// The number of pre-defined pairs of cell indices that are used to compute the actual binary descriptor
467 static constexpr size_t numberOfCellPairs = 512;
468
469 /// The pre-defined pairs of cell indices that uare used to compute the actual binary descriptor (pairs have been randomly shuffled)
470 static const std::uint8_t cellPairs[numberOfCellPairs][2];
471
472 /// Number of elements in the circular kernel with radius 1
473 static constexpr size_t kernelRadius1Elements = 5;
474
475 /// The pre-defined horizontal coordinates of the circular kernel with radius 1
477
478 /// The pre-defined vertical coordinates of the circular kernel with radius 1
480
481 /// Number of elements in the circular kernel with radius 2
482 static constexpr size_t kernelRadius2Elements = 13;
483
484 /// The pre-defined horizontal coordinates of the circular kernel with radius 2
486
487 /// The pre-defined vertical coordinates of the circular kernel with radius 2
489
490 /// Number of elements in the circular kernel with radius 3
491 static constexpr size_t kernelRadius3Elements = 29;
492
493 /// The pre-defined horizontal coordinates of the circular kernel with radius 3
495
496 /// The pre-defined vertical coordinates of the circular kernel with radius 3
498
499 /// Number of elements in the circular kernel with radius 7
500 static constexpr size_t kernelRadius7Elements = 149;
501
502 /// The pre-defined horizontal coordinates of the circular kernel with radius 7
504
505 /// The pre-defined vertical coordinates of the circular kernel with radius 7
507
508 protected:
509
510 /// The orientation of this descriptor in radian, range: [-pi, pi]
511 float orientation_ = 0.0f;
512
513 /// The actual FREAK descriptor data
515
516 /// Number of valid levels in the multi-level descriptor data above, range: [0, 3]
517 unsigned int dataLevels_ = 0u;
518};
519
520template <size_t tSize>
521FREAKDescriptorT<tSize>::FREAKDescriptorT(MultilevelDescriptorData&& data, const unsigned int levels, const float orientation) noexcept :
522 orientation_(orientation),
523 data_(std::move(data)),
524 dataLevels_(levels)
525{
526 ocean_assert(levels >= 1u && levels <= 3u);
527 ocean_assert(NumericF::isInsideRange(-NumericF::pi(), orientation, NumericF::pi()));
528}
529
530template <size_t tSize>
532{
533 ocean_assert(NumericF::isInsideRange(-NumericF::pi(), orientation_, NumericF::pi()));
534 return orientation_;
535}
536
537template <size_t tSize>
542
543template <size_t tSize>
545{
546 return data_;
547}
548
549template <size_t tSize>
551{
552 ocean_assert(dataLevels_ <= 3u);
553 return dataLevels_;
554}
555
556template <size_t tSize>
557OCEAN_FORCE_INLINE unsigned int FREAKDescriptorT<tSize>::distance(const FREAKDescriptorT<tSize>& descriptor) const
558{
559 ocean_assert(isValid() && descriptor.isValid());
560
561 unsigned int bestDistance = (unsigned int)(-1);
562
563 for (unsigned int nOuter = 0u; nOuter < dataLevels_; ++nOuter)
564 {
565 const SinglelevelDescriptorData& outerData = data_[nOuter];
566
567 for (unsigned int nInner = 0u; nInner < descriptor.dataLevels_; ++nInner)
568 {
569 const SinglelevelDescriptorData& innerData = descriptor.data_[nInner];
570
571 const unsigned int distance = Descriptor::calculateHammingDistance<tSize * 8u>(outerData.data(), innerData.data());
572
573 if (distance < bestDistance)
574 {
575 bestDistance = distance;
576 }
577 }
578 }
579
580 ocean_assert(bestDistance != (unsigned int)(-1));
581
582 return bestDistance;
583}
584
585template <size_t tSize>
587{
588 return descriptorLevels() >= 1u && descriptorLevels() <= 3u && NumericF::isInsideRange(-NumericF::pi(), orientation_, NumericF::pi());
589}
590
591template <size_t tSize>
593{
594 return tSize;
595}
596
597template <size_t tSize>
598template <typename TImagePoint>
599inline void FREAKDescriptorT<tSize>::computeDescriptors(const SharedAnyCamera& camera, const FramePyramid& framePyramid, const TImagePoint* points, const size_t pointsSize, const unsigned int pointsPyramidLevel, FREAKDescriptorT<tSize>* freakDescriptors, Worker* worker)
600{
601 ocean_assert(camera && camera->isValid());
602 ocean_assert(camera->width() == framePyramid.finestWidth() && camera->height() == framePyramid.finestHeight());
603
604 const AnyCameraDerivativeFunctor cameraDerivativeFunctor(camera, framePyramid.layers());
605
606 return computeDescriptors<TImagePoint>(framePyramid, points, pointsSize, pointsPyramidLevel, freakDescriptors, cameraDerivativeFunctor, worker);
607}
608
609template <size_t tSize>
610template <typename TImagePoint>
611inline void FREAKDescriptorT<tSize>::computeDescriptors(const FramePyramid& framePyramid, const TImagePoint* points, const size_t pointsSize, const unsigned int pointsPyramidLevel, FREAKDescriptorT<tSize>* freakDescriptors, const CameraDerivativeFunctor& projectionDerivativeDataCallback, Worker* worker)
612{
613 ocean_assert(framePyramid.isValid());
614 ocean_assert(points != nullptr && pointsSize != 0u);
615 ocean_assert(pointsPyramidLevel < framePyramid.layers());
616 ocean_assert(freakDescriptors != nullptr);
617
618 if (worker)
619 {
620 worker->executeFunction(Worker::Function::createStatic(&FREAKDescriptorT<tSize>::computeDescriptorsSubset<TImagePoint>, &framePyramid, points, pointsSize, pointsPyramidLevel, freakDescriptors, &projectionDerivativeDataCallback, 0u, 0u), 0u, (unsigned int)(pointsSize));
621 }
622 else
623 {
624 FREAKDescriptorT<tSize>::computeDescriptorsSubset<TImagePoint>(&framePyramid, points, pointsSize, pointsPyramidLevel, freakDescriptors, &projectionDerivativeDataCallback, 0u, (unsigned int)(pointsSize));
625 }
626}
627
628template <size_t tSize>
629const float FREAKDescriptorT<tSize>::cellsX[numberOfCells] =
630{
631 // clang-format off
632 0.0f, -14.7216f, -14.7216f, 0.0f, 14.7216f, 14.7216f, -6.3745f, -12.749f, -6.3745f, 6.3745f,
633 12.749f, 6.3745f, 0.0f, -7.97392f, -7.97392f, 0.0f, 7.97392f, 7.97392f, -3.18725f, -6.3745f,
634 -3.18725f, 3.18725f, 6.3745f, 3.18725f, 0.0f, -3.67983f, -3.67983f, 0.0f, 3.67983f, 3.67983f,
635 -1.4163f, -2.8326f, -1.4163f, 1.4163f, 2.8326f, 1.4163f, 0.0f, -1.84049f, -1.84049f, 0.0f,
636 1.84049f, 1.84049f, 0.0f
637 // clang-format on
638};
639
640template <size_t tSize>
641const float FREAKDescriptorT<tSize>::cellsY[numberOfCells] =
642{
643 // clang-format off
644 16.9991f, 8.49895f, -8.49895f, -16.9991f, -8.49895f, 8.49895f, 11.0406f, 0.0f, -11.0406f, -11.0406f,
645 0.0f, 11.0406f, 9.2071f, 4.60355f, -4.60355f, -9.2071f, -4.60355f, 4.60355f, 5.52032f, 0.0f,
646 -5.52032f, -5.52032f, 0.0f, 5.52032f, 4.25005f, 2.12445f, -2.12445f, -4.25005f, -2.12445f, 2.12445f,
647 2.4536f, 0.0f, -2.4536f, -2.4536f, 0.0f, 2.4536f, 2.12445f, 1.0628f, -1.0628f, -2.12445f,
648 -1.0628f, 1.0628f, 0.0f
649 // clang-format on
650};
651
652template <size_t tSize>
653const uint8_t FREAKDescriptorT<tSize>::cellPairs[numberOfCellPairs][2] =
654{
655 // clang-format off
656 {37, 4}, {38, 4}, {12, 0}, {39,10}, {27, 7}, {37,29}, {20,16}, {33,16}, {14, 0}, {31, 3},
657 {17, 4}, {24,12}, {33,22}, {31, 7}, {35,30}, {25, 6}, {34,31}, {20,19}, {22,17}, {16, 6},
658 {23, 5}, {26,10}, {13, 5}, {31,17}, {17,10}, {31,28}, {22, 4}, {29,11}, {28, 2}, {29,19},
659 {30, 6}, {37,10}, {31, 2}, {41,13}, {14, 7}, {15, 3}, {33, 4}, {18,17}, {23,19}, {33,28},
660 {41,24}, {34,16}, { 7, 1}, {26, 5}, {36,13}, {42, 9}, {20,14}, {27,26}, {41, 6}, {40,19},
661 {26, 3}, {36,29}, {23,13}, {40, 7}, {18, 0}, {28,22}, {22, 9}, {26,16}, {21,16}, {39,20},
662 { 8, 3}, {14, 1}, {12,11}, {31,25}, {29, 4}, {15, 1}, {41,22}, {35, 1}, {26, 2}, {34,14},
663 {25, 1}, {34,17}, {34,29}, {16,14}, {19, 3}, {26,14}, {15, 5}, {25,17}, {25, 5}, {34,25},
664 { 6, 0}, {23,10}, {29,24}, {28,16}, {20, 3}, { 7, 4}, {25,11}, {36,24}, {27, 9}, {11,10},
665 {23, 7}, {32,19}, {32,16}, {37,18}, {25,24}, {19, 1}, {22,20}, {38,14}, {41,31}, {16,10},
666 {19, 6}, {16,11}, {31,20}, { 8, 0}, {14, 2}, {19, 0}, {37,13}, {34, 4}, {31,14}, { 6, 1},
667 {40, 1}, {24,18}, {41, 1}, {41, 7}, {36,23}, {40,20}, {40,27}, {13, 0}, {19,12}, {42,38},
668 {16, 7}, {34, 7}, { 9, 2}, {28, 4}, {11, 5}, {40,38}, {17, 2}, { 5, 0}, {19,14}, {12, 6},
669 {19,17}, {40,22}, {26, 7}, {19, 5}, {19,11}, {28,26}, {12, 1}, {34, 0}, { 5, 1}, {27,16},
670 {21,15}, {29,25}, {19, 8}, {32,26}, {37,17}, {11, 6}, {22, 6}, {39,27}, {41,37}, {21, 5},
671 {14,11}, {31,16}, {38,28}, {16, 0}, {29,10}, {31,26}, {10, 1}, {22,13}, {10, 3}, {17, 3},
672 {42,30}, { 8, 4}, {26, 6}, {22, 8}, {38,27}, {26,22}, {41,10}, {42,13}, {40,34}, {13, 7},
673 {30,11}, {38,22}, {33,27}, {19,15}, {29, 7}, {31,10}, {26,15}, {13,12}, {29, 2}, { 5, 3},
674 {15, 7}, {28,10}, {29,17}, {40,10}, {21, 1}, {15,10}, {37,11}, {40,13}, {26, 1}, {39,21},
675 {34,21}, {40,31}, {19, 7}, {16, 5}, {40,39}, {37, 7}, {30,23}, {10, 9}, {36,30}, {38, 0},
676 {18, 6}, {40,32}, {38,10}, {22, 3}, {26,19}, {18,13}, {39,22}, {35,17}, {31,19}, {18,11},
677 {28,19}, {28, 0}, {37,31}, {30, 7}, {27,20}, {34,10}, {38, 3}, {37,23}, {18, 7}, {38,20},
678 {25,19}, {20, 7}, {22,18}, { 7, 3}, {15, 2}, {23,12}, {26,13}, {38, 7}, {11, 1}, {20, 8},
679 {33,21}, {37,36}, {17,16}, {36,35}, {41, 2}, {37,35}, {37, 2}, {15,14}, {10, 7}, {41,29},
680 { 7, 6}, {32,22}, {34,26}, {33, 2}, {38,26}, {31, 0}, {11, 3}, {24,23}, {13,11}, {41,19},
681 {41,25}, {30,13}, {27,10}, {39,38}, {21, 3}, {31, 4}, {27,14}, {37,24}, {20, 2}, {25,23},
682 {29, 1}, {39,28}, {17, 0}, { 7, 0}, { 9, 5}, {22, 2}, {33,32}, {27,21}, {30,25}, {41,23},
683 {41,30}, {15, 9}, {22,10}, {31,22}, {29, 5}, {34,20}, {24,13}, {31,11}, {36,25}, {21,19},
684 {19,13}, {30,29}, {33, 5}, { 6, 4}, { 5, 2}, { 8, 2}, {10, 2}, {25,13}, {37,19}, {28,14},
685 {15, 4}, {10, 8}, {12, 5}, {14,13}, {24, 1}, {31,12}, {14,10}, {32,27}, {19,18}, {32, 4},
686 {22, 1}, {39,26}, {17,14}, { 2, 1}, { 1, 0}, {35,23}, {34, 2}, {33,19}, {13, 3}, {39,16},
687 {25, 2}, {41, 4}, {28, 7}, {31,21}, {26, 4}, {39,19}, {24,17}, {28,20}, {21, 8}, {25, 7},
688 {34,15}, {41,36}, {16, 3}, {21,20}, {31,15}, {26,20}, {14, 5}, {38,16}, {40, 2}, {18,10},
689 {27, 8}, {29,13}, {41,18}, {18,12}, {40,26}, {36, 0}, {21,14}, {22, 0}, {27, 2}, {11, 0},
690 {21,10}, {20,10}, {23, 6}, {13, 4}, {28,21}, {22,16}, {25,22}, {35,24}, { 4, 0}, {31, 1},
691 {32,21}, {21, 4}, {37, 6}, {15, 8}, { 8, 7}, {29,22}, {28,15}, {25,18}, {41,35}, {39,14},
692 {34,12}, {23,17}, {25,10}, {39, 9}, {34,13}, {22,14}, { 7, 2}, {20, 9}, {28,11}, {10, 4},
693 {40, 0}, {35,13}, {38,32}, {13, 2}, {39, 1}, { 2, 0}, {38,19}, {41,11}, {32,28}, {39,33},
694 {30,17}, {16, 2}, {17, 6}, {13,10}, { 4, 1}, {10, 0}, {22,19}, { 4, 3}, {12, 7}, {26,21},
695 { 9, 0}, {19,16}, {34,28}, {16, 9}, { 9, 8}, {23, 0}, { 7, 5}, {10, 5}, {34,18}, {14, 6},
696 {30, 5}, {31,18}, {20,15}, {34,22}, {35,12}, {23, 1}, {35,10}, { 9, 3}, {27,15}, {17,13},
697 {37,30}, {26, 0}, {28,17}, {38,33}, {38, 5}, {16, 4}, {13, 1}, {28, 3}, { 5, 4}, {12, 2},
698 {17, 9}, {31,29}, {22,11}, {40,17}, {25, 4}, {28,27}, {29, 6}, {34, 1}, {14, 8}, {32,15},
699 {39,32}, { 6, 5}, {19, 4}, {18, 5}, {32,20}, {38,13}, {12,10}, {24, 0}, {22,15}, {36,18},
700 { 6, 3}, {34,23}, {33,15}, {22, 7}, {22,12}, {40,28}, {35,18}, {22, 5}, {29,23}, {37,34},
701 {16,13}, {23,18}, {37,22}, {29,12}, {19, 2}, {14, 9}, {34,19}, {19,10}, {25,12}, {38,21},
702 {28, 1}, {33,20}, {27, 4}, {11, 7}, {31,23}, {17, 7}, {17, 8}, {39, 8}, {40,21}, {16,15},
703 {17, 5}, {30,18}, {39, 7}, {37,25}, {41,34}, {30,24}, {18, 1}, { 3, 1}, { 9, 4}, {22,21},
704 {31, 5}, {40, 3}, {35,25}, {32, 2}, { 4, 2}, {38,31}, {14, 3}, {21, 9}, {17,12}, {16, 1},
705 {35,29}, {23,22}, {20, 1}, {34, 3}, {17, 1}, {13, 6}, {40,14}, {17,11}, {38,17}, {40,16},
706 {20, 4}, {23,11}, {12, 4}, { 3, 2}, {40,33}, {14, 4}, {21, 2}, {33,26}, {38,34}, {29,18},
707 {21, 7}, {16, 8}
708 // clang-format on
709};
710
711template <size_t tSize>
712const int FREAKDescriptorT<tSize>::kernelRadius1X[kernelRadius1Elements] = { 0, -1, 0, 1, 0 };
713
714template <size_t tSize>
715const int FREAKDescriptorT<tSize>::kernelRadius1Y[kernelRadius1Elements] = { -1, 0, 0, 0, 1 };
716
717template <size_t tSize>
718const int FREAKDescriptorT<tSize>::kernelRadius2X[kernelRadius2Elements] = { 0, -1, 0, 1, -2, -1, 0, 1, 2, -1, 0, 1, 0 };
719
720template <size_t tSize>
721const int FREAKDescriptorT<tSize>::kernelRadius2Y[kernelRadius2Elements] = { -2, -1, -1, -1, 0, 0, 0, 0, 0, 1, 1, 1, 2 };
722
723template <size_t tSize>
724const int FREAKDescriptorT<tSize>::kernelRadius3X[kernelRadius3Elements] =
725{
726 // clang-format off
727 0, -2, -1, 0, 1, 2, -2, -1, 0, 1,
728 2, -3, -2, -1, 0, 1, 2, 3, -2, -1,
729 0, 1, 2, -2, -1, 0, 1, 2, 0
730 // clang-format on
731};
732
733template <size_t tSize>
734const int FREAKDescriptorT<tSize>::kernelRadius3Y[kernelRadius3Elements] =
735{
736 // clang-format off
737 -3, -2, -2, -2, -2, -2, -1, -1, -1, -1,
738 -1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
739 1, 1, 1, 2, 2, 2, 2, 2, 3
740 // clang-format on
741};
742
743template <size_t tSize>
744const int FREAKDescriptorT<tSize>::kernelRadius7X[kernelRadius7Elements] =
745{
746 // clang-format off
747 0, -3, -2, -1, 0, 1, 2, 3, -4, -3,
748 -2, -1, 0, 1, 2, 3, 4, -5, -4, -3,
749 -2, -1, 0, 1, 2, 3, 4, 5, -6, -5,
750 -4, -3, -2, -1, 0, 1, 2, 3, 4, 5,
751 6, -6, -5, -4, -3, -2, -1, 0, 1, 2,
752 3, 4, 5, 6, -6, -5, -4, -3, -2, -1,
753 0, 1, 2, 3, 4, 5, 6, -7, -6, -5,
754 -4, -3, -2, -1, 0, 1, 2, 3, 4, 5,
755 6, 7, -6, -5, -4, -3, -2, -1, 0, 1,
756 2, 3, 4, 5, 6, -6, -5, -4, -3, -2,
757 -1, 0, 1, 2, 3, 4, 5, 6, -6, -5,
758 -4, -3, -2, -1, 0, 1, 2, 3, 4, 5,
759 6, -5, -4, -3, -2, -1, 0, 1, 2, 3,
760 4, 5, -4, -3, -2, -1, 0, 1, 2, 3,
761 4, -3, -2, -1, 0, 1, 2, 3, 0
762 // clang-format on
763};
764
765template <size_t tSize>
766const int FREAKDescriptorT<tSize>::kernelRadius7Y[kernelRadius7Elements] =
767{
768 // clang-format off
769 -7, -6, -6, -6, -6, -6, -6, -6, -5, -5,
770 -5, -5, -5, -5, -5, -5, -5, -4, -4, -4,
771 -4, -4, -4, -4, -4, -4, -4, -4, -3, -3,
772 -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
773 -3, -2, -2, -2, -2, -2, -2, -2, -2, -2,
774 -2, -2, -2, -2, -1, -1, -1, -1, -1, -1,
775 -1, -1, -1, -1, -1, -1, -1, 0, 0, 0,
776 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
777 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
778 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
779 2, 2, 2, 2, 2, 2, 2, 2, 3, 3,
780 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
781 3, 4, 4, 4, 4, 4, 4, 4, 4, 4,
782 4, 4, 5, 5, 5, 5, 5, 5, 5, 5,
783 5, 6, 6, 6, 6, 6, 6, 6, 7
784 // clang-format on
785};
786
787template <size_t tSize>
788bool FREAKDescriptorT<tSize>::computeDescriptor(const FramePyramid& pyramid, const Eigen::Vector2f& point, const unsigned int pointPyramidLevel, FREAKDescriptorT<tSize>& freakDescriptor, const Eigen::Vector3f& unprojectRayIF, const float inverseFocalLength, const PointJacobianMatrix2x3& pointJacobianMatrixIF)
789{
790 ocean_assert(pointPyramidLevel < pyramid.layers());
791 ocean_assert(inverseFocalLength > 0.0f);
792
793 // No descriptors can be computed for points in the coarsest layer of the frame pyramid
794
795 if (pointPyramidLevel + 1u >= pyramid.layers())
796 {
797 return false;
798 }
799
800 // Invalidate the descriptor for now
801
802 freakDescriptor.dataLevels_ = 0u;
803 ocean_assert(freakDescriptor.isValid() == false);
804
805 // Compute the deformation matrix from the position of the image point (and its Jacobian, etc)
806
807 Eigen::Matrix<float, 2, 2> cellDeformationMatrix;
808 if (computeLocalDeformationMatrixAndOrientation(pyramid, point, pointPyramidLevel, unprojectRayIF, inverseFocalLength, pointJacobianMatrixIF, cellDeformationMatrix, freakDescriptor.orientation_) == false)
809 {
810 return false;
811 }
812
813 // Apply the deformation matrix to the locations of all cells
814
817
818 for (size_t i = 0; i < FREAKDescriptorT<tSize>::numberOfCells; ++i)
819 {
820 const Eigen::Vector2f warpedCell = cellDeformationMatrix * Eigen::Vector2f(FREAKDescriptorT<tSize>::cellsX[i], FREAKDescriptorT<tSize>::cellsY[i]);
821 warpedCellX[i] = warpedCell[0];
822 warpedCellY[i] = warpedCell[1];
823 }
824
825 // Compute a descriptor for each intra-level:
826 //
827 // 2^(0/3) = 1,
828 // 2^(1/3) = 1.2599,
829 // 2^(2/3) = 1.5874
830 //
831 const float scaleFactors[3] = { 1.0f, 1.2599f, 1.5874f };
832 for (size_t scaleLevel = 0; scaleLevel < 3; ++scaleLevel)
833 {
835 bool computationFailed = false;
836
837 // Compute the average intensity per cell
838 //
839 // In order to reduce the number of if-branches, we'll split the range of cell IDs such that they are partitioned into groups with identical conditions, cf. table below:
840 //
841 // Cell ID: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
842 // Pyramid evel offsets: 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
843 // Radii: 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
844 // Check pixel in image: T, T, T, T, T, T, T, T, T, T, T, T, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F
845 // for-loop ID: |--- 0 ------------------------->| |--- 1 -------------------------->| |--- 2 -------->| |--- 3 ----------------------------->|
846
847 unsigned int cellId = 0u;
848
849 // Loop 0
850 for (; cellId < 12u; ++cellId)
851 {
852 ocean_assert(pointPyramidLevel + 1u < pyramid.layers());
853 const Frame& nextFramePyramidLayer = pyramid.layer(pointPyramidLevel + 1u);
854
855 const float cellX = point[0] + scaleFactors[scaleLevel] * warpedCellX[cellId];
856 const float cellY = point[1] + scaleFactors[scaleLevel] * warpedCellY[cellId];
857
858 const int cellXi = NumericF::round32(((cellX + 0.5f) * 0.5f) - 0.5f);
859 const int cellYi = NumericF::round32(((cellY + 0.5f) * 0.5f) - 0.5f);
860
861 // Check if the (half-)radius fits into the image, radius = 3, radius / 2 = 1
862 if (cellXi - 1 < 0 || cellXi + 1 >= int(nextFramePyramidLayer.width()) || cellYi - 1 < 0 || cellYi + 1 >= int(nextFramePyramidLayer.height()))
863 {
864 computationFailed = true;
865 break;
866 }
867
868 ocean_assert(cellXi >= 0 && cellXi < int(nextFramePyramidLayer.width()) && cellYi >= 0 && cellYi < int(nextFramePyramidLayer.height()));
869
870 if (computeAverageCellIntensity<true>(nextFramePyramidLayer, cellXi, cellYi, kernelRadius3X, kernelRadius3Y, kernelRadius3Elements, cellIntensities[cellId]) == false)
871 {
872 computationFailed = true;
873 break;
874 }
875 }
876
877 // Loop 1
878 const Frame& currentFramePyramidLayer = pyramid.layer(pointPyramidLevel);
879
880 ocean_assert(cellId == 12u || computationFailed == true);
881 for (; computationFailed == false && cellId < 24u; ++cellId)
882 {
883 const float cellX = point[0] + scaleFactors[scaleLevel] * warpedCellX[cellId];
884 const float cellY = point[1] + scaleFactors[scaleLevel] * warpedCellY[cellId];
885
886 const int cellXi = NumericF::round32(cellX);
887 const int cellYi = NumericF::round32(cellY);
888
889 ocean_assert(cellXi >= 0 && cellXi < int(currentFramePyramidLayer.width()) && cellYi >= 0 && cellYi < int(currentFramePyramidLayer.height()));
890
891 if (computeAverageCellIntensity<false>(currentFramePyramidLayer, cellXi, cellYi, kernelRadius3X, kernelRadius3Y, kernelRadius3Elements, cellIntensities[cellId]) == false)
892 {
893 computationFailed = true;
894 break;
895 }
896 }
897
898 // Loop 2
899 ocean_assert(cellId == 24u || computationFailed == true);
900 for (; computationFailed == false && cellId < 30u; ++cellId)
901 {
902 const float cellX = point[0] + scaleFactors[scaleLevel] * warpedCellX[cellId];
903 const float cellY = point[1] + scaleFactors[scaleLevel] * warpedCellY[cellId];
904
905 const int cellXi = NumericF::round32(cellX);
906 const int cellYi = NumericF::round32(cellY);
907
908 ocean_assert(cellXi >= 0 && cellXi < int(currentFramePyramidLayer.width()) && cellYi >= 0 && cellYi < int(currentFramePyramidLayer.height()));
909 if (computeAverageCellIntensity<false>(currentFramePyramidLayer, cellXi, cellYi, kernelRadius2X, kernelRadius2Y, kernelRadius2Elements, cellIntensities[cellId]) == false)
910 {
911 computationFailed = true;
912 break;
913 }
914 }
915
916 // Loop 3
917 ocean_assert(cellId == 30u || computationFailed == true);
918 for (; computationFailed == false && cellId < FREAKDescriptorT<tSize>::numberOfCells; ++cellId)
919 {
920 const float cellX = point[0] + scaleFactors[scaleLevel] * warpedCellX[cellId];
921 const float cellY = point[1] + scaleFactors[scaleLevel] * warpedCellY[cellId];
922
923 const int cellXi = NumericF::round32(cellX);
924 const int cellYi = NumericF::round32(cellY);
925
926 ocean_assert(cellXi >= 0 && cellXi < int(currentFramePyramidLayer.width()) && cellYi >= 0 && cellYi < int(currentFramePyramidLayer.height()));
927
928 if (computeAverageCellIntensity<false>(currentFramePyramidLayer, cellXi, cellYi, kernelRadius1X, kernelRadius1Y, kernelRadius1Elements, cellIntensities[cellId]) == false)
929 {
930 computationFailed = true;
931 break;
932 }
933 }
934
935 if (computationFailed)
936 {
937 break;
938 }
939
940 // Compute the binary descriptor for the current scale level
941
942 for (size_t i = 0; i < tSize; ++i)
943 {
944 uint8_t partialDescriptor = 0u;
945
946 for (size_t j = 0; j < 8; ++j)
947 {
948 partialDescriptor = uint8_t(partialDescriptor << 1u);
949
950 const size_t pair = i * 8 + j;
954
955 if (cellIntensities[FREAKDescriptorT<tSize>::cellPairs[pair][0]] > cellIntensities[FREAKDescriptorT<tSize>::cellPairs[pair][1]])
956 {
957 partialDescriptor = partialDescriptor | 1u;
958 }
959 }
960 freakDescriptor.data_[scaleLevel][i] = partialDescriptor;
961 }
962
963 freakDescriptor.dataLevels_ = (unsigned int)(scaleLevel + 1);
964 }
965
966 ocean_assert(freakDescriptor.isValid() == false || (NumericF::isInsideRange(-NumericF::pi(), freakDescriptor.orientation_, NumericF::pi()) && freakDescriptor.dataLevels_ <= 3u));
967
968 return freakDescriptor.isValid();
969}
970
971template <size_t tSize>
972template <bool tEnableBorderChecks>
973bool FREAKDescriptorT<tSize>::computeAverageCellIntensity(const Frame& framePyramidLayer, int cellX, int cellY, const int* kernelX, const int* kernelY, const size_t kernelElements, PixelType& averageIntensity)
974{
975 ocean_assert(framePyramidLayer.isValid());
976 ocean_assert(kernelX != nullptr && kernelY != nullptr && kernelElements != 0u);
977
978 const unsigned int width = framePyramidLayer.width();
979 const unsigned int height = framePyramidLayer.height();
980 const unsigned int frameStrideElements = framePyramidLayer.strideElements();
981 const PixelType* frame = framePyramidLayer.constdata<PixelType>();
982
983 if constexpr (tEnableBorderChecks)
984 {
985 unsigned int sum = 0u;
986 unsigned int sumElements = 0u;
987
988 for (size_t i = 0; i < kernelElements; ++i)
989 {
990 const int x = cellX + kernelX[i];
991 const int y = cellY + kernelY[i];
992
993 if (x >= 0 && x < int(width) && y >= 0 && y < int(height))
994 {
995 sum += frame[(unsigned int)y * frameStrideElements + (unsigned int)x];
996 sumElements++;
997 }
998 }
999
1000 ocean_assert(sumElements != 0u);
1001 ocean_assert(float(sum) / float(sumElements) <= 255.0f);
1002
1003 averageIntensity = PixelType(float(sum) / float(sumElements)); // TODOX No rounding in original. Add it here?
1004 }
1005 else
1006 {
1007 unsigned int sum = 0u;
1008
1009 for (size_t i = 0; i < kernelElements; ++i)
1010 {
1011 const int x = cellX + kernelX[i];
1012 const int y = cellY + kernelY[i];
1013 ocean_assert_and_suppress_unused(x >= 0 && x < int(width) && y >= 0 && y < int(height), height);
1014
1015 sum += frame[(unsigned int)y * frameStrideElements + (unsigned int)x];
1016 }
1017
1018 ocean_assert(float(sum) / float(kernelElements) <= 255.0f);
1019
1020 averageIntensity = PixelType(float(sum) / float(kernelElements)); // TODOX No rounding in original. Add it here?
1021 }
1022
1023 return true;
1024}
1025
1026template <size_t tSize>
1027template <typename TImagePoint>
1028void FREAKDescriptorT<tSize>::computeDescriptorsSubset(const FramePyramid* framePyramid, const TImagePoint* points, const size_t pointsSize, const unsigned int pointsPyramidLevel, FREAKDescriptorT<tSize>* freakDescriptor, const CameraDerivativeFunctor* cameraDerivativeFunctor, const unsigned int firstPoint, const unsigned int numberOfPoints)
1029{
1030 ocean_assert(framePyramid != nullptr && framePyramid->isValid());
1031 ocean_assert(points != nullptr && pointsSize != 0u);
1032 ocean_assert(pointsPyramidLevel < framePyramid->layers());
1033 ocean_assert(freakDescriptor != nullptr);
1034 ocean_assert(cameraDerivativeFunctor != nullptr);
1035 ocean_assert_and_suppress_unused(firstPoint + numberOfPoints <= pointsSize && numberOfPoints != 0u, pointsSize);
1036
1037 for (unsigned int i = firstPoint; i < firstPoint + numberOfPoints; ++i)
1038 {
1039 ocean_assert(i < pointsSize);
1040
1041 const TImagePoint& point = points[i];
1042
1043 const Eigen::Vector2f eigenPoint(float(point.x()), float(point.y()));
1044
1045 float inverseFocalLength;
1046 const CameraDerivativeData data = cameraDerivativeFunctor->computeCameraDerivativeData(eigenPoint, pointsPyramidLevel, inverseFocalLength);
1047
1048 computeDescriptor(*framePyramid, eigenPoint, pointsPyramidLevel, freakDescriptor[i], data.unprojectRayIF, inverseFocalLength, data.pointJacobianMatrixIF);
1049 }
1050}
1051
1052template <size_t tSize>
1053bool FREAKDescriptorT<tSize>::computeLocalDeformationMatrixAndOrientation(const FramePyramid& pyramid, const Eigen::Vector2f& point, const unsigned int pointPyramidLevel, const Eigen::Vector3f& unprojectRayIF, const float inverseFocalLength, const PointJacobianMatrix2x3& projectionJacobianMatrix, Eigen::Matrix<float, 2, 2> & deformationMatrix, float& orientation)
1054{
1055 ocean_assert(pyramid.isValid());
1056 ocean_assert(pointPyramidLevel < pyramid.layers());
1058 ocean_assert(NumericF::isEqualEps(unprojectRayIF.norm() - 1.0f));
1059 ocean_assert(inverseFocalLength > 0.0f);
1060 ocean_assert(projectionJacobianMatrix.IsRowMajor == false);
1061
1062 // In the plane perpendicular to the unprojection ray, determine two arbitrary but perpendicular vectors
1063
1064 const Eigen::Vector3f directionY(0, 1, 0);
1065 const Eigen::Vector3f nx = directionY.cross(unprojectRayIF).normalized() * inverseFocalLength;
1066 const Eigen::Vector3f ny = unprojectRayIF.cross(nx);
1067
1068 // Compute an initial warping matrix from the perpendicular vectors
1069
1070 Eigen::Matrix<float, 3, 2> N;
1071 N.col(0) = nx;
1072 N.col(1) = ny;
1073 const Eigen::Matrix<float, 2, 2> initialDeformationMatrix = projectionJacobianMatrix * N;
1074
1075 // Make sure that the orientation kernel (radius 7) fits inside the current pyramid layer
1076
1077 constexpr float cornerX[4] = {-7.0f, -7.0f, 7.0f, 7.0f};
1078 constexpr float cornerY[4] = {-7.0f, 7.0f, -7.0f, 7.0f};
1079 const Frame& framePyramidLevel = pyramid.layer(pointPyramidLevel);
1080
1081 for (size_t i = 0; i < 4; ++i)
1082 {
1083 const Eigen::Vector2f warpedCorner = point + initialDeformationMatrix * Eigen::Vector2f(cornerX[i], cornerY[i]);
1084
1085 const unsigned int x = (unsigned int)(NumericF::round32(warpedCorner.x()));
1086 const unsigned int y = (unsigned int)(NumericF::round32(warpedCorner.y()));
1087
1088 if (x >= framePyramidLevel.width() || y >= framePyramidLevel.height())
1089 {
1090 return false;
1091 }
1092 }
1093
1094 // Compute weighted intensity over the kernel
1095
1096 int magnitudeX = 0;
1097 int magnitudeY = 0;
1098 const unsigned int strideElements = framePyramidLevel.strideElements();
1099 const PixelType* data = framePyramidLevel.constdata<PixelType>();
1100
1101 for (size_t i = 0; i < FREAKDescriptorT<tSize>::kernelRadius7Elements; ++i)
1102 {
1103 const Eigen::Vector2f p = point + initialDeformationMatrix * Eigen::Vector2f(float(FREAKDescriptorT<tSize>::kernelRadius7X[i]), float(FREAKDescriptorT<tSize>::kernelRadius7Y[i]));
1104
1105 const int u = NumericF::round32(p[0]);
1106 const int v = NumericF::round32(p[1]);
1107
1108 ocean_assert(((unsigned int)(v) * strideElements + (unsigned int)(u)) < framePyramidLevel.size());
1109 const int intensity = int(data[(unsigned int)(v) * strideElements + (unsigned int)(u)]);
1110
1111 // TODOX Is weighting with the relative x-/y-offsets of the kernel correct? Pixels on the border of kernel have much larger weight (up to +/-7) than ones closer to the kernel center (as low as 0 for the center)
1112 magnitudeX += FREAKDescriptorT<tSize>::kernelRadius7X[i] * intensity;
1113 magnitudeY += FREAKDescriptorT<tSize>::kernelRadius7Y[i] * intensity;
1114 }
1115
1116 if (magnitudeX == 0 && magnitudeY == 0)
1117 {
1118 return false;
1119 }
1120
1121 // Compute axes aligned with keypoint orientation and use them to compute the deformation matrix
1122
1123 const Eigen::Vector3f gy = (nx * float(magnitudeX) + ny * float(magnitudeY)).normalized() * inverseFocalLength;
1124 const Eigen::Vector3f gx = gy.cross(unprojectRayIF);
1125
1126 Eigen::Matrix<float, 3, 2> G;
1127 G.col(0) = gx;
1128 G.col(1) = gy;
1129
1130 deformationMatrix = projectionJacobianMatrix * G;
1131
1132 // Compute angle in image coordinates
1133
1134 const Eigen::Vector2f patchY = projectionJacobianMatrix * gy;
1135 orientation = NumericF::atan2(patchY[1], patchY[0]);
1136 ocean_assert(-NumericF::pi() < orientation && orientation <= NumericF::pi());
1137
1138 return true;
1139}
1140
1141template <size_t tSize>
1142FramePyramid FREAKDescriptorT<tSize>::createFramePyramidWithBlur8BitsPerChannel(const Frame& frame, const unsigned int kernelWidth, const unsigned int kernelHeight, const unsigned int layers, Worker* worker)
1143{
1144 ocean_assert(frame.isValid() && frame.dataType() == FrameType::DT_UNSIGNED_INTEGER_8);
1145 ocean_assert(kernelWidth != 0u && kernelWidth % 2u == 1u);
1146 ocean_assert(kernelHeight != 0u && kernelHeight % 2u == 1u);
1147 ocean_assert(layers >= 1u);
1148
1149 Frame reusableFrame;
1150
1151 const FramePyramid::DownsamplingFunction downsamplingFunction = std::bind(&FREAKDescriptorT<tSize>::blurAndDownsampleByTwo11, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, kernelWidth, kernelHeight, reusableFrame);
1152
1153 FramePyramid framePyramid(frame, downsamplingFunction, layers, true /*copyFirstLayer*/, worker);
1154
1155 if (framePyramid.layers() != layers)
1156 {
1157 return FramePyramid();
1158 }
1159
1160 return framePyramid;
1161}
1162
1163template <size_t tSize>
1164bool FREAKDescriptorT<tSize>::blurAndDownsampleByTwo11(const Frame& finerLayer, Frame& coarserLayer, Worker* worker, const unsigned int kernelWidth, const unsigned int kernelHeight, Frame& reusableFrame)
1165{
1166 ocean_assert(finerLayer.isValid());
1167 ocean_assert(coarserLayer.isValid());
1168
1169 ocean_assert(kernelWidth >= 1u && kernelWidth % 2u == 1u);
1170 ocean_assert(kernelHeight >= 1u && kernelHeight % 2u == 1u);
1171
1172 ocean_assert(finerLayer.numberPlanes() == 1u && finerLayer.dataType() == FrameType::DT_UNSIGNED_INTEGER_8);
1173 ocean_assert(finerLayer.isPixelFormatCompatible(coarserLayer.pixelFormat()));
1174
1175 if (!reusableFrame.set(finerLayer.frameType(), false /*forceOwner*/, true /*forceWritable*/))
1176 {
1177 ocean_assert(false && "This should never happen!");
1178 return false;
1179 }
1180
1181 ocean_assert(reusableFrame.isValid());
1182
1183 const Frame* sourceLayer = &finerLayer;
1184
1185 if (kernelWidth <= finerLayer.width() && kernelHeight <= finerLayer.height())
1186 {
1187 if (!CV::FrameFilterGaussian::filter<uint8_t, uint32_t>(finerLayer.constdata<uint8_t>(), reusableFrame.data<uint8_t>(), finerLayer.width(), finerLayer.height(), finerLayer.channels(), finerLayer.paddingElements(), reusableFrame.paddingElements(), kernelWidth, kernelHeight, -1.0f, worker))
1188 {
1189 return false;
1190 }
1191
1192 sourceLayer = &reusableFrame;
1193 }
1194
1195 CV::FrameShrinker::downsampleByTwo8BitPerChannel11(sourceLayer->constdata<uint8_t>(), coarserLayer.data<uint8_t>(), sourceLayer->width(), sourceLayer->height(), sourceLayer->channels(), sourceLayer->paddingElements(), coarserLayer.paddingElements(), worker);
1196
1197 return true;
1198}
1199
1200template <size_t tSize>
1201bool FREAKDescriptorT<tSize>::extractHarrisCornersAndComputeDescriptors(const Frame& yFrame, const unsigned int maxFrameArea, const unsigned int minFrameArea, const unsigned int expectedHarrisCorners640x480, const Scalar harrisCornersReductionScale, const unsigned int harrisCornerThreshold, const CameraDerivativeFunctor& cameraDerivativeFunctor, HarrisCorners& corners, Indices32& cornerPyramidLevels, std::vector<FREAKDescriptorT<tSize>>& descriptors, const bool removeInvalid, const Scalar border, const bool determineExactHarrisCornerPositions, const bool yFrameIsUndistorted, Worker* worker)
1202{
1203 ocean_assert(yFrame.isValid() && FrameType::arePixelFormatsCompatible(yFrame.pixelFormat(), FrameType::genericPixelFormat<std::uint8_t, 1u>()));
1204 ocean_assert(minFrameArea != 0u && minFrameArea <= maxFrameArea);
1205 ocean_assert(expectedHarrisCorners640x480 != 0u);
1206 ocean_assert(harrisCornersReductionScale > Scalar(0) && harrisCornersReductionScale < Scalar(1));
1207 ocean_assert(harrisCornerThreshold <= 512u);
1208 ocean_assert(border > Scalar(0) && Scalar(2) * border < Scalar(yFrame.width()) && Scalar(2) * border < Scalar(yFrame.height()));
1209
1210 corners.clear();
1211 cornerPyramidLevels.clear();
1212 descriptors.clear();
1213
1214 // Determine the range of layers in the pyramid that are of interest (expressed as a range of minimum and maximum area of the frames). Note: area shrinks by a factor of 4 with each coarser layer
1215
1216 const unsigned int frameArea = yFrame.width() * yFrame.height();
1217 const unsigned int startLayerIndex = (unsigned int)std::max(0, Numeric::round32((Numeric::log10(Scalar(frameArea) / Scalar(maxFrameArea)) / Numeric::log10(Scalar(4)))));
1218 const unsigned int lastLayerIndex = (unsigned int)(Numeric::log10(Scalar(frameArea) / Scalar(minFrameArea)) / Numeric::log10(Scalar(4)));
1219 ocean_assert(startLayerIndex <= lastLayerIndex);
1220
1221 // Generate a frame pyramid (+1 extra layer)
1222
1223 const FramePyramid pyramid = FREAKDescriptorT<tSize>::createFramePyramidWithBlur8BitsPerChannel(yFrame, 5u, 5u, lastLayerIndex + 2u, worker);
1224
1225 if (pyramid.isValid() == false || pyramid.layers() <= lastLayerIndex || cameraDerivativeFunctor.supportedPyramidLevels() <= lastLayerIndex)
1226 {
1227 return false;
1228 }
1229
1230 // The number of expected Harris corners is defined at a reference image size of 640x480 pixels. So, it necessary to scale this number
1231 // to the actual size of the first pyramid layer that will be used and then scale it such that the total number of requested points is
1232 // distributed over all used pyramid layers.
1233
1234 const unsigned int startLayerArea = pyramid[startLayerIndex].width() * pyramid[startLayerIndex].height();
1235
1236#if 0
1237 // Disabled but leaving it here as reference
1238 unsigned int expectedHarrisCornersOnLevel = (unsigned int)(Numeric::round32(Scalar(expectedHarrisCorners640x480) * Scalar(startLayerArea) / Scalar(640u * 480u)));
1239 expectedHarrisCornersOnLevel = (unsigned int)(Scalar(expectedHarrisCornersOnLevel) * (Scalar(1) - harrisCornersReductionScale) / (Scalar(1) - std::pow(harrisCornersReductionScale, Scalar(lastLayerIndex - startLayerIndex))));
1240#else
1241 const Scalar expectedHarrisCornersOnStartLayerF = Scalar(expectedHarrisCorners640x480) * Scalar(startLayerArea) / Scalar(640u * 480u);
1242 unsigned int expectedHarrisCornersOnLevel = (unsigned int)Numeric::round32(expectedHarrisCornersOnStartLayerF * (Scalar(1) - harrisCornersReductionScale) / (Scalar(1) - std::pow(harrisCornersReductionScale, Scalar(lastLayerIndex - startLayerIndex))));
1243#endif
1244
1245 // For each layer of the pyramid, extract Harris corners and compute their descriptors
1246
1247 for (unsigned int layer = startLayerIndex; layer <= lastLayerIndex; ++layer)
1248 {
1249 ocean_assert(layer + 1u < pyramid.layers());
1250 ocean_assert(corners.size() == descriptors.size());
1251 ocean_assert(corners.size() == cornerPyramidLevels.size());
1252
1253 if (expectedHarrisCornersOnLevel == 0u)
1254 {
1255 break;
1256 }
1257
1258 const Frame& pyramidLayer = pyramid[layer];
1259
1260 if (pyramidLayer.width() < Scalar(2) * border + Scalar(10) || pyramidLayer.height() < Scalar(2) * border + Scalar(10))
1261 {
1262 break;
1263 }
1264
1265 HarrisCorners harrisCornersOnLevel;
1266 if (CV::Detector::HarrisCornerDetector::detectCorners(pyramidLayer.constdata<uint8_t>(), pyramidLayer.width(), pyramidLayer.height(), pyramidLayer.paddingElements(), harrisCornerThreshold, yFrameIsUndistorted, harrisCornersOnLevel, determineExactHarrisCornerPositions, worker) == false)
1267 {
1268 return false;
1269 }
1270
1271 if (harrisCornersOnLevel.empty())
1272 {
1273 continue;
1274 }
1275
1276 // Select new corners. Make sure they are distributed approximately equal. Append them to the corresponding return value (corners)
1277
1278 ocean_assert(corners.size() == descriptors.size());
1279 ocean_assert(corners.size() == cornerPyramidLevels.size());
1280 const size_t firstNewCornerIndex = corners.size();
1281
1282 if (harrisCornersOnLevel.size() > expectedHarrisCornersOnLevel)
1283 {
1284 // Sort corners by the corner strength in descending order and distribute them over a regular grid of bins
1285
1286 std::sort(harrisCornersOnLevel.begin(), harrisCornersOnLevel.end());
1287
1288 unsigned int horizontalBins = 0u;
1289 unsigned int verticalBins = 0u;
1290 Geometry::SpatialDistribution::idealBins(pyramidLayer.width(), pyramidLayer.height(), expectedHarrisCornersOnLevel / 2u, horizontalBins, verticalBins);
1291 ocean_assert(horizontalBins != 0u && verticalBins != 0u);
1292
1293 const HarrisCorners newCorners = Geometry::SpatialDistribution::distributeAndFilter<HarrisCorner, HarrisCorner::corner2imagePoint>(harrisCornersOnLevel.data(), harrisCornersOnLevel.size(), border, border, Scalar(pyramidLayer.width()) - Scalar(2) * border, Scalar(pyramidLayer.height()) - Scalar(2) * border, horizontalBins, verticalBins, size_t(expectedHarrisCornersOnLevel));
1294
1295 corners.insert(corners.end(), newCorners.begin(), newCorners.end());
1296 }
1297 else
1298 {
1299 for (const HarrisCorner& corner : harrisCornersOnLevel)
1300 {
1301 if (corner.observation().x() >= border && corner.observation().x() < Scalar(pyramidLayer.width()) - border &&
1302 corner.observation().y() >= border && corner.observation().y() < Scalar(pyramidLayer.height()) - border)
1303 {
1304 corners.emplace_back(corner);
1305 }
1306 }
1307 }
1308
1309 ocean_assert(firstNewCornerIndex <= corners.size());
1310 const size_t newCornersAdded = corners.size() - firstNewCornerIndex;
1311
1312 if (newCornersAdded == 0)
1313 {
1314 continue;
1315 }
1316
1317 ocean_assert(firstNewCornerIndex + newCornersAdded == corners.size());
1318
1319#if defined(OCEAN_DEBUG)
1320 for (size_t i = firstNewCornerIndex; i < corners.size(); ++i)
1321 {
1322 ocean_assert(corners[i].observation().x() >= border && corners[i].observation().x() <= Scalar(pyramidLayer.width()) - border);
1323 ocean_assert(corners[i].observation().y() >= border && corners[i].observation().y() <= Scalar(pyramidLayer.height()) - border);
1324 }
1325#endif // OCEAN_DEBUG
1326
1327 // Store the pyramid level of the newly detected corners
1328
1329 cornerPyramidLevels.insert(cornerPyramidLevels.end(), newCornersAdded, layer);
1330
1331 // Extract the locations of the detected corners for the computation of their descriptors
1332
1333 std::vector<Eigen::Vector2f> observations;
1334 observations.reserve(newCornersAdded);
1335
1336 for (size_t i = firstNewCornerIndex; i < corners.size(); ++i)
1337 {
1338 observations.emplace_back(float(corners[i].observation().x()), float(corners[i].observation().y()));
1339 }
1340
1341 // Compute the descriptors (and directly append them to the return value)
1342
1343 ocean_assert(corners.size() > descriptors.size());
1344 descriptors.resize(corners.size());
1345
1346 ocean_assert(descriptors.begin() + size_t(firstNewCornerIndex) + observations.size() == descriptors.end());
1347 FREAKDescriptorT<tSize>::computeDescriptors(pyramid, observations.data(), observations.size(), layer, descriptors.data() + firstNewCornerIndex, cameraDerivativeFunctor, worker);
1348
1349 expectedHarrisCornersOnLevel = (unsigned int)Numeric::round32(Scalar(expectedHarrisCornersOnLevel) * harrisCornersReductionScale);
1350
1351 ocean_assert(corners.size() == descriptors.size());
1352 ocean_assert(corners.size() == cornerPyramidLevels.size());
1353 }
1354
1355 ocean_assert(corners.size() == descriptors.size());
1356 ocean_assert(corners.size() == cornerPyramidLevels.size());
1357
1358 if (removeInvalid && corners.empty() == false)
1359 {
1360 size_t i = 0;
1361 while (i < corners.size())
1362 {
1363 if (descriptors[i].isValid())
1364 {
1365 i++;
1366 }
1367 else
1368 {
1369 ocean_assert(corners.empty() == false);
1370 corners[i] = corners.back();
1371 corners.pop_back();
1372
1373 cornerPyramidLevels[i] = cornerPyramidLevels.back();
1374 cornerPyramidLevels.pop_back();
1375
1376 descriptors[i] = descriptors.back();
1377 descriptors.pop_back();
1378 }
1379 }
1380 }
1381
1382 ocean_assert(corners.size() == descriptors.size());
1383 ocean_assert(corners.size() == cornerPyramidLevels.size());
1384
1385 return true;
1386}
1387
1388} // namespace Detector
1389
1390} // namespace CV
1391
1392} // namespace Ocean
1393
1394#endif // META_OCEAN_CV_DETECTOR_FREAK_DESCRIPTOR_H
This class implements the abstract base class for all AnyCamera objects.
Definition AnyCamera.h:131
Functor that can be used to obtain the 2x3 Jacobian of the camera projection matrix wrt.
Definition FREAKDescriptor.h:175
AnyCameraDerivativeFunctor(const SharedAnyCamera &camera, const unsigned int pyramidLevels=1u)
Constructs a valid functor to compute pinhole camera derivative data.
unsigned int supportedPyramidLevels() const override
Returns the maximum number of pyramid levels for which camera derivative data can be computed.
CameraDerivativeData computeCameraDerivativeData(const Eigen::Vector2f &point, const unsigned int pointPyramidLevel, float &inverseFocalLength) const override
Computes the point Jacobian of the projection matrix and unprojection ray for a specified point.
static CameraDerivativeData computeCameraDerivativeData(const AnyCamera &camera, const Eigen::Vector2f &point)
Computes the point Jacobian of the projection matrix and unprojection ray for a specified point.
SharedAnyCameras cameras_
The camera instance used to compute the Jacobian matrix and unprojection ray at the finest layer of a...
Definition FREAKDescriptor.h:206
std::vector< float > inverseFocalLengths_
The inverse focal length of the cameras, one for each pyramid level.
Definition FREAKDescriptor.h:209
Base class to compute the Jacobian of the camera projection matrix wrt.
Definition FREAKDescriptor.h:107
virtual unsigned int supportedPyramidLevels() const =0
Returns the maximum number of pyramid levels for which camera derivative data can be computed.
virtual ~CameraDerivativeFunctor()=default
Default destructor.
virtual CameraDerivativeData computeCameraDerivativeData(const Eigen::Vector2f &point, const unsigned int pointPyramidLevel, float &inverseFocalLength) const =0
Purely virtual function to compute the camera derivative data; has to be implemented in any derived c...
unsigned int supportedPyramidLevels() const override
Returns the maximum number of pyramid levels for which camera derivative data can be computed.
PinholeCameraDerivativeFunctor(const PinholeCamera &pinholeCamera, const unsigned int pyramidLevels=1u)
Constructs a valid functor to compute pinhole camera derivative data.
CameraDerivativeData computeCameraDerivativeData(const Eigen::Vector2f &point, const unsigned int pointPyramidLevel, float &inverseFocalLength) const override
Computes the point Jacobian of the projection matrix and unprojection ray for a specified point.
PinholeCameras cameras_
The camera instance used to compute the Jacobian matrix and unprojection ray at the finest layer of a...
Definition FREAKDescriptor.h:168
static CameraDerivativeData computeCameraDerivativeData(const PinholeCamera &pinholeCamera, const Eigen::Vector2f &point)
Computes the point Jacobian of the projection matrix and unprojection ray for a specified point.
Implementation the template-free base class for the FREAK descriptors.
Definition FREAKDescriptor.h:82
std::uint8_t PixelType
Typedef for the selected pixel type. This might be turned into a template parameter at some point.
Definition FREAKDescriptor.h:86
Eigen::Matrix< float, 2, 3 > PointJacobianMatrix2x3
The Jacobian of the projection matrix at a specific 3D location (ray from projection center to pixel ...
Definition FREAKDescriptor.h:89
Forward-declaration of the descriptor class.
Definition FREAKDescriptor.h:220
MultilevelDescriptorData data_
The actual FREAK descriptor data.
Definition FREAKDescriptor.h:514
static const float cellsY[numberOfCells]
The pre-defined vertical coordinates of the cells.
Definition FREAKDescriptor.h:464
static const std::uint8_t cellPairs[numberOfCellPairs][2]
The pre-defined pairs of cell indices that uare used to compute the actual binary descriptor (pairs h...
Definition FREAKDescriptor.h:470
static constexpr size_t kernelRadius3Elements
Number of elements in the circular kernel with radius 3.
Definition FREAKDescriptor.h:491
static const float cellsX[numberOfCells]
The pre-defined horizontal coordinates of the cells.
Definition FREAKDescriptor.h:461
static bool computeLocalDeformationMatrixAndOrientation(const FramePyramid &framePyramid, const Eigen::Vector2f &point, const unsigned int pointPyramidLevel, const Eigen::Vector3f &unprojectRayIF, const float inverseFocalLength, const PointJacobianMatrix2x3 &pointJacobianMatrix2x3, Eigen::Matrix< float, 2, 2 > &deformationMatrix, float &orientation)
Computes the transformation to deform receptive fields and the orientation of the descriptor.
Definition FREAKDescriptor.h:1053
static constexpr size_t numberOfCellPairs
The number of pre-defined pairs of cell indices that are used to compute the actual binary descriptor...
Definition FREAKDescriptor.h:467
FREAKDescriptorT(const FREAKDescriptorT< tSize > &)=default
Creates a new FREAK descriptor object by copying from an existing one.
FREAKDescriptorT()=default
Creates a new and invalid FREAK descriptor object.
static const int kernelRadius1Y[kernelRadius1Elements]
The pre-defined vertical coordinates of the circular kernel with radius 1.
Definition FREAKDescriptor.h:479
OCEAN_FORCE_INLINE unsigned int distance(const FREAKDescriptorT< tSize > &descriptor) const
Returns the distance between this descriptor and a second descriptor.
Definition FREAKDescriptor.h:557
float orientation() const
Returns the orientation of the descriptor in Radian.
Definition FREAKDescriptor.h:531
FREAKDescriptorT & operator=(const FREAKDescriptorT< tSize > &) noexcept=default
Copy assignment operator, needs to be defined since there is a custom copy constructor.
float orientation_
The orientation of this descriptor in radian, range: [-pi, pi].
Definition FREAKDescriptor.h:511
unsigned int descriptorLevels() const
Returns the number of levels stored in the multi-level descriptor.
Definition FREAKDescriptor.h:550
static void computeDescriptorsSubset(const FramePyramid *framePyramid, const TImagePoint *points, const size_t pointsSize, const unsigned int pointPyramidLevel, FREAKDescriptorT< tSize > *freakDescriptors, const CameraDerivativeFunctor *cameraDerivativeFunctor, const unsigned int firstPoint, const unsigned int numberOfPoints)
Compute FREAK descriptors for a subset of points.
Definition FREAKDescriptor.h:1028
std::array< SinglelevelDescriptorData, 3 > MultilevelDescriptorData
Multi-level FREAK descriptor data; if possible, this implementation computes the descriptor at three ...
Definition FREAKDescriptor.h:229
static const int kernelRadius7X[kernelRadius7Elements]
The pre-defined horizontal coordinates of the circular kernel with radius 7.
Definition FREAKDescriptor.h:503
static bool blurAndDownsampleByTwo11(const Frame &finerLayer, Frame &coarserLayer, Worker *worker, const unsigned int kernelWidth, const unsigned int kernelHeight, Frame &reusableFrame)
Downsamples a frame by two applying a 1-1 filter after applying a Gaussian blur to the source layer.
Definition FREAKDescriptor.h:1164
std::array< PixelType, tSize > SinglelevelDescriptorData
Single-level FREAK descriptor.
Definition FREAKDescriptor.h:226
static bool extractHarrisCornersAndComputeDescriptors(const Frame &yFrame, const unsigned int maxFrameArea, const unsigned int minFrameArea, const unsigned int expectedHarrisCorners640x480, const Scalar harrisCornersReductionScale, const unsigned int harrisCornerThreshold, const CameraDerivativeFunctor &cameraDerivativeFunctor, HarrisCorners &corners, Indices32 &cornerPyramidLevels, std::vector< FREAKDescriptorT< tSize > > &descriptors, const bool removeInvalid=false, const Scalar border=Scalar(20), const bool determineExactHarrisCornerPositions=false, const bool yFrameIsUndistorted=true, Worker *worker=nullptr)
Extract Harris corners from an image pyramid and compute FREAK descriptors.
Definition FREAKDescriptor.h:1201
static const int kernelRadius7Y[kernelRadius7Elements]
The pre-defined vertical coordinates of the circular kernel with radius 7.
Definition FREAKDescriptor.h:506
static constexpr size_t numberOfCells
The number of cells per keypoint that this implementation is using.
Definition FREAKDescriptor.h:458
static FramePyramid createFramePyramidWithBlur8BitsPerChannel(const Frame &frame, const unsigned int kernelWidth, const unsigned int kernelHeight, const unsigned int layers, Worker *worker=nullptr)
Creates a new pyramid frame for a specific pixel format (a specific number of channels) and applies a...
Definition FREAKDescriptor.h:1142
unsigned int dataLevels_
Number of valid levels in the multi-level descriptor data above, range: [0, 3].
Definition FREAKDescriptor.h:517
MultilevelDescriptorData & data()
Returns the descriptor data (writable)
Definition FREAKDescriptor.h:538
static void computeDescriptors(const SharedAnyCamera &camera, const FramePyramid &framePyramid, const TImagePoint *points, const size_t pointsSize, const unsigned int pointsPyramidLevel, FREAKDescriptorT< tSize > *freakDescriptors, Worker *worker=nullptr)
Compute FREAK descriptors for multiple image points.
Definition FREAKDescriptor.h:599
static const int kernelRadius3X[kernelRadius3Elements]
The pre-defined horizontal coordinates of the circular kernel with radius 3.
Definition FREAKDescriptor.h:494
static const int kernelRadius3Y[kernelRadius3Elements]
The pre-defined vertical coordinates of the circular kernel with radius 3.
Definition FREAKDescriptor.h:497
static const int kernelRadius2Y[kernelRadius2Elements]
The pre-defined vertical coordinates of the circular kernel with radius 2.
Definition FREAKDescriptor.h:488
static const int kernelRadius2X[kernelRadius2Elements]
The pre-defined horizontal coordinates of the circular kernel with radius 2.
Definition FREAKDescriptor.h:485
static constexpr size_t kernelRadius2Elements
Number of elements in the circular kernel with radius 2.
Definition FREAKDescriptor.h:482
static bool computeAverageCellIntensity(const Frame &framePyramidLayer, int x, int y, const int *kernelX, const int *kernelY, const size_t kernelElements, PixelType &averageIntensity)
Computes the average intensity of a cell.
Definition FREAKDescriptor.h:973
static constexpr size_t size()
Returns the length of this descriptor in bytes.
Definition FREAKDescriptor.h:592
static bool computeDescriptor(const FramePyramid &framePyramid, const Eigen::Vector2f &point, const unsigned int pointPyramidLevel, FREAKDescriptorT< tSize > &freakDescriptor, const Eigen::Vector3f &unprojectRayIF, const float inverseFocalLength, const PointJacobianMatrix2x3 &pointJacobianMatrix2x3)
Compute a FREAK descriptor for a single image point.
Definition FREAKDescriptor.h:788
static const int kernelRadius1X[kernelRadius1Elements]
The pre-defined horizontal coordinates of the circular kernel with radius 1.
Definition FREAKDescriptor.h:476
static constexpr size_t kernelRadius1Elements
Number of elements in the circular kernel with radius 1.
Definition FREAKDescriptor.h:473
bool isValid() const
Returns true if this is a valid descriptor.
Definition FREAKDescriptor.h:586
static constexpr size_t kernelRadius7Elements
Number of elements in the circular kernel with radius 7.
Definition FREAKDescriptor.h:500
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
This class implements a Harris corner.
Definition HarrisCorner.h:37
This class implements a frame pyramid.
Definition FramePyramid.h:46
unsigned int finestWidth() const
Returns the width of the finest (first) layer.
Definition FramePyramid.h:828
bool isValid() const
Returns whether this pyramid holds at least one frame layer.
Definition FramePyramid.h:913
std::function< bool(const Frame &sourceLayer, Frame &targetLayer, Worker *worker)> DownsamplingFunction
Definition of a function allowing to downsample a frame.
Definition FramePyramid.h:91
unsigned int width(const unsigned int layer) const
Returns the width of a given layer.
Definition FramePyramid.h:816
unsigned int layers() const
Returns the number of layers this pyramid holds.
Definition FramePyramid.h:811
const FrameType & frameType() const
Returns the frame type of the finest layer.
Definition FramePyramid.h:861
unsigned int finestHeight() const
Returns the height of the finest (first) layer.
Definition FramePyramid.h:834
unsigned int height(const unsigned int layer) const
Returns the height of a given layer.
Definition FramePyramid.h:822
const Frame & layer(const unsigned int layer) const
Returns the frame of a specified layer.
Definition FramePyramid.h:773
static void downsampleByTwo8BitPerChannel11(const uint8_t *source, uint8_t *target, const unsigned int sourceWidth, const unsigned int sourceHeight, const unsigned int channels, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, Worker *worker=nullptr)
Reduces the resolution of a given frame by two, applying a 1-1 downsampling.
Definition FrameShrinker.h:508
static Caller< void > createStatic(typename StaticFunctionPointerMaker< void, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass >::Type function)
Creates a new caller container for a static function with no function parameter.
Definition Caller.h:2877
This class implements Ocean's image class.
Definition Frame.h:1879
unsigned int strideElements(const unsigned int planeIndex=0u) const
Returns the number of elements within one row, including optional padding at the end of a row for a s...
Definition Frame.h:4222
const T * constdata(const unsigned int planeIndex=0u) const
Returns a pointer to the read-only pixel data of a specific plane.
Definition Frame.h:4332
const FrameType & frameType() const
Returns the frame type of this frame.
Definition Frame.h:3936
T * data(const unsigned int planeIndex=0u)
Returns a pointer to the pixel data of a specific plane.
Definition Frame.h:4323
bool isValid() const
Returns whether this frame is valid.
Definition Frame.h:4612
bool set(const FrameType &frameType, const bool forceOwner, const bool forceWritable=false, const Indices32 &planePaddingElements=Indices32(), const Timestamp &timestamp=Timestamp(false), bool *reallocated=nullptr)
Sets a new frame type for this frame.
unsigned int size(const unsigned int planeIndex=0u) const
Returns the number of bytes necessary for a specific plane including optional padding at the end of p...
Definition Frame.h:4198
unsigned int paddingElements(const unsigned int planeIndex=0u) const
Returns the optional number of padding elements at the end of each row for a specific plane.
Definition Frame.h:4206
@ FORMAT_Y8
Pixel format for grayscale images with byte order Y and 8 bits per pixel.
Definition Frame.h:594
unsigned int width() const
Returns the width of the frame format in pixel.
Definition Frame.h:3241
bool isPixelFormatDataLayoutCompatible(const PixelFormat pixelFormat) const
Returns whether this pixel format has a compatible data layout with a given pixel format.
Definition Frame.h:3303
uint32_t numberPlanes() const
Returns the number of planes of the pixel format of this frame.
Definition Frame.h:3281
static bool arePixelFormatsCompatible(const PixelFormat pixelFormatA, const PixelFormat pixelFormatB)
Returns whether two given pixel formats are compatible.
PixelFormat pixelFormat() const
Returns the pixel format of the frame.
Definition Frame.h:3251
@ DT_UNSIGNED_INTEGER_8
Unsigned 8 bit integer data type (uint8_t).
Definition Frame.h:41
unsigned int height() const
Returns the height of the frame in pixel.
Definition Frame.h:3246
bool isPixelFormatCompatible(const PixelFormat pixelFormat) const
Returns whether the pixel format of this frame type is compatible with a given pixel format.
Definition Frame.h:3298
unsigned int channels() const
Returns the number of individual channels the frame has.
Definition Frame.h:3271
DataType dataType() const
Returns the data type of the pixel format of this frame.
Definition Frame.h:3261
static void idealBins(const unsigned int width, const unsigned int height, const size_t numberBins, unsigned int &horizontalBins, unsigned int &verticalBins, const unsigned int minimalHorizontalBins=2u, const unsigned int minimalVerticalBins=2u)
Calculates the ideal number of horizontal and vertical bins for an array if the overall number of bin...
static constexpr bool isInsideRange(const T lower, const T value, const T upper, const T epsilon=NumericT< T >::eps())
Returns whether a value lies between a given range up to a provided epsilon border.
Definition Numeric.h:2881
static T atan2(const T y, const T x)
Returns the arctangent of a given value in radian.
Definition Numeric.h:1636
static constexpr T pi()
Returns PI which is equivalent to 180 degree.
Definition Numeric.h:926
static T log10(const T value)
Returns the logarithm to base 10 of a given value.
Definition Numeric.h:1715
static constexpr int32_t round32(const T value)
Returns the rounded 32 bit integer value of a given value.
Definition Numeric.h:2073
static constexpr bool isEqualEps(const T value)
Returns whether a value is smaller than or equal to a small epsilon.
Definition Numeric.h:2096
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.
std::vector< Index32 > Indices32
Definition of a vector holding 32 bit index values.
Definition Base.h:96
std::vector< HarrisCorner > HarrisCorners
Definition of a vector holding Harris corners.
Definition HarrisCorner.h:30
PinholeCamerasT< Scalar > PinholeCameras
Definition of a vector holding pinhole camera objects.
Definition PinholeCamera.h:68
float Scalar
Definition of a scalar type.
Definition Math.h:129
std::shared_ptr< AnyCamera > SharedAnyCamera
Definition of a shared pointer holding an AnyCamera object with Scalar precision.
Definition AnyCamera.h:61
SharedAnyCamerasT< Scalar > SharedAnyCameras
Definition of a vector holding AnyCamera objects.
Definition AnyCamera.h:91
std::vector< FREAKDescriptor32 > FREAKDescriptors32
Vector of 32-bytes long FREAK descriptors.
Definition FREAKDescriptor.h:69
FREAKDescriptorT< 32 > FREAKDescriptor32
Typedef for the 32-bytes long FREAK descriptor.
Definition FREAKDescriptor.h:66
std::vector< FREAKDescriptor64 > FREAKDescriptors64
Vector of 64-bytes long FREAK descriptors.
Definition FREAKDescriptor.h:75
The namespace covering the entire Ocean framework.
Definition Accessor.h:15
The camera data that is required to compute the FREAK descriptor of an image point.
Definition FREAKDescriptor.h:95
Eigen::Vector3f unprojectRayIF
The normalized ray that points from projection center to a 2D pixel location in the image plane of ca...
Definition FREAKDescriptor.h:97
PointJacobianMatrix2x3 pointJacobianMatrixIF
The 2-by-3 Jacobian matrix of a projection matrix wrt. to the above 2D pixel location in the image pl...
Definition FREAKDescriptor.h:100