ReUseX  0.0.5
3D Point Cloud Processing for Building Reuse
Loading...
Searching...
No Matches
reusex::geometry Namespace Reference

Namespaces

namespace  cgal
namespace  detail
 Internals of detect_loop_edges, exposed ONLY so the geometry stages of the pipeline can be regression-tested without a database and without RGB-D fixtures (docs/STANDARDS.md §7; same rationale as plane_factor_consistent_residual() in PlaneGraphOptimizer.cpp).
namespace  se3
 Minimal se(3) exp/log used by the joint pose optimizer.

Classes

struct  AccuracyMetricsOptions
 Options for ground-truth accuracy scoring. More...
struct  AccuracyReport
 Ground-truth accuracy report for a reconstructed point cloud. More...
struct  BuildingComponent
 A detected or manual building component (window, door, wall, ...). More...
struct  CameraData
 Camera data for texture mapping. More...
class  CellComplex
struct  CellData
struct  CellEdgeData
struct  CellVertexData
struct  CoplanarPolygon
 A closed 3D polygon whose vertices are coplanar. More...
struct  CreateWindowsOptions
 Configuration for the create_windows pipeline. More...
struct  CreateWindowsResult
 Output of create_windows(). More...
struct  DensifyParams
 Parameters for Multi-View Stereo dense reconstruction (OpenMVS). More...
struct  DoorData
struct  FaceData
struct  FrameSurfels
 A sensor frame represented as a surfel set for joint registration. More...
class  JointPairwiseRegistration
 Joint pairwise pose optimizer operating purely in memory. More...
struct  JprParams
 Parameters for joint pairwise pose refinement. More...
struct  JprResult
 Summary statistics from a registration run. More...
struct  LoopClosureOptions
 Parameters for wide-baseline loop-edge detection. More...
struct  LoopClosureResult
 Summary statistics from loop-edge detection. More...
struct  LoopEdge
 A metric relative-pose constraint between two frames, ready to become a gtsam::BetweenFactor<Pose3>. More...
struct  MeshOptions
 Options for mesh generation. More...
struct  NoiseEstimate
 Result of estimate_cloud_noise. More...
struct  NoiseEstimateOptions
 Options controlling the deterministic local-PCA cloud-noise estimator. More...
struct  OdometryEdgeMotion
 Seed motion of one consecutive-frame odometry edge. More...
struct  OdometrySigmaScale
 Multiplicative scale applied to an odometry edge's base sigmas. More...
struct  OrphanedInstance
 A previous instance whose GUID could not be matched to any new instance. More...
struct  PanoramaAlignmentOptions
 Parameters for content-based panorama alignment. More...
struct  PanoramaAlignmentResult
 Outcome of aligning one panorama. More...
struct  PanoramaLoopOptions
 Parameters for panorama-derived loop-edge detection. More...
struct  PanoramaLoopResult
 Statistics from panorama loop-edge detection. More...
struct  PerspectiveView
 A virtual pinhole view rendered out of an equirectangular panorama. More...
struct  PlaneFitQuality
 Fit statistics of one per-frame plane detection: the inputs from which the measurement noise of its OrientedPlane3Factor is derived. More...
struct  PlaneFitSigmas
 Statistical standard deviations of a plane fit, in the units of the OrientedPlane3Factor residual. More...
class  PlaneGraphOptimizer
 Plane-landmark pose-graph optimizer operating purely in memory. More...
struct  PlaneGraphOptions
 Parameters for plane-landmark pose-graph optimization. More...
struct  PlaneGraphResult
 Summary statistics from a plane-graph optimization run. More...
struct  PlaneQuality
 Quality statistics for a single detected plane. More...
