UFO 1.0.0
An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown
Loading...
Searching...
No Matches
surfel.hpp
1
41#ifndef UFO_CORE_SURFEL_HPP
42#define UFO_CORE_SURFEL_HPP
43
44// UFO
45#include <ufo/numeric/mat.hpp>
46#include <ufo/numeric/vec.hpp>
47
48// STL
49#include <algorithm>
50#include <array>
51#include <concepts>
52#include <cstdint>
53#include <format>
54#include <ostream>
55#include <ranges>
56
57namespace ufo
58{
96class Surfel
97{
98 public:
99 //
100 // Constructors
101 //
102
106 constexpr Surfel() = default;
107
119 constexpr Surfel(Vec<3, float> sum, std::array<float, 6> sum_squares,
120 std::uint32_t num_points)
121 : sum_squares_(sum_squares), sum_(sum), num_points_(num_points)
122 {
123 }
124
130 constexpr Surfel(Vec<3, float> position, Mat<3, 3, float> scatter = {})
131 : sum_squares_{scatter[0][0], scatter[0][1], scatter[0][2],
132 scatter[1][1], scatter[1][2], scatter[2][2]}
133 , sum_(position)
134 , num_points_(1)
135 {
136 }
137
145 template <std::input_iterator InputIt>
146 requires std::constructible_from<Vec<3, float>, std::iter_value_t<InputIt>>
147 constexpr Surfel(InputIt first, InputIt last)
148 {
149 add(first, last);
150 }
151
158 template <std::ranges::input_range Points>
159 requires std::constructible_from<Vec<3, float>, std::ranges::range_value_t<Points>>
160 constexpr Surfel(Points const& points) : Surfel(std::cbegin(points), std::cend(points))
161 {
162 }
163
168 constexpr Surfel(std::initializer_list<Vec<3, float>> points)
169 : Surfel(begin(points), end(points))
170 {
171 }
172
177 [[nodiscard]] constexpr bool operator==(Surfel const&) const noexcept = default;
178
179 //
180 // Empty
181 //
182
187 [[nodiscard]] constexpr bool empty() const noexcept { return 0 == num_points_; }
188
189 //
190 // Add
191 //
192
200 constexpr Surfel& operator+=(Surfel const& rhs) noexcept
201 {
202 add(rhs);
203 return *this;
204 }
205
213 constexpr Surfel& operator+=(Vec<3, float> rhs) noexcept
214 {
215 add(rhs);
216 return *this;
217 }
218
226 [[nodiscard]] friend Surfel operator+(Surfel lhs, Surfel const& rhs) noexcept
227 {
228 return lhs += rhs;
229 }
230
238 [[nodiscard]] friend Surfel operator+(Surfel lhs, Vec<3, float> rhs) noexcept
239 {
240 return lhs += rhs;
241 }
242
258 constexpr void add(Surfel const& surfel) noexcept
259 {
260 if (empty()) {
261 *this = surfel;
262 return;
263 }
264 add(toDouble(surfel.sum_squares_), cast<double>(surfel.sum_), surfel.num_points_);
265 }
266
279 constexpr void add(Vec<3, float> point) noexcept
280 {
281 add(std::array<double, 6>{}, cast<double>(point), 1);
282 }
283
295 template <std::input_iterator InputIt>
296 requires std::constructible_from<Vec<3, float>, std::iter_value_t<InputIt>>
297 constexpr void add(InputIt first, InputIt last)
298 {
299 if (first == last) {
300 return;
301 }
302 auto [ss, s, n] = batchScatter(first, last);
303 add(ss, s, n);
304 }
305
312 template <std::ranges::input_range Points>
313 requires std::constructible_from<Vec<3, float>, std::ranges::range_value_t<Points>>
314 constexpr void add(Points const& points)
315 {
316 add(begin(points), end(points));
317 }
318
324 constexpr void add(std::initializer_list<Vec<3, float>> points) noexcept
325 {
326 add(begin(points), end(points));
327 }
328
329 //
330 // Remove
331 //
332
340 constexpr Surfel& operator-=(Surfel const& rhs) noexcept
341 {
342 remove(rhs);
343 return *this;
344 }
345
352 constexpr Surfel& operator-=(Vec<3, float> rhs) noexcept
353 {
354 remove(rhs);
355 return *this;
356 }
357
365 [[nodiscard]] friend Surfel operator-(Surfel lhs, Surfel const& rhs) noexcept
366 {
367 return lhs -= rhs;
368 }
369
377 [[nodiscard]] friend Surfel operator-(Surfel lhs, Vec<3, float> rhs) noexcept
378 {
379 return lhs -= rhs;
380 }
381
396 constexpr void remove(Surfel const& surfel) noexcept
397 {
398 remove(toDouble(surfel.sum_squares_), cast<double>(surfel.sum_), surfel.num_points_);
399 }
400
413 constexpr void remove(Vec<3, float> point) noexcept
414 {
415 remove(std::array<double, 6>{}, cast<double>(point), 1);
416 }
417
428 template <std::input_iterator InputIt>
429 requires std::constructible_from<Vec<3, float>, std::iter_value_t<InputIt>>
430 constexpr void remove(InputIt first, InputIt last)
431 {
432 if (first == last) {
433 return;
434 }
435 auto [ss, s, n] = batchScatter(first, last);
436 remove(ss, s, n);
437 }
438
445 template <std::ranges::input_range Points>
446 requires std::constructible_from<Vec<3, float>, std::ranges::range_value_t<Points>>
447 constexpr void remove(Points const& points)
448 {
449 remove(begin(points), end(points));
450 }
451
457 constexpr void remove(std::initializer_list<Vec<3, float>> points) noexcept
458 {
459 remove(begin(points), end(points));
460 }
461
462 //
463 // Clear
464 //
465
473 constexpr void clear() noexcept
474 {
475 sum_squares_ = {};
476 sum_ = {};
477 num_points_ = {};
478 }
479
480 //
481 // Get mean
482 //
483
491 [[nodiscard]] constexpr Vec<3, double> mean() const noexcept
492 {
493 return cast<double>(sum_) / static_cast<double>(num_points_);
494 }
495
496 //
497 // Get covariance
498 //
499
511 [[nodiscard]] constexpr Mat<3, 3, double> covariance() const noexcept
512 {
513 double const n = static_cast<double>(num_points_ - 1);
514 auto const s = [&](std::size_t i) noexcept {
515 return static_cast<double>(sum_squares_[i]) / n;
516 };
517 return Mat<3, 3, double>{s(0), s(1), s(2), s(1), s(3), s(4), s(2), s(4), s(5)};
518 }
519
520 //
521 // Get normal
522 //
523
532 [[nodiscard]] constexpr Vec3d normal() const noexcept
533 {
534 return eigenVectors(covariance())[0];
535 }
536
537 //
538 // Get planarity
539 //
540
554 [[nodiscard]] constexpr double planarity() const noexcept
555 {
556 auto const e = eigenValues(covariance());
557 return 2.0 * (e[1] - e[0]) / (e[0] + e[1] + e[2]);
558 }
559
560 //
561 // Get num points
562 //
563
569 [[nodiscard]] constexpr std::uint32_t numPoints() const noexcept { return num_points_; }
570
571 //
572 // Get sum
573 //
574
584 [[nodiscard]] constexpr Vec<3, float> sum() const noexcept { return sum_; }
585
586 //
587 // Get sum squares
588 //
589
599 [[nodiscard]] constexpr std::array<float, 6> sumSquares() const noexcept
600 {
601 return sum_squares_;
602 }
603
604 protected:
605 //
606 // Helpers
607 //
608
616 [[nodiscard]] static constexpr std::array<double, 6> toDouble(
617 std::array<float, 6> const& arr) noexcept
618 {
619 std::array<double, 6> result;
620 std::ranges::transform(arr, result.begin(),
621 [](float v) { return static_cast<double>(v); });
622 return result;
623 }
624
631 static constexpr void toFloat(std::array<double, 6> const& src,
632 std::array<float, 6>& dst) noexcept
633 {
634 std::ranges::transform(src, dst.begin(),
635 [](double v) { return static_cast<float>(v); });
636 }
637
645 static constexpr void addOuter(std::array<double, 6>& ss, Vec<3, double> const& v,
646 double scale) noexcept
647 {
648 ss[0] += v[0] * v[0] * scale;
649 ss[1] += v[0] * v[1] * scale;
650 ss[2] += v[0] * v[2] * scale;
651 ss[3] += v[1] * v[1] * scale;
652 ss[4] += v[1] * v[2] * scale;
653 ss[5] += v[2] * v[2] * scale;
654 }
655
672 template <std::input_iterator InputIt>
673 requires std::constructible_from<Vec<3, double>, std::iter_value_t<InputIt>>
674 [[nodiscard]] static constexpr std::tuple<std::array<double, 6>, Vec<3, double>,
675 std::uint32_t>
676 batchScatter(InputIt first, InputIt last) noexcept
677 {
678 std::array<double, 6> ss{};
679 Vec<3, double> s{};
680 std::uint32_t n{};
681
682 for (; first != last; ++first) {
683 Vec<3, double> const p(*first);
684 ss[0] += p[0] * p[0];
685 ss[1] += p[0] * p[1];
686 ss[2] += p[0] * p[2];
687 ss[3] += p[1] * p[1];
688 ss[4] += p[1] * p[2];
689 ss[5] += p[2] * p[2];
690 s += p;
691 ++n;
692 }
693
694 double const nd = static_cast<double>(n);
695 ss[0] -= s[0] * s[0] / nd;
696 ss[1] -= s[0] * s[1] / nd;
697 ss[2] -= s[0] * s[2] / nd;
698 ss[3] -= s[1] * s[1] / nd;
699 ss[4] -= s[1] * s[2] / nd;
700 ss[5] -= s[2] * s[2] / nd;
701
702 return {ss, s, n};
703 }
704
705 //
706 // Private add/remove
707 //
708
716 constexpr void add(std::array<double, 6> ss, Vec<3, double> const& sum,
717 std::uint32_t num_points)
718 {
719 if (empty()) {
721 sum_ = cast<float>(sum);
722 num_points_ = num_points;
723 return;
724 }
725
726 double const n = static_cast<double>(num_points_);
727 double const n_o = static_cast<double>(num_points);
728 Vec<3, double> const s(sum_);
729
730 std::ranges::transform(ss, sum_squares_, ss.begin(),
731 [](double a, float b) { return a + static_cast<double>(b); });
732 addOuter(ss, s * n_o - sum * n, 1.0 / (n * n_o * (n + n_o)));
733
735 sum_ = cast<float>(s + sum);
736 num_points_ += num_points;
737 }
738
746 constexpr void remove(std::array<double, 6> ss, Vec<3, double> const& sum,
747 std::uint32_t num_points)
748 {
749 if (num_points >= num_points_) {
750 clear();
751 return;
752 }
753
754 double const n_a = static_cast<double>(num_points_ - num_points);
755 double const n_b = static_cast<double>(num_points);
756 Vec<3, double> const sum_a = cast<double>(sum_) - sum;
757
758 std::ranges::transform(sum_squares_, ss, ss.begin(),
759 [](float a, double b) { return static_cast<double>(a) - b; });
760 addOuter(ss, sum_a * n_b - sum * n_a, -1.0 / (n_a * n_b * (n_a + n_b)));
761
763 sum_ = cast<float>(sum_a);
764 num_points_ -= num_points;
765 }
766
767 //
768 // Data members
769 //
770
776 std::array<float, 6> sum_squares_{};
777
784
792 std::uint32_t num_points_{};
793};
794
798} // namespace ufo
799
800template <>
801struct std::formatter<ufo::Surfel> {
802 constexpr auto parse(std::format_parse_context& ctx) const { return ctx.begin(); }
803
804 auto format(ufo::Surfel const& s, std::format_context& ctx) const
805 {
806 if (s.empty()) {
807 return std::format_to(ctx.out(), "Surfel{{empty}}");
808 }
809 auto const m = s.mean();
810 return std::format_to(ctx.out(), "Surfel{{n={}, mean=({}, {}, {})}}", s.numPoints(),
811 m[0], m[1], m[2]);
812 }
813};
814
815namespace ufo
816{
829inline std::ostream& operator<<(std::ostream& out, Surfel const& s)
830{
831 return out << std::format("{}", s);
832}
833} // namespace ufo
834#endif // UFO_CORE_SURFEL_HPP
Represents an incremental surface element (surfel) that tracks the sufficient statistics of a 3D poin...
Definition surfel.hpp:97
constexpr void add(Surfel const &surfel) noexcept
Merges another surfel into this one using a numerically stable parallel update rule.
Definition surfel.hpp:258
constexpr void add(Points const &points)
Adds all points in points using the batch algorithm.
Definition surfel.hpp:314
constexpr Surfel()=default
Default-constructs an empty surfel with no points.
static constexpr void addOuter(std::array< double, 6 > &ss, Vec< 3, double > const &v, double scale) noexcept
Helper to add the outer product correction term in the parallel update.
Definition surfel.hpp:645
constexpr Mat< 3, 3, double > covariance() const noexcept
Returns the 3×3 sample covariance matrix.
Definition surfel.hpp:511
friend Surfel operator+(Surfel lhs, Vec< 3, float > rhs) noexcept
Returns a new surfel that is the result of adding point rhs to lhs.
Definition surfel.hpp:238
constexpr Vec3d normal() const noexcept
Returns the surface normal as the eigenvector of the covariance matrix corresponding to the smallest ...
Definition surfel.hpp:532
constexpr void add(Vec< 3, float > point) noexcept
Adds a single 3D point using an online (Welford-like) update rule.
Definition surfel.hpp:279
constexpr Surfel & operator-=(Surfel const &rhs) noexcept
Removes another surfel's contribution from this one using a numerically stable parallel update rule.
Definition surfel.hpp:340
constexpr void remove(std::array< double, 6 > ss, Vec< 3, double > const &sum, std::uint32_t num_points)
Helper to remove a batch of points represented by their scatter statistics.
Definition surfel.hpp:746
constexpr bool empty() const noexcept
Returns true if no points have been added.
Definition surfel.hpp:187
Vec< 3, float > sum_
The sum of all point positions (a Vec<3, float>). This is the unnormalized sum; the mean can be obtai...
Definition surfel.hpp:783
constexpr Surfel(InputIt first, InputIt last)
Constructs a surfel by adding all points in [first, last).
Definition surfel.hpp:147
friend Surfel operator-(Surfel lhs, Vec< 3, float > rhs) noexcept
Returns a new surfel that is the result of removing point rhs from lhs.
Definition surfel.hpp:377
constexpr double planarity() const noexcept
Returns a planarity measure in [0, 1].
Definition surfel.hpp:554
constexpr Surfel(std::initializer_list< Vec< 3, float > > points)
Constructs a surfel from a brace-enclosed list of Vec3f points.
Definition surfel.hpp:168
constexpr Surfel(Vec< 3, float > position, Mat< 3, 3, float > scatter={})
Constructs a surfel from a single position and optional scatter matrix.
Definition surfel.hpp:130
constexpr void remove(std::initializer_list< Vec< 3, float > > points) noexcept
Removes all points in a brace-enclosed initializer list.
Definition surfel.hpp:457
static constexpr void toFloat(std::array< double, 6 > const &src, std::array< float, 6 > &dst) noexcept
Converts an array of six doubles to floats for storage.
Definition surfel.hpp:631
std::array< float, 6 > sum_squares_
Upper triangle of the scatter matrix S, stored as six floats in the order (Sxx, Sxy,...
Definition surfel.hpp:776
std::uint32_t num_points_
The number of contributing points. This is used to compute the mean and covariance from the raw sums....
Definition surfel.hpp:792
constexpr std::array< float, 6 > sumSquares() const noexcept
Returns the raw scatter matrix upper triangle (Sxx, Sxy, Sxz, Syy, Syz, Szz).
Definition surfel.hpp:599
constexpr void add(std::initializer_list< Vec< 3, float > > points) noexcept
Adds points from a brace-enclosed initializer list.
Definition surfel.hpp:324
constexpr void add(std::array< double, 6 > ss, Vec< 3, double > const &sum, std::uint32_t num_points)
Helper to add a batch of points represented by their scatter statistics.
Definition surfel.hpp:716
friend Surfel operator+(Surfel lhs, Surfel const &rhs) noexcept
Returns a new surfel that is the merge of lhs and rhs.
Definition surfel.hpp:226
static constexpr std::tuple< std::array< double, 6 >, Vec< 3, double >, std::uint32_t > batchScatter(InputIt first, InputIt last) noexcept
Computes scatter statistics (ss, sum, n) for a batch of points.
Definition surfel.hpp:676
constexpr Vec< 3, float > sum() const noexcept
Returns the raw sum of all point positions.
Definition surfel.hpp:584
constexpr void remove(InputIt first, InputIt last)
Removes all points in [first, last) using a batch algorithm.
Definition surfel.hpp:430
constexpr Surfel & operator+=(Vec< 3, float > rhs) noexcept
Adds a single 3D point to this surfel using an online (Welford-like) update rule.
Definition surfel.hpp:213
constexpr void add(InputIt first, InputIt last)
Adds all points in [first, last) using a batch algorithm.
Definition surfel.hpp:297
friend Surfel operator-(Surfel lhs, Surfel const &rhs) noexcept
Returns a new surfel that is the result of removing rhs from lhs.
Definition surfel.hpp:365
constexpr std::uint32_t numPoints() const noexcept
Returns the number of points currently represented by this surfel.
Definition surfel.hpp:569
constexpr Vec< 3, double > mean() const noexcept
Returns the centroid (mean position) of all accumulated points.
Definition surfel.hpp:491
constexpr Surfel(Vec< 3, float > sum, std::array< float, 6 > sum_squares, std::uint32_t num_points)
Constructs a surfel directly from its raw sufficient statistics.
Definition surfel.hpp:119
constexpr void remove(Surfel const &surfel) noexcept
Removes a previously-added surfel's contribution from this one.
Definition surfel.hpp:396
static constexpr std::array< double, 6 > toDouble(std::array< float, 6 > const &arr) noexcept
Converts an array of six floats to doubles for intermediate calculations.
Definition surfel.hpp:616
constexpr void remove(Points const &points)
Removes all points in points using the batch algorithm.
Definition surfel.hpp:447
constexpr Surfel & operator+=(Surfel const &rhs) noexcept
Merges another surfel into this one using a numerically stable parallel update rule.
Definition surfel.hpp:200
constexpr void clear() noexcept
Clears the surfel to an empty state (no points, zero statistics).
Definition surfel.hpp:473
constexpr bool operator==(Surfel const &) const noexcept=default
Equality comparison on all three stored statistics.
constexpr void remove(Vec< 3, float > point) noexcept
Removes a single previously-added 3D point.
Definition surfel.hpp:413
constexpr Surfel & operator-=(Vec< 3, float > rhs) noexcept
Removes a single 3D point from this surfel and returns *this.
Definition surfel.hpp:352
constexpr Surfel(Points const &points)
Constructs a surfel by adding all points in points.
Definition surfel.hpp:160
constexpr Vec< 3, T > eigenValues(Mat< 3, 3, T > const &m) noexcept
Computes the eigenvalues of a real symmetric 3×3 matrix, sorted ascending.
Definition mat.hpp:1252
STL namespace.
All vision-related classes and functions.
Definition cloud.hpp:49
constexpr T b(Lab< T, Flags > color) noexcept
Returns the un-weighted blue–yellow axis value.
Definition lab.hpp:326
constexpr T a(Lab< T, Flags > color) noexcept
Returns the un-weighted green–red axis value.
Definition lab.hpp:310
constexpr std::array< Vec< 3, T >, 3 > eigenVectors(Mat< 3, 3, T > const &m) noexcept
Computes the eigenvectors of a real symmetric 3×3 matrix.
Definition mat.hpp:1297
A fixed-size matrix with Rows rows and Cols columns.
Definition mat.hpp:113
A fixed-size arithmetic vector of up to 4 dimensions.
Definition vec.hpp:76