Expand description
Library part of the cumulus3d executable: the incremental stream pipeline and the stage-by-stage compatible subcommands (interop).
session: the state valuesession::Sessionand the reducer functionssession::step/session::poll/session::command/session::finish.pipeline:pipeline::Pipeline, which wraps the reducer and provides hooks, queue policy, panic isolation and subscription channels.events: per-stage resultsevents::Eventand commandsevents::Command.sinks: default hooks that produce the file output ofcumulus3d stream(timeline.txt, run.log, PLY, snapshots).stream: input folder -> session -> pipeline assembly (stream::run_stream).compose: a persistent node for live input. Declare the current input state (compose::FrameState) and it is compared with the accepted record to process incrementally.declare: the declarative builder layer. It only records a plan (declare::Plan);buildvalidates it andrunexecutes it in one go (declare::Recon). Plans are saved and read as TOML (cumulus3d run plan.toml).
§Usage example
Feed an image folder (images/camF|camR|camL/*.jpg, gps_ref.txt) position by position for incremental reconstruction,
and use the default output hook to produce the same files as cumulus3d stream.
use cumulus3d_cli::events::{Event, EventKind};
use cumulus3d_cli::pipeline::Pipeline;
use cumulus3d_cli::session::{Input, Session, SessionConfig};
use cumulus3d_cli::sinks::{self, SinkOptions};
use cumulus3d_cli::stream::{frame_set, Layout};
use cumulus3d_core::io::read_gps_file;
use std::path::Path;
fn main() -> Result<(), String> {
let src = Path::new("data");
let layout = Layout::discover(src, 3)?;
let mut cfg = SessionConfig::new(src.join("images"));
cfg.gps = read_gps_file(src.join("gps_ref.txt")).map_err(|e| e.to_string())?;
cfg.total_positions = Some(layout.frames.len());
let pipeline = Pipeline::new(Session::new(cfg)).on(EventKind::ZonePreview, |e: &Event| {
if let Event::ZonePreview { range, cloud, .. } = e {
println!("preview zone {}: {} points", range.zone, cloud.len());
}
});
let mut pipeline = sinks::attach(pipeline, Path::new("out"), &SinkOptions::default()).map_err(|e| e.to_string())?;
for p in 0..layout.frames.len() {
pipeline.push(Input::Frames(frame_set(&layout, p)));
}
let (_session, summary) = pipeline.finish();
println!("{} events", summary.events);
Ok(())
}Declarative (record a plan -> validate -> run):
use cumulus3d_cli::declare::{Dense, Recon, Sinks};
let recon = Recon::declare()
.input("data")
.preset("aerial-formation")
.zones(12, 2)
.dense(Dense::profile("fast").fusion_min_views(3))
.sinks(Sinks::default_files("out"))
.seed(7)
.build()
.unwrap_or_else(|e| panic!("{e}"));
let summary = recon.run().unwrap();
println!("{} events", summary.events);Functional use that only passes state values around, without hooks:
use cumulus3d_cli::session::{finish, step, FrameSet, Session, SessionConfig};
let s0 = Session::new(SessionConfig::new("data/images"));
let frames = FrameSet::new([("camF", "camF/camF_0000.jpg"), ("camR", "camR/camR_0000.jpg")]);
let (s1, events) = step(&s0, frames);
for e in &events {
if let Some(t) = e.timeline_text() {
println!("{t}");
}
}
let (_done, _summary_events) = finish(&s1);Modules§
- blocks
- Live stream (
<out>/live/stream/, format version 4): after every live event the composed display cloud (point keys and the owner zone of every point) is diffed against what the viewer shows and only the changes are written. Every shown point owns a denseu32slot id assigned here, so the viewer writes straight into its GPU buffers at index = id. - compose
- Declarative incremental composition for live frame sources.
- declare
- Declarative builder layer: record a plan first and run it all at once later.
- densewrap
- Shared densification path: exclude images outside the zone -> undistortion -> scene conversion -> densify.
Used by both the stream and the
densifysubcommand. - events
- Pipeline events. Each time a new frame bundle is processed, the result decomposed by stage is emitted. This file is the shared contract of three modules (session, pipeline, sinks). Adding fields is allowed; changing or removing existing fields is forbidden.
- fusion_
variants densify --fusion-variants <file>: build the depth maps once and fuse several times, changing only the fusion settings.- interop
- Stage-by-stage subcommands compatible with an external SfM tool (COLMAP) (thin wrappers). Subcommand names and option strings follow
that tool’s command line.
State between stages is carried by
--database_path(a FeatureStore binary file) and model folders (cumulus3d_core::interopformat). GPU-related options (--FeatureExtraction.use_gpuetc.) are accepted and ignored. - intrinsics
- Soft intrinsics prior (
cumulus3d stream --intrinsics-prior): known camera parameters (from an earlier calibration or run) that start every camera at the right values and pull the bundle adjustment back when the data alone are weak. - live
- In-memory point-keyed display updates (no files).
- pipeline
- Closure hook pipeline (
Pipeline): hook registration, queues, policies, panic isolation, channels. - post
- Post-processing: performs the Python part at the end of the script (preview alignment, per-event snapshots, manifest) and incremental_reanchor.py with the align crate. The model uses the in-memory Reconstruction as is.
- session
- State value and reducer:
Session+step/poll/command/finish. - sinks
- Default output hooks: receive events and produce the file output of
cumulus3d stream. - stream
cumulus3d stream: feed the positions of the input folder one by one into theSessionreducer (Pipeline), and the default output hooks (crate::sinks) produce timeline.txt, run.log, point clouds and snapshot files. The computation (features -> pair matching -> first model / continued registration -> per-zone preview and refinement) lives incrate::session.run_streamconverts the settings into a plan (crate::declare::Plan) and runs it through the declarative layer (crate::declare).- util
- Logging, time measurement and small serialization helpers.