A frame that did not fit ended the job with its name, and the only way on was Back, a smaller selection and every frame read, demosaiced and searched for keypoints again. Each row on the page now has a box. An unticked frame is left out and the rest are solved again from what the first pass measured. dr_pano::align is split for it: match_pairs does the matching and the pairwise RANSAC once over every frame (about 5 s for twelve), and solve takes a subset of the frames and uses only the links among them (about 0.1 s). Solving a subset by re-aligning it also moved every RANSAC seed, which are keyed on frame position, and on the fixture set that was enough to lose a marginal link and strand a neighbour of the frame left out. A frame that cannot be placed no longer stops the job either. Its row names it and Merge stays off until it is unticked; the headless example leaves such frames out the same way, and takes --leave-out N to try it.
87 lines
3.0 KiB
Rust
87 lines
3.0 KiB
Rust
//! TRACES: FR-MRG-1 | FR-MRG-10
|
|
//! Panorama geometry — from several frames to the rotations that relate
|
|
//! them, and the projections that lay them out.
|
|
//!
|
|
//! This is the CPU half of a merge (FR-MRG-10): keypoints, matching, the
|
|
//! rotation solve and the choice of output surface. The per-pixel half —
|
|
//! rendering, warping, seams, blending — is the GPU's and lives in
|
|
//! `dr-gpu`, driven from above; nothing here touches a full-resolution
|
|
//! pixel. The split is the whole design (panorama.md §4): everything in
|
|
//! this crate is bounded by the number of frames, not the size of the
|
|
//! composite, and runs on proxies.
|
|
//!
|
|
//! # Layout
|
|
//!
|
|
//! - [`image`] — the grayscale proxy a detector reads: oriented, resampled.
|
|
//! - [`features`] — keypoints with descriptors, and the XFeat decoder.
|
|
//! - [`xfeat`] — the network under tract (feature `xfeat`).
|
|
//! - [`matching`] — mutual nearest neighbours.
|
|
//! - [`homography`] — a robust pairwise homography, the focal length read
|
|
//! off it, and the rotation it implies.
|
|
//! - [`bundle`] — every rotation and the focal length refined together.
|
|
//! - [`align`] — the whole thing, from features to cameras, honest about
|
|
//! what it could not place.
|
|
//! - [`projection`] — perspective, cylindrical, spherical.
|
|
//! - [`linalg`] — the small dense algebra all of it uses.
|
|
//!
|
|
//! # What it depends on
|
|
//!
|
|
//! Nothing, without the `xfeat` feature: the geometry is pure Rust with
|
|
//! hand-rolled linear algebra (`linalg` says why) so that it tests without
|
|
//! a model, a GPU or a device, on synthetic sets whose answer is known
|
|
//! exactly. With the feature it adds the same `ort`-over-tract runtime the
|
|
//! rest of the application already carries.
|
|
|
|
pub mod align;
|
|
pub mod bundle;
|
|
pub mod features;
|
|
pub mod fill;
|
|
pub mod homography;
|
|
pub mod image;
|
|
pub mod linalg;
|
|
pub mod matching;
|
|
#[cfg(feature = "xfeat")]
|
|
pub mod migan;
|
|
pub mod projection;
|
|
#[cfg(feature = "xfeat")]
|
|
pub mod xfeat;
|
|
|
|
pub use align::{align, match_pairs, solve, AlignOptions, Alignment, Link, Pairs, Unaligned};
|
|
pub use bundle::Cameras;
|
|
pub use features::{Features, Keypoint};
|
|
pub use fill::{fill_border, Inpainter, Observer, Params as FillParams};
|
|
pub use image::Gray;
|
|
pub use projection::Projection;
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
pub enum PanoError {
|
|
#[error("bad input: {0}")]
|
|
Input(String),
|
|
#[error("geometry: {0}")]
|
|
Geometry(String),
|
|
#[error("model: {0}")]
|
|
Model(String),
|
|
#[error("could not read the model: {0}")]
|
|
ModelRead(#[source] std::io::Error),
|
|
#[cfg(feature = "xfeat")]
|
|
#[error("inference: {0}")]
|
|
Inference(#[source] ort::Error),
|
|
}
|
|
|
|
#[cfg(feature = "xfeat")]
|
|
impl From<ort::Error> for PanoError {
|
|
fn from(e: ort::Error) -> Self {
|
|
PanoError::Inference(e)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "xfeat")]
|
|
impl From<dr_inference_engine::Error> for PanoError {
|
|
fn from(e: dr_inference_engine::Error) -> Self {
|
|
match e {
|
|
dr_inference_engine::Error::Inference(e) => PanoError::Inference(e),
|
|
dr_inference_engine::Error::Io(e) => PanoError::ModelRead(e),
|
|
}
|
|
}
|
|
}
|