ReUseX  0.0.5
3D Point Cloud Processing for Building Reuse
Loading...
Searching...
No Matches
PanoramaAlignment.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 Povl Filip Sonne-Frederiksen
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4//
5// Content-based alignment of 360 panoramas to the scan.
6//
7// A panorama is imported and placed on the trajectory purely by nearest EXIF
8// timestamp (`rux import 360`), inheriting the matched sensor frame's pose.
9// That is only as good as the clock sync and gives the panorama no independent
10// orientation. This module refines the panorama's 6-DoF pose from image
11// content:
12//
13// 1. render virtual pinhole slices of the equirect
14// (geometry/EquirectProjection)
15// 2. ORB-match each slice against nearby sensor frames — the SAME
16// license-clean
17// OpenCV ORB + Lowe-ratio front-end used by LoopClosure
18// 3. lift frame matches to metric 3D via the frame's stored depth + pose
19// 4. resect the panorama pose per slice with solvePnPRansac (each slice is a
20// true pinhole with known K and known slice->panorama rotation), then
21// refine over all slices' inliers in panorama-bearing space
22//
23// The refined pose is a panorama-optical->world transform in the SAME
24// convention as sensor_frame_pose, so it drops straight into the viewer /
25// exporters and (as a follow-up, issue #236) into the pose graph as loop
26// constraints.
27
28#pragma once
29
30#include <Eigen/Core>
31#include <Eigen/Geometry>
32
33#include <array>
34#include <string>
35
36namespace reusex {
37class ProjectDB;
38}
39
40namespace reusex::geometry {
41
44 // --- candidate sensor frames -------------------------------------------
52
53 // --- panorama slicing (geometry/EquirectProjection) --------------------
54 int n_yaw = 8;
55 double fov_deg = 90.0;
56 int slice = 1008;
57
58 // --- ORB front-end (mirrors LoopClosureOptions) ------------------------
59 int max_features = 3000;
60 float ratio_test = 0.85f;
61 float min_depth = 0.3f;
62 float max_depth = 6.0f;
63
64 // --- PnP resection -----------------------------------------------------
66 int min_inliers = 25;
67 float ransac_reproj_px = 5.0f;
70
78 double max_correction_m = 5.0;
79 unsigned seed = 42;
80
84 std::string debug_dir;
85 std::string debug_name;
86};
87
90 int pano_id = -1;
91 int seed_node = -1;
92 bool aligned = false;
93 int inliers = 0;
94 double rms_deg = -1.0;
97 std::array<double, 16> pose = {1, 0, 0, 0, 0, 1, 0, 0,
98 0, 0, 1, 0, 0, 0, 0, 1};
99
103 double delta_from_seed_m = -1.0;
104};
105
116 int seed_node_id,
117 const PanoramaAlignmentOptions &opt);
118
119} // namespace reusex::geometry
PanoramaAlignmentResult align_panorama(ProjectDB &db, int pano_id, int seed_node_id, const PanoramaAlignmentOptions &opt)
Align one panorama against nearby sensor frames.
Parameters for content-based panorama alignment.
double max_correction_m
Plausibility gate: reject an alignment whose refined centre is further than this from the timestamp-s...
int min_inliers
accept alignment above this (total)
double fov_deg
per-slice horizontal FOV (overlap for matching)
int refine_iterations
bearing-space Gauss-Newton refinement steps
float ransac_reproj_px
solvePnPRansac reprojection threshold
int candidate_window
Try the timestamp-seed frame plus this many frames on each side of it (in sensor-frame-id order).
int n_yaw
perspective slices around the equator
int min_slice_correspondences
need this many to PnP a slice
float min_depth
ignore frame keypoints outside [min,max] depth
std::string debug_dir
If non-empty, write an ORB-correspondence figure (panorama slice <-> the best-matching sensor frame,...
int max_candidates
Cap the number of candidate frames actually matched (nearest-to-seed first), bounding the O(slices x ...
Outcome of aligning one panorama.
double delta_from_seed_m
Translation delta from the timestamp-seed placement (m); -1 when there is no seed,...
std::array< double, 16 > pose
panorama-optical->world, row-major 4x4 (same convention as ProjectDB::sensor_frame_pose).
double rms_deg
angular reprojection RMS over inliers (deg)
int inliers
pooled PnP inliers supporting the pose