use crate::{
dust,
error::Error,
protocol::{decode::Samples, model::Model, window::Channel},
scan::{meter::Metering, pass::Pass},
};
use tracing::*;
const PRESCAN_PIXELS: usize = 2_500_000;
fn decimate(plane: &[u16], cols: usize, step: usize) -> Vec<u16> {
let rows = plane.len() / cols;
let mut out = Vec::with_capacity((rows / step) * (cols / step));
for y in (0..rows - rows % step).step_by(step) {
for x in (0..cols - cols % step).step_by(step) {
out.push(plane[y * cols + x]);
}
}
out
}
pub fn clean_frame(
samples: &mut Samples,
pass: &Pass,
model: Option<Model>,
) -> Result<usize, Error> {
let ids: Vec<u8> = pass.layout.colors().collect();
let at = |want: Channel| ids.iter().position(|&id| Channel::from(id) == want);
let (Some(r), Some(g), Some(b)) = (at(Channel::Red), at(Channel::Green), at(Channel::Blue))
else {
return Err(Error::Unsupported {
op: "clean",
reason: format!("needs a red, green and blue plane, this pass has {ids:?}"),
});
};
let model = model.map(dust::Model::from).unwrap_or_else(|| {
warn!("unrecognized scanner, cleaning with a default profile");
dust::Model::Ls9000
});
let opts = dust::Options {
model,
quality: dust::Quality::Normal,
dpi: pass.layout.dpi,
metering_target: Metering::default().target,
};
let Some(ir) = samples.ir.as_deref() else {
return Err(Error::Unsupported {
op: "clean",
reason: "needs the infrared pass".into(),
});
};
let step = ((pass.rows * pass.cols) / PRESCAN_PIXELS).isqrt().max(1);
let small_red = decimate(&samples.colors[r], pass.cols, step);
let small_ir = decimate(ir, pass.cols, step);
let cal = dust::calibrate(&dust::Prescan {
red: &small_red,
ir: &small_ir,
rows: pass.rows / step,
cols: pass.cols / step,
})
.or_else(|| {
warn!("no clear film in the decimated prescan, calibrating off the whole frame");
dust::calibrate(&dust::Prescan {
red: &samples.colors[r],
ir,
rows: pass.rows,
cols: pass.cols,
})
})
.ok_or_else(|| Error::Unsupported {
op: "clean",
reason: "no clear film in this frame to calibrate against".into(),
})?;
debug!(?cal, step, "ICE calibration");
let [pr, pg, pb] = samples
.colors
.get_disjoint_mut([r, g, b])
.expect("three distinct color planes");
Ok(dust::clean(
[pr, pg, pb],
ir,
&cal,
pass.rows,
pass.cols,
&opts,
))
}