Skip to main content

hooks/
hooks.rs

1//! 람다 훅 예: 합성 장면(드론 3대 × 8위치)을 위치 단위로 밀어 넣고, 이벤트·초벌/정밀 점 수를 출력하며
2//! 기본 출력 훅으로 `cumulus3d stream` 과 같은 파일(timeline.txt, run.log, snapshots/ …)을 만든다.
3//!
4//! ```bash
5//! cargo run --release -p cumulus3d-cli --example hooks -- [출력 폴더]
6//! ```
7//! CUDA 장치가 있으면 조밀화까지 하고, 없으면 조밀화 없이(점군 0, 희소 점 수만) 돈다.
8
9#[path = "../tests/common/mod.rs"]
10mod common;
11
12use cumulus3d_cli::densewrap::{make_pm_backend, parse_profile, DenseConfig};
13use cumulus3d_cli::events::{Event, EventKind};
14use cumulus3d_cli::pipeline::Pipeline;
15use cumulus3d_cli::session::{FrameSet, Input, Session, SessionConfig};
16use cumulus3d_cli::sinks::{self, SinkOptions};
17use cumulus3d_cli::stream::{Layout, CAMS};
18use cumulus3d_core::io::read_gps_file;
19use std::path::PathBuf;
20
21fn main() -> Result<(), String> {
22    let out = std::env::args().nth(1).map(PathBuf::from).unwrap_or_else(|| std::env::temp_dir().join("cumulus3d_hooks"));
23    let src = out.with_extension("src");
24    if !src.join("gps_ref.txt").exists() {
25        common::make_dataset(&src);
26    }
27    if out.exists() {
28        std::fs::remove_dir_all(&out).map_err(|e| e.to_string())?;
29    }
30    let layout = Layout::discover(&src, 1)?;
31    let npos = layout.frames.len();
32
33    let mut cfg = SessionConfig::new(src.join("images"));
34    cfg.gps = read_gps_file(src.join("gps_ref.txt")).map_err(|e| e.to_string())?;
35    (cfg.span, cfg.overlap, cfg.total_positions, cfg.seed) = (4, 1, Some(npos), Some(7));
36    if cumulus3d_cuda::is_available() {
37        let mut d = DenseConfig::new(make_pm_backend("cuda"), parse_profile("fast")?);
38        d.undistort.max_image_size = 160;
39        cfg.dense = Some(d);
40    }
41
42    // 훅: 사건 문구 출력, 구역 점군 점 수, 경고·오류. 그리고 기본 출력 훅(파일).
43    let pipeline = Pipeline::new(Session::new(cfg))
44        .on_any(|e: &Event| {
45            if let Some(t) = e.timeline_text() {
46                println!("[event v{}] {t}", e.meta().version);
47            }
48        })
49        .on(EventKind::ZonePreview, |e: &Event| {
50            if let Event::ZonePreview { range, cloud, model, .. } = e {
51                println!("  초벌 {}: 조밀 점 {} / 희소 점 {}", range.zone, cloud.len(), model.num_points3d());
52            }
53        })
54        .on(EventKind::ZoneRefined, |e: &Event| {
55            if let Event::ZoneRefined { range, cloud, model, .. } = e {
56                println!("  정밀 {}: 조밀 점 {} / 희소 점 {}", range.zone, cloud.len(), model.num_points3d());
57            }
58        })
59        .on_message(|k, m| eprintln!("  {k:?}: {m}"));
60    let mut pipeline = sinks::attach(pipeline, &out, &SinkOptions::default()).map_err(|e| e.to_string())?;
61
62    for p in 0..npos {
63        pipeline.push(Input::Frames(FrameSet::new(CAMS.iter().map(|c| (*c, layout.name(c, p))))));
64    }
65    let (_, summary) = pipeline.finish();
66    println!("이벤트 {} 개, 훅 호출 {} 번, 훅 패닉 {}", summary.events, summary.hook_calls, summary.hook_panics);
67
68    for f in ["timeline.txt", "run.log", "snapshots/manifest.json", "snapshots/timeline.txt", "DONE"] {
69        let p = out.join(f);
70        println!("{} {}", if p.exists() { "있음" } else { "없음" }, p.display());
71    }
72    Ok(())
73}