struct  PointCluster
struct  PoseDriftOptions
 Parameters for synthetic trajectory drift (#338). More...
struct  PoseDriftResult
 What a drift-synthesis run did, in the units the benchmark reports. More...
struct  PriorInstance
 Prior instance state (typically loaded from ProjectDB::instances()). More...
struct  QualityMetricsOptions
struct  QualityReport
 Ground-truth-free reconstruction quality report. More...
struct  ReconciledInstance
 A single reconciled instance: its (new) label value, the semantic class it belongs to, its point count, and the stable GUID it should carry. More...
struct  ReconcileOptions
 Options controlling instance identity reconciliation. More...
struct  ReconcileResult
 Result of reconciling a freshly-segmented instance set against a previous one. More...
struct  ReconstructionParams
 Parameters for point cloud reconstruction from stored sensor frames. More...
class  Registry
struct  SceneEdgeData
class  SceneGraph
struct  SceneObject
struct  ScenePlane
struct  SceneVertexData
struct  SectionSolveStats
 Per-section solve diagnostics (issue #226). More...
struct  SegmentInstancesRequest
 Request structure for instance segmentation via Euclidean clustering. More...
struct  SegmentInstancesResult
 Result structure for instance segmentation. More...
struct  SegmentPlanesOptions
struct  SegmentRoomsOptions
class  Solidifier
 Solidifier solves room segmentation using Mixed Integer Programming. More...
struct  SolidifierOptions
struct  SurfelExtractionParams
 Parameters controlling per-frame surfel extraction (back-projection + normal estimation) used by the registration pipeline. More...
struct  SyncDownsampleOptions
 Options controlling a synchronized project-cloud downsample. More...
struct  SyncDownsampleResult
 Outcome of a synchronized downsample, for logging / reporting. More...
struct  TextureQualityParams
 Quality parameters for texture projection. More...
struct  VertexProperties
struct  VoxelAssignment
 Per-input-point assignment to an output voxel bucket. More...
struct  WallCandidate
 A connected component of coplanar mesh faces that may host a window. More...
struct  WallData
struct  WindowData

Typedefs

using ResolveInstanceGuidFn = std::function<std::string(uint32_t)>
 Resolves the stable GUID of an instance from its integer label id.
template<typename Scalar>
using Plane = Eigen::Matrix<Scalar, 4, 1>
template<typename Scalar>
using PlaneVector

Enumerations

enum class  ComponentType { window , door , wall }
 Discriminator for building component types. More...
enum class  CellNodeType { cell , face , vertex }
enum class  WindowBoundaryMode { rectangle , polyline }
 How to compute the window boundary polygon. More...
enum class  SceneNodeType { point_cluster , plane , object }
enum class  SolveStatus {
  not_solved , optimal , feasible , infeasible ,
  unbounded , time_limit , iteration_limit , numerical_error ,
  error
}
 Backend-agnostic classification of the last MIP solve outcome. More...
enum class  SolverChoice { automatic , cuopt , highs }
 Which MIP backend the Solidifier should use for each (sub)problem. More...
enum class  LoopProposal { automatic , spatial , appearance , exhaustive }
 How loop-candidate frame pairs are proposed before geometric verification. More...
enum class  PlaneNoiseModel { uniform , inlier_count , fit_geometry }
 How the measurement noise of an individual plane observation is chosen. More...
enum class  OdometryNoiseModel { fixed , motion }
 How the per-edge measurement noise of the consecutive-frame odometry BetweenFactors is derived (#225 odometry-trust experiment). More...

Functions

std::string_view to_string (ComponentType type)
ComponentType component_type_from_string (std::string_view str)
std::string component_data_to_json (const BuildingComponent &c)
 Serialize the type-specific variant data to JSON TEXT.
void component_data_from_json (BuildingComponent &c, const std::string &json)
 Deserialize JSON TEXT into the variant data on a BuildingComponent.
core::ComponentRecord to_component_record (const BuildingComponent &component)
 Flatten a component into the persistence POD.
BuildingComponent from_component_record (const core::ComponentRecord &record)
 Rebuild a component from the persistence POD.
void save_building_component (ProjectDB &db, const BuildingComponent &component)
 Insert or replace component, keyed by its name.
void update_building_component_by_guid (ProjectDB &db, const BuildingComponent &component)
 Update an existing component's mutable fields, matched by its immutable guid.
BuildingComponent building_component (const ProjectDB &db, std::string_view name)
 Load the component stored under name. Throws if absent.
std::vector< std::string > list_building_components (const ProjectDB &db, ComponentType type)
 Names of all stored components of the given type.
Eigen::Vector3d pixel_to_bearing (const cv::Size &equirect, double u, double v)
 Equirect pixel (u,v) -> unit bearing in the panorama frame.
Eigen::Vector2d bearing_to_pixel (const cv::Size &equirect, const Eigen::Vector3d &bearing)
 Unit bearing (need not be normalised) in panorama frame -> equirect pixel (u,v).
PerspectiveView extract_perspective (const cv::Mat &equirect, double yaw_deg, double pitch_deg, double fov_deg, int out_w, int out_h, int interp=1)
 Render a virtual pinhole view centred at (yaw,pitch) with the given horizontal FOV.
PerspectiveView extract_perspective (const cv::Mat &equirect, const Eigen::Matrix3d &R_pano_from_view, const Eigen::Matrix3d &K, int out_w, int out_h, int interp=1)
 Render a virtual pinhole view with an explicit view->panorama rotation and intrinsics.
std::vector< PerspectiveViewcube_faces (const cv::Mat &equirect, int face_size, int interp=1)
 Tile the whole sphere as the six faces of a cube (90 deg FOV each).
std::vector< PerspectiveViewoverlapping_views (const cv::Mat &equirect, int n_yaw, double fov_deg, int tile, int interp=1)
 Tile the sphere as n_yaw evenly spaced views around the equator plus one up and one down view, each with fov_deg horizontal FOV and tile pixels square.
cv::Mat stitch_labels_to_equirect (const cv::Size &out, const std::vector< PerspectiveView > &views, const std::vector< cv::Mat > &tile_labels)
 Stitch per-tile label maps (CV_32S, -1 = background) back into a single equirect label map of size out.
Eigen::Affine3f to_affine (const std::array< double, 16 > &m)
 Build a 4x4 Eigen affine (float) from a row-major double[16] array.
std::array< double, 16 > to_array16 (const Eigen::Affine3f &aff)
 Flatten a 4x4 Eigen affine (float) into a row-major double[16] array.
pcl::PolygonMeshPtr unweld_mesh (const pcl::PolygonMesh &mesh, float threshold_radians)
 Unweld (split) mesh vertices along sharp edges.
auto dist_plane_point (const Eigen::Vector4d &plane, const Eigen::Vector3d &point) -> double
 Calculate distance from a point to a plane.
auto make_pairs (EigenVectorContainer< double, 4 > &planes, std::vector< IndicesPtr > &inliers, EigenVectorContainer< double, 3 > &centroids, const double threshold=0.6, const double new_plane_offset=0.5) -> std::vector< std::pair< size_t, size_t > >
 Create pairs of opposite parallel planes.
auto force_orthogonal_planes (EigenVectorContainer< double, 4 > &planes, const double threshold=0.1, const Eigen::Matrix< double, 3, 1 > &up=Eigen::Matrix< double, 3, 1 >(0, 0, 1)) -> EigenVectorContainer< double, 4 >
 Force planes to be orthogonal to a reference direction.
auto compute_number_of_inliers (CloudConstPtr cloud, Eigen::Vector4d const &plane, IndicesConstPtr indices, const float threshold=0.2) -> size_t
 Count number of inliers for a plane.
auto merge_planes (EigenVectorContainer< double, 4 > const &planes_, std::vector< IndicesPtr > const &inliers_, EigenVectorContainer< double, 3 > const &centroids_, CloudConstPtr cloud, const double angle_threshold=0.1, const double distance_threshold=0.5) -> std::tuple< EigenVectorContainer< double, 4 >, std::vector< IndicesPtr >, EigenVectorContainer< double, 3 > >
 Merge similar planes based on angle and distance.
auto separate_planes (const EigenVectorContainer< double, 4 > &planes, const Eigen::Vector3d &up=Eigen::Vector3d(0, 0, 1), const double epsilon=0.1) -> std::tuple< std::vector< size_t >, std::vector< size_t > >
 Separate planes into horizontal and vertical based on up vector.
template<typename CloudPtr>
auto compute_polygon_normal (const pcl::Vertices &poly, const CloudPtr &cloud) -> Eigen::Vector3f
 Compute the normal vector of a polygon.
AccuracyReport compute_accuracy (const Cloud &cloud, const CloudLoc &gt, const AccuracyMetricsOptions &opt={})
 Score a reconstructed cloud against a ground-truth point cloud.
std::vector< WallCandidateextract_wall_candidates (const pcl::PolygonMesh &mesh, float normal_z_threshold=kWallVerticalityGateOff, float coplanarity_angle_deg=10.0f)
 Extract planar wall candidates from a triangle mesh.
CreateWindowsResult create_windows (CloudConstPtr cloud, CloudLConstPtr instance_labels, const std::map< uint32_t, uint32_t > &instance_to_semantic, const pcl::PolygonMesh &mesh, const std::vector< uint32_t > &window_semantic_labels, const CreateWindowsOptions &options={}, const ResolveInstanceGuidFn &resolve_instance_guid={})
 Create window BuildingComponents from instance-labeled points and wall geometry.
pcl::PolygonMeshPtr mesh (CloudConstPtr cloud, CloudNConstPtr normals, EigenVectorContainer< double, 4 > &planes, EigenVectorContainer< double, 3 > &centroids, std::vector< IndicesPtr > &inliers, CloudLConstPtr rooms, MeshOptions const opt=MeshOptions{})
 Generate a mesh from point cloud and geometric primitives.
QualityReport compute_plane_quality (const Cloud &cloud, const CloudL &labels, const QualityMetricsOptions &options={})
 Compute per-plane flatness/thickness quality metrics.
template<typename Scalar, typename PointT>
auto regularizePlanes (PlaneVector< Scalar > &planes, typename pcl::PointCloud< PointT >::ConstPtr points, std::vector< pcl::IndicesPtr > &inliers, double angle_threshold=25.0, double distance_threshold=0.01)
const char * to_string (SolveStatus status)
 Human-readable name for a SolveStatus (for logging).
SolverChoice parse_solver_choice (const std::string &s)
 Parse a –solver CLI string ("auto"/"cuopt"/"highs") into a SolverChoice.
std::filesystem::path prepare_texture_dir (const std::filesystem::path &requested={})
 Prepare the directory the generated texture images are staged in.
pcl::TextureMesh::Ptr texture_mesh_with_cloud (pcl::PolygonMesh::Ptr mesh, CloudConstPtr cloud, CloudNConstPtr normals=nullptr, bool debug_distinct_colors=false, const TextureQualityParams &quality=TextureQualityParams())
pcl::TextureMesh::Ptr texture_mesh (pcl::PolygonMesh::Ptr mesh, std::map< int, rtabmap::Transform > const &poses, std::map< int, rtabmap::Signature > const &nodes, const std::filesystem::path &texture_dir={})
 Texture mesh using RTABMap signatures (legacy API).
pcl::TextureMesh::Ptr texture_mesh (pcl::PolygonMesh::Ptr mesh, std::map< int, CameraData > const &cameras, const std::filesystem::path &texture_dir={})
 Texture mesh using simple camera data (ProjectDB API).
void densify_from_images (ProjectDB &db, const DensifyParams &params)
 Run OpenMVS Multi-View Stereo in-process to produce a dense point cloud from the project's sensor frames.
void apply_depth_discontinuity_filter (cv::Mat &depth, cv::Mat &confidence, float gradient_threshold=0.5f)
 Remove "flying pixels" at depth discontinuity edges.
void apply_ray_consistency_filter (cv::Mat &depth, cv::Mat &confidence, float consistency_threshold=0.2f)
 Remove isolated noisy depth measurements.
VoxelAssignment voxel_assignment (const Cloud &cloud, float leaf_size)
 Build a voxel assignment for cloud at the given leaf size.
CloudPtr downsample (const Cloud &cloud, const VoxelAssignment &a)
 Downsample cloud using the precomputed assignment.
CloudNPtr downsample (const CloudN &cloud, const VoxelAssignment &a)
 Downsample a parallel normals cloud using the same assignment that was built from a sibling XYZRGB cloud.
CloudLPtr downsample (const CloudL &cloud, const VoxelAssignment &a)
 Downsample a parallel label cloud (labels, planes, rooms, instances) using the same assignment that was built from a sibling XYZRGB cloud.
auto estimate_cloud_noise (CloudConstPtr cloud, CloudNConstPtr normals, const NoiseEstimateOptions &options=NoiseEstimateOptions{}) -> NoiseEstimate
 Estimate per-cloud sensor noise (sigma) and point density from local PCA patches.
ReconcileResult reconcile_instance_identities (const CloudL *old_labels, const CloudL &new_labels, const std::vector< PriorInstance > &prior, const std::map< uint32_t, uint32_t > &new_semantic, const std::map< uint32_t, size_t > &new_sizes, const std::function< std::string()> &make_guid, const ReconcileOptions &opts={})
 Carry stable GUIDs across an instance-cloud regeneration.
void reconstruct_point_clouds (ProjectDB &db, const ReconstructionParams &params)
 Generate merged point clouds from sensor frames stored in a ProjectDB.
auto segment_instances (const SegmentInstancesRequest &request) -> SegmentInstancesResult
 Segment point cloud into instances using Euclidean clustering.
auto segment_instances_impl (const SegmentInstancesRequest &request) -> SegmentInstancesResult
 Implementation function for segment_instances.
auto segment_planes_impl (CloudConstPtr cloud, CloudNConstPtr normals, const SegmentPlanesOptions &options) -> std::tuple< CloudLPtr, CloudLocPtr, CloudNPtr >
auto segment_planes (CloudConstPtr cloud, CloudNConstPtr normals, const SegmentPlanesOptions &options=SegmentPlanesOptions{}) -> std::tuple< CloudLPtr, CloudLocPtr, CloudNPtr >
auto propagate_room_labels (CloudConstPtr cloud, CloudLPtr labels, IndicesConstPtr sampled_indices, IndicesConstPtr missing_indices, int k, float max_radius) -> size_t
 Propagate room labels from a labelled subset to a set of missing points using a distance-bounded k-NN majority vote.
auto segment_rooms_impl (CloudConstPtr cloud, CloudNConstPtr normals, CloudLConstPtr planes, const SegmentRoomsOptions &options) -> CloudLPtr
auto segment_rooms (CloudConstPtr cloud, CloudNConstPtr normals, CloudLConstPtr planes, const SegmentRoomsOptions &options=SegmentRoomsOptions{}) -> CloudLPtr
std::optional< FrameSurfelsextract_frame_surfels (ProjectDB &db, int node_id, const SurfelExtractionParams &params)
 Back-project one sensor frame into an optical-frame surfel set and seed its world pose from the stored transform and local (optical->sensor) transform.
SyncDownsampleResult sync_downsample (ProjectDB &db, const std::string &primary, const SyncDownsampleOptions &opts)
 Downsample primary and — unless only_primary is set — all of its index-aligned sibling clouds in db, using one shared voxel assignment so the outputs stay row-aligned.
JprResult refine_sensor_poses (ProjectDB &db, const JprParams &params, bool dry_run=false)
 High-level entry point: extract surfels for every sensor frame in db, jointly refine their poses via JPR, and (unless dry_run) write the refined world poses back into the sensor_frames table.
double trajectory_extent (const std::vector< Eigen::Matrix4d > &poses)
 Spatial extent of a trajectory: the diagonal of the axis-aligned bounding box of the camera centres, in metres.
double seed_disagreement_gate (double extent, double fraction, double floor_m)
 Effective seed-disagreement gate in metres, combining a scale-relative term with an absolute floor: max(fraction * extent, floor), with negative inputs treated as 0.
std::vector< LoopEdgedetect_loop_edges (ProjectDB &db, const std::vector< int > &node_ids, const std::vector< Eigen::Matrix4d > &seed_poses, const LoopClosureOptions &options, LoopClosureResult *out_result=nullptr)
 Detect wide-baseline loop edges among the given frames.
std::vector< LoopEdgeload_loop_edges (const std::string &path, const std::vector< int > &node_ids, LoopClosureResult *out_result=nullptr)
 Load externally-computed loop edges from a JSON file and map them onto the optimizer's frame indices.
std::vector< LoopEdgefilter_consistent_loop_edges (std::vector< LoopEdge > edges, const std::vector< Eigen::Matrix4d > &seed, const LoopClosureOptions &options)
 Keep only the largest mutually consistent subset of edges (Pairwise Consistency Maximization, Mangelson et al.
PanoramaAlignmentResult align_panorama (ProjectDB &db, int pano_id, int seed_node_id, const PanoramaAlignmentOptions &opt)
 Align one panorama against nearby sensor frames.
std::vector< LoopEdgedetect_panorama_loop_edges (ProjectDB &db, const std::vector< int > &node_ids, const std::vector< Eigen::Matrix4d > &seed_poses, const PanoramaLoopOptions &options, const LoopClosureOptions &gates, PanoramaLoopResult *out_result=nullptr)
 Detect panorama-derived wide-baseline loop edges.
PoseDriftResult perturb_trajectory (std::vector< Eigen::Matrix4d > &poses, const PoseDriftOptions &opt)
 Apply synthetic drift to a trajectory in place (the pure, database-free core — this is what the unit tests exercise).
double median_pair_disagreement (const std::vector< Eigen::Matrix4d > &reference, const std::vector< Eigen::Matrix4d > &other, int min_frame_gap)
 Measure the median relative-pose disagreement between two trajectories over temporally distant frame pairs (m).
PoseDriftResult perturb_sensor_poses (ProjectDB &db, const PoseDriftOptions &opt, bool dry_run=false)
 Apply synthetic drift to every sensor-frame pose stored in db (#338).
std::vector< OdometrySigmaScaleodometry_motion_scales (const std::vector< OdometryEdgeMotion > &motion, double min_scale, double max_scale)
 Per-edge sigma scales for OdometryNoiseModel::motion.
PlaneFitSigmas plane_fit_sigmas (const PlaneFitQuality &q)
 Standard first-order uncertainty of a least-squares plane fit through q .inliers points with point noise sigma = q.residual_rms and in-plane RMS extent r = q.extent_minor:
PlaneGraphResult optimize_sensor_poses (ProjectDB &db, const PlaneGraphOptions &options, bool dry_run=false)
 High-level entry point: extract surfels for every sensor frame in db, globally optimize their poses via the plane-landmark graph, and (unless dry_run) write the optimized world poses back into the sensor_frames table.

Variables

constexpr float kWallVerticalityGateOff = 1.0f
 Verticality-gate threshold at which the gate is off (#326).

Typedef Documentation

◆ Plane

template<typename Scalar>
using reusex::geometry::Plane = Eigen::Matrix<Scalar, 4, 1>

Definition at line 71 of file regularization.hpp.

◆ PlaneVector

template<typename Scalar>
using reusex::geometry::PlaneVector
Initial value:
std::vector<Plane<Scalar>, Eigen::aligned_allocator<Plane<Scalar>>>

Definition at line 74 of file regularization.hpp.

◆ ResolveInstanceGuidFn

using reusex::geometry::ResolveInstanceGuidFn = std::function<std::string(uint32_t)>

Resolves the stable GUID of an instance from its integer label id.

Returns an empty string when no GUID is available (unknown/legacy). The caller (CLI) wires this to ProjectDB::instance_guid once per-instance identity exists; the library keeps no dependency on the database. See #211.

Definition at line 96 of file create_windows.hpp.

Enumeration Type Documentation

◆ CellNodeType

enum class reusex::geometry::CellNodeType
strong
Enumerator
cell 
face 
vertex 

Definition at line 28 of file CellComplex.hpp.

◆ ComponentType

Discriminator for building component types.

Enumerator
window 
door 
wall 

Definition at line 17 of file BuildingComponent.hpp.

◆ LoopProposal

enum class reusex::geometry::LoopProposal
strong

How loop-candidate frame pairs are proposed before geometric verification.

Enumerator
automatic 

Pick automatically (default): exhaustive when the temporally-distant pair count is within exhaustive_budget (small scans — the reliable choice, since the appearance shortlist can miss the true loop under indoor perceptual aliasing), otherwise appearance.

spatial 

Spatial shortlist from the SEED poses (camera-centre proximity + view agreement).

Cheap, but structurally BLIND to loops the drift has pulled apart — a start/end revisit under trajectory drift is never proposed because the drifted poses place those frames far apart. Use only when the seed poses are already globally consistent.

appearance 

Appearance shortlist from a pose-INDEPENDENT bag-of-words over the frames' own ORB descriptors.

Finds revisits by how the images LOOK, not where the (possibly drifted) poses say the camera was — this is what detects the start/end loop that fixes accumulated drift. What automatic falls back to once the exhaustive pair count exceeds exhaustive_budget.

exhaustive 

Every temporally distant pair (i, j) with j - i >= min_frame_gap.

Pose- and appearance-independent; the ground-truth proposer for small scans where the O(N^2) matcher cost is affordable (a few hundred frames).

Definition at line 61 of file LoopClosure.hpp.

◆ OdometryNoiseModel

How the per-edge measurement noise of the consecutive-frame odometry BetweenFactors is derived (#225 odometry-trust experiment).

Enumerator
fixed 

Every edge gets odometry_sigma_rot / odometry_sigma_trans unscaled (modulo the observability guard).

The shipped default.

motion 

Sigma proportional to the edge's OWN seed motion, normalised by the median motion over the run: a frame pair the device barely moved between is a near-noiseless relative measurement, while one spanning a fast sweep integrates far more sensor error.

This is the textbook "odometry error grows with distance travelled" model, and — like fit_geometry for the plane term — the median normalisation leaves the odometry chain's AGGREGATE authority against the plane factors unchanged, redistributing it rather than re-scaling it.

Definition at line 74 of file PlaneGraphOptimizer.hpp.

◆ PlaneNoiseModel

How the measurement noise of an individual plane observation is chosen.

Every observation enters the graph as an OrientedPlane3Factor whose 3-DoF residual is (two normal-tilt components [rad], one plane-offset component [m]). This enum selects how much authority a particular observation gets relative to the others; plane_sigma_normal / plane_sigma_distance always remain the sigmas of a median-quality observation, so switching models redistributes authority without changing the plane term's aggregate weight against odometry.

Enumerator
uniform 

Every observation gets plane_sigma_* unscaled.

A plane fit from 40 surfels on a cluttered shelf carries exactly as much authority as one fit from 4000 surfels on a bare wall — measured to be actively harmful once plane extraction gets dense (honka laser-GT F 0.795 -> 0.759).

inlier_count 

Legacy (pre-#225 round 4): sigma *= clamp(sqrt(median_N / N), min, max), the SAME scalar on both the normal and the distance channel.

Retained so the numbers recorded in #225 stay reproducible.

fit_geometry 

Sigmas derived from the fit statistics of each detection — its inlier count, its point-to-plane residual RMS and its in-plane extent — see plane_fit_sigmas.

Unlike inlier_count this weights the two channels separately, which matters because extent affects only the normal.

Definition at line 55 of file PlaneGraphOptimizer.hpp.

◆ SceneNodeType

Enumerator
point_cluster 
plane 
object 

Definition at line 31 of file SceneGraph.hpp.

◆ SolverChoice

enum class reusex::geometry::SolverChoice
strong

Which MIP backend the Solidifier should use for each (sub)problem.

  • auto: use the compiled-in primary backend (cuOpt if built with GPU, else HiGHS) and, on a solver error/OOM, retry the same problem with HiGHS on the CPU before giving up (issue #226).
  • cuopt: force the GPU backend (falls back to the compiled primary if the build has no cuOpt).
  • highs: force the CPU HiGHS backend.
Enumerator
automatic 
cuopt 
highs 

Definition at line 48 of file Solidifier.hpp.

◆ SolveStatus

enum class reusex::geometry::SolveStatus
strong

Backend-agnostic classification of the last MIP solve outcome.

Lets callers distinguish why a solve failed (infeasible vs. time-limit vs. error) without depending on the underlying solver's status enum.

Enumerator
not_solved 
optimal 
feasible 
infeasible 
unbounded 
time_limit 
iteration_limit 
numerical_error 
error 

Definition at line 25 of file Solidifier.hpp.

◆ WindowBoundaryMode

How to compute the window boundary polygon.

Enumerator
rectangle 
polyline 

Definition at line 41 of file create_windows.hpp.

Function Documentation

◆ align_panorama()

PanoramaAlignmentResult reusex::geometry::align_panorama ( ProjectDB & db,
int pano_id,
int seed_node_id,
const PanoramaAlignmentOptions & opt )

Align one panorama against nearby sensor frames.

Read-only on db (does not write the pose back — the caller decides, e.g. rux align 360 --dry-run).

Parameters
dbproject database
pano_idpanoramic_images.id
seed_node_idtimestamp-matched sensor frame node id (>=0), used to seed the candidate window and measure the correction
optparameters

◆ apply_depth_discontinuity_filter()

void reusex::geometry::apply_depth_discontinuity_filter ( cv::Mat & depth,
cv::Mat & confidence,
float gradient_threshold = 0.5f )

Remove "flying pixels" at depth discontinuity edges.

Computes depth gradients via Sobel operators and zeros out pixels with gradient magnitudes exceeding gradient_threshold.

Parameters
depthInput/output depth map (single-channel), modified in-place.
confidenceInput/output confidence map, modified in-place (may be empty).
gradient_thresholdMaximum allowed gradient in meters per pixel.

◆ apply_ray_consistency_filter()

void reusex::geometry::apply_ray_consistency_filter ( cv::Mat & depth,
cv::Mat & confidence,
float consistency_threshold = 0.2f )

Remove isolated noisy depth measurements.

Compares each pixel to the median of its 5x5 neighbourhood and zeros out pixels deviating by more than consistency_threshold.

Parameters
depthInput/output depth map (single-channel), modified in-place.
confidenceInput/output confidence map, modified in-place (may be empty).
consistency_thresholdMaximum allowed deviation from neighbourhood median in meters.

◆ bearing_to_pixel()

Eigen::Vector2d reusex::geometry::bearing_to_pixel ( const cv::Size & equirect,
const Eigen::Vector3d & bearing )

Unit bearing (need not be normalised) in panorama frame -> equirect pixel (u,v).

u wraps into [0,W); v is clamped to [0,H).

◆ building_component()

BuildingComponent reusex::geometry::building_component ( const ProjectDB & db,
std::string_view name )
inline

Load the component stored under name. Throws if absent.

Definition at line 92 of file component_persistence.hpp.

References reusex::ProjectDB::component_record(), and from_component_record().

◆ component_data_from_json()

void reusex::geometry::component_data_from_json ( BuildingComponent & c,
const std::string & json )

Deserialize JSON TEXT into the variant data on a BuildingComponent.

Referenced by from_component_record().

◆ component_data_to_json()

std::string reusex::geometry::component_data_to_json ( const BuildingComponent & c)

Serialize the type-specific variant data to JSON TEXT.

Referenced by to_component_record().

◆ component_type_from_string()

ComponentType reusex::geometry::component_type_from_string ( std::string_view str)

Referenced by from_component_record().

◆ compute_accuracy()

AccuracyReport reusex::geometry::compute_accuracy ( const Cloud & cloud,
const CloudLoc & gt,
const AccuracyMetricsOptions & opt = {} )

Score a reconstructed cloud against a ground-truth point cloud.

Nearest-neighbour distances are computed with a KdTree; the ground truth is optionally voxel-downsampled first (opt.gt_voxel). Non-finite points in either cloud are skipped.

Exceptions
std::invalid_argumentif either input is empty, or if every point of either input is non-finite (nothing left to score).

◆ compute_number_of_inliers()

auto reusex::geometry::compute_number_of_inliers ( CloudConstPtr cloud,
Eigen::Vector4d const & plane,
IndicesConstPtr indices,
const float threshold = 0.2 ) -> size_t

Count number of inliers for a plane.

Parameters
cloudInput point cloud.
planePlane coefficients.
indicesPoint indices to check.
thresholdDistance threshold for inliers. Default 0.2.
Returns
Number of inliers.

◆ compute_plane_quality()

QualityReport reusex::geometry::compute_plane_quality ( const Cloud & cloud,
const CloudL & labels,
const QualityMetricsOptions & options = {} )

Compute per-plane flatness/thickness quality metrics.

Each plane (points sharing a label > 0 in labels) is refit by least squares (PCA), independent of any stored plane parameters, and residuals are measured against the refit plane.

Exceptions
std::invalid_argumentif cloud and labels sizes differ or the cloud is empty.

◆ compute_polygon_normal()

template<typename CloudPtr>
auto reusex::geometry::compute_polygon_normal ( const pcl::Vertices & poly,
const CloudPtr & cloud ) -> Eigen::Vector3f

Compute the normal vector of a polygon.

Calculates the normal vector of a polygon using the cross product sum method (also known as Newell's method). The normal is automatically normalized.

Template Parameters
CloudPtrPoint cloud pointer type (const or non-const, e.g., CloudLocConstPtr).
Parameters
polyPolygon with vertex indices.
cloudPoint cloud containing the vertices (read-only).
Returns
Normalized normal vector of the polygon.
Exceptions
std::invalid_argumentif polygon has fewer than 3 vertices.
std::runtime_errorif polygon is degenerate (zero normal magnitude).

Definition at line 137 of file utils.hpp.

◆ create_windows()

CreateWindowsResult reusex::geometry::create_windows ( CloudConstPtr cloud,
CloudLConstPtr instance_labels,
const std::map< uint32_t, uint32_t > & instance_to_semantic,
const pcl::PolygonMesh & mesh,
const std::vector< uint32_t > & window_semantic_labels,
const CreateWindowsOptions & options = {},
const ResolveInstanceGuidFn & resolve_instance_guid = {} )

Create window BuildingComponents from instance-labeled points and wall geometry.

For each window instance, projects its points onto the nearest wall plane, computes a boundary polygon (AABB or concave hull), and offsets it along the outward wall normal. Performs validation to filter out windows that intersect the mesh, are out of bounds, or are internal (optional).

Parameters
resolve_instance_guidOptional callback mapping an instance label id to its stable GUID; the result is stored on each component's source_instance_guid for provenance (issue #211). Defaults to a function returning empty (no provenance).

References mesh().

◆ cube_faces()

std::vector< PerspectiveView > reusex::geometry::cube_faces ( const cv::Mat & equirect,
int face_size,
int interp = 1 )

Tile the whole sphere as the six faces of a cube (90 deg FOV each).

Poles are covered by the up/down faces, avoiding the equirect's polar stretch.

◆ densify_from_images()

void reusex::geometry::densify_from_images ( ProjectDB & db,
const DensifyParams & params )

Run OpenMVS Multi-View Stereo in-process to produce a dense point cloud from the project's sensor frames.

This is the C++ library equivalent of the historical COLMAP patch_match_stereo + stereo_fusion pipeline, but linked into libreusex rather than invoked as a subprocess.

The function constructs an MVS::Scene directly from ProjectDB sensor frames (one MVS Platform per intrinsics group, one Pose per frame), runs Scene::DenseReconstruction(), and converts the resulting point cloud into a pcl::PointCloud<PointXYZRGB> saved under params.output_name.

Decoded JPEGs are staged in a temp directory because OpenMVS's depth estimator loads pixels through cv::imread regardless of API surface. The temp directory is removed on success.

Exceptions
std::runtime_errorif no sensor frames are usable, the scene cannot be built, or DenseReconstruction returns failure.

◆ detect_loop_edges()

std::vector< LoopEdge > reusex::geometry::detect_loop_edges ( ProjectDB & db,
const std::vector< int > & node_ids,
const std::vector< Eigen::Matrix4d > & seed_poses,
const LoopClosureOptions & options,
LoopClosureResult * out_result = nullptr )

Detect wide-baseline loop edges among the given frames.

Parameters
dbproject database (read-only image/depth/intrinsics access)
node_idsdatabase node id of each frame, in frame-index order (so LoopEdge::i/j index straight into the caller's vector)
seed_posesoptical->world pose of each frame (used to propose spatial candidates and sanity-check edges)
optionsdetection parameters
out_resultoptional statistics
Returns
accepted loop edges (possibly empty)

◆ detect_panorama_loop_edges()

std::vector< LoopEdge > reusex::geometry::detect_panorama_loop_edges ( ProjectDB & db,
const std::vector< int > & node_ids,
const std::vector< Eigen::Matrix4d > & seed_poses,
const PanoramaLoopOptions & options,
const LoopClosureOptions & gates,
PanoramaLoopResult * out_result = nullptr )

Detect panorama-derived wide-baseline loop edges.

Deterministic given the same database and options (STANDARDS §6): panoramas and frames are processed in sorted id order, the OpenCV RNG is re-seeded per panorama, and edge ordering/capping breaks ties on (i, j).

A project with no panoramas returns an empty vector and is NOT an error — the caller decides whether the user explicitly asked for panorama edges and should therefore be warned. out_result->panoramas reports the count either way.

Parameters
dbproject database (read-only: panorama images, frame colour/depth/intrinsics; panorama POSES are never read)
node_idsdatabase node id of each frame, in frame-index order, so LoopEdge::i/j index straight into the caller's vector
seed_posesoptical->world seed pose of each frame. Used ONLY by the seed-disagreement gates — never to build the measurement.
optionsdetection parameters
gatesmin_frame_gap / min_seed_disagreement / max_seed_disagreement reused from the ORB front-end, so one set of CLI knobs governs every edge source
out_resultoptional statistics
Returns
accepted loop edges (possibly empty)

◆ dist_plane_point()

auto reusex::geometry::dist_plane_point ( const Eigen::Vector4d & plane,
const Eigen::Vector3d & point ) -> double

Calculate distance from a point to a plane.

Parameters
planePlane coefficients (nx, ny, nz, d) where n is the normal.
point3D point coordinates.
Returns
Signed distance from point to plane.

◆ downsample() [1/3]

CloudPtr reusex::geometry::downsample ( const Cloud & cloud,
const VoxelAssignment & a )

Downsample cloud using the precomputed assignment.

The output point in bucket b is the centroid (mean position and mean RGB) of every input point that fell into bucket b.

cloud.size() must equal a.point_to_bucket.size().

◆ downsample() [2/3]

CloudLPtr reusex::geometry::downsample ( const CloudL & cloud,
const VoxelAssignment & a )

Downsample a parallel label cloud (labels, planes, rooms, instances) using the same assignment that was built from a sibling XYZRGB cloud.

The output label in bucket b is the majority label among all input points that fell into b. Ties are broken deterministically by choosing the lowest label value, so results are reproducible (docs/STANDARDS.md §6).

Output rows stay aligned position-for-position with the primary downsampled cloud: every bucket produces exactly one row, and buckets with no input points (which cannot happen for the primary cloud) emit label 0 (unlabeled, per the label contract in docs/STANDARDS.md §3.1).

cloud.size() must equal a.point_to_bucket.size().

◆ downsample() [3/3]

CloudNPtr reusex::geometry::downsample ( const CloudN & cloud,
const VoxelAssignment & a )

Downsample a parallel normals cloud using the same assignment that was built from a sibling XYZRGB cloud.

Per-bucket normals are averaged and renormalized. Curvature is averaged.

cloud.size() must equal a.point_to_bucket.size().

◆ estimate_cloud_noise()

auto reusex::geometry::estimate_cloud_noise ( CloudConstPtr cloud,
CloudNConstPtr normals,
const NoiseEstimateOptions & options = NoiseEstimateOptions{} ) -> NoiseEstimate

Estimate per-cloud sensor noise (sigma) and point density from local PCA patches.

For each of NoiseEstimateOptions::seed_count random seed points, the NoiseEstimateOptions::k_neighbors nearest neighbours are found via a KdTree and a plane is fit by PCA (smallest-eigenvalue eigenvector = normal). The per-seed residual is the median absolute point-to-plane distance within the patch; sigma is the median of those residuals scaled by 1.4826 (the MAD→sigma consistency constant for Gaussian noise). Density is the median nearest-neighbour spacing across the seeds.

Deterministic for fixed inputs and options (STANDARDS §6).

Parameters
cloudInput point cloud (must be non-empty; needs enough points for at least a few k-neighbour patches).
normalsPoint normals (currently unused by the fit but kept for interface symmetry with the segmentation pipeline).
optionsSampling/neighbourhood parameters.
Returns
sigma, density and the number of contributing samples.
Exceptions
std::runtime_errorif cloud is empty.

◆ extract_frame_surfels()

std::optional< FrameSurfels > reusex::geometry::extract_frame_surfels ( ProjectDB & db,
int node_id,
const SurfelExtractionParams & params )

Back-project one sensor frame into an optical-frame surfel set and seed its world pose from the stored transform and local (optical->sensor) transform.

Points and normals are returned in the camera optical frame; normals are estimated with the viewpoint at the optical-frame origin so they face the camera. The returned world_pose equals worldTf * localTf, so that world_pt = world_pose * optical_pt reproduces the reconstruction geometry.

Returns
std::nullopt when the frame lacks usable color/depth, has no usable stored pose (ProjectDB::has_sensor_frame_pose(), #336), has invalid intrinsics, or yields too few points for normal estimation.

◆ extract_perspective() [1/2]

PerspectiveView reusex::geometry::extract_perspective ( const cv::Mat & equirect,
const Eigen::Matrix3d & R_pano_from_view,
const Eigen::Matrix3d & K,
int out_w,
int out_h,
int interp = 1 )

Render a virtual pinhole view with an explicit view->panorama rotation and intrinsics.

Used to reproject a specific camera's view out of the panorama (e.g. render what a sensor frame should see, given the aligned pose).

◆ extract_perspective() [2/2]

PerspectiveView reusex::geometry::extract_perspective ( const cv::Mat & equirect,
double yaw_deg,
double pitch_deg,
double fov_deg,
int out_w,
int out_h,
int interp = 1 )

Render a virtual pinhole view centred at (yaw,pitch) with the given horizontal FOV.

Longitude wraps (BORDER_WRAP); the horizon stays level (no roll). interp is an OpenCV interpolation flag (default INTER_LINEAR).

◆ extract_wall_candidates()

std::vector< WallCandidate > reusex::geometry::extract_wall_candidates ( const pcl::PolygonMesh & mesh,
float normal_z_threshold = kWallVerticalityGateOff,
float coplanarity_angle_deg = 10.0f )

Extract planar wall candidates from a triangle mesh.

Decomposes the mesh into connected components of coplanar faces. Each component becomes a WallCandidate with a fitted plane, centroid, outward normal, and boundary vertices.

Parameters
normal_z_thresholdVerticality gate: a region is kept only when |mean_normal.z| < normal_z_threshold. The default kWallVerticalityGateOff keeps every orientation (floors, ceilings and tilted roofs included) so skylight windows still find a host surface; pass e.g. 0.3 to keep near-vertical walls only (#326).
coplanarity_angle_degMaximum angular deviation inside one region.

References kWallVerticalityGateOff, and mesh().

◆ filter_consistent_loop_edges()

std::vector< LoopEdge > reusex::geometry::filter_consistent_loop_edges ( std::vector< LoopEdge > edges,
const std::vector< Eigen::Matrix4d > & seed,
const LoopClosureOptions & options )

Keep only the largest mutually consistent subset of edges (Pairwise Consistency Maximization, Mangelson et al.

2018): two edges are consistent when chaining them through the seed odometry between their endpoints closes a near-identity cycle. True loops satisfy this even under heavy drift; a perceptual-aliasing false positive does not.

Public because loop edges reach the graph from TWO sources — internal detection (detect_loop_edges, which PCM-filters its own output) and the external --loop-edges file — and consistency is a property of the UNION, not of either source alone. Callers that union the two must run this over the combined set, otherwise an aliasing false positive in the file has no consistency filter at all (which is exactly the case --loop-trust makes dangerous). Honour options.pcm (CLI --loop-no-pcm) at the call site so one switch governs both sources.

Parameters
edgescandidate edges; indices must address seed
seedseed (pre-optimization) pose of every frame
optionspcm_trans_threshold / pcm_rot_threshold / pcm_max_edges
Returns
the retained subset (sets of <= 2 edges are returned unchanged — a pair cannot outvote itself)

◆ force_orthogonal_planes()

auto reusex::geometry::force_orthogonal_planes ( EigenVectorContainer< double, 4 > & planes,
const double threshold = 0.1,
const Eigen::Matrix< double, 3, 1 > & up = Eigen::Matrix< double, 3, 1 >(0, 0, 1) ) -> EigenVectorContainer< double, 4 >

Force planes to be orthogonal to a reference direction.

Adjusts plane normals to be either parallel or perpendicular to the up vector.

Parameters
planesPlane coefficients to adjust.
thresholdAngular threshold for orthogonality. Default 0.1.
upReference up vector. Default (0, 0, 1).
Returns
Adjusted plane coefficients.

◆ from_component_record()

◆ list_building_components()

std::vector< std::string > reusex::geometry::list_building_components ( const ProjectDB & db,
ComponentType type )
inline

Names of all stored components of the given type.

Definition at line 98 of file component_persistence.hpp.

References reusex::ProjectDB::list_building_components(), and to_string().

◆ load_loop_edges()

std::vector< LoopEdge > reusex::geometry::load_loop_edges ( const std::string & path,
const std::vector< int > & node_ids,
LoopClosureResult * out_result = nullptr )

Load externally-computed loop edges from a JSON file and map them onto the optimizer's frame indices.

This is the license-clean bridge for learned matchers: any external producer — a commercial-safe matcher (XFeat / EfficientLoFTR / LightGlue+ALIKED / MapAnything-apache) or an offline research oracle (MASt3R / MapAnything-NC — non-commercial, run in Python only for accuracy-ceiling probes) — writes a file of relative-pose constraints, and the GPL C++ consumes it as DATA. No non-commercial or non-GPL model ever links into the binary; see docs/research/loop-closure-learned-matchers.md.

File schema "reusex.loop_edges.v1" (JSON): { "schema": "reusex.loop_edges.v1", "producer": "<free-text tag, e.g. mast3r@<sha> or xfeat>", "edges": [ { "node_i": <int>, "node_j": <int>, "T_ij": [16 doubles, row-major 4x4 = pose(i)^-1 * pose(j) in the optical->world convention the graph uses], "sigma_rot": <double, rad>, "sigma_trans": <double, m>, "inliers": <int> } ] } node_i/node_j are DB sensor_frame node ids; they are mapped to frame indices via node_ids (node_ids[k] is the DB id of frame k). Edges that reference a node id absent from node_ids, self-loops, or duplicate the same (i,j) are skipped with a warning (never silently dropped — docs/STANDARDS.md §5). Missing sigma/inliers fall back to the LoopEdge defaults.

The numeric payload is validated, not merely shape-checked: an edge is rejected as malformed when T_ij is not finite, its 3x3 block is not a proper rotation (R^T R != I beyond 1e-3, or det(R) < 0 — a reflection would corrupt the solve), its bottom row is not [0,0,0,1] (beyond 1e-6), or a supplied sigma is non-finite or <= 0. A zero sigma is specifically dangerous: it becomes a gtsam Constrained (hard-equality) noise model, so one bad producer line would nail two poses together.

This loader does NOT apply consistency filtering. PCM and the seed-disagreement gate are applied by the caller over the UNION of the external and internally-detected edges — see filter_consistent_loop_edges and optimize_sensor_poses().

Parameters
pathJSON file path
node_idsDB node id of each frame, in frame-index order
out_resultoptional statistics (edges accepted, summed inliers)
Returns
accepted loop edges with i/j as frame indices
Exceptions
std::runtime_errorif the file cannot be opened or is not valid JSON of the expected schema (a bad edge file must fail loudly, not run a silently-unconstrained optimization).

◆ make_pairs()

auto reusex::geometry::make_pairs ( EigenVectorContainer< double, 4 > & planes,
std::vector< IndicesPtr > & inliers,
EigenVectorContainer< double, 3 > & centroids,
const double threshold = 0.6,
const double new_plane_offset = 0.5 ) -> std::vector< std::pair< size_t, size_t > >

Create pairs of opposite parallel planes.

Finds pairs of planes with opposite normals within a distance threshold. Creates new planes for unpaired planes.

Parameters
planesPlane coefficients.
inliersIndices of points belonging to each plane.
centroidsPlane centroids.
thresholdMaximum distance threshold for pairing. Default 0.6.
new_plane_offsetOffset for creating new planes. Default 0.5.
Returns
Vector of plane index pairs.

◆ median_pair_disagreement()

double reusex::geometry::median_pair_disagreement ( const std::vector< Eigen::Matrix4d > & reference,
const std::vector< Eigen::Matrix4d > & other,
int min_frame_gap )

Measure the median relative-pose disagreement between two trajectories over temporally distant frame pairs (m).

This is the §9.5 "edge <-> seed disagreement" statistic, computed against a known reference instead of against a matcher, and is exported so the benchmark can report the same number for a drifted scan that #337 reported for a real one.

Both vectors must be the same length and in the same temporal order.

◆ merge_planes()

auto reusex::geometry::merge_planes ( EigenVectorContainer< double, 4 > const & planes_,
std::vector< IndicesPtr > const & inliers_,
EigenVectorContainer< double, 3 > const & centroids_,
CloudConstPtr cloud,
const double angle_threshold = 0.1,
const double distance_threshold = 0.5 ) -> std::tuple< EigenVectorContainer< double, 4 >, std::vector< IndicesPtr >, EigenVectorContainer< double, 3 > >

Merge similar planes based on angle and distance.

Agglomerative clustering: a pair merges when it is coplanar (normal and offset within the tolerances below) and spatially adjacent (inlier bounding boxes overlap, or their gap is below the coplanarity tolerance).

There is no inlier-overlap gate. The min_overlap parameter this function used to accept was removed in #325: measured on a real scan, honouring it at its documented 0.8 default blocked legitimate split-wall merges (the very regression #215 fixed), and any lower value changed nothing while doubling the merge cost.

Parameters
planes_Input plane coefficients.
inliers_Inliers for each plane.
centroids_Plane centroids.
cloudPoint cloud.
angle_thresholdAngular similarity threshold. Default 0.1.
distance_thresholdDistance threshold for merging. Default 0.5.
Returns
Tuple of (merged planes, merged inliers, merged centroids).

◆ mesh()

pcl::PolygonMeshPtr reusex::geometry::mesh ( CloudConstPtr cloud,
CloudNConstPtr normals,
EigenVectorContainer< double, 4 > & planes,
EigenVectorContainer< double, 3 > & centroids,
std::vector< IndicesPtr > & inliers,
CloudLConstPtr rooms,
MeshOptions const opt = MeshOptions{} )

Generate a mesh from point cloud and geometric primitives.

Parameters
cloudInput point cloud.
normalsPoint cloud normals.
planesDetected plane coefficients.
centroidsPlane centroids.
inliersIndices of points belonging to each plane.
roomsRoom labels for points.
optMesh generation options (includes optional filter).
Returns
Generated polygon mesh.

Referenced by create_windows(), reusex::geometry::cgal::decompose_mesh(), extract_wall_candidates(), texture_mesh(), texture_mesh(), texture_mesh_with_cloud(), and unweld_mesh().

◆ odometry_motion_scales()

std::vector< OdometrySigmaScale > reusex::geometry::odometry_motion_scales ( const std::vector< OdometryEdgeMotion > & motion,
double min_scale,
double max_scale )

Per-edge sigma scales for OdometryNoiseModel::motion.

Each channel is scaled by that edge's motion relative to the median motion over the run, clamped to [min_scale, max_scale]:

scale_trans_i = clamp(|dt_i| / median_j(|dt_j|), min, max)
scale_rot_i   = clamp(|dr_i| / median_j(|dr_j|), min, max)

Properties this function guarantees, and which the unit tests pin:

  • a uniform-motion run yields all-1.0 scales, i.e. it degenerates exactly to OdometryNoiseModel::fixed (so the model is a strict generalisation of the shipped one);
  • a zero or non-finite median leaves every scale at 1.0 rather than producing infinities (a stationary capture must not be a divide-by-zero);
  • the mapping is monotone: a larger motion never earns a smaller sigma.

The two channels are normalised independently because a pure-rotation sweep and a pure-translation dolly are different failure modes of the seed.

◆ optimize_sensor_poses()

PlaneGraphResult reusex::geometry::optimize_sensor_poses ( ProjectDB & db,
const PlaneGraphOptions & options,
bool dry_run = false )

High-level entry point: extract surfels for every sensor frame in db, globally optimize their poses via the plane-landmark graph, and (unless dry_run) write the optimized world poses back into the sensor_frames table.

The optimizer works on the combined optical->world pose; on write-back the constant local (optical->sensor) transform is removed so the stored transform column keeps its worldTf meaning (identical to refine_sensor_poses).

Parameters
dbProject database (read/write).
optionsOptimization parameters.
dry_runWhen true, compute and report statistics without writing.
Returns
Optimization statistics.

◆ overlapping_views()

std::vector< PerspectiveView > reusex::geometry::overlapping_views ( const cv::Mat & equirect,
int n_yaw,
double fov_deg,
int tile,
int interp = 1 )

Tile the sphere as n_yaw evenly spaced views around the equator plus one up and one down view, each with fov_deg horizontal FOV and tile pixels square.

Overlap between neighbours (fov_deg > 360/n_yaw) hides seams; the larger overlap (vs cube faces) is what the ORB matcher wants so a feature is never split across a tile boundary.

◆ parse_solver_choice()

SolverChoice reusex::geometry::parse_solver_choice ( const std::string & s)

Parse a –solver CLI string ("auto"/"cuopt"/"highs") into a SolverChoice.

Throws std::runtime_error on an unrecognized value.

◆ perturb_sensor_poses()

PoseDriftResult reusex::geometry::perturb_sensor_poses ( ProjectDB & db,
const PoseDriftOptions & opt,
bool dry_run = false )

Apply synthetic drift to every sensor-frame pose stored in db (#338).

Frames are ordered by node id (the same ordering optimize_sensor_poses uses) and only frames that actually carry a pose participate. The stored poses are OVERWRITTEN — this is a destructive operation intended for a disposable COPY of a project, never for an original capture; rux edit perturb-poses says so in its help and the bench harness copies first.

Parameters
dbproject to perturb, opened read-write
optdrift parameters
dry_runmeasure and report without writing anything back (how the harness reads a trajectory's extent before committing)
Returns
realised drift statistics
Exceptions
std::runtime_errorif fewer than two frames carry a pose

◆ perturb_trajectory()

PoseDriftResult reusex::geometry::perturb_trajectory ( std::vector< Eigen::Matrix4d > & poses,
const PoseDriftOptions & opt )

Apply synthetic drift to a trajectory in place (the pure, database-free core — this is what the unit tests exercise).

Parameters
posesoptical->world poses in temporal order; perturbed in place. Fewer than 2 poses is a no-op.
optdrift parameters
Returns
realised drift statistics

◆ pixel_to_bearing()

Eigen::Vector3d reusex::geometry::pixel_to_bearing ( const cv::Size & equirect,
double u,
double v )

Equirect pixel (u,v) -> unit bearing in the panorama frame.

◆ plane_fit_sigmas()

PlaneFitSigmas reusex::geometry::plane_fit_sigmas ( const PlaneFitQuality & q)

Standard first-order uncertainty of a least-squares plane fit through q .inliers points with point noise sigma = q.residual_rms and in-plane RMS extent r = q.extent_minor:

sigma_distance = sigma / sqrt(N)
sigma_normal   = sigma / (r * sqrt(N))

The offset is an average over the inliers, so it tightens as 1/sqrt(N). The tilt is a lever-arm estimate: the same point noise at the edge of a 3 m wall subtends a far smaller angle than at the edge of a 20 cm patch, so the normal additionally tightens as 1/r. This factor of r is the whole reason the two channels must not share one scalar — two detections with identical N and sigma but 15x different extent are equally certain in offset and 15x apart in tilt.

The returned values are relative quality indicators, not absolute calibrated sigmas: the dominant real error is model error (surface roughness, depth bias, slight mis-association), not the fit's own statistical error. The optimizer therefore normalises them by their median across the run and applies the result as a scale on plane_sigma_normal / plane_sigma_distance.

Degenerate inputs are clamped rather than propagated, so the result is always finite and strictly positive.

◆ prepare_texture_dir()

std::filesystem::path reusex::geometry::prepare_texture_dir ( const std::filesystem::path & requested = {})

Prepare the directory the generated texture images are staged in.

The library never writes to — or deletes from — the process working directory (issue #245):

  • requested empty (the default): a fresh, uniquely named directory is created under std::filesystem::temp_directory_path(). Because it is created here it is guaranteed not to have existed before, so no caller data can be clobbered. Concurrent rux create texture runs therefore never collide.
  • requested non-empty: the path is made absolute and created if missing. Existing content is left untouched — a caller-supplied directory is never cleared, so the caller owns its lifetime.
Returns
an absolute path to an existing, writable directory.
Exceptions
std::runtime_errorif the directory cannot be created.

◆ propagate_room_labels()

auto reusex::geometry::propagate_room_labels ( CloudConstPtr cloud,
CloudLPtr labels,
IndicesConstPtr sampled_indices,
IndicesConstPtr missing_indices,
int k,
float max_radius ) -> size_t

Propagate room labels from a labelled subset to a set of missing points using a distance-bounded k-NN majority vote.

For each index in missing_indices the function polls up to k nearest neighbours drawn from sampled_indices, discards any farther than max_radius, and assigns the majority room label among the remainder. Ties break deterministically towards the smallest room label. Points with no labelled neighbour inside the radius keep their current label (expected to be reusex::core::kUnlabeled == 0) and are counted.

Parameters
cloudFull point cloud (positions used for the search).
labelsIn/out labels; sampled_indices must already hold the seed room labels, missing points are written here.
sampled_indicesIndices whose labels act as propagation seeds.
missing_indicesIndices to assign a label to.
kNeighbours polled per missing point (clamped to >=1).
max_radiusMaximum neighbour distance in meters.
Returns
Number of missing points that remained unlabeled.

◆ reconcile_instance_identities()

ReconcileResult reusex::geometry::reconcile_instance_identities ( const CloudL * old_labels,
const CloudL & new_labels,
const std::vector< PriorInstance > & prior,
const std::map< uint32_t, uint32_t > & new_semantic,
const std::map< uint32_t, size_t > & new_sizes,
const std::function< std::string()> & make_guid,
const ReconcileOptions & opts = {} )

Carry stable GUIDs across an instance-cloud regeneration.

Both old_labels and new_labels are index-aligned instance-label clouds over the same point cloud (identical indices; 0 = unlabeled). For each new instance we find the old instance of the same semantic class with the greatest mutual point-set overlap (intersection over the smaller instance); if that overlap exceeds ReconcileOptions::min_overlap the new instance inherits the old GUID, otherwise it receives a fresh GUID from make_guid. Matching is greedy by descending overlap and one-to-one: each old GUID is carried to at most one new instance.

Parameters
old_labelsPrevious instance-label cloud (may be null/empty).
new_labelsNewly-segmented instance-label cloud.
priorPrior instance rows (semantic class + GUID per old id).
new_semanticMap: new instance_id -> semantic class.
new_sizesMap: new instance_id -> point count.
make_guidFactory for fresh GUIDs (e.g. a UUID generator).
optsReconciliation options.
Exceptions
std::invalid_argumentif old/new label clouds differ in size.

◆ reconstruct_point_clouds()

void reusex::geometry::reconstruct_point_clouds ( ProjectDB & db,
const ReconstructionParams & params )

Generate merged point clouds from sensor frames stored in a ProjectDB.

For each sensor frame the function reads depth, confidence, color, pose, and intrinsics; applies depth filters; performs custom pinhole back-projection; estimates normals; and accumulates into a single cloud. Post-processing includes voxelisation, NaN removal, label majority voting, statistical outlier removal, and radius outlier removal.

Results are saved into the ProjectDB as "cloud", "normals", and "labels".

Parameters
dbProject database (read/write).
paramsReconstruction parameters.

◆ refine_sensor_poses()

JprResult reusex::geometry::refine_sensor_poses ( ProjectDB & db,
const JprParams & params,
bool dry_run = false )

High-level entry point: extract surfels for every sensor frame in db, jointly refine their poses via JPR, and (unless dry_run) write the refined world poses back into the sensor_frames table.

The optimizer works on the combined optical->world pose; on write-back the constant local (optical->sensor) transform is removed so the stored transform column keeps its worldTf meaning.

Parameters
dbProject database (read/write).
paramsRefinement parameters.
dry_runWhen true, compute and report statistics without writing.
Returns
Convergence statistics.

◆ regularizePlanes()

template<typename Scalar, typename PointT>
auto reusex::geometry::regularizePlanes ( PlaneVector< Scalar > & planes,
typename pcl::PointCloud< PointT >::ConstPtr points,
std::vector< pcl::IndicesPtr > & inliers,
double angle_threshold = 25.0,
double distance_threshold = 0.01 )

Definition at line 78 of file regularization.hpp.

References reusex::trace().

◆ save_building_component()

void reusex::geometry::save_building_component ( ProjectDB & db,
const BuildingComponent & component )
inline

Insert or replace component, keyed by its name.

A guid is generated on first save and preserved across later saves.

Definition at line 78 of file component_persistence.hpp.

References reusex::ProjectDB::save_component_record(), and to_component_record().

◆ seed_disagreement_gate()

double reusex::geometry::seed_disagreement_gate ( double extent,
double fraction,
double floor_m )

Effective seed-disagreement gate in metres, combining a scale-relative term with an absolute floor: max(fraction * extent, floor), with negative inputs treated as 0.

A result of 0 means "no gate" (keep every edge).

Issue #339: expressing the gate purely in metres does not transfer across capture scales. The 0.50 m external default was tuned on a scan with ~16 m of drift over an 18 m extent; applied unchanged to a 1.8 m ARKitScenes room scan it stops selecting informative edges and starts selecting the most disagreeing — i.e. the wrong — ones, measured at F@50mm 0.29 (registration-improvements.md §9.4). The fraction term fixes that. The floor term is kept because a matcher's own error is a sensor property that does NOT shrink with the room: on a small scan the floor is what keeps loop closure a no-op instead of injecting depth noise.

◆ segment_instances()

auto reusex::geometry::segment_instances ( const SegmentInstancesRequest & request) -> SegmentInstancesResult

Segment point cloud into instances using Euclidean clustering.

Applies PCL's Euclidean clustering algorithm within each semantic class to separate spatially-disconnected regions into distinct instances.

Algorithm:

  1. Extract unique semantic labels (skip label 0)
  2. For each semantic class (parallelized with OpenMP):
    • Build KdTree with points from that class
    • Apply pcl::extractEuclideanClusters
    • Assign sequential instance IDs
  3. Store metadata (instance → semantic mapping, sizes)
Parameters
requestConfiguration and input data
Returns
Result containing instance labels and metadata
Exceptions
std::invalid_argumentif inputs are invalid
std::runtime_errorif cancellation requested

◆ segment_instances_impl()

auto reusex::geometry::segment_instances_impl ( const SegmentInstancesRequest & request) -> SegmentInstancesResult

Implementation function for segment_instances.

Separated for future enhancements (e.g., different clustering algorithms)

◆ segment_planes()

auto reusex::geometry::segment_planes ( CloudConstPtr cloud,
CloudNConstPtr normals,
const SegmentPlanesOptions & options = SegmentPlanesOptions{} ) -> std::tuple< CloudLPtr, CloudLocPtr, CloudNPtr >

◆ segment_planes_impl()

auto reusex::geometry::segment_planes_impl ( CloudConstPtr cloud,
CloudNConstPtr normals,
const SegmentPlanesOptions & options ) -> std::tuple< CloudLPtr, CloudLocPtr, CloudNPtr >

◆ segment_rooms()

auto reusex::geometry::segment_rooms ( CloudConstPtr cloud,
CloudNConstPtr normals,
CloudLConstPtr planes,
const SegmentRoomsOptions & options = SegmentRoomsOptions{} ) -> CloudLPtr

◆ segment_rooms_impl()

auto reusex::geometry::segment_rooms_impl ( CloudConstPtr cloud,
CloudNConstPtr normals,
CloudLConstPtr planes,
const SegmentRoomsOptions & options ) -> CloudLPtr

◆ separate_planes()

auto reusex::geometry::separate_planes ( const EigenVectorContainer< double, 4 > & planes,
const Eigen::Vector3d & up = Eigen::Vector3d(0, 0, 1),
const double epsilon = 0.1 ) -> std::tuple< std::vector< size_t >, std::vector< size_t > >

Separate planes into horizontal and vertical based on up vector.

Parameters
planesPlane coefficients.
upReference up vector. Default (0, 0, 1).
epsilonAngular epsilon for classification. Default 0.1.
Returns
Tuple of (horizontal indices, vertical indices).

◆ stitch_labels_to_equirect()

cv::Mat reusex::geometry::stitch_labels_to_equirect ( const cv::Size & out,
const std::vector< PerspectiveView > & views,
const std::vector< cv::Mat > & tile_labels )

Stitch per-tile label maps (CV_32S, -1 = background) back into a single equirect label map of size out.

For each equirect pixel the covering tile whose optical axis is closest to that bearing wins (most central sample, least perspective distortion). Tiles must align 1:1 with views. Background (-1) samples never override a real label from another tile.

◆ sync_downsample()

SyncDownsampleResult reusex::geometry::sync_downsample ( ProjectDB & db,
const std::string & primary,
const SyncDownsampleOptions & opts )

Downsample primary and — unless only_primary is set — all of its index-aligned sibling clouds in db, using one shared voxel assignment so the outputs stay row-aligned.

Sibling discovery: every point cloud whose point count equals the primary cloud's point count (excluding the primary itself) is treated as a sibling. Supported sibling types are downsampled with the matching overload:

  • PointXYZRGB → centroid
  • Normal → averaged + renormalized
  • Label → per-bucket majority vote (deterministic tie-break) Label definitions attached to a Label sibling are carried over to its output. A sibling of an unsupported type causes the whole operation to throw (no partial, silently-desynchronized write).

When only_primary is true and index-aligned siblings exist, the call throws unless force_desync is also set; the exception message names every sibling that would be invalidated.

The operation is recorded in the pipeline log (start/end), naming the affected clouds, so staleness is auditable.

Throws std::invalid_argument if the primary is missing, not PointXYZRGB, empty, or the leaf size is non-positive; std::runtime_error on unsupported sibling types, size mismatches, or a refused desync.

◆ texture_mesh() [1/2]

pcl::TextureMesh::Ptr reusex::geometry::texture_mesh ( pcl::PolygonMesh::Ptr mesh,
std::map< int, CameraData > const & cameras,
const std::filesystem::path & texture_dir = {} )

Texture mesh using simple camera data (ProjectDB API).

Parameters
texture_dirstaging directory for the texture images; see prepare_texture_dir() for the empty-default semantics.

References mesh().

◆ texture_mesh() [2/2]

pcl::TextureMesh::Ptr reusex::geometry::texture_mesh ( pcl::PolygonMesh::Ptr mesh,
std::map< int, rtabmap::Transform > const & poses,
std::map< int, rtabmap::Signature > const & nodes,
const std::filesystem::path & texture_dir = {} )

Texture mesh using RTABMap signatures (legacy API).

Parameters
texture_dirstaging directory for the texture images; see prepare_texture_dir() for the empty-default semantics.

References mesh().

◆ texture_mesh_with_cloud()

pcl::TextureMesh::Ptr reusex::geometry::texture_mesh_with_cloud ( pcl::PolygonMesh::Ptr mesh,
CloudConstPtr cloud,
CloudNConstPtr normals = nullptr,
bool debug_distinct_colors = false,
const TextureQualityParams & quality = TextureQualityParams() )

References mesh().

◆ to_affine()

Eigen::Affine3f reusex::geometry::to_affine ( const std::array< double, 16 > & m)
inline

Build a 4x4 Eigen affine (float) from a row-major double[16] array.

Definition at line 17 of file transform_utils.hpp.

◆ to_array16()

std::array< double, 16 > reusex::geometry::to_array16 ( const Eigen::Affine3f & aff)
inline

Flatten a 4x4 Eigen affine (float) into a row-major double[16] array.

Definition at line 28 of file transform_utils.hpp.

◆ to_component_record()

◆ to_string() [1/2]

std::string_view reusex::geometry::to_string ( ComponentType type)

◆ to_string() [2/2]

const char * reusex::geometry::to_string ( SolveStatus status)

Human-readable name for a SolveStatus (for logging).

◆ trajectory_extent()

double reusex::geometry::trajectory_extent ( const std::vector< Eigen::Matrix4d > & poses)

Spatial extent of a trajectory: the diagonal of the axis-aligned bounding box of the camera centres, in metres.

This is the capture-scale denominator every scale-relative threshold in the loop-closure front-end divides by (issue #339). It is deliberately the bbox diagonal rather than the travelled path length: what a loop edge can be asked to correct is how far apart two places are, not how long the operator walked between them, and a scan that walks the same corridor twice must not read as twice the scale. Deterministic, O(N), independent of frame ordering (STANDARDS §6). Returns 0 for fewer than two poses.

Reference values (registration-improvements.md §9.5): office 18.01 m, ARKitScenes 41069048/50/51 1.82 / 1.86 / 2.57 m.

◆ unweld_mesh()

pcl::PolygonMeshPtr reusex::geometry::unweld_mesh ( const pcl::PolygonMesh & mesh,
float threshold_radians )

Unweld (split) mesh vertices along sharp edges.

Vertices shared by faces whose normals differ by more than threshold_radians are duplicated so each smooth face group gets its own copy. This produces correct per-face normals for export to formats like OBJ, Speckle, or Rhino.

Parameters
meshInput polygon mesh (const ref, not modified).
threshold_radiansMaximum dihedral angle (in radians) between face normals for vertices to remain shared. 0 = fully unweld, pi = preserve all sharing.
Returns
New polygon mesh with split vertices.

References mesh().

◆ update_building_component_by_guid()

void reusex::geometry::update_building_component_by_guid ( ProjectDB & db,
const BuildingComponent & component )
inline

Update an existing component's mutable fields, matched by its immutable guid.

Throws if no component has that guid.

Definition at line 86 of file component_persistence.hpp.

References to_component_record(), and reusex::ProjectDB::update_component_record_by_guid().

◆ voxel_assignment()

VoxelAssignment reusex::geometry::voxel_assignment ( const Cloud & cloud,
float leaf_size )

Build a voxel assignment for cloud at the given leaf size.

Non-finite points are recorded with kSkippedPoint in point_to_bucket and contribute neither a bucket nor a centroid.

Throws std::invalid_argument if leaf_size <= 0, the cloud is empty, or all points are non-finite. Throws std::out_of_range if the cloud's bounding-box extent exceeds ~2 million voxels along any axis at the chosen leaf size (raise the leaf size). Throws std::overflow_error if the number of occupied voxels exceeds 2^32 - 1 (raise the leaf size).

Variable Documentation

◆ kWallVerticalityGateOff

float reusex::geometry::kWallVerticalityGateOff = 1.0f
inlineconstexpr

Verticality-gate threshold at which the gate is off (#326).

The gate keeps a region when |mean_normal.z| < normal_z_threshold. Since every unit normal has |n.z| <= 1, this value admits every orientation — and it is treated as an exact opt-out so perfectly horizontal faces (|n.z| == 1) are kept too.

Definition at line 49 of file create_windows.hpp.

Referenced by extract_wall_candidates().