//! TRACES: FR-DEV-3g //! Learned demosaic and denoise on the raw mosaic (docs/dev/denoise.md). //! //! A network trained on the library's own base-ISO raws with the 6D's //! measured noise added takes the repaired, normalised mosaic and a σ for //! every photosite, and returns linear camera RGB at full resolution — the //! texture the classical demosaic would have produced, with the noise gone. //! It replaces the demosaic box; nothing downstream changes (§2). //! //! - [`noise`] says how noisy each photosite is, from the best source the //! frame has. //! - [`tile`] runs a fixed-shape network over a whole frame, exactly. //! - [`onnx`] is that network under the inference engine. //! //! The input must already have been through the app's hot-pixel pass //! (`dr_gpu::Demosaicer::repair_hot_pixels`): the noise model was fitted //! with what that pass removes left out. pub mod noise; #[cfg(feature = "onnx")] pub mod onnx; pub mod tile; use dr_decode::RawImage; pub use noise::{NoiseModel, Source}; pub use tile::{TileNet, HALO}; #[derive(Debug, thiserror::Error)] pub enum DenoiseError { #[error("the network cannot take this photograph: {0}")] Unsupported(String), #[error("the denoise model misbehaved: {0}")] Model(String), #[error("could not read the denoise model: {0}")] ModelRead(#[from] std::io::Error), #[cfg(feature = "onnx")] #[error(transparent)] Engine(#[from] dr_inference_engine::Error), #[cfg(feature = "onnx")] #[error(transparent)] Ort(#[from] ort::Error), } /// Whether the learned stage can take this frame at all: a Bayer mosaic. /// X-Trans needs its own model (§9); a linear DNG has no photosites. pub fn eligible(raw: &RawImage) -> bool { raw.samples_per_pixel == 1 && tile::rggb_offset(raw.cfa_pattern).is_some() } /// The active area of `raw`, denoised and demosaiced: `crop.height × /// crop.width` interleaved RGB, linear camera space, normalised black 0 and /// white 1 per photosite as the classical demosaic normalises. /// /// `raw` must be hot-pixel repaired. `None` when `progress` stopped it. pub fn denoise( raw: &RawImage, noise: &NoiseModel, net: &mut dyn TileNet, progress: &mut dyn FnMut(usize, usize) -> bool, ) -> Result>, DenoiseError> { if !eligible(raw) { return Err(DenoiseError::Unsupported(format!( "{:?} with {} samples per photosite", raw.cfa_pattern, raw.samples_per_pixel ))); } let active = noise::active(raw); let (h, w) = (active.h, active.w); tile::run_tiled( net, h, w, raw.cfa_pattern, &|y, x| active.at(y, x), &|c, v| noise.sigma(c, v), progress, ) }