|
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... | |
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) |
| 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. | |
| std::size_t | import_mushroom (ProjectDB &db, const std::filesystem::path &capture_dir) |
| Import a MuSHRoom dataset capture directory into a ReUseX project. | |
| 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 55 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).
| auto reusex::io::configure_rhino_model | ( | ) | -> std::unique_ptr< ONX_Model > |
Create a configured ONX_Model with ReUseX metadata, units, and tolerances.
| 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. |
| 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. |
| 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). |
| 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.
| ExportScene reusex::io::gather_export_scene | ( | const ProjectDB & | db | ) |
Gather all exportable data from a ProjectDB.
Only populates layers that have data.
| 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. |
| std::size_t reusex::io::import_arkitscenes | ( | ProjectDB & | db, |
| const std::filesystem::path & | scene_dir ) |
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.
| db | Open project database (frames are written to it). |
| scene_dir | Scene directory (frames dir or its parent). |
| std::runtime_error | on missing/malformed inputs or zero frames. |
| 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. |
| std::size_t reusex::io::import_mushroom | ( | ProjectDB & | db, |
| const std::filesystem::path & | capture_dir ) |
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.
| db | Open project database (frames are written to it). |
| capture_dir | Path to the capture directory described above. |
| std::runtime_error | on missing/malformed files. |
| 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. |
| 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. |
| 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 kArkitMaxPoseGap.
| 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. |
| auto reusex::io::make_rhino_mesh | ( | const pcl::PolygonMesh & | mesh | ) | -> std::unique_ptr< ON_Mesh > |
Convert a PCL PolygonMesh to an ON_Mesh.
| auto reusex::io::make_rhino_pointcloud | ( | CloudConstPtr | cloud | ) | -> std::unique_ptr< ON_PointCloud > |
Convert a PCL XYZRGB point cloud to an ON_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.
| auto reusex::io::make_sphere_mesh | ( | double | cx, |
| double | cy, | ||
| double | cz, | ||
| double | radius, | ||
| int | resolution = 16 ) -> std::unique_ptr< ON_Mesh > |
Create a UV sphere 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).
| 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. |
|
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. |
| 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.
|
nodiscard |
Save plane data to file.
| output_path | Path to output file. |
| model_coefficients | Plane model coefficients. |
| centroids | Plane centroids. |
| inlier_indices | Indices of points belonging to each plane. |
|
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 61 of file arkitscenes.hpp.
Referenced by interpolate_pose().