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 */
136 class OCEAN_CV_DETECTOR_EXPORT PinholeCameraDerivativeFunctor : public CameraDerivativeFunctor
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 */
174 class OCEAN_CV_DETECTOR_EXPORT AnyCameraDerivativeFunctor : public CameraDerivativeFunctor
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 the minimal distance between this descriptor and a vector of descriptors.
285 * @param descriptors The vector of descriptors to compare against, must not be empty
286 * @return The minimal distance between this descriptor and any of the given descriptors (the hamming distance), with range [0, tSize * 8]
287 */
288 unsigned int distance(const std::vector<FREAKDescriptorT<tSize>>& descriptors) const;
289
290 /**
291 * Returns true if this is a valid descriptor
292 * @return True if this is a valid descriptor, otherwise false
293 */
294 inline bool isValid() const;
295
296 /**
297 * Returns the length of this descriptor in bytes.
298 * @return The descriptor's length in bytes
299 */
300 static constexpr size_t size();
301
302 /**
303 * Copy assignment operator, needs to be defined since there is a custom copy constructor.
304 * @return Reference to this object
305 */
306 inline FREAKDescriptorT& operator=(const FREAKDescriptorT<tSize>&) noexcept = default;
307
308 /**
309 * Returns the distance between two descriptors.
310 * @param descriptorA The first descriptor to use, must be valid
311 * @param descriptorB The second descriptor to use, must be valid
312 * @return The distance between both descriptors
313 */
314 static unsigned int calculateDistance(const FREAKDescriptorT<tSize>& descriptorA, const FREAKDescriptorT<tSize>& descriptorB);
315
316 /**
317 * Returns the minimal distance between a descriptor and a vector of descriptors.
318 * @param descriptor The descriptor to compare, must be valid
319 * @param descriptors The vector of descriptors to compare against, must not be empty
320 * @return The minimal distance between the descriptor and any of the given descriptors (the hamming distance), with range [0, tSize * 8]
321 */
322 static unsigned int calculateDistance(const FREAKDescriptorT<tSize>& descriptor, const std::vector<FREAKDescriptorT<tSize>>& descriptors);
323
324 /**
325 * Returns the descriptor matching threshold based on a percentage of the descriptor size.
326 * @param percent The percentage of the descriptor bits to use as threshold, with range [0, 100]
327 * @return The threshold value (number of bits that can differ), with range [0, tSize * 8]
328 */
329 static constexpr unsigned int descriptorMatchingThreshold(const unsigned int percent);
330
331 /**
332 * Compute a FREAK descriptor for a single image point.
333 * @param framePyramid Frame pyramid in which the location 'point' has been defined, must be valid
334 * @param point Point defined at level 'pointPyramidLevel' in 'framePyramid' for which a descriptor will be computed, must be valid
335 * @param pointPyramidLevel Level of the frame pyramid at which the input point is located, range: [0, framePyramid.layers() - 1)
336 * @param freakDescriptor The FREAK descriptor that will be computed for the input point, will be valid only if this function returns true
337 * @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
338 * @param inverseFocalLength The inverse focal length (assumes identical vertical and horizontal focal lengths), defined at pyramid level 'pointPyramidLevel', with range (0, infinity)
339 * @param pointJacobianMatrix2x3 The 2-by-3 Jacobian of the camera projection matrix, cf. 'Geometry::Jacobian::calculatePointJacobian2x3()', must be valid
340 * @return True if the descriptor was successfully computed, otherwise false
341 */
342 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);
343
344 /**
345 * Compute FREAK descriptors for multiple image points.
346 * @param camera The camera profile defining the projection (of the finest pyramid layer), must be valid
347 * @param framePyramid Frame pyramid in which the location 'points' has been defined, must be valid
348 * @param points The 2D image points which are defined at level 'pointsPyramidLevel' in 'framePyramid' for which descriptors will be computed, must be valid
349 * @param pointsSize The number of elements in 'points', range: [0, infinity)
350 * @param pointsPyramidLevel Level of the frame pyramid at which the input points are located, range: [0, framePyramid.layers() - 1)
351 * @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
352 * @param worker Optional worker instance for parallelization
353 * @tparam TImagePoint The data type of the image points, with defined TImagePoint::x() and TImagePoint::y()
354 */
355 template <typename TImagePoint>
356 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);
357
358 /**
359 * Compute FREAK descriptors for multiple image points.
360 * This function requires a callback function which is used internally to determine the (normalized) ray from the
361 * 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.
362 *
363 * Example usage:
364 *
365 * @code
366 * class YourCameraDerivativeFunctor : public CameraDerivativeFunctor
367 * {
368 * typename FREAKDescriptorT<tSize>::CameraDerivativeData computeCameraDerivativeData(const Eigen::Vector2f& point, const unsigned int pointPyramidLevel, float& inverseFocalLength) override
369 * {
370 * FREAKDescriptorT<tSize>::CameraDerivativeData data;
371 *
372 * inverseFocalLength = ... // Compute inverse focal length for the camera at pointPyramidLevel
373 * data.pointJacobianMatrixIF = ... // Add your computation here
374 * data.unprojectRayIF = ... // Add your computation here
375 *
376 * return data;
377 * }
378 * };
379 *
380 * YourCameraDerivativeFunctor YourCameraDerivativeFunctor;
381 * FREAKDescriptorT<tSize>::computeDescriptors(yFramePyramid, points.data(), points.size(), level, oceanFreakDescriptorsMulticore.data(), yourCameraDerivativeFunctor, &worker);
382 * @endcode
383 *
384 * @param framePyramid Frame pyramid in which the location 'points' has been defined, must be valid
385 * @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
386 * @param pointsSize The number of elements in 'points', range: [0, infinity)
387 * @param pointsPyramidLevel Level of the frame pyramid at which the input points are located, range: [0, framePyramid.layers() - 1)
388 * @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
389 * @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.
390 * @param worker Optional worker instance for parallelization
391 * @tparam TImagePoint The data type of the image points, with defined TImagePoint::x() and TImagePoint::y()
392 */
393 template <typename TImagePoint>
394 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);
395
396 /**
397 * Extract Harris corners from an image pyramid and compute FREAK descriptors
398 * @param yFrame The 8-bit grayscale image for which Harris corners and FREAK descriptors will be computed, must be valid
399 * @param maxFrameArea This value determines the first layer of the frame pyramid for which corners and descriptors will be computed, range: (minFrameArea, infinity)
400 * @param minFrameArea This value determines the last layer of the frame pyramid for which corners and descriptors will be computed, range: [0, maxFrameArea)
401 * @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)
402 * @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)
403 * @param harrisCornerThreshold Threshold value for the Harris corner detector, range: [0, 512]
404 * @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
405 * @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'
406 * @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'
407 * @param descriptors Will hold the FREAK descriptors of each Harris corner. Descriptors may be invalid. Will have the same size as 'corners' and 'cornerPyramidLevels'
408 * @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
409 * @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)
410 * @param determineExactHarrisCornerPositions If true, force the subpixel interpolation to determine the exact position of the extracted Harris corners
411 * @param yFrameIsUndistorted If true the original input frame is undistorted and all extracted 2D feature positions will be marked as undistorted, too
412 * @param worker Optional worker instance for parallelization
413 */
414 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);
415
416 protected:
417
418 /**
419 * Compute FREAK descriptors for a subset of points
420 * @param framePyramid Frame pyramid in which the location 'points' has been defined, must be valid
421 * @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
422 * @param pointsSize The number of elements in 'points', range: [1, infinity)
423 * @param pointPyramidLevel Level of the frame pyramid at which the input points are located, range: [0, framePyramid.layers() - 1)
424 * @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
425 * @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
426 * @param firstPoint The index of the first point that will be processed by this function, range: [0, pointsSize)
427 * @param numberOfPoints Number of points that should be processed in this function starting at 'firstIndex', range: [1, pointsSize - firstIndex]
428 * @tparam TImagePoint The data type of the image points, with defined TImagePoint::x() and TImagePoint::y()
429 */
430 template <typename TImagePoint>
431 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);
432
433 /**
434 * Computes the transformation to deform receptive fields and the orientation of the descriptor
435 * @param framePyramid Frame pyramid in which the location 'point' has been defined, must be valid
436 * @param point The point defined at level 'pointPyramidLevel' in 'framePyramid' for which a descriptor will be computed, must be valid
437 * @param pointPyramidLevel Level of the frame pyramid at which the input point is located, range: [0, framePyramid.layers() - 1)
438 * @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
439 * @param inverseFocalLength The inverse focal length (assumes identical vertical and horizontal focal lengths), defined at pyramid level 'pointPyramidLevel', with range (0, infinity)
440 * @param pointJacobianMatrix2x3 The 2-by-3 Jacobian of the camera projection matrix, cf. 'Geometry::Jacobian::calculatePointJacobian2x3()', must be valid
441 * @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
442 * @param orientation The orientation of this descriptor in radian, range: [-pi, pi]
443 * @return True if this is a valid descriptor, otherwise false
444 */
445 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);
446
447 /**
448 * Computes the average intensity of a cell
449 * @param framePyramidLayer Layer of a frame pyramid in which the location '(x, y)' has been defined, must be valid
450 * @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
451 * @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
452 * @param kernelX Pointer to the horizontal offsets of the kernel elements (relative to 'x'), must be valid and have 'kernelElements' elements
453 * @param kernelY Pointer to the vertical offsets of the kernel elements (relative to 'y'), must be valid and have 'kernelElements' elements
454 * @param kernelElements Number of elements in the kernel, range: [1, infinity]
455 * @param averageIntensity The average intensity of the selected cell
456 * @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)
457 * @return True if the average intensity was successfully computed, otherwise false (e.g. location (x, y) too close to the image border)
458 */
459 template <bool tEnableBorderChecks>
460 static bool computeAverageCellIntensity(const Frame& framePyramidLayer, int x, int y, const int* kernelX, const int* kernelY, const size_t kernelElements, PixelType& averageIntensity);
461
462 /**
463 * 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.
464 * @param frame The frame pyramid will be built using this frame, must be valid and use 8 bits per channel
465 * @param kernelWidth Width of the Gaussian kernel that is applied before a down-size step, range: [1, infinity), value must be odd
466 * @param kernelHeight Height of the Gaussian kernel that is applied before a down-size step, range: [1, infinity), value must be odd
467 * @param layers The number of pyramid layers to be created, with range [1, infinity)
468 * @param worker Optional worker object to distribute the computation
469 * @return The created frame pyramid (will be invalid in case of a failure)
470 */
471 static FramePyramid createFramePyramidWithBlur8BitsPerChannel(const Frame& frame, const unsigned int kernelWidth, const unsigned int kernelHeight, const unsigned int layers, Worker* worker = nullptr);
472
473 /**
474 * Downsamples a frame by two applying a 1-1 filter after applying a Gaussian blur to the source layer.
475 * @param finerLayer The finer pyramid layer, must be valid
476 * @param coarserLayer The coarser pyramid layer, must be valid
477 * @param worker The optional worker to distribute the computation
478 * @param kernelWidth The width of the Gaussian kernel, in pixel, with range [1, infinity), must odd
479 * @param kernelHeight The height of the Gaussian kernel, in pixel, with range [1, infinity), must odd
480 * @param reusableFrame A reusable frame which can be used internally
481 * @return True, if succeeded
482 */
483 static bool blurAndDownsampleByTwo11(const Frame& finerLayer, Frame& coarserLayer, Worker* worker, const unsigned int kernelWidth, const unsigned int kernelHeight, Frame& reusableFrame);
484
485 private:
486
487 /// The number of cells per keypoint that this implementation is using
488 static constexpr size_t numberOfCells = 43;
489
490 /// The pre-defined horizontal coordinates of the cells
491 static const float cellsX[numberOfCells];
492
493 /// The pre-defined vertical coordinates of the cells
494 static const float cellsY[numberOfCells];
495
496 /// The number of pre-defined pairs of cell indices that are used to compute the actual binary descriptor
497 static constexpr size_t numberOfCellPairs = 512;
498
499 /// The pre-defined pairs of cell indices that uare used to compute the actual binary descriptor (pairs have been randomly shuffled)
500 static const std::uint8_t cellPairs[numberOfCellPairs][2];
501
502 /// Number of elements in the circular kernel with radius 1
503 static constexpr size_t kernelRadius1Elements = 5;
504
505 /// The pre-defined horizontal coordinates of the circular kernel with radius 1
507
508 /// The pre-defined vertical coordinates of the circular kernel with radius 1
510
511 /// Number of elements in the circular kernel with radius 2
512 static constexpr size_t kernelRadius2Elements = 13;
513
514 /// The pre-defined horizontal coordinates of the circular kernel with radius 2
516
517 /// The pre-defined vertical coordinates of the circular kernel with radius 2
519
520 /// Number of elements in the circular kernel with radius 3
521 static constexpr size_t kernelRadius3Elements = 29;
522
523 /// The pre-defined horizontal coordinates of the circular kernel with radius 3
525
526 /// The pre-defined vertical coordinates of the circular kernel with radius 3
528
529 /// Number of elements in the circular kernel with radius 7
530 static constexpr size_t kernelRadius7Elements = 149;
531
532 /// The pre-defined horizontal coordinates of the circular kernel with radius 7
534
535 /// The pre-defined vertical coordinates of the circular kernel with radius 7
537
538 protected:
539
540 /// The orientation of this descriptor in radian, range: [-pi, pi]
541 float orientation_ = 0.0f;
542
543 /// The actual FREAK descriptor data
545
546 /// Number of valid levels in the multi-level descriptor data above, range: [0, 3]
547 unsigned int dataLevels_ = 0u;
548};
549
550template <size_t tSize>
551FREAKDescriptorT<tSize>::FREAKDescriptorT(MultilevelDescriptorData&& data, const unsigned int levels, const float orientation) noexcept :
552 orientation_(orientation),
553 data_(std::move(data)),
554 dataLevels_(levels)
555{
556 ocean_assert(levels >= 1u && levels <= 3u);
557 ocean_assert(NumericF::isInsideRange(-NumericF::pi(), orientation, NumericF::pi()));
558}
559
560template <size_t tSize>
562{
563 ocean_assert(NumericF::isInsideRange(-NumericF::pi(), orientation_, NumericF::pi()));
564 return orientation_;
565}
566
567template <size_t tSize>
572
573template <size_t tSize>
575{
576 return data_;
577}
578
579template <size_t tSize>
581{
582 ocean_assert(dataLevels_ <= 3u);
583 return dataLevels_;
584}
585
586template <size_t tSize>
587OCEAN_FORCE_INLINE unsigned int FREAKDescriptorT<tSize>::distance(const FREAKDescriptorT<tSize>& descriptor) const
588{
589 ocean_assert(isValid() && descriptor.isValid());
590
591 unsigned int bestDistance = (unsigned int)(-1);
592
593 for (unsigned int nOuter = 0u; nOuter < dataLevels_; ++nOuter)
594 {
595 const SinglelevelDescriptorData& outerData = data_[nOuter];
596
597 for (unsigned int nInner = 0u; nInner < descriptor.dataLevels_; ++nInner)
598 {
599 const SinglelevelDescriptorData& innerData = descriptor.data_[nInner];
600
601 const unsigned int distance = Descriptor::calculateHammingDistance<tSize * 8u>(outerData.data(), innerData.data());
602
603 if (distance < bestDistance)
604 {
605 bestDistance = distance;
606 }
607 }
608 }
609
610 ocean_assert(bestDistance != (unsigned int)(-1));
611
612 return bestDistance;
613}
614
615template <size_t tSize>
616unsigned int FREAKDescriptorT<tSize>::distance(const std::vector<FREAKDescriptorT<tSize>>& descriptors) const
617{
618 ocean_assert(isValid());
619 ocean_assert(!descriptors.empty());
620
621 unsigned int bestDistance = (unsigned int)(-1);
622
623 for (const FREAKDescriptorT<tSize>& descriptor : descriptors)
624 {
625 ocean_assert(descriptor.isValid());
626
627 const unsigned int currentDistance = distance(descriptor);
628
629 if (currentDistance < bestDistance)
630 {
631 bestDistance = currentDistance;
632 }
633 }
634
635 return bestDistance;
636}
637
638template <size_t tSize>
640{
641 return descriptorA.distance(descriptorB);
642}
643
644template <size_t tSize>
645unsigned int FREAKDescriptorT<tSize>::calculateDistance(const FREAKDescriptorT<tSize>& descriptor, const std::vector<FREAKDescriptorT<tSize>>& descriptors)
646{
647 return descriptor.distance(descriptors);
648}
649
650template <size_t tSize>
651constexpr unsigned int FREAKDescriptorT<tSize>::descriptorMatchingThreshold(const unsigned int percent)
652{
653 ocean_assert(percent <= 100u);
654
655 constexpr size_t descriptorBits = tSize * 8;
656
657 return (unsigned int)(descriptorBits * percent / 100u);
658}
659
660template <size_t tSize>
662{
663 return descriptorLevels() >= 1u && descriptorLevels() <= 3u && NumericF::isInsideRange(-NumericF::pi(), orientation_, NumericF::pi());
664}
665
666template <size_t tSize>
668{
669 return tSize;
670}
671
672template <size_t tSize>
673template <typename TImagePoint>
674inline 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)
675{
676 ocean_assert(camera && camera->isValid());
677 ocean_assert(camera->width() == framePyramid.finestWidth() && camera->height() == framePyramid.finestHeight());
678
679 const AnyCameraDerivativeFunctor cameraDerivativeFunctor(camera, framePyramid.layers());
680
681 return computeDescriptors<TImagePoint>(framePyramid, points, pointsSize, pointsPyramidLevel, freakDescriptors, cameraDerivativeFunctor, worker);
682}
683
684template <size_t tSize>
685template <typename TImagePoint>
686inline 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)
687{
688 ocean_assert(framePyramid.isValid());
689 ocean_assert(points != nullptr && pointsSize != 0u);
690 ocean_assert(pointsPyramidLevel < framePyramid.layers());
691 ocean_assert(freakDescriptors != nullptr);
692
693 if (worker)
694 {
695 worker->executeFunction(Worker::Function::createStatic(&FREAKDescriptorT<tSize>::computeDescriptorsSubset<TImagePoint>, &framePyramid, points, pointsSize, pointsPyramidLevel, freakDescriptors, &projectionDerivativeDataCallback, 0u, 0u), 0u, (unsigned int)(pointsSize));
696 }
697 else
698 {
699 FREAKDescriptorT<tSize>::computeDescriptorsSubset<TImagePoint>(&framePyramid, points, pointsSize, pointsPyramidLevel, freakDescriptors, &projectionDerivativeDataCallback, 0u, (unsigned int)(pointsSize));
700 }
701}
702
703template <size_t tSize>
704const float FREAKDescriptorT<tSize>::cellsX[numberOfCells] =
705{
706 // clang-format off
707 0.0f, -14.7216f, -14.7216f, 0.0f, 14.7216f, 14.7216f, -6.3745f, -12.749f, -6.3745f, 6.3745f,
708 12.749f, 6.3745f, 0.0f, -7.97392f, -7.97392f, 0.0f, 7.97392f, 7.97392f, -3.18725f, -6.3745f,
709 -3.18725f, 3.18725f, 6.3745f, 3.18725f, 0.0f, -3.67983f, -3.67983f, 0.0f, 3.67983f, 3.67983f,
710 -1.4163f, -2.8326f, -1.4163f, 1.4163f, 2.8326f, 1.4163f, 0.0f, -1.84049f, -1.84049f, 0.0f,
711 1.84049f, 1.84049f, 0.0f
712 // clang-format on
713};
714
715template <size_t tSize>
716const float FREAKDescriptorT<tSize>::cellsY[numberOfCells] =
717{
718 // clang-format off
719 16.9991f, 8.49895f, -8.49895f, -16.9991f, -8.49895f, 8.49895f, 11.0406f, 0.0f, -11.0406f, -11.0406f,
720 0.0f, 11.0406f, 9.2071f, 4.60355f, -4.60355f, -9.2071f, -4.60355f, 4.60355f, 5.52032f, 0.0f,
721 -5.52032f, -5.52032f, 0.0f, 5.52032f, 4.25005f, 2.12445f, -2.12445f, -4.25005f, -2.12445f, 2.12445f,
722 2.4536f, 0.0f, -2.4536f, -2.4536f, 0.0f, 2.4536f, 2.12445f, 1.0628f, -1.0628f, -2.12445f,
723 -1.0628f, 1.0628f, 0.0f
724 // clang-format on
725};
726
727template <size_t tSize>
728const uint8_t FREAKDescriptorT<tSize>::cellPairs[numberOfCellPairs][2] =
729{
730 // clang-format off
731 {37, 4}, {38, 4}, {12, 0}, {39,10}, {27, 7}, {37,29}, {20,16}, {33,16}, {14, 0}, {31, 3},
732 {17, 4}, {24,12}, {33,22}, {31, 7}, {35,30}, {25, 6}, {34,31}, {20,19}, {22,17}, {16, 6},
733 {23, 5}, {26,10}, {13, 5}, {31,17}, {17,10}, {31,28}, {22, 4}, {29,11}, {28, 2}, {29,19},
734 {30, 6}, {37,10}, {31, 2}, {41,13}, {14, 7}, {15, 3}, {33, 4}, {18,17}, {23,19}, {33,28},
735 {41,24}, {34,16}, { 7, 1}, {26, 5}, {36,13}, {42, 9}, {20,14}, {27,26}, {41, 6}, {40,19},
736 {26, 3}, {36,29}, {23,13}, {40, 7}, {18, 0}, {28,22}, {22, 9}, {26,16}, {21,16}, {39,20},
737 { 8, 3}, {14, 1}, {12,11}, {31,25}, {29, 4}, {15, 1}, {41,22}, {35, 1}, {26, 2}, {34,14},
738 {25, 1}, {34,17}, {34,29}, {16,14}, {19, 3}, {26,14}, {15, 5}, {25,17}, {25, 5}, {34,25},
739 { 6, 0}, {23,10}, {29,24}, {28,16}, {20, 3}, { 7, 4}, {25,11}, {36,24}, {27, 9}, {11,10},
740 {23, 7}, {32,19}, {32,16}, {37,18}, {25,24}, {19, 1}, {22,20}, {38,14}, {41,31}, {16,10},
741 {19, 6}, {16,11}, {31,20}, { 8, 0}, {14, 2}, {19, 0}, {37,13}, {34, 4}, {31,14}, { 6, 1},
742 {40, 1}, {24,18}, {41, 1}, {41, 7}, {36,23}, {40,20}, {40,27}, {13, 0}, {19,12}, {42,38},
743 {16, 7}, {34, 7}, { 9, 2}, {28, 4}, {11, 5}, {40,38}, {17, 2}, { 5, 0}, {19,14}, {12, 6},
744 {19,17}, {40,22}, {26, 7}, {19, 5}, {19,11}, {28,26}, {12, 1}, {34, 0}, { 5, 1}, {27,16},
745 {21,15}, {29,25}, {19, 8}, {32,26}, {37,17}, {11, 6}, {22, 6}, {39,27}, {41,37}, {21, 5},
746 {14,11}, {31,16}, {38,28}, {16, 0}, {29,10}, {31,26}, {10, 1}, {22,13}, {10, 3}, {17, 3},
747 {42,30}, { 8, 4}, {26, 6}, {22, 8}, {38,27}, {26,22}, {41,10}, {42,13}, {40,34}, {13, 7},
748 {30,11}, {38,22}, {33,27}, {19,15}, {29, 7}, {31,10}, {26,15}, {13,12}, {29, 2}, { 5, 3},
749 {15, 7}, {28,10}, {29,17}, {40,10}, {21, 1}, {15,10}, {37,11}, {40,13}, {26, 1}, {39,21},
750 {34,21}, {40,31}, {19, 7}, {16, 5}, {40,39}, {37, 7}, {30,23}, {10, 9}, {36,30}, {38, 0},
751 {18, 6}, {40,32}, {38,10}, {22, 3}, {26,19}, {18,13}, {39,22}, {35,17}, {31,19}, {18,11},
752 {28,19}, {28, 0}, {37,31}, {30, 7}, {27,20}, {34,10}, {38, 3}, {37,23}, {18, 7}, {38,20},
753 {25,19}, {20, 7}, {22,18}, { 7, 3}, {15, 2}, {23,12}, {26,13}, {38, 7}, {11, 1}, {20, 8},
754 {33,21}, {37,36}, {17,16}, {36,35}, {41, 2}, {37,35}, {37, 2}, {15,14}, {10, 7}, {41,29},
755 { 7, 6}, {32,22}, {34,26}, {33, 2}, {38,26}, {31, 0}, {11, 3}, {24,23}, {13,11}, {41,19},
756 {41,25}, {30,13}, {27,10}, {39,38}, {21, 3}, {31, 4}, {27,14}, {37,24}, {20, 2}, {25,23},
757 {29, 1}, {39,28}, {17, 0}, { 7, 0}, { 9, 5}, {22, 2}, {33,32}, {27,21}, {30,25}, {41,23},
758 {41,30}, {15, 9}, {22,10}, {31,22}, {29, 5}, {34,20}, {24,13}, {31,11}, {36,25}, {21,19},
759 {19,13}, {30,29}, {33, 5}, { 6, 4}, { 5, 2}, { 8, 2}, {10, 2}, {25,13}, {37,19}, {28,14},
760 {15, 4}, {10, 8}, {12, 5}, {14,13}, {24, 1}, {31,12}, {14,10}, {32,27}, {19,18}, {32, 4},
761 {22, 1}, {39,26}, {17,14}, { 2, 1}, { 1, 0}, {35,23}, {34, 2}, {33,19}, {13, 3}, {39,16},
762 {25, 2}, {41, 4}, {28, 7}, {31,21}, {26, 4}, {39,19}, {24,17}, {28,20}, {21, 8}, {25, 7},
763 {34,15}, {41,36}, {16, 3}, {21,20}, {31,15}, {26,20}, {14, 5}, {38,16}, {40, 2}, {18,10},
764 {27, 8}, {29,13}, {41,18}, {18,12}, {40,26}, {36, 0}, {21,14}, {22, 0}, {27, 2}, {11, 0},
765 {21,10}, {20,10}, {23, 6}, {13, 4}, {28,21}, {22,16}, {25,22}, {35,24}, { 4, 0}, {31, 1},
766 {32,21}, {21, 4}, {37, 6}, {15, 8}, { 8, 7}, {29,22}, {28,15}, {25,18}, {41,35}, {39,14},
767 {34,12}, {23,17}, {25,10}, {39, 9}, {34,13}, {22,14}, { 7, 2}, {20, 9}, {28,11}, {10, 4},
768 {40, 0}, {35,13}, {38,32}, {13, 2}, {39, 1}, { 2, 0}, {38,19}, {41,11}, {32,28}, {39,33},
769 {30,17}, {16, 2}, {17, 6}, {13,10}, { 4, 1}, {10, 0}, {22,19}, { 4, 3}, {12, 7}, {26,21},
770 { 9, 0}, {19,16}, {34,28}, {16, 9}, { 9, 8}, {23, 0}, { 7, 5}, {10, 5}, {34,18}, {14, 6},
771 {30, 5}, {31,18}, {20,15}, {34,22}, {35,12}, {23, 1}, {35,10}, { 9, 3}, {27,15}, {17,13},
772 {37,30}, {26, 0}, {28,17}, {38,33}, {38, 5}, {16, 4}, {13, 1}, {28, 3}, { 5, 4}, {12, 2},
773 {17, 9}, {31,29}, {22,11}, {40,17}, {25, 4}, {28,27}, {29, 6}, {34, 1}, {14, 8}, {32,15},
774 {39,32}, { 6, 5}, {19, 4}, {18, 5}, {32,20}, {38,13}, {12,10}, {24, 0}, {22,15}, {36,18},
775 { 6, 3}, {34,23}, {33,15}, {22, 7}, {22,12}, {40,28}, {35,18}, {22, 5}, {29,23}, {37,34},
776 {16,13}, {23,18}, {37,22}, {29,12}, {19, 2}, {14, 9}, {34,19}, {19,10}, {25,12}, {38,21},
777 {28, 1}, {33,20}, {27, 4}, {11, 7}, {31,23}, {17, 7}, {17, 8}, {39, 8}, {40,21}, {16,15},
778 {17, 5}, {30,18}, {39, 7}, {37,25}, {41,34}, {30,24}, {18, 1}, { 3, 1}, { 9, 4}, {22,21},
779 {31, 5}, {40, 3}, {35,25}, {32, 2}, { 4, 2}, {38,31}, {14, 3}, {21, 9}, {17,12}, {16, 1},
780 {35,29}, {23,22}, {20, 1}, {34, 3}, {17, 1}, {13, 6}, {40,14}, {17,11}, {38,17}, {40,16},
781 {20, 4}, {23,11}, {12, 4}, { 3, 2}, {40,33}, {14, 4}, {21, 2}, {33,26}, {38,34}, {29,18},
782 {21, 7}, {16, 8}
783 // clang-format on
784};
785
786template <size_t tSize>
787const int FREAKDescriptorT<tSize>::kernelRadius1X[kernelRadius1Elements] = { 0, -1, 0, 1, 0 };
788
789template <size_t tSize>
790const int FREAKDescriptorT<tSize>::kernelRadius1Y[kernelRadius1Elements] = { -1, 0, 0, 0, 1 };
791
792template <size_t tSize>
793const int FREAKDescriptorT<tSize>::kernelRadius2X[kernelRadius2Elements] = { 0, -1, 0, 1, -2, -1, 0, 1, 2, -1, 0, 1, 0 };
794
795template <size_t tSize>
796const int FREAKDescriptorT<tSize>::kernelRadius2Y[kernelRadius2Elements] = { -2, -1, -1, -1, 0, 0, 0, 0, 0, 1, 1, 1, 2 };
797
798template <size_t tSize>
799const int FREAKDescriptorT<tSize>::kernelRadius3X[kernelRadius3Elements] =
800{
801 // clang-format off
802 0, -2, -1, 0, 1, 2, -2, -1, 0, 1,
803 2, -3, -2, -1, 0, 1, 2, 3, -2, -1,
804 0, 1, 2, -2, -1, 0, 1, 2, 0
805 // clang-format on
806};
807
808template <size_t tSize>
809const int FREAKDescriptorT<tSize>::kernelRadius3Y[kernelRadius3Elements] =
810{
811 // clang-format off
812 -3, -2, -2, -2, -2, -2, -1, -1, -1, -1,
813 -1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
814 1, 1, 1, 2, 2, 2, 2, 2, 3
815 // clang-format on
816};
817
818template <size_t tSize>
819const int FREAKDescriptorT<tSize>::kernelRadius7X[kernelRadius7Elements] =
820{
821 // clang-format off
822 0, -3, -2, -1, 0, 1, 2, 3, -4, -3,
823 -2, -1, 0, 1, 2, 3, 4, -5, -4, -3,
824 -2, -1, 0, 1, 2, 3, 4, 5, -6, -5,
825 -4, -3, -2, -1, 0, 1, 2, 3, 4, 5,
826 6, -6, -5, -4, -3, -2, -1, 0, 1, 2,
827 3, 4, 5, 6, -6, -5, -4, -3, -2, -1,
828 0, 1, 2, 3, 4, 5, 6, -7, -6, -5,
829 -4, -3, -2, -1, 0, 1, 2, 3, 4, 5,
830 6, 7, -6, -5, -4, -3, -2, -1, 0, 1,
831 2, 3, 4, 5, 6, -6, -5, -4, -3, -2,
832 -1, 0, 1, 2, 3, 4, 5, 6, -6, -5,
833 -4, -3, -2, -1, 0, 1, 2, 3, 4, 5,
834 6, -5, -4, -3, -2, -1, 0, 1, 2, 3,
835 4, 5, -4, -3, -2, -1, 0, 1, 2, 3,
836 4, -3, -2, -1, 0, 1, 2, 3, 0
837 // clang-format on
838};
839
840template <size_t tSize>
841const int FREAKDescriptorT<tSize>::kernelRadius7Y[kernelRadius7Elements] =
842{
843 // clang-format off
844 -7, -6, -6, -6, -6, -6, -6, -6, -5, -5,
845 -5, -5, -5, -5, -5, -5, -5, -4, -4, -4,
846 -4, -4, -4, -4, -4, -4, -4, -4, -3, -3,
847 -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
848 -3, -2, -2, -2, -2, -2, -2, -2, -2, -2,
849 -2, -2, -2, -2, -1, -1, -1, -1, -1, -1,
850 -1, -1, -1, -1, -1, -1, -1, 0, 0, 0,
851 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
852 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
853 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
854 2, 2, 2, 2, 2, 2, 2, 2, 3, 3,
855 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
856 3, 4, 4, 4, 4, 4, 4, 4, 4, 4,
857 4, 4, 5, 5, 5, 5, 5, 5, 5, 5,
858 5, 6, 6, 6, 6, 6, 6, 6, 7
859 // clang-format on
860};
861
862template <size_t tSize>
863bool 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)
864{
865 ocean_assert(pointPyramidLevel < pyramid.layers());
866 ocean_assert(inverseFocalLength > 0.0f);
867
868 // No descriptors can be computed for points in the coarsest layer of the frame pyramid
869
870 if (pointPyramidLevel + 1u >= pyramid.layers())
871 {
872 return false;
873 }
874
875 // Invalidate the descriptor for now
876
877 freakDescriptor.dataLevels_ = 0u;
878 ocean_assert(freakDescriptor.isValid() == false);
879
880 // Compute the deformation matrix from the position of the image point (and its Jacobian, etc)
881
882 Eigen::Matrix<float, 2, 2> cellDeformationMatrix;
883 if (computeLocalDeformationMatrixAndOrientation(pyramid, point, pointPyramidLevel, unprojectRayIF, inverseFocalLength, pointJacobianMatrixIF, cellDeformationMatrix, freakDescriptor.orientation_) == false)
884 {
885 return false;
886 }
887
888 // Apply the deformation matrix to the locations of all cells
889
892
893 for (size_t i = 0; i < FREAKDescriptorT<tSize>::numberOfCells; ++i)
894 {
895 const Eigen::Vector2f warpedCell = cellDeformationMatrix * Eigen::Vector2f(FREAKDescriptorT<tSize>::cellsX[i], FREAKDescriptorT<tSize>::cellsY[i]);
896 warpedCellX[i] = warpedCell[0];
897 warpedCellY[i] = warpedCell[1];
898 }
899
900 // Compute a descriptor for each intra-level:
901 //
902 // 2^(0/3) = 1,
903 // 2^(1/3) = 1.2599,
904 // 2^(2/3) = 1.5874
905 //
906 const float scaleFactors[3] = { 1.0f, 1.2599f, 1.5874f };
907 for (size_t scaleLevel = 0; scaleLevel < 3; ++scaleLevel)
908 {
910 bool computationFailed = false;
911
912 // Compute the average intensity per cell
913 //
914 // 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:
915 //
916 // 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
917 // 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
918 // 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
919 // 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
920 // for-loop ID: |--- 0 ------------------------->| |--- 1 -------------------------->| |--- 2 -------->| |--- 3 ----------------------------->|
921
922 unsigned int cellId = 0u;
923
924 // Loop 0
925 for (; cellId < 12u; ++cellId)
926 {
927 ocean_assert(pointPyramidLevel + 1u < pyramid.layers());
928 const Frame& nextFramePyramidLayer = pyramid.layer(pointPyramidLevel + 1u);
929
930 const float cellX = point[0] + scaleFactors[scaleLevel] * warpedCellX[cellId];
931 const float cellY = point[1] + scaleFactors[scaleLevel] * warpedCellY[cellId];
932
933 const int cellXi = NumericF::round32(((cellX + 0.5f) * 0.5f) - 0.5f);
934 const int cellYi = NumericF::round32(((cellY + 0.5f) * 0.5f) - 0.5f);
935
936 // Check if the (half-)radius fits into the image, radius = 3, radius / 2 = 1
937 if (cellXi - 1 < 0 || cellXi + 1 >= int(nextFramePyramidLayer.width()) || cellYi - 1 < 0 || cellYi + 1 >= int(nextFramePyramidLayer.height()))
938 {
939 computationFailed = true;
940 break;
941 }
942
943 ocean_assert(cellXi >= 0 && cellXi < int(nextFramePyramidLayer.width()) && cellYi >= 0 && cellYi < int(nextFramePyramidLayer.height()));
944
945 if (computeAverageCellIntensity<true>(nextFramePyramidLayer, cellXi, cellYi, kernelRadius3X, kernelRadius3Y, kernelRadius3Elements, cellIntensities[cellId]) == false)
946 {
947 computationFailed = true;
948 break;
949 }
950 }
951
952 // Loop 1
953 const Frame& currentFramePyramidLayer = pyramid.layer(pointPyramidLevel);
954
955 ocean_assert(cellId == 12u || computationFailed == true);
956 for (; computationFailed == false && cellId < 24u; ++cellId)
957 {
958 const float cellX = point[0] + scaleFactors[scaleLevel] * warpedCellX[cellId];
959 const float cellY = point[1] + scaleFactors[scaleLevel] * warpedCellY[cellId];
960
961 const int cellXi = NumericF::round32(cellX);
962 const int cellYi = NumericF::round32(cellY);
963
964 ocean_assert(cellXi >= 0 && cellXi < int(currentFramePyramidLayer.width()) && cellYi >= 0 && cellYi < int(currentFramePyramidLayer.height()));
965
966 if (computeAverageCellIntensity<false>(currentFramePyramidLayer, cellXi, cellYi, kernelRadius3X, kernelRadius3Y, kernelRadius3Elements, cellIntensities[cellId]) == false)
967 {
968 computationFailed = true;
969 break;
970 }
971 }
972
973 // Loop 2
974 ocean_assert(cellId == 24u || computationFailed == true);
975 for (; computationFailed == false && cellId < 30u; ++cellId)
976 {
977 const float cellX = point[0] + scaleFactors[scaleLevel] * warpedCellX[cellId];
978 const float cellY = point[1] + scaleFactors[scaleLevel] * warpedCellY[cellId];
979
980 const int cellXi = NumericF::round32(cellX);
981 const int cellYi = NumericF::round32(cellY);
982
983 ocean_assert(cellXi >= 0 && cellXi < int(currentFramePyramidLayer.width()) && cellYi >= 0 && cellYi < int(currentFramePyramidLayer.height()));
984 if (computeAverageCellIntensity<false>(currentFramePyramidLayer, cellXi, cellYi, kernelRadius2X, kernelRadius2Y, kernelRadius2Elements, cellIntensities[cellId]) == false)
985 {
986 computationFailed = true;
987 break;
988 }
989 }
990
991 // Loop 3
992 ocean_assert(cellId == 30u || computationFailed == true);
993 for (; computationFailed == false && cellId < FREAKDescriptorT<tSize>::numberOfCells; ++cellId)
994 {
995 const float cellX = point[0] + scaleFactors[scaleLevel] * warpedCellX[cellId];
996 const float cellY = point[1] + scaleFactors[scaleLevel] * warpedCellY[cellId];
997
998 const int cellXi = NumericF::round32(cellX);
999 const int cellYi = NumericF::round32(cellY);
1000
1001 ocean_assert(cellXi >= 0 && cellXi < int(currentFramePyramidLayer.width()) && cellYi >= 0 && cellYi < int(currentFramePyramidLayer.height()));
1002
1003 if (computeAverageCellIntensity<false>(currentFramePyramidLayer, cellXi, cellYi, kernelRadius1X, kernelRadius1Y, kernelRadius1Elements, cellIntensities[cellId]) == false)
1004 {
1005 computationFailed = true;
1006 break;
1007 }
1008 }
1009
1010 if (computationFailed)
1011 {
1012 break;
1013 }
1014
1015 // Compute the binary descriptor for the current scale level
1016
1017 for (size_t i = 0; i < tSize; ++i)
1018 {
1019 uint8_t partialDescriptor = 0u;
1020
1021 for (size_t j = 0; j < 8; ++j)
1022 {
1023 partialDescriptor = uint8_t(partialDescriptor << 1u);
1024
1025 const size_t pair = i * 8 + j;
1026 ocean_assert(pair < FREAKDescriptorT<tSize>::numberOfCellPairs);
1029
1030 if (cellIntensities[FREAKDescriptorT<tSize>::cellPairs[pair][0]] > cellIntensities[FREAKDescriptorT<tSize>::cellPairs[pair][1]])
1031 {
1032 partialDescriptor = partialDescriptor | 1u;
1033 }
1034 }
1035 freakDescriptor.data_[scaleLevel][i] = partialDescriptor;
1036 }
1037
1038 freakDescriptor.dataLevels_ = (unsigned int)(scaleLevel + 1);
1039 }
1040
1041 ocean_assert(freakDescriptor.isValid() == false || (NumericF::isInsideRange(-NumericF::pi(), freakDescriptor.orientation_, NumericF::pi()) && freakDescriptor.dataLevels_ <= 3u));
1042
1043 return freakDescriptor.isValid();
1044}
1045
1046template <size_t tSize>
1047template <bool tEnableBorderChecks>
1048bool FREAKDescriptorT<tSize>::computeAverageCellIntensity(const Frame& framePyramidLayer, int cellX, int cellY, const int* kernelX, const int* kernelY, const size_t kernelElements, PixelType& averageIntensity)
1049{
1050 ocean_assert(framePyramidLayer.isValid());
1051 ocean_assert(kernelX != nullptr && kernelY != nullptr && kernelElements != 0u);
1052
1053 const unsigned int width = framePyramidLayer.width();
1054 const unsigned int height = framePyramidLayer.height();
1055 const unsigned int frameStrideElements = framePyramidLayer.strideElements();
1056 const PixelType* frame = framePyramidLayer.constdata<PixelType>();
1057
1058 if constexpr (tEnableBorderChecks)
1059 {
1060 unsigned int sum = 0u;
1061 unsigned int sumElements = 0u;
1062
1063 for (size_t i = 0; i < kernelElements; ++i)
1064 {
1065 const int x = cellX + kernelX[i];
1066 const int y = cellY + kernelY[i];
1067
1068 if (x >= 0 && x < int(width) && y >= 0 && y < int(height))
1069 {
1070 sum += frame[(unsigned int)y * frameStrideElements + (unsigned int)x];
1071 sumElements++;
1072 }
1073 }
1074
1075 ocean_assert(sumElements != 0u);
1076 ocean_assert(float(sum) / float(sumElements) <= 255.0f);
1077
1078 averageIntensity = PixelType(float(sum) / float(sumElements)); // TODOX No rounding in original. Add it here?
1079 }
1080 else
1081 {
1082 unsigned int sum = 0u;
1083
1084 for (size_t i = 0; i < kernelElements; ++i)
1085 {
1086 const int x = cellX + kernelX[i];
1087 const int y = cellY + kernelY[i];
1088 ocean_assert_and_suppress_unused(x >= 0 && x < int(width) && y >= 0 && y < int(height), height);
1089
1090 sum += frame[(unsigned int)y * frameStrideElements + (unsigned int)x];
1091 }
1092
1093 ocean_assert(float(sum) / float(kernelElements) <= 255.0f);
1094
1095 averageIntensity = PixelType(float(sum) / float(kernelElements)); // TODOX No rounding in original. Add it here?
1096 }
1097
1098 return true;
1099}
1100
1101template <size_t tSize>
1102template <typename TImagePoint>
1103void 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)
1104{
1105 ocean_assert(framePyramid != nullptr && framePyramid->isValid());
1106 ocean_assert(points != nullptr && pointsSize != 0u);
1107 ocean_assert(pointsPyramidLevel < framePyramid->layers());
1108 ocean_assert(freakDescriptor != nullptr);
1109 ocean_assert(cameraDerivativeFunctor != nullptr);
1110 ocean_assert_and_suppress_unused(firstPoint + numberOfPoints <= pointsSize && numberOfPoints != 0u, pointsSize);
1111
1112 for (unsigned int i = firstPoint; i < firstPoint + numberOfPoints; ++i)
1113 {
1114 ocean_assert(i < pointsSize);
1115
1116 const TImagePoint& point = points[i];
1117
1118 const Eigen::Vector2f eigenPoint(float(point.x()), float(point.y()));
1119
1120 float inverseFocalLength;
1121 const CameraDerivativeData data = cameraDerivativeFunctor->computeCameraDerivativeData(eigenPoint, pointsPyramidLevel, inverseFocalLength);
1122
1123 computeDescriptor(*framePyramid, eigenPoint, pointsPyramidLevel, freakDescriptor[i], data.unprojectRayIF, inverseFocalLength, data.pointJacobianMatrixIF);
1124 }
1125}
1126
1127template <size_t tSize>
1128bool 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)
1129{
1130 ocean_assert(pyramid.isValid());
1131 ocean_assert(pointPyramidLevel < pyramid.layers());
1133 ocean_assert(NumericF::isEqualEps(unprojectRayIF.norm() - 1.0f));
1134 ocean_assert(inverseFocalLength > 0.0f);
1135 ocean_assert(projectionJacobianMatrix.IsRowMajor == false);
1136
1137 // In the plane perpendicular to the unprojection ray, determine two arbitrary but perpendicular vectors
1138
1139 const Eigen::Vector3f directionY(0, 1, 0);
1140 const Eigen::Vector3f nx = directionY.cross(unprojectRayIF).normalized() * inverseFocalLength;
1141 const Eigen::Vector3f ny = unprojectRayIF.cross(nx);
1142
1143 // Compute an initial warping matrix from the perpendicular vectors
1144
1145 Eigen::Matrix<float, 3, 2> N;
1146 N.col(0) = nx;
1147 N.col(1) = ny;
1148 const Eigen::Matrix<float, 2, 2> initialDeformationMatrix = projectionJacobianMatrix * N;
1149
1150 // Make sure that the orientation kernel (radius 7) fits inside the current pyramid layer
1151
1152 constexpr float cornerX[4] = {-7.0f, -7.0f, 7.0f, 7.0f};
1153 constexpr float cornerY[4] = {-7.0f, 7.0f, -7.0f, 7.0f};
1154 const Frame& framePyramidLevel = pyramid.layer(pointPyramidLevel);
1155
1156 for (size_t i = 0; i < 4; ++i)
1157 {
1158 const Eigen::Vector2f warpedCorner = point + initialDeformationMatrix * Eigen::Vector2f(cornerX[i], cornerY[i]);
1159
1160 const unsigned int x = (unsigned int)(NumericF::round32(warpedCorner.x()));
1161 const unsigned int y = (unsigned int)(NumericF::round32(warpedCorner.y()));
1162
1163 if (x >= framePyramidLevel.width() || y >= framePyramidLevel.height())
1164 {
1165 return false;
1166 }
1167 }
1168
1169 // Compute weighted intensity over the kernel
1170
1171 int magnitudeX = 0;
1172 int magnitudeY = 0;
1173 const unsigned int strideElements = framePyramidLevel.strideElements();
1174 const PixelType* data = framePyramidLevel.constdata<PixelType>();
1175
1176 for (size_t i = 0; i < FREAKDescriptorT<tSize>::kernelRadius7Elements; ++i)
1177 {
1178 const Eigen::Vector2f p = point + initialDeformationMatrix * Eigen::Vector2f(float(FREAKDescriptorT<tSize>::kernelRadius7X[i]), float(FREAKDescriptorT<tSize>::kernelRadius7Y[i]));
1179
1180 const int u = NumericF::round32(p[0]);
1181 const int v = NumericF::round32(p[1]);
1182
1183 ocean_assert(((unsigned int)(v) * strideElements + (unsigned int)(u)) < framePyramidLevel.size());
1184 const int intensity = int(data[(unsigned int)(v) * strideElements + (unsigned int)(u)]);
1185
1186 // 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)
1187 magnitudeX += FREAKDescriptorT<tSize>::kernelRadius7X[i] * intensity;
1188 magnitudeY += FREAKDescriptorT<tSize>::kernelRadius7Y[i] * intensity;
1189 }
1190
1191 if (magnitudeX == 0 && magnitudeY == 0)
1192 {
1193 return false;
1194 }
1195
1196 // Compute axes aligned with keypoint orientation and use them to compute the deformation matrix
1197
1198 const Eigen::Vector3f gy = (nx * float(magnitudeX) + ny * float(magnitudeY)).normalized() * inverseFocalLength;
1199 const Eigen::Vector3f gx = gy.cross(unprojectRayIF);
1200
1201 Eigen::Matrix<float, 3, 2> G;
1202 G.col(0) = gx;
1203 G.col(1) = gy;
1204
1205 deformationMatrix = projectionJacobianMatrix * G;
1206
1207 // Compute angle in image coordinates
1208
1209 const Eigen::Vector2f patchY = projectionJacobianMatrix * gy;
1210 orientation = NumericF::atan2(patchY[1], patchY[0]);
1211 ocean_assert(-NumericF::pi() < orientation && orientation <= NumericF::pi());
1212
1213 return true;
1214}
1215
1216template <size_t tSize>
1217FramePyramid FREAKDescriptorT<tSize>::createFramePyramidWithBlur8BitsPerChannel(const Frame& frame, const unsigned int kernelWidth, const unsigned int kernelHeight, const unsigned int layers, Worker* worker)
1218{
1219 ocean_assert(frame.isValid() && frame.dataType() == FrameType::DT_UNSIGNED_INTEGER_8);
1220 ocean_assert(kernelWidth != 0u && kernelWidth % 2u == 1u);
1221 ocean_assert(kernelHeight != 0u && kernelHeight % 2u == 1u);
1222 ocean_assert(layers >= 1u);
1223
1224 Frame reusableFrame;
1225
1226 const FramePyramid::DownsamplingFunction downsamplingFunction = std::bind(&FREAKDescriptorT<tSize>::blurAndDownsampleByTwo11, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, kernelWidth, kernelHeight, reusableFrame);
1227
1228 FramePyramid framePyramid(frame, downsamplingFunction, layers, true /*copyFirstLayer*/, worker);
1229
1230 if (framePyramid.layers() != layers)
1231 {
1232 return FramePyramid();
1233 }
1234
1235 return framePyramid;
1236}
1237
1238template <size_t tSize>
1239bool FREAKDescriptorT<tSize>::blurAndDownsampleByTwo11(const Frame& finerLayer, Frame& coarserLayer, Worker* worker, const unsigned int kernelWidth, const unsigned int kernelHeight, Frame& reusableFrame)
1240{
1241 ocean_assert(finerLayer.isValid());
1242 ocean_assert(coarserLayer.isValid());
1243
1244 ocean_assert(kernelWidth >= 1u && kernelWidth % 2u == 1u);
1245 ocean_assert(kernelHeight >= 1u && kernelHeight % 2u == 1u);
1246
1247 ocean_assert(finerLayer.numberPlanes() == 1u && finerLayer.dataType() == FrameType::DT_UNSIGNED_INTEGER_8);
1248 ocean_assert(finerLayer.isPixelFormatCompatible(coarserLayer.pixelFormat()));
1249
1250 if (!reusableFrame.set(finerLayer.frameType(), false /*forceOwner*/, true /*forceWritable*/))
1251 {
1252 ocean_assert(false && "This should never happen!");
1253 return false;
1254 }
1255
1256 ocean_assert(reusableFrame.isValid());
1257
1258 const Frame* sourceLayer = &finerLayer;
1259
1260 if (kernelWidth <= finerLayer.width() && kernelHeight <= finerLayer.height())
1261 {
1262 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))
1263 {
1264 return false;
1265 }
1266
1267 sourceLayer = &reusableFrame;
1268 }
1269
1270 CV::FrameShrinker::downsampleByTwo8BitPerChannel11(sourceLayer->constdata<uint8_t>(), coarserLayer.data<uint8_t>(), sourceLayer->width(), sourceLayer->height(), sourceLayer->channels(), sourceLayer->paddingElements(), coarserLayer.paddingElements(), worker);
1271
1272 return true;
1273}
1274
1275template <size_t tSize>
1276bool 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)
1277{
1278 ocean_assert(yFrame.isValid() && FrameType::arePixelFormatsCompatible(yFrame.pixelFormat(), FrameType::genericPixelFormat<std::uint8_t, 1u>()));
1279 ocean_assert(minFrameArea != 0u && minFrameArea <= maxFrameArea);
1280 ocean_assert(expectedHarrisCorners640x480 != 0u);
1281 ocean_assert(harrisCornersReductionScale > Scalar(0) && harrisCornersReductionScale < Scalar(1));
1282 ocean_assert(harrisCornerThreshold <= 512u);
1283 ocean_assert(border > Scalar(0) && Scalar(2) * border < Scalar(yFrame.width()) && Scalar(2) * border < Scalar(yFrame.height()));
1284
1285 corners.clear();
1286 cornerPyramidLevels.clear();
1287 descriptors.clear();
1288
1289 // 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
1290
1291 const unsigned int frameArea = yFrame.width() * yFrame.height();
1292 const unsigned int startLayerIndex = (unsigned int)std::max(0, Numeric::round32((Numeric::log10(Scalar(frameArea) / Scalar(maxFrameArea)) / Numeric::log10(Scalar(4)))));
1293 const unsigned int lastLayerIndex = (unsigned int)(Numeric::log10(Scalar(frameArea) / Scalar(minFrameArea)) / Numeric::log10(Scalar(4)));
1294 ocean_assert(startLayerIndex <= lastLayerIndex);
1295
1296 // Generate a frame pyramid (+1 extra layer)
1297
1298 const FramePyramid pyramid = FREAKDescriptorT<tSize>::createFramePyramidWithBlur8BitsPerChannel(yFrame, 5u, 5u, lastLayerIndex + 2u, worker);
1299
1300 if (pyramid.isValid() == false || pyramid.layers() <= lastLayerIndex || cameraDerivativeFunctor.supportedPyramidLevels() <= lastLayerIndex)
1301 {
1302 return false;
1303 }
1304
1305 // The number of expected Harris corners is defined at a reference image size of 640x480 pixels. So, it necessary to scale this number
1306 // 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
1307 // distributed over all used pyramid layers.
1308
1309 const unsigned int startLayerArea = pyramid[startLayerIndex].width() * pyramid[startLayerIndex].height();
1310
1311#if 0
1312 // Disabled but leaving it here as reference
1313 unsigned int expectedHarrisCornersOnLevel = (unsigned int)(Numeric::round32(Scalar(expectedHarrisCorners640x480) * Scalar(startLayerArea) / Scalar(640u * 480u)));
1314 expectedHarrisCornersOnLevel = (unsigned int)(Scalar(expectedHarrisCornersOnLevel) * (Scalar(1) - harrisCornersReductionScale) / (Scalar(1) - std::pow(harrisCornersReductionScale, Scalar(lastLayerIndex - startLayerIndex))));
1315#else
1316 const Scalar expectedHarrisCornersOnStartLayerF = Scalar(expectedHarrisCorners640x480) * Scalar(startLayerArea) / Scalar(640u * 480u);
1317 unsigned int expectedHarrisCornersOnLevel = (unsigned int)Numeric::round32(expectedHarrisCornersOnStartLayerF * (Scalar(1) - harrisCornersReductionScale) / (Scalar(1) - std::pow(harrisCornersReductionScale, Scalar(lastLayerIndex - startLayerIndex))));
1318#endif
1319
1320 // For each layer of the pyramid, extract Harris corners and compute their descriptors
1321
1322 for (unsigned int layer = startLayerIndex; layer <= lastLayerIndex; ++layer)
1323 {
1324 ocean_assert(layer + 1u < pyramid.layers());
1325 ocean_assert(corners.size() == descriptors.size());
1326 ocean_assert(corners.size() == cornerPyramidLevels.size());
1327
1328 if (expectedHarrisCornersOnLevel == 0u)
1329 {
1330 break;
1331 }
1332
1333 const Frame& pyramidLayer = pyramid[layer];
1334
1335 if (pyramidLayer.width() < Scalar(2) * border + Scalar(10) || pyramidLayer.height() < Scalar(2) * border + Scalar(10))
1336 {
1337 break;
1338 }
1339
1340 HarrisCorners harrisCornersOnLevel;
1341 if (CV::Detector::HarrisCornerDetector::detectCorners(pyramidLayer.constdata<uint8_t>(), pyramidLayer.width(), pyramidLayer.height(), pyramidLayer.paddingElements(), harrisCornerThreshold, yFrameIsUndistorted, harrisCornersOnLevel, determineExactHarrisCornerPositions, worker) == false)
1342 {
1343 return false;
1344 }
1345
1346 if (harrisCornersOnLevel.empty())
1347 {
1348 continue;
1349 }
1350
1351 // Select new corners. Make sure they are distributed approximately equal. Append them to the corresponding return value (corners)
1352
1353 ocean_assert(corners.size() == descriptors.size());
1354 ocean_assert(corners.size() == cornerPyramidLevels.size());
1355 const size_t firstNewCornerIndex = corners.size();
1356
1357 if (harrisCornersOnLevel.size() > expectedHarrisCornersOnLevel)
1358 {
1359 // Sort corners by the corner strength in descending order and distribute them over a regular grid of bins
1360
1361 std::sort(harrisCornersOnLevel.begin(), harrisCornersOnLevel.end());
1362
1363 unsigned int horizontalBins = 0u;
1364 unsigned int verticalBins = 0u;
1365 Geometry::SpatialDistribution::idealBins(pyramidLayer.width(), pyramidLayer.height(), expectedHarrisCornersOnLevel / 2u, horizontalBins, verticalBins);
1366 ocean_assert(horizontalBins != 0u && verticalBins != 0u);
1367
1368 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));
1369
1370 corners.insert(corners.end(), newCorners.begin(), newCorners.end());
1371 }
1372 else
1373 {
1374 for (const HarrisCorner& corner : harrisCornersOnLevel)
1375 {
1376 if (corner.observation().x() >= border && corner.observation().x() < Scalar(pyramidLayer.width()) - border &&
1377 corner.observation().y() >= border && corner.observation().y() < Scalar(pyramidLayer.height()) - border)
1378 {
1379 corners.emplace_back(corner);
1380 }
1381 }
1382 }
1383
1384 ocean_assert(firstNewCornerIndex <= corners.size());
1385 const size_t newCornersAdded = corners.size() - firstNewCornerIndex;
1386
1387 if (newCornersAdded == 0)
1388 {
1389 continue;
1390 }
1391
1392 ocean_assert(firstNewCornerIndex + newCornersAdded == corners.size());
1393
1394#if defined(OCEAN_DEBUG)
1395 for (size_t i = firstNewCornerIndex; i < corners.size(); ++i)
1396 {
1397 ocean_assert(corners[i].observation().x() >= border && corners[i].observation().x() <= Scalar(pyramidLayer.width()) - border);
1398 ocean_assert(corners[i].observation().y() >= border && corners[i].observation().y() <= Scalar(pyramidLayer.height()) - border);
1399 }
1400#endif // OCEAN_DEBUG
1401
1402 // Store the pyramid level of the newly detected corners
1403
1404 cornerPyramidLevels.insert(cornerPyramidLevels.end(), newCornersAdded, layer);
1405
1406 // Extract the locations of the detected corners for the computation of their descriptors
1407
1408 std::vector<Eigen::Vector2f> observations;
1409 observations.reserve(newCornersAdded);
1410
1411 for (size_t i = firstNewCornerIndex; i < corners.size(); ++i)
1412 {
1413 observations.emplace_back(float(corners[i].observation().x()), float(corners[i].observation().y()));
1414 }
1415
1416 // Compute the descriptors (and directly append them to the return value)
1417
1418 ocean_assert(corners.size() > descriptors.size());
1419 descriptors.resize(corners.size());
1420
1421 ocean_assert(descriptors.begin() + size_t(firstNewCornerIndex) + observations.size() == descriptors.end());
1422 FREAKDescriptorT<tSize>::computeDescriptors(pyramid, observations.data(), observations.size(), layer, descriptors.data() + firstNewCornerIndex, cameraDerivativeFunctor, worker);
1423
1424 expectedHarrisCornersOnLevel = (unsigned int)Numeric::round32(Scalar(expectedHarrisCornersOnLevel) * harrisCornersReductionScale);
1425
1426 ocean_assert(corners.size() == descriptors.size());
1427 ocean_assert(corners.size() == cornerPyramidLevels.size());
1428 }
1429
1430 ocean_assert(corners.size() == descriptors.size());
1431 ocean_assert(corners.size() == cornerPyramidLevels.size());
1432
1433 if (removeInvalid && corners.empty() == false)
1434 {
1435 size_t i = 0;
1436 while (i < corners.size())
1437 {
1438 if (descriptors[i].isValid())
1439 {
1440 i++;
1441 }
1442 else
1443 {
1444 ocean_assert(corners.empty() == false);
1445 corners[i] = corners.back();
1446 corners.pop_back();
1447
1448 cornerPyramidLevels[i] = cornerPyramidLevels.back();
1449 cornerPyramidLevels.pop_back();
1450
1451 descriptors[i] = descriptors.back();
1452 descriptors.pop_back();
1453 }
1454 }
1455 }
1456
1457 ocean_assert(corners.size() == descriptors.size());
1458 ocean_assert(corners.size() == cornerPyramidLevels.size());
1459
1460 return true;
1461}
1462
1463} // namespace Detector
1464
1465} // namespace CV
1466
1467} // namespace Ocean
1468
1469#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:544
static const float cellsY[numberOfCells]
The pre-defined vertical coordinates of the cells.
Definition FREAKDescriptor.h:494
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:500
static constexpr size_t kernelRadius3Elements
Number of elements in the circular kernel with radius 3.
Definition FREAKDescriptor.h:521
static const float cellsX[numberOfCells]
The pre-defined horizontal coordinates of the cells.
Definition FREAKDescriptor.h:491
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:1128
static constexpr unsigned int descriptorMatchingThreshold(const unsigned int percent)
Returns the descriptor matching threshold based on a percentage of the descriptor size.
Definition FREAKDescriptor.h:651
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:497
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:509
OCEAN_FORCE_INLINE unsigned int distance(const FREAKDescriptorT< tSize > &descriptor) const
Returns the distance between this descriptor and a second descriptor.
Definition FREAKDescriptor.h:587
float orientation() const
Returns the orientation of the descriptor in Radian.
Definition FREAKDescriptor.h:561
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:541
unsigned int descriptorLevels() const
Returns the number of levels stored in the multi-level descriptor.
Definition FREAKDescriptor.h:580
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:1103
std::array< SinglelevelDescriptorData, 3 > MultilevelDescriptorData
Multi-level FREAK descriptor data; if possible, this implementation computes the descriptor at three ...
Definition FREAKDescriptor.h:229
static unsigned int calculateDistance(const FREAKDescriptorT< tSize > &descriptorA, const FREAKDescriptorT< tSize > &descriptorB)
Returns the distance between two descriptors.
Definition FREAKDescriptor.h:639
static const int kernelRadius7X[kernelRadius7Elements]
The pre-defined horizontal coordinates of the circular kernel with radius 7.
Definition FREAKDescriptor.h:533
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:1239
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:1276
static const int kernelRadius7Y[kernelRadius7Elements]
The pre-defined vertical coordinates of the circular kernel with radius 7.
Definition FREAKDescriptor.h:536
static constexpr size_t numberOfCells
The number of cells per keypoint that this implementation is using.
Definition FREAKDescriptor.h:488
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:1217
unsigned int dataLevels_
Number of valid levels in the multi-level descriptor data above, range: [0, 3].
Definition FREAKDescriptor.h:547
MultilevelDescriptorData & data()
Returns the descriptor data (writable)
Definition FREAKDescriptor.h:568
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:674
static const int kernelRadius3X[kernelRadius3Elements]
The pre-defined horizontal coordinates of the circular kernel with radius 3.
Definition FREAKDescriptor.h:524
static const int kernelRadius3Y[kernelRadius3Elements]
The pre-defined vertical coordinates of the circular kernel with radius 3.
Definition FREAKDescriptor.h:527
static const int kernelRadius2Y[kernelRadius2Elements]
The pre-defined vertical coordinates of the circular kernel with radius 2.
Definition FREAKDescriptor.h:518
static const int kernelRadius2X[kernelRadius2Elements]
The pre-defined horizontal coordinates of the circular kernel with radius 2.
Definition FREAKDescriptor.h:515
static constexpr size_t kernelRadius2Elements
Number of elements in the circular kernel with radius 2.
Definition FREAKDescriptor.h:512
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:1048
static constexpr size_t size()
Returns the length of this descriptor in bytes.
Definition FREAKDescriptor.h:667
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:863
static const int kernelRadius1X[kernelRadius1Elements]
The pre-defined horizontal coordinates of the circular kernel with radius 1.
Definition FREAKDescriptor.h:506
static constexpr size_t kernelRadius1Elements
Number of elements in the circular kernel with radius 1.
Definition FREAKDescriptor.h:503
bool isValid() const
Returns true if this is a valid descriptor.
Definition FREAKDescriptor.h:661
static constexpr size_t kernelRadius7Elements
Number of elements in the circular kernel with radius 7.
Definition FREAKDescriptor.h:530
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