|
ReUseX
0.0.5
3D Point Cloud Processing for Building Reuse
|
Namespaces | |
| namespace | speckle |
Classes | |
| struct | ArkitPincam |
| Parsed contents of an ARKitScenes .pincam intrinsics line. More... | |
| struct | ArkitPoseSample |
| One trajectory sample: a device-relative timestamp and the optical-to-world (camera->world) pose at that instant, row-major 4x4. More... | |
| struct | ColmapExportOptions |
| Options for COLMAP sparse model export. More... | |
| struct | E57ScanExport |
| A single scan position to write into an E57 file. More... | |
| struct | ExportScene |
| Intermediate representation for exporting all project data. More... | |
| struct | ImportPanoramasOptions |
| Controls auto-discovery and import of companion 360° panoramas. More... | |
Typedefs | |
| using | ArkitTrajectory = std::vector<ArkitPoseSample> |
| Trajectory as a timestamp-ascending sequence of camera->world samples. | |
Functions | |
| ArkitPincam | parse_pincam (const std::string &line) |
| Parse a single ARKitScenes .pincam line: width height fx fy cx cy (six whitespace-separated doubles). | |
| std::array< double, 16 > | arkit_traj_to_optical_world (double rx, double ry, double rz, double tx, double ty, double tz) |
| Convert one ARKitScenes trajectory pose to an optical-to-world 4x4 matrix (row-major, 16 doubles). | |
| ArkitTrajectory | load_arkit_trajectory (const std::filesystem::path &traj_path) |
| Parse the trajectory file into timestamp-sorted camera->world poses. | |
| std::optional< std::array< double, 16 > > | interpolate_pose (const ArkitTrajectory &traj, double ts, double max_gap=kArkitMaxPoseGap) |
Interpolate the camera->world pose at ts from the bracketing trajectory samples: SLERP on the rotation, linear interpolation on the camera position. | |
| bool | parse_frame_timestamp (const std::filesystem::path &file, double &ts) |
| Extract the timestamp encoded in an ARKitScenes stream filename such as 41069021_452.395.png: drop the extension, then take everything after the last underscore (a bare std::stod would stop at the video-id prefix). | |
| std::size_t | import_arkitscenes (ProjectDB &db, const std::filesystem::path &scene_dir, const ImportPanoramasOptions &pano_opts={}) |
| Import an ARKitScenes scene (lowres iPad-LiDAR streams) into a ReUseX project. | |
| void | export_colmap_scene (const ProjectDB &db, const std::filesystem::path &out_dir, const ColmapExportOptions &opt={}) |
| Export a COLMAP sparse model directory from a ProjectDB. | |
| void | import_e57 (ProjectDB &db, const std::filesystem::path &e57_path) |
| Import point cloud data from an E57 file into a ProjectDB. | |
| void | export_e57 (const std::filesystem::path &path, const std::vector< E57ScanExport > &scans) |
| Export one or more XYZRGB clouds as separate scan positions in an E57 file. | |
| double | read_exif_timestamp (const std::filesystem::path &jpeg_path) |
| Read EXIF DateTimeOriginal from a JPEG file. | |
| ExportScene | gather_export_scene (const ProjectDB &db) |
| Gather all exportable data from a ProjectDB. | |
| cv::Mat | stitch_insta360_x4 (const cv::Mat &dual_fisheye) |
| Stitch a dual-fisheye Insta360 X4 frame into an equirectangular BGR image. | |
| bool | is_insta360_dual_fisheye (const std::filesystem::path &path) |
| Returns true iff the file extension is ".insp" (case-insensitive). | |
| std::size_t | import_mushroom (ProjectDB &db, const std::filesystem::path &capture_dir, const ImportPanoramasOptions &pano_opts={}) |
| Import a MuSHRoom dataset capture directory into a ReUseX project. | |
| std::size_t | import_panoramas (ProjectDB &db, const std::filesystem::path &dir, const ImportPanoramasOptions &opts={}) |
| Auto-discover and import companion 360° equirect JPEGs from a capture directory into the project database. | |
| void | import_ply (ProjectDB &db, const std::filesystem::path &ply_path) |
| Import a point cloud from a PLY file into a ProjectDB. | |
| void | export_ply (const std::filesystem::path &path, const Cloud &cloud, const CloudN *normals=nullptr) |
| Export a point cloud to a binary PLY file. | |
| auto | getPlanes (CloudLConstPtr planes, CloudNConstPtr normals, CloudLocConstPtr locations) -> std::tuple< EigenVectorContainer< double, 4 >, EigenVectorContainer< double, 3 >, std::vector< IndicesPtr > > |
| Extract plane data from labeled point clouds. | |
| bool | save (std::filesystem::path const &output_path, std::vector< pcl::ModelCoefficients > const &model_coefficients, std::vector< Eigen::Vector4f, Eigen::aligned_allocator< Eigen::Vector4f > > const ¢roids, std::vector< std::shared_ptr< pcl::Indices > > const &inlier_indices) |
| Save plane data to file. | |
| bool | read (std::filesystem::path const &input_path, std::vector< pcl::ModelCoefficients > &model_coefficients, std::vector< Eigen::Vector4f, Eigen::aligned_allocator< Eigen::Vector4f > > ¢roids, std::vector< std::shared_ptr< pcl::Indices > > &inlier_indices) |
| Read plane data from file. | |
| auto | configure_rhino_model () -> std::unique_ptr< ONX_Model > |
| Create a configured ONX_Model with ReUseX metadata, units, and tolerances. | |
| auto | export_to_rhino (const ExportScene &scene) -> std::unique_ptr< ONX_Model > |
| Export a full ExportScene to a Rhino ONX_Model with layer hierarchy. | |
| auto | make_rhino_pointcloud (CloudConstPtr cloud) -> std::unique_ptr< ON_PointCloud > |
| Convert a PCL XYZRGB point cloud to an ON_PointCloud. | |
| auto | make_rhino_pointcloud (CloudConstPtr cloud, CloudNConstPtr normals) -> std::unique_ptr< ON_PointCloud > |
| Convert a PCL XYZRGB point cloud with normals to an ON_PointCloud. | |
| auto | make_rhino_mesh (const pcl::PolygonMesh &mesh) -> std::unique_ptr< ON_Mesh > |
| Convert a PCL PolygonMesh to an ON_Mesh. | |
| auto | make_sphere_mesh (double cx, double cy, double cz, double radius, int resolution=16) -> std::unique_ptr< ON_Mesh > |
| Create a UV sphere mesh. | |
| void | import_rtabmap (ProjectDB &db, const std::filesystem::path &rtabmap_db_path) |
| Import raw sensor data from an RTABMap database into a ProjectDB. | |
Variables | |
| constexpr double | kArkitMaxPoseGap = 0.5 |
| Largest bracketing interval interpolate_pose will interpolate across. | |
| using reusex::io::ArkitTrajectory = std::vector<ArkitPoseSample> |
Trajectory as a timestamp-ascending sequence of camera->world samples.
(load_arkit_trajectory guarantees the ordering; interpolate_pose requires it.)
Definition at line 57 of file arkitscenes.hpp.
| std::array< double, 16 > reusex::io::arkit_traj_to_optical_world | ( | double | rx, |
| double | ry, | ||
| double | rz, | ||
| double | tx, | ||
| double | ty, | ||
| double | tz ) |
Convert one ARKitScenes trajectory pose to an optical-to-world 4x4 matrix (row-major, 16 doubles).
(rx,ry,rz) is a Rodrigues rotation vector, (tx,ty,tz) a translation. Per Apple's traj_string_to_matrix, the assembled extrinsic E=[R|t] is world->camera, so camera->world = E.inverse(). ARKitScenes' camera frame is already the OpenCV optical convention (x-right, y-down, z-forward) — the same frame reconstruct back-projects into — so c2w is returned directly as the optical-to-world pose, with no axis flip (an inserted flip empirically fans the reconstructed walls into a spiral).
References arkit_traj_to_optical_world().
Referenced by arkit_traj_to_optical_world().
| auto reusex::io::configure_rhino_model | ( | ) | -> std::unique_ptr< ONX_Model > |
Create a configured ONX_Model with ReUseX metadata, units, and tolerances.
References configure_rhino_model().
Referenced by configure_rhino_model().
| void reusex::io::export_colmap_scene | ( | const ProjectDB & | db, |
| const std::filesystem::path & | out_dir, | ||
| const ColmapExportOptions & | opt = {} ) |
Export a COLMAP sparse model directory from a ProjectDB.
Produces this layout under out_dir :
<out_dir>/ images/<frame_id>.jpg # one per sensor_frame sparse/0/cameras.txt # PINHOLE camera per unique intrinsics sparse/0/images.txt # one entry per sensor_frame (T_cw) sparse/0/points3D.txt # optionally seeded from LiDAR cloud
| db | Source project database (read-only access). |
| out_dir | Output directory; created if missing. Existing files are overwritten. |
| opt | Export options. |
| std::runtime_error | on I/O failure or if db has no sensor frames. |
References export_colmap_scene().
Referenced by export_colmap_scene().
| void reusex::io::export_e57 | ( | const std::filesystem::path & | path, |
| const std::vector< E57ScanExport > & | scans ) |
Export one or more XYZRGB clouds as separate scan positions in an E57 file.
Each entry in scans becomes one Data3D block. RGB color is always written. Normals are written using the E57_EXT_surface_normals extension when provided.
| path | Output .e57 file path (created or overwritten). |
| scans | One or more scans to embed in the file. |
References export_e57().
Referenced by export_e57().
| void reusex::io::export_ply | ( | const std::filesystem::path & | path, |
| const Cloud & | cloud, | ||
| const CloudN * | normals = nullptr ) |
Export a point cloud to a binary PLY file.
When normals is provided it must have the same number of points as cloud; both are written as a single PLY element with merged fields (x, y, z, rgb, normal_x, normal_y, normal_z).
| path | Output .ply file path (created or overwritten). |
| cloud | XYZRGB point cloud to export. |
| normals | Surface normals to merge into the output (nullptr = skip). |
References export_ply().
Referenced by export_ply().
| auto reusex::io::export_to_rhino | ( | const ExportScene & | scene | ) | -> std::unique_ptr< ONX_Model > |
Export a full ExportScene to a Rhino ONX_Model with layer hierarchy.
References export_to_rhino().
Referenced by export_to_rhino().
| ExportScene reusex::io::gather_export_scene | ( | const ProjectDB & | db | ) |
Gather all exportable data from a ProjectDB.
Only populates layers that have data.
References gather_export_scene().
Referenced by gather_export_scene().
| auto reusex::io::getPlanes | ( | CloudLConstPtr | planes, |
| CloudNConstPtr | normals, | ||
| CloudLocConstPtr | locations ) -> std::tuple< EigenVectorContainer< double, 4 >, EigenVectorContainer< double, 3 >, std::vector< IndicesPtr > > |
Extract plane data from labeled point clouds.
| planes | Labeled point cloud where labels indicate plane IDs. |
| normals | Point cloud containing plane normals. |
| locations | Point cloud containing plane centroid locations. |
References getPlanes().
Referenced by getPlanes().
| std::size_t reusex::io::import_arkitscenes | ( | ProjectDB & | db, |
| const std::filesystem::path & | scene_dir, | ||
| const ImportPanoramasOptions & | pano_opts = {} ) |
Import an ARKitScenes scene (lowres iPad-LiDAR streams) into a ReUseX project.
ARKitScenes (Apple/Dehghan et al., NeurIPS 2021, research-only license) provides room-scale RGB-D sequences captured with the same iPad-LiDAR sensor class as ReUseX's own scans — an external benchmark for reconstruction quality (issue #224).
Expects the scene frames directory (the one directly containing lowres_wide.traj); if scene_dir does not contain it, one level of nesting is searched (e.g. <video_id>/ or <video_id>_frames/). The frames dir holds the per-stream folders: lowres_wide/ (RGB PNG), lowres_depth/ (CV_16UC1 mm PNG), confidence/ (CV_8UC1 {0,1,2}) and lowres_wide_intrinsics/ (<ts>.pincam), plus lowres_wide.traj.
Poses in lowres_wide.traj are world->camera in ARKit axes and are converted to optical-to-world before storage (see arkit_traj_to_optical_world). The trajectory is ~10 Hz while depth is ~60 Hz, so one frame in six lands on a trajectory sample: each frame's pose is interpolated to its own timestamp (see interpolate_pose), and frames outside the trajectory's time span are skipped. Depth is already CV_16UC1 millimeters and is passed through unchanged.
After importing sensor frames, companion 360° equirect JPEGs are auto-discovered and imported via import_panoramas() unless pano_opts.skip is set.
| db | Open project database (frames are written to it). |
| scene_dir | Scene directory (frames dir or its parent). |
| pano_opts | Controls companion panorama auto-discovery. |
| std::runtime_error | on missing/malformed inputs or zero frames. |
References import_arkitscenes().
Referenced by import_arkitscenes().
| void reusex::io::import_e57 | ( | ProjectDB & | db, |
| const std::filesystem::path & | e57_path ) |
Import point cloud data from an E57 file into a ProjectDB.
Each scan in the E57 file is stored as a separate named XYZRGB point cloud. Color, normals, and intensity data are extracted when available. If color is absent but intensity is present, intensity is mapped to grayscale RGB. Normals are stored as a separate cloud with a "_normals" suffix.
Cloud naming: uses the scan name from the E57 header when set, otherwise "{file_stem}_scan_{N}".
| db | Target project database (must be open in write mode). |
| e57_path | Path to the source .e57 file. |
References import_e57().
Referenced by import_e57().
| std::size_t reusex::io::import_mushroom | ( | ProjectDB & | db, |
| const std::filesystem::path & | capture_dir, | ||
| const ImportPanoramasOptions & | pano_opts = {} ) |
Import a MuSHRoom dataset capture directory into a ReUseX project.
MuSHRoom (Ren et al., WACV 2024, CC-BY-4.0) provides room-scale RGB-D sequences from consumer devices (iPhone LiDAR / Azure Kinect) together with Faro laser-scanned ground-truth meshes — the same sensor class as ReUseX's own iPad scans, which makes it the primary external benchmark for reconstruction quality (issue #221 / #224).
Expects a capture directory (e.g. <room>/iphone/long_capture, or its sdf_dataset_*_interp_4 subdirectory) containing the sdfstudio-style export: meta_data.json + NNNNNN_rgb.png + NNNNNN_sensor_depth.npy.
meta_data.json stores OpenCV-convention camera-to-world poses in normalized unit-box coordinates plus a worldtogt similarity transform back to the metric ground-truth-mesh frame. The importer applies that transform (keeping rotations orthonormal), so imported poses and reconstructed clouds live directly in the GT mesh frame — ready for accuracy comparison against gt_mesh.ply. Depth is stored as normalized float32 .npy and converted to metric CV_16UC1 millimeters.
After importing sensor frames, companion 360° equirect JPEGs are auto-discovered and imported via import_panoramas() unless pano_opts.skip is set.
| db | Open project database (frames are written to it). |
| capture_dir | Path to the capture directory described above. |
| pano_opts | Controls companion panorama auto-discovery. |
| std::runtime_error | on missing/malformed files. |
References import_mushroom().
Referenced by import_mushroom().
| std::size_t reusex::io::import_panoramas | ( | ProjectDB & | db, |
| const std::filesystem::path & | dir, | ||
| const ImportPanoramasOptions & | opts = {} ) |
Auto-discover and import companion 360° equirect JPEGs from a capture directory into the project database.
Discovery order: for each name in opts.subdir_hints, check whether dir/ <name> is a directory and collect it; if any hint directories are found those are scanned exclusively. If none exist, dir itself is scanned one level deep as a fallback (non-recursive, to avoid picking up RGB-D frame images that live in known subdirectories).
Equirect guard: only files whose JPEG header reports width == 2 × height are treated as panoramas. Non-equirect JPEGs are skipped at debug level.
Each candidate is matched to the nearest sensor frame by EXIF timestamp. If sensor frames carry no timestamps, or a file has no EXIF timestamp, the panorama is imported with node_id=NULL and a warn-level log is emitted — such panoramas are stored in the DB but are skipped by rux align 360 (which requires a linked sensor frame as an alignment seed).
| db | Open, writable project database. |
| dir | Capture root directory to search. |
| opts | Discovery and filtering options. |
References import_panoramas().
Referenced by import_panoramas().
| void reusex::io::import_ply | ( | ProjectDB & | db, |
| const std::filesystem::path & | ply_path ) |
Import a point cloud from a PLY file into a ProjectDB.
Detects available fields and extracts all data present:
| db | Target project database (must be open in write mode). |
| ply_path | Path to the source .ply file. |
References import_ply().
Referenced by import_ply().
| void reusex::io::import_rtabmap | ( | ProjectDB & | db, |
| const std::filesystem::path & | rtabmap_db_path ) |
Import raw sensor data from an RTABMap database into a ProjectDB.
For each node the function extracts color, depth, confidence, the optimized world pose, and camera intrinsics in their original RTABMap format and stores everything in the project database. Images are stored in their native orientation without rotation.
No heavy processing (voxelization, normal estimation, etc.) is performed. Point cloud generation is handled separately by reusex::geometry::reconstruct_point_clouds().
| db | Target project database (must be open in write mode). |
| rtabmap_db_path | Path to the source RTABMap .db file. |
References import_rtabmap().
Referenced by import_rtabmap().
| std::optional< std::array< double, 16 > > reusex::io::interpolate_pose | ( | const ArkitTrajectory & | traj, |
| double | ts, | ||
| double | max_gap = kArkitMaxPoseGap ) |
Interpolate the camera->world pose at ts from the bracketing trajectory samples: SLERP on the rotation, linear interpolation on the camera position.
Interpolation is done on the camera->world pose (rather than the raw world->camera extrinsic) so the lerped translation is the camera's world position, which moves smoothly; lerping the world->camera translation -R·p would mix in the rotation and bend the camera path.
The depth stream (~60 Hz) is six times denser than the trajectory (~10 Hz), so nearest-pose matching would hand most frames a pose up to ~17 ms stale (tens of millimetres of lateral error at typical hand-held speeds); interpolating removes that temporal error.
| traj | Timestamp-ascending trajectory (see load_arkit_trajectory). |
| ts | Query timestamp, in the trajectory's own time base. |
| max_gap | Reject if the bracketing samples are farther apart than this (tracking loss). |
ts lies outside the trajectory's span or the bracketing gap exceeds max_gap. No extrapolation is performed. References interpolate_pose(), and kArkitMaxPoseGap.
Referenced by interpolate_pose().
| bool reusex::io::is_insta360_dual_fisheye | ( | const std::filesystem::path & | path | ) |
Returns true iff the file extension is ".insp" (case-insensitive).
".insp" is the Insta360 proprietary dual-fisheye capture extension; every file with this extension is routed through stitch_insta360_x4 by the import path.
References is_insta360_dual_fisheye().
Referenced by is_insta360_dual_fisheye().
| ArkitTrajectory reusex::io::load_arkit_trajectory | ( | const std::filesystem::path & | traj_path | ) |
Parse the trajectory file into timestamp-sorted camera->world poses.
Each line is ts rx ry rz tx ty tz (world->camera; see arkit_traj_to_optical_world); unparseable and blank lines are skipped.
| std::runtime_error | if the file yields no poses. |
References load_arkit_trajectory().
Referenced by load_arkit_trajectory().
| auto reusex::io::make_rhino_mesh | ( | const pcl::PolygonMesh & | mesh | ) | -> std::unique_ptr< ON_Mesh > |
Convert a PCL PolygonMesh to an ON_Mesh.
References make_rhino_mesh().
Referenced by make_rhino_mesh().
| auto reusex::io::make_rhino_pointcloud | ( | CloudConstPtr | cloud | ) | -> std::unique_ptr< ON_PointCloud > |
Convert a PCL XYZRGB point cloud to an ON_PointCloud.
References make_rhino_pointcloud().
Referenced by make_rhino_pointcloud(), and make_rhino_pointcloud().
| auto reusex::io::make_rhino_pointcloud | ( | CloudConstPtr | cloud, |
| CloudNConstPtr | normals ) -> std::unique_ptr< ON_PointCloud > |
Convert a PCL XYZRGB point cloud with normals to an ON_PointCloud.
References make_rhino_pointcloud().
| auto reusex::io::make_sphere_mesh | ( | double | cx, |
| double | cy, | ||
| double | cz, | ||
| double | radius, | ||
| int | resolution = 16 ) -> std::unique_ptr< ON_Mesh > |
| bool reusex::io::parse_frame_timestamp | ( | const std::filesystem::path & | file, |
| double & | ts ) |
Extract the timestamp encoded in an ARKitScenes stream filename such as 41069021_452.395.png: drop the extension, then take everything after the last underscore (a bare std::stod would stop at the video-id prefix).
References parse_frame_timestamp().
Referenced by parse_frame_timestamp().
| ArkitPincam reusex::io::parse_pincam | ( | const std::string & | line | ) |
Parse a single ARKitScenes .pincam line: width height fx fy cx cy (six whitespace-separated doubles).
| std::runtime_error | if fewer than six numbers can be parsed. |
References parse_pincam().
Referenced by parse_pincam().
|
nodiscard |
Read plane data from file.
| input_path | Path to input file. |
| model_coefficients | Output plane model coefficients. |
| centroids | Output plane centroids. |
| inlier_indices | Output indices of points belonging to each plane. |
References read().
Referenced by read().
| double reusex::io::read_exif_timestamp | ( | const std::filesystem::path & | jpeg_path | ) |
Read EXIF DateTimeOriginal from a JPEG file.
Returns epoch seconds (double), or -1.0 if not available.
References read_exif_timestamp().
Referenced by read_exif_timestamp().
|
nodiscard |
| cv::Mat reusex::io::stitch_insta360_x4 | ( | const cv::Mat & | dual_fisheye | ) |
Stitch a dual-fisheye Insta360 X4 frame into an equirectangular BGR image.
The calibration is baked from the Hugin reference template in docs/guides/Process walk through.md Phase 5. Input must be a dual-fisheye cv::Mat with width == 2×height (two fisheye circles side by side; left half = front lens, right half = back lens). Resolution scaling is handled automatically relative to the reference resolution (5888×2944).
Remap maps are computed on the first call for a given input resolution and cached for subsequent calls — construction is ~10 ms at 5888×2944.
Lens model: equidistant fisheye, ~200° FoV, no polynomial distortion correction (a=b=c=0 in the reference .pto). Gain compensation aligns brightness between hemispheres; a multi-band (Laplacian-pyramid) blend hides the seam.
| dual_fisheye | BGR (or BGRA/grey) cv::Mat with dual-fisheye pixels. |
| std::runtime_error | if input dimensions are unexpected. |
References stitch_insta360_x4().
Referenced by stitch_insta360_x4().
|
constexpr |
Largest bracketing interval interpolate_pose will interpolate across.
The lowres_wide.traj stream is ~10 Hz (0.1 s spacing); a gap an order of magnitude larger means ARKit lost tracking, and interpolating across it would invent a pose, so such frames are rejected instead.
Definition at line 63 of file arkitscenes.hpp.
Referenced by interpolate_pose().