41#ifndef UFO_CORE_SURFEL_HPP
42#define UFO_CORE_SURFEL_HPP
45#include <ufo/numeric/mat.hpp>
46#include <ufo/numeric/vec.hpp>
120 std::uint32_t num_points)
131 :
sum_squares_{scatter[0][0], scatter[0][1], scatter[0][2],
132 scatter[1][1], scatter[1][2], scatter[2][2]}
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)
158 template <std::ranges::input_range Po
ints>
159 requires std::constructible_from<Vec<3, float>, std::ranges::range_value_t<Points>>
169 :
Surfel(begin(points), end(points))
264 add(
toDouble(surfel.sum_squares_), cast<double>(surfel.sum_), surfel.num_points_);
281 add(std::array<double, 6>{}, cast<double>(point), 1);
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)
312 template <std::ranges::input_range Po
ints>
313 requires std::constructible_from<Vec<3, float>, std::ranges::range_value_t<Points>>
314 constexpr void add(Points
const& points)
316 add(begin(points), end(points));
326 add(begin(points), end(points));
398 remove(
toDouble(surfel.sum_squares_), cast<double>(surfel.sum_), surfel.num_points_);
415 remove(std::array<double, 6>{}, cast<double>(point), 1);
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)
445 template <std::ranges::input_range Po
ints>
446 requires std::constructible_from<Vec<3, float>, std::ranges::range_value_t<Points>>
447 constexpr void remove(Points
const& points)
449 remove(begin(points), end(points));
459 remove(begin(points), end(points));
513 double const n =
static_cast<double>(
num_points_ - 1);
514 auto const s = [&](std::size_t i)
noexcept {
517 return Mat<3, 3, double>{s(0), s(1), s(2), s(1), s(3), s(4), s(2), s(4), s(5)};
554 [[nodiscard]]
constexpr double planarity() const noexcept
557 return 2.0 * (e[1] - e[0]) / (e[0] + e[1] + e[2]);
599 [[nodiscard]]
constexpr std::array<float, 6>
sumSquares() const noexcept
616 [[nodiscard]]
static constexpr std::array<double, 6>
toDouble(
617 std::array<float, 6>
const& arr)
noexcept
619 std::array<double, 6> result;
620 std::ranges::transform(arr, result.begin(),
621 [](
float v) { return static_cast<double>(v); });
631 static constexpr void toFloat(std::array<double, 6>
const& src,
632 std::array<float, 6>& dst)
noexcept
634 std::ranges::transform(src, dst.begin(),
635 [](
double v) { return static_cast<float>(v); });
646 double scale)
noexcept
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;
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>,
678 std::array<double, 6> ss{};
682 for (; first != last; ++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];
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;
717 std::uint32_t num_points)
727 double const n_o =
static_cast<double>(num_points);
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)));
747 std::uint32_t num_points)
754 double const n_a =
static_cast<double>(
num_points_ - num_points);
755 double const n_b =
static_cast<double>(num_points);
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)));
763 sum_ = cast<float>(sum_a);
801struct std::formatter<
ufo::Surfel> {
802 constexpr auto parse(std::format_parse_context& ctx)
const {
return ctx.begin(); }
804 auto format(
ufo::Surfel const& s, std::format_context& ctx)
const
807 return std::format_to(ctx.out(),
"Surfel{{empty}}");
809 auto const m = s.
mean();
810 return std::format_to(ctx.out(),
"Surfel{{n={}, mean=({}, {}, {})}}", s.
numPoints(),
829inline std::ostream& operator<<(std::ostream& out,
Surfel const& s)
831 return out << std::format(
"{}", s);
Represents an incremental surface element (surfel) that tracks the sufficient statistics of a 3D poin...
constexpr void add(Surfel const &surfel) noexcept
Merges another surfel into this one using a numerically stable parallel update rule.
constexpr void add(Points const &points)
Adds all points in points using the batch algorithm.
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.
constexpr Mat< 3, 3, double > covariance() const noexcept
Returns the 3×3 sample covariance matrix.
friend Surfel operator+(Surfel lhs, Vec< 3, float > rhs) noexcept
Returns a new surfel that is the result of adding point rhs to lhs.
constexpr Vec3d normal() const noexcept
Returns the surface normal as the eigenvector of the covariance matrix corresponding to the smallest ...
constexpr void add(Vec< 3, float > point) noexcept
Adds a single 3D point using an online (Welford-like) update rule.
constexpr Surfel & operator-=(Surfel const &rhs) noexcept
Removes another surfel's contribution from this one using a numerically stable parallel update rule.
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.
constexpr bool empty() const noexcept
Returns true if no points have been added.
Vec< 3, float > sum_
The sum of all point positions (a Vec<3, float>). This is the unnormalized sum; the mean can be obtai...
constexpr Surfel(InputIt first, InputIt last)
Constructs a surfel by adding all points in [first, last).
friend Surfel operator-(Surfel lhs, Vec< 3, float > rhs) noexcept
Returns a new surfel that is the result of removing point rhs from lhs.
constexpr double planarity() const noexcept
Returns a planarity measure in [0, 1].
constexpr Surfel(std::initializer_list< Vec< 3, float > > points)
Constructs a surfel from a brace-enclosed list of Vec3f points.
constexpr Surfel(Vec< 3, float > position, Mat< 3, 3, float > scatter={})
Constructs a surfel from a single position and optional scatter matrix.
constexpr void remove(std::initializer_list< Vec< 3, float > > points) noexcept
Removes all points in a brace-enclosed initializer list.
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.
std::array< float, 6 > sum_squares_
Upper triangle of the scatter matrix S, stored as six floats in the order (Sxx, Sxy,...
std::uint32_t num_points_
The number of contributing points. This is used to compute the mean and covariance from the raw sums....
constexpr std::array< float, 6 > sumSquares() const noexcept
Returns the raw scatter matrix upper triangle (Sxx, Sxy, Sxz, Syy, Syz, Szz).
constexpr void add(std::initializer_list< Vec< 3, float > > points) noexcept
Adds points from a brace-enclosed initializer list.
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.
friend Surfel operator+(Surfel lhs, Surfel const &rhs) noexcept
Returns a new surfel that is the merge of lhs and rhs.
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.
constexpr Vec< 3, float > sum() const noexcept
Returns the raw sum of all point positions.
constexpr void remove(InputIt first, InputIt last)
Removes all points in [first, last) using a batch algorithm.
constexpr Surfel & operator+=(Vec< 3, float > rhs) noexcept
Adds a single 3D point to this surfel using an online (Welford-like) update rule.
constexpr void add(InputIt first, InputIt last)
Adds all points in [first, last) using a batch algorithm.
friend Surfel operator-(Surfel lhs, Surfel const &rhs) noexcept
Returns a new surfel that is the result of removing rhs from lhs.
constexpr std::uint32_t numPoints() const noexcept
Returns the number of points currently represented by this surfel.
constexpr Vec< 3, double > mean() const noexcept
Returns the centroid (mean position) of all accumulated points.
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.
constexpr void remove(Surfel const &surfel) noexcept
Removes a previously-added surfel's contribution from this one.
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.
constexpr void remove(Points const &points)
Removes all points in points using the batch algorithm.
constexpr Surfel & operator+=(Surfel const &rhs) noexcept
Merges another surfel into this one using a numerically stable parallel update rule.
constexpr void clear() noexcept
Clears the surfel to an empty state (no points, zero statistics).
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.
constexpr Surfel & operator-=(Vec< 3, float > rhs) noexcept
Removes a single 3D point from this surfel and returns *this.
constexpr Surfel(Points const &points)
Constructs a surfel by adding all points in points.
constexpr Vec< 3, T > eigenValues(Mat< 3, 3, T > const &m) noexcept
Computes the eigenvalues of a real symmetric 3×3 matrix, sorted ascending.
All vision-related classes and functions.
constexpr T b(Lab< T, Flags > color) noexcept
Returns the un-weighted blue–yellow axis value.
constexpr T a(Lab< T, Flags > color) noexcept
Returns the un-weighted green–red axis value.
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.
A fixed-size matrix with Rows rows and Cols columns.
A fixed-size arithmetic vector of up to 4 dimensions.