Files
DarkRoom/ui/dr-ui/examples/merge.rs
T
dtourolle 42d11d919b cargo fmt and clippy across the panorama work, and one lint master carried
The dr-face comparison is master's: a negated partial-order test on the
eye box's width, rewritten as the two conditions it meant.
2026-09-19 15:53:06 +02:00

102 lines
3.7 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Merge a set of frames into a panorama DNG, headless.
//!
//! ```sh
//! cargo run -p dr-ui --example merge --release -- fixtures/pano/2025-08-05/*.CR2
//! cargo run -p dr-ui --example merge --release -- --out /var/tmp frame1.CR2 …
//! ```
//!
//! The job the interface runs (`dr_ui::merge`), driven from a loop instead
//! of a timer: the same code, the same events, a file at the end. What it
//! proves that the alignment example cannot is the second half — the tap,
//! the chunked warp, the resolve to sixteen bits and the DNG — and that the
//! result opens in the application as one RAW.
use std::path::PathBuf;
use std::time::Instant;
fn main() {
env_logger::init();
let mut args: Vec<String> = std::env::args().skip(1).collect();
let mut out_dir = None;
if args.first().map(String::as_str) == Some("--out") {
args.remove(0);
out_dir = Some(PathBuf::from(args.remove(0)));
}
if args.len() < 2 {
eprintln!("usage: merge [--out DIR] <frame> <frame>...");
std::process::exit(2);
}
let Ok(gpu) = pollster::block_on(dr_gpu::GpuContext::new_headless()) else {
eprintln!("no GPU");
std::process::exit(1);
};
let frames: Vec<dr_ui::merge::MergeInput> = args
.iter()
.map(|a| dr_ui::merge::MergeInput::read(std::path::Path::new(a)).expect("read"))
.collect();
let dir = out_dir.unwrap_or_else(|| {
PathBuf::from(&args[0])
.parent()
.map(std::path::Path::to_path_buf)
.unwrap_or_else(|| PathBuf::from("."))
});
let request =
dr_ui::merge::MergeRequest::new(frames, dr_ui::merge::MergeDestination::Local(dir));
let (tx, rx) = std::sync::mpsc::channel();
let (decide, decision) = std::sync::mpsc::channel();
let cancel = dr_ui::merge::Cancel::default();
let t = Instant::now();
let worker = std::thread::spawn(move || dr_ui::merge::run(gpu, request, tx, decision, cancel));
let mut last_stage = "";
for event in rx {
use dr_ui::merge::MergeEvent::*;
match event {
Progress { stage, done, total } => {
if stage != last_stage {
println!("{stage}…");
last_stage = stage;
}
if done == total || done % 4 == 0 {
println!(" {done}/{total}");
}
}
Aligned(r) => {
println!(
"aligned: {:.1} mm equivalent, rms {:.2} px, {} links, {:?} {}×{}",
r.focal_mm, r.rms_px, r.links, r.projection, r.width, r.height
);
for (k, f) in r.frames.iter().enumerate() {
match f {
Ok((yaw, pitch)) => {
println!(" frame {k}: yaw {yaw:7.2}° pitch {pitch:6.2}°")
}
Err(why) => println!(" frame {k}: NOT ALIGNED — {why}"),
}
}
// Headless: the confirmation the dialog would ask for.
let _ = decide.send(dr_ui::merge::Decision::Merge { projection: None });
}
Done {
path,
width,
height,
..
} => {
println!(
"wrote {} ({width}×{height}) in {:?}",
path.display(),
t.elapsed()
);
}
Failed(e) => {
eprintln!("failed: {e}");
std::process::exit(1);
}
Cancelled => println!("cancelled"),
}
}
let _ = worker.join();
}