use std::sync::Arc;
use crate::dense_photo_map::DensePhotoMap;
use crate::error::Error;
use crate::photo::Photo;
use crate::pixelmap_processor::{PixelMapProcessor, DEFAULT_SEED};
use crate::processing_mode::{IterationParams, Quality};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct Progress {
pub step: usize,
pub total: usize,
}
impl Progress {
pub fn fraction(&self) -> f32 {
if self.total == 0 {
1.0
} else {
self.step as f32 / self.total as f32
}
}
}
#[derive(Clone, Debug)]
pub struct Correspondence {
forward: DensePhotoMap,
backward: DensePhotoMap,
comparisons: usize,
source_size: (usize, usize),
}
impl Correspondence {
pub fn builder() -> Builder {
Builder::new()
}
pub fn lookup(&self, x: f32, y: f32) -> Option<(f32, f32)> {
let scale = self.working_scale();
let (mx, my) = self.forward.lookup(x * scale, y * scale)?;
Some((mx / scale, my / scale))
}
pub fn lookup_back(&self, x: f32, y: f32) -> Option<(f32, f32)> {
let scale = self.working_scale();
let (mx, my) = self.backward.lookup(x * scale, y * scale)?;
Some((mx / scale, my / scale))
}
pub fn source_dimensions(&self) -> (usize, usize) {
self.source_size
}
pub fn working_scale(&self) -> f32 {
self.forward.dimensions().0 as f32 / self.source_size.0 as f32
}
pub fn forward(&self) -> &DensePhotoMap {
&self.forward
}
pub fn backward(&self) -> &DensePhotoMap {
&self.backward
}
pub fn into_parts(self) -> (DensePhotoMap, DensePhotoMap) {
(self.forward, self.backward)
}
pub fn coverage(&self) -> f32 {
self.forward.calculate_used_area()
}
pub fn comparisons(&self) -> usize {
self.comparisons
}
}
#[derive(Clone, Debug)]
pub struct Builder {
quality: Quality,
schedule: Option<Vec<IterationParams>>,
seed: u64,
final_max_dist: f32,
}
impl Default for Builder {
fn default() -> Self {
Builder::new()
}
}
impl Builder {
pub fn new() -> Self {
Builder {
quality: Quality::Low,
schedule: None,
seed: DEFAULT_SEED,
final_max_dist: 2.0,
}
}
pub fn quality(mut self, quality: Quality) -> Self {
self.quality = quality;
self
}
pub fn schedule(mut self, steps: impl Into<Vec<IterationParams>>) -> Self {
self.schedule = Some(steps.into());
self
}
pub fn seed(mut self, seed: u64) -> Self {
self.seed = seed;
self
}
pub fn max_round_trip_error(mut self, max_dist: f32) -> Self {
self.final_max_dist = max_dist;
self
}
pub fn run(
&self,
photo1: impl Into<Arc<Photo>>,
photo2: impl Into<Arc<Photo>>,
) -> Result<Correspondence, Error> {
self.run_with_progress(photo1, photo2, |_| {})
}
pub fn run_with_progress(
&self,
photo1: impl Into<Arc<Photo>>,
photo2: impl Into<Arc<Photo>>,
mut on_progress: impl FnMut(Progress),
) -> Result<Correspondence, Error> {
let photo1 = photo1.into();
let photo2 = photo2.into();
let source_size = (photo1.width(), photo1.height());
photo1.validate()?;
photo2.validate()?;
if (photo1.width(), photo1.height()) != (photo2.width(), photo2.height()) {
return Err(Error::SizeMismatch {
first: (photo1.width(), photo1.height()),
second: (photo2.width(), photo2.height()),
});
}
let steps: &[IterationParams] = self
.schedule
.as_deref()
.unwrap_or_else(|| self.quality.steps());
let total = steps.len() + 1;
let mut processor =
PixelMapProcessor::with_seed(photo1, photo2, self.quality.photo_width(), self.seed);
processor.init();
on_progress(Progress { step: 1, total });
for (index, params) in steps.iter().enumerate() {
params.apply(&mut processor);
on_progress(Progress {
step: index + 2,
total,
});
}
let comparisons = processor.total_comparisons();
let (forward, backward) = processor.finish(self.final_max_dist);
Ok(Correspondence {
forward,
backward,
comparisons,
source_size,
})
}
}
pub fn correspond(
photo1: impl Into<Arc<Photo>>,
photo2: impl Into<Arc<Photo>>,
) -> Result<Correspondence, Error> {
Correspondence::builder().run(photo1, photo2)
}