Ocean
HemiCube.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) Meta Platforms, Inc. and affiliates.
3  *
4  * This source code is licensed under the MIT license found in the
5  * LICENSE file in the root directory of this source tree.
6  */
7 
8 #ifndef META_OCEAN_CV_DETECTOR_HEMI_CUBE_H
9 #define META_OCEAN_CV_DETECTOR_HEMI_CUBE_H
10 
12 
13 #include "ocean/base/Frame.h"
14 
15 #include "ocean/cv/PixelPosition.h"
16 
17 #include "ocean/math/FiniteLine2.h"
18 #include "ocean/math/Vector2.h"
19 
20 namespace Ocean
21 {
22 
23 namespace CV
24 {
25 
26 namespace Detector
27 {
28 
29 /**
30  * Data structure used for efficient grouping to 2D line segments
31  * This data structure is inspired by and derived from the HemiCube of Rick Szeliski and Daniel Scharstein.
32  * @ingroup cvdetector
33  */
34 class OCEAN_CV_DETECTOR_EXPORT HemiCube
35 {
36  public:
37 
38  /// The location of a line in the cube map is defined as the triple (binX, binY, faceIndex), faceIndex = {0, 1, 2}
40 
41  /**
42  * Helper to compute hash values for map indices
43  */
44  struct MapIndexHash
45  {
46  /**
47  * Compute hash value for a map index
48  * @param mapIndex The map index to be hashed
49  * @return Hash value of the input map index
50  */
51  size_t operator()(const MapIndex& mapIndex) const noexcept;
52  };
53 
54  /// The actual cube map: maps a line to a bin (set of line indices)
55  typedef std::unordered_map<MapIndex, IndexSet32, MapIndexHash> Map;
56 
57  public:
58 
59  /**
60  * Default constructor
61  */
62  HemiCube() = default;
63 
64  /**
65  * Constructor
66  * @param bins Number of bins along one dimension (cube), range: [1, infinity) (suggested range: [1, 20])
67  * @param imageWidth Width of the image in which the above lines were found, range: [1, infinity)
68  * @param imageHeight Height of the image in which the above lines were found, range: [1, infinity)
69  * @param focalLength Focal length of the original camera; it's used to convert 2D image points into 3D rays; if unknown use something like 1.0; range: (0, infinity)
70  */
71  HemiCube(const unsigned int bins, const unsigned int imageWidth, const unsigned int imageHeight, const Scalar focalLength);
72 
73  /**
74  * Check if the Hemi cube is fully initialized
75  * @return True if the Hemi cube has been fully initialized, otherwise false
76  */
77  inline bool isValid() const;
78 
79  /**
80  * Add a line to the Hemi cube
81  * The line will be added as-is, it will not be merged with similar lines in the Hemi cube
82  * @note There are no checks for duplicate lines.
83  * @param newLine Line to be added to the map, line must be valid
84  */
85  inline void insert(const FiniteLine2& newLine);
86 
87  /**
88  * Add multiple lines to the Hemi cube
89  * The lines will be added as-is, they will not be merged with similar lines in the Hemi cube
90  * The are no checks for duplicate lines.
91  * @param lines Lines to be added to the map, must be valid
92  */
93  inline void insert(const FiniteLines2& lines);
94 
95  /**
96  * Merge a similar lines in set of line segments (brute-force search/no use of Hemi cubes, slow)
97  * Lines that cannot be merged will be added to the output as-is.
98  * @param lines Lines to be merged
99  * @param maxLineDistance Maximum allowed distance of the end-points of one line segment to infinite line of another line segment in order to be considered as collinear, range: [0, infinity)
100  * @param maxLineGap Maximum allowed distance between a pair of collinear line segments in order to be considered mergeable, range: [0, infinity)
101  * @param mapping If specified, this will be the mapping of indices of the lines in parameter `lines` to lines in the return value (i.e. input line i merged into output line j), identical size as parameter `lines`, will be ignored if set to `nullptr`
102  * @param cosAngle Cosine of the maximum angle that is allowed in order for the two line segments to be considered parallel; default: cos(weakEps()), i.e., approx. one, range: [0, 1]
103  * @return The merged lines
104  */
105  static FiniteLines2 mergeGreedyBruteForce(const FiniteLines2& lines, const Scalar maxLineDistance, const Scalar maxLineGap, Indices32* mapping = nullptr, const Scalar cosAngle = Numeric::cos(Numeric::weakEps()));
106 
107  /**
108  * Merge a similar lines in set of line segments
109  * Lines will be added to a Hemi cube internally. For each new input line, retrieve similar lines from the Hemi cube and try to merge it. Then update the Hemi cube.
110  * Lines that cannot be merged will be added to the output as-is.
111  * @param lines Lines to be merged
112  * @param maxLineDistance Maximum allowed distance of the end-points of one line segment to infinite line of another line segment in order to be considered as collinear, range: [0, infinity)
113  * @param maxLineGap Maximum allowed distance between a pair of collinear line segments in order to be considered mergeable
114  * @param mapping If specified, this will be the mapping of indices of the lines in parameter `lines` to lines in the return value (i.e. input line i merged into output line j), identical size as parameter `lines`, will be ignored if set to `nullptr`
115  * @param cosAngle Cosine of the maximum angle that is allowed in order for the two line segments to be considered parallel; default: cos(weakEps()), i.e., approx. one, range: [0, 1]
116  */
117  void merge(const FiniteLines2& lines, const Scalar maxLineDistance, const Scalar maxLineGap, Indices32* mapping = nullptr, const Scalar cosAngle = Numeric::cos(Numeric::weakEps()));
118 
119  /**
120  * Compute line segment that minimizes the distances to the endpoints of the input line segments
121  * Computes the infinite line that minimizes the weighted distances to the endpoints of the input line segments, `line0` and `line1`.
122  * The weights are computed as `w_i = len(line_i) / (len(line0) + len(line1))`
123  * The endpoints of the input lines are then projected on the infinite line to generate a new line segments such that the resulting length is maximized.
124  * @param line0 First line segment
125  * @param line1 Second line segment
126  * @return Merged line segment
127  */
128  static FiniteLine2 fuse(const FiniteLine2& line0, const FiniteLine2& line1);
129 
130  /**
131  * Find similar lines in Hemi cube
132  * @param line Line for which similar lines are searched, must be valid
133  * @param radius Search radius in the Hemi cube, does not search across different faces of the Hemi cube, range: [1, infinity)
134  * @return Indices of the lines in `linesInMap` that are similar to input parameter `line`.
135  * @sa lines(), operator[]()
136  */
137  IndexSet32 find(const FiniteLine2& line, const Scalar radius = Scalar(1)) const;
138 
139  /**
140  * Return the number of lines stored in the Hemi cube
141  * @return Number of lines stored in the Hemi cube
142  */
143  inline size_t size() const;
144 
145  /**
146  * Return the number of bins in the Hemi cube which actually contain data
147  * @return Number of non-empty bins
148  */
149  inline size_t nonEmptyBins() const;
150 
151  /**
152  * Clear this Hemi cube
153  */
154  inline void clear();
155 
156  /**
157  * Get a reference to the lines stored in the Hemi cube
158  * @return All lines in the Hemi cube
159  * @sa find()
160  */
161  inline const FiniteLines2& lines() const;
162 
163  /**
164  * Returns a reference to the internal map of lines indices
165  * @return The map with line indices
166  */
167  const Map& map() const;
168 
169  /**
170  * Get a const reference to a line in the Hemi cube
171  * @param index Index of a line in the Hemi cube, range: [0, HemiCube::size())
172  * @return Const reference a line in the Hemi cube
173  * @sa find()
174  */
175  inline const FiniteLine2& operator[](const unsigned int index) const;
176 
177  /**
178  * Get a reference to a line in the Hemi cube
179  * @param index Index of a line in the Hemi cube, range: [0, HemiCube::size())
180  * @return Reference a line in the Hemi cube
181  * @sa find()
182  */
183  inline FiniteLine2& operator[](const unsigned int index);
184 
185  protected:
186 
187  /**
188  * Given the map index of a line, compute its pixel location in a image representation of the cube map
189  * @param mapIndex The map index of a line segment with range [0, bins - 1]x[0, bins - 1]x[0, 2]
190  * @return The pixel location of the line segment in the cube map
191  * @sa computeMapIndex()
192  */
193  inline PixelPosition hemiCubeCoordinatesFrom(const MapIndex& mapIndex) const;
194 
195  /**
196  * Given a 2D line segment, compute its location (map index) in the cube map
197  * @param line The line for which its map index is computed
198  * @return The map index of the input line
199  */
200  MapIndex mapIndexFrom(const FiniteLine2& line) const;
201 
202  /**
203  * For a given 2D line segment compute its representation as a line equation (3D vector)
204  * The line equation is in normalized coordinates rather than image coordinates
205  * The line equation is the normal of the plane that intersects the line segment and the camera's center of projection
206  * @param line Line for which a line equation is computed
207  * @return The line equation represention of input line
208  * @tparam tScale If true, the line equation (3D vector) will be scaled to point to a face of the cube map, otherwise no scaling will be applied
209  */
210  template <bool tScale>
211  Vector3 lineEquationFrom(const FiniteLine2& line) const;
212 
213  /**
214  * Compute the point on the ray at a specific depth (3D ray which points from the center of projection to the image point on the project plane)
215  * @param point Point that is converted into a 3D ray
216  * @return The 3D ray corresponding to the input point
217  */
218  inline Vector3 rayFrom(const Vector2& point) const;
219 
220  /**
221  * Update a line segment stored in the Hemi cube
222  * Updates the line segment at index `index` and its map index in the Hemi cube.
223  * @param index Index of a line in the Hemi cube, range: [0, HemiCube::size())
224  * @param updatedLine Updated line segment that will be stored at index `index` in the Hemi cube
225  */
226  void updateLine(unsigned int index, const FiniteLine2& updatedLine);
227 
228  protected:
229 
230  /// All lines which are represented by their indices in the map
232 
233  /// Width of the image in which the lines in the cube map have been found; used to convert 2D image points into 3D rays
234  unsigned int imageWidth_ = 0u;
235 
236  /// Height of the image in which the lines in the cube map have been found; used to convert 2D image points into 3D rays
237  unsigned int imageHeight_ = 0u;
238 
239  /// Principal point of the image in which the lines have have been found; using the image center should work just as fine
240  Vector2 principalPoint_ = Vector2(Scalar(0), Scalar(0));
241 
242  /// Focal length of the original camera; used convert 2D image points into 3D rays.
243  Scalar focalLength_ = Scalar(0);
244 
245  /// The actual map data structure
247 
248  /// Number of bins along one dimension (cube)
249  unsigned int numberBins_ = 0u;
250 };
251 
252 inline bool HemiCube::isValid() const
253 {
255 }
256 
257 inline void HemiCube::insert(const FiniteLine2& newLine)
258 {
259  ocean_assert(isValid());
260  ocean_assert(newLine.isValid());
261 
262  // Find or create the bin into which the new line will be placed
263  IndexSet32& bin = map_[mapIndexFrom(newLine)];
264 
265  const unsigned int newLineIndex = (unsigned int)linesInMap_.size();
266  ocean_assert(bin.find(newLineIndex) == bin.end());
267 
268  bin.insert(newLineIndex);
269  linesInMap_.emplace_back(newLine);
270 }
271 
272 inline void HemiCube::insert(const FiniteLines2& lines)
273 {
274  for (const FiniteLine2& line : lines)
275  {
276  insert(line);
277  }
278 }
279 
280 inline size_t HemiCube::size() const
281 {
282  return linesInMap_.size();
283 }
284 
285 inline size_t HemiCube::nonEmptyBins() const
286 {
287  return map_.size();
288 }
289 
290 inline void HemiCube::clear()
291 {
292  linesInMap_.clear();
293  map_.clear();
294 }
295 
296 inline const FiniteLines2& HemiCube::lines() const
297 {
298  return linesInMap_;
299 }
300 
301 inline const HemiCube::Map& HemiCube::map() const
302 {
303  return map_;
304 }
305 
306 inline const FiniteLine2& HemiCube::operator[](const unsigned int index) const
307 {
308  ocean_assert(index < (unsigned int)linesInMap_.size());
309  return linesInMap_[index];
310 }
311 
312 inline FiniteLine2& HemiCube::operator[](const unsigned int index)
313 {
314  ocean_assert(index < (unsigned int)linesInMap_.size());
315  return linesInMap_[index];
316 }
317 
319 {
320  ocean_assert(isValid());
321  ocean_assert(mapIndex[0] < numberBins_ && mapIndex[1] < numberBins_ && mapIndex[2] <= 2u);
322  return CV::PixelPosition(mapIndex[2] * numberBins_ + mapIndex[0], mapIndex[1]);
323 }
324 
325 template <bool tScale>
327 {
328  ocean_assert(line.isValid());
329  const Vector3 ray0 = rayFrom(line.point0());
330  const Vector3 ray1 = rayFrom(line.point1());
331  ocean_assert(Numeric::isNotEqualEps(ray0.length()));
332  ocean_assert(Numeric::isNotEqualEps(ray1.length()));
333 
334  const Vector3 lineEquation = ray0.cross(ray1);
335  ocean_assert(Numeric::isNotEqualEps(lineEquation.length()));
336 
337  if constexpr (tScale)
338  {
339  const Scalar maxValue = std::max(std::abs(lineEquation[0]), std::max(std::abs(lineEquation[1]), std::abs(lineEquation[2])));
340  ocean_assert(Numeric::isNotEqualEps(maxValue));
341  return lineEquation * (Scalar(1.0) / maxValue);
342  }
343 
344  return lineEquation;
345 }
346 
347 Vector3 HemiCube::rayFrom(const Vector2& point) const
348 {
349  const Scalar inPixels = Scalar(0.5) * Scalar(std::max(imageWidth_, imageHeight_));
350  return Vector3(point - principalPoint_, focalLength_ * inPixels);
351 }
352 
353 }
354 
355 }
356 
357 }
358 
359 #endif // META_OCEAN_CV_DETECTOR_HEMI_CUBE_H
Data structure used for efficient grouping to 2D line segments This data structure is inspired by and...
Definition: HemiCube.h:35
Vector2 principalPoint_
Principal point of the image in which the lines have have been found; using the image center should w...
Definition: HemiCube.h:240
size_t size() const
Return the number of lines stored in the Hemi cube.
Definition: HemiCube.h:280
Vector3 rayFrom(const Vector2 &point) const
Compute the point on the ray at a specific depth (3D ray which points from the center of projection t...
Definition: HemiCube.h:347
unsigned int numberBins_
Number of bins along one dimension (cube)
Definition: HemiCube.h:249
const FiniteLine2 & operator[](const unsigned int index) const
Get a const reference to a line in the Hemi cube.
Definition: HemiCube.h:306
Map map_
The actual map data structure.
Definition: HemiCube.h:246
HemiCube()=default
Default constructor.
std::unordered_map< MapIndex, IndexSet32, MapIndexHash > Map
The actual cube map: maps a line to a bin (set of line indices)
Definition: HemiCube.h:55
bool isValid() const
Check if the Hemi cube is fully initialized.
Definition: HemiCube.h:252
size_t nonEmptyBins() const
Return the number of bins in the Hemi cube which actually contain data.
Definition: HemiCube.h:285
MapIndex mapIndexFrom(const FiniteLine2 &line) const
Given a 2D line segment, compute its location (map index) in the cube map.
FiniteLines2 linesInMap_
All lines which are represented by their indices in the map.
Definition: HemiCube.h:231
PixelPosition hemiCubeCoordinatesFrom(const MapIndex &mapIndex) const
Given the map index of a line, compute its pixel location in a image representation of the cube map.
Definition: HemiCube.h:318
VectorT3< unsigned int > MapIndex
The location of a line in the cube map is defined as the triple (binX, binY, faceIndex),...
Definition: HemiCube.h:39
Vector3 lineEquationFrom(const FiniteLine2 &line) const
For a given 2D line segment compute its representation as a line equation (3D vector) The line equati...
Definition: HemiCube.h:326
unsigned int imageWidth_
Width of the image in which the lines in the cube map have been found; used to convert 2D image point...
Definition: HemiCube.h:234
const FiniteLines2 & lines() const
Get a reference to the lines stored in the Hemi cube.
Definition: HemiCube.h:296
HemiCube(const unsigned int bins, const unsigned int imageWidth, const unsigned int imageHeight, const Scalar focalLength)
Constructor.
void insert(const FiniteLine2 &newLine)
Add a line to the Hemi cube The line will be added as-is, it will not be merged with similar lines in...
Definition: HemiCube.h:257
unsigned int imageHeight_
Height of the image in which the lines in the cube map have been found; used to convert 2D image poin...
Definition: HemiCube.h:237
void clear()
Clear this Hemi cube.
Definition: HemiCube.h:290
void merge(const FiniteLines2 &lines, const Scalar maxLineDistance, const Scalar maxLineGap, Indices32 *mapping=nullptr, const Scalar cosAngle=Numeric::cos(Numeric::weakEps()))
Merge a similar lines in set of line segments Lines will be added to a Hemi cube internally.
IndexSet32 find(const FiniteLine2 &line, const Scalar radius=Scalar(1)) const
Find similar lines in Hemi cube.
const Map & map() const
Returns a reference to the internal map of lines indices.
Definition: HemiCube.h:301
Scalar focalLength_
Focal length of the original camera; used convert 2D image points into 3D rays.
Definition: HemiCube.h:243
static FiniteLine2 fuse(const FiniteLine2 &line0, const FiniteLine2 &line1)
Compute line segment that minimizes the distances to the endpoints of the input line segments Compute...
void updateLine(unsigned int index, const FiniteLine2 &updatedLine)
Update a line segment stored in the Hemi cube Updates the line segment at index index and its map ind...
static FiniteLines2 mergeGreedyBruteForce(const FiniteLines2 &lines, const Scalar maxLineDistance, const Scalar maxLineGap, Indices32 *mapping=nullptr, const Scalar cosAngle=Numeric::cos(Numeric::weakEps()))
Merge a similar lines in set of line segments (brute-force search/no use of Hemi cubes,...
bool isValid() const
Returns whether this line has valid parameters.
Definition: FiniteLine2.h:622
const VectorT2< T > & point0() const
Returns the first end point of the line.
Definition: FiniteLine2.h:348
const VectorT2< T > & point1() const
Returns the second end point of the line.
Definition: FiniteLine2.h:354
static constexpr T weakEps()
Returns a weak epsilon.
static T cos(const T value)
Returns the cosine of a given value.
Definition: Numeric.h:1584
static constexpr bool isNotEqualEps(const T value)
Returns whether a value is not smaller than or equal to a small epsilon.
Definition: Numeric.h:2237
This class implements a vector with three elements.
Definition: Vector3.h:97
VectorT3< T > cross(const VectorT3< T > &vector) const
Returns the cross product of two vectors.
Definition: Vector3.h:597
T length() const
Returns the length of the vector.
Definition: Vector3.h:664
std::set< Index32 > IndexSet32
Definition of a set holding 32 bit indices.
Definition: Base.h:114
std::vector< Index32 > Indices32
Definition of a vector holding 32 bit index values.
Definition: Base.h:96
PixelPositionT< unsigned int > PixelPosition
Definition of the default PixelPosition object with a data type allowing only positive coordinate val...
Definition: PixelPosition.h:27
float Scalar
Definition of a scalar type.
Definition: Math.h:128
std::vector< FiniteLine2 > FiniteLines2
Definition of a vector holding FiniteLine2 objects.
Definition: FiniteLine2.h:57
VectorT3< Scalar > Vector3
Definition of a 3D vector.
Definition: Vector3.h:22
VectorT2< Scalar > Vector2
Definition of a 2D vector.
Definition: Vector2.h:21
The namespace covering the entire Ocean framework.
Definition: Accessor.h:15
Helper to compute hash values for map indices.
Definition: HemiCube.h:45
size_t operator()(const MapIndex &mapIndex) const noexcept
Compute hash value for a map index.