diff --git a/.gitea/workflows/build-and-test.yml b/.gitea/workflows/build-and-test.yml index cd18b1c..760a7c3 100644 --- a/.gitea/workflows/build-and-test.yml +++ b/.gitea/workflows/build-and-test.yml @@ -487,10 +487,11 @@ jobs: wine "$SETUP" /S 2>/dev/null INST=$(echo "$HOME"/.wine/drive_c/users/*/AppData/Local/Programs/DarkRoom) ls "$INST" - # As many files as package.sh stages: everything but the READMEs in - # the directories it copies. A literal here went stale the first - # time a model was added. - WANT=$(find models/face models/scene models/inpaint models/denoise -maxdepth 1 -type f ! -name README.md | wc -l) + # As many files as package.sh stages: everything but the READMEs and + # the Hexagon's quantised siblings in the directories it copies. A + # literal here went stale the first time a model was added. + WANT=$(find models/face models/scene models/inpaint models/denoise -maxdepth 1 -type f ! -name README.md \ + ! -name '*.int8.onnx' ! -name '*.a16w8.onnx' ! -name '*.a16w16.onnx' | wc -l) GOT=$(ls "$INST/models" | wc -l) [ "$GOT" = "$WANT" ] || { echo "FAIL: expected $WANT model files, installed $GOT"; exit 1; } # The manual, and every picture it shows, counted the same way. diff --git a/apps/darkroom-android/src/lib.rs b/apps/darkroom-android/src/lib.rs index 55d4aee..11a6dd3 100644 --- a/apps/darkroom-android/src/lib.rs +++ b/apps/darkroom-android/src/lib.rs @@ -332,27 +332,38 @@ fn unpack_bundled_models(app: &slint::android::AndroidApp) { // eyes-open filter has something to read, and a tablet has no other way // to get them either. // - // The int8 forms beside the three detectors are what the Hexagon runs - // (docs/dev/inference.md §5); the engine loads the sibling when the probe - // chose that rung and ignores it otherwise. - const BUNDLED: [(&std::ffi::CStr, &str); 15] = [ + // The quantised siblings — `.a16w8.onnx`, `.a16w16.onnx` — are what the + // Hexagon runs (docs/dev/inference.md §1.5), each in the narrowest form + // that held that model's accuracy on the tablet; the engine loads the + // sibling when the probe chose that rung and ignores it otherwise. The + // segmenter's and XFeat's forms are compiled into the binary instead, + // beside their f32 graphs. + const BUNDLED: [(&std::ffi::CStr, &str); 19] = [ (c"models/scrfd_500m_640.onnx", "scrfd_500m_640.onnx"), ( - c"models/scrfd_500m_640.int8.onnx", - "scrfd_500m_640.int8.onnx", + c"models/scrfd_500m_640.a16w8.onnx", + "scrfd_500m_640.a16w8.onnx", ), (c"models/scrfd_2.5g_640.onnx", "scrfd_2.5g_640.onnx"), ( - c"models/scrfd_2.5g_640.int8.onnx", - "scrfd_2.5g_640.int8.onnx", + c"models/scrfd_2.5g_640.a16w8.onnx", + "scrfd_2.5g_640.a16w8.onnx", ), (c"models/scrfd_10g_640.onnx", "scrfd_10g_640.onnx"), - (c"models/scrfd_10g_640.int8.onnx", "scrfd_10g_640.int8.onnx"), + ( + c"models/scrfd_10g_640.a16w8.onnx", + "scrfd_10g_640.a16w8.onnx", + ), (c"models/arcface_mbf_b1.onnx", "arcface_mbf_b1.onnx"), (c"models/2d106det_b1.onnx", "2d106det_b1.onnx"), + (c"models/2d106det_b1.a16w8.onnx", "2d106det_b1.a16w8.onnx"), (c"models/ocec_s_b1.onnx", "ocec_s_b1.onnx"), (c"models/sgc_l_48_b1.onnx", "sgc_l_48_b1.onnx"), (c"models/yolo26s-sem-ade20k.onnx", "yolo26s-sem-ade20k.onnx"), + ( + c"models/yolo26s-sem-ade20k.a16w16.onnx", + "yolo26s-sem-ade20k.a16w16.onnx", + ), ( c"models/yolo26s-sem-ade20k.classes.json", "yolo26s-sem-ade20k.classes.json", @@ -360,7 +371,9 @@ fn unpack_bundled_models(app: &slint::android::AndroidApp) { (c"models/categories.txt", "categories.txt"), // The panorama border filler (FR-MRG-4); MIT, 28 MB. (c"models/migan-512.onnx", "migan-512.onnx"), + (c"models/migan-512.a16w16.onnx", "migan-512.a16w16.onnx"), (c"models/mosaic-1408.onnx", "mosaic-1408.onnx"), + (c"models/mosaic-1408.a16w16.onnx", "mosaic-1408.a16w16.onnx"), ]; let dir = dr_ui::shared_face_models_dir(); diff --git a/core/dr-denoise/src/onnx.rs b/core/dr-denoise/src/onnx.rs index 4e903c2..ed9a20d 100644 --- a/core/dr-denoise/src/onnx.rs +++ b/core/dr-denoise/src/onnx.rs @@ -5,7 +5,11 @@ //! returns `rgb`, `1×3×1408×1408` (darkroom-denoise `denoise/export.py`, //! fixed shape because every model the engine runs is). The engine picks the //! rung: fp16 on TensorRT and MIGraphX, which measured 0.00 dB from f32; f32 -//! on CUDA and the CPU; never the Hexagon, where int8 lost 6–9 dB. +//! on CUDA and the CPU; on the Hexagon the `.a16w16.onnx` sibling, 16-bit +//! activations and weights, 0.00 dB from f32 on the tablet itself where int8 +//! lost 5–9 dB (docs/dev/inference.md §1.5). That sibling is the same network +//! with the Bayer packing spelled `SpaceToDepth`, which QNN can hold and the +//! 6-D reshape it replaces it cannot. use crate::tile::TileNet; use crate::DenoiseError; diff --git a/core/dr-face/src/detect.rs b/core/dr-face/src/detect.rs index 653c146..0debb7a 100644 --- a/core/dr-face/src/detect.rs +++ b/core/dr-face/src/detect.rs @@ -137,8 +137,8 @@ impl Detection { /// A loaded SCRFD graph. pub struct Detector { session: Model, - /// f32 or int8 — the int8 form finds a different set of faces and is a - /// different detector in `model_id` (docs/dev/inference.md §7). + /// f32 or a quantised form — which finds a different set of faces and is + /// a different detector in `model_id` (docs/dev/inference.md §7). form: Form, /// Feature-map count: 3 for strides {8,16,32}, 4 for {8,16,32,64}. /// @@ -155,7 +155,7 @@ impl Detector { } /// Load the canonical f32 file at `path`, or the form the device's - /// backend wants instead — the `.int8.onnx` beside it on a Hexagon — + /// backend wants instead — the `.a16w8.onnx` beside it on a Hexagon — /// which [`Detector::form`] then reports. pub fn from_path(path: impl AsRef) -> Result { let (path, form) = dr_inference_engine::resolve_model(Role::Detector, path.as_ref()); diff --git a/core/dr-face/src/landmarks.rs b/core/dr-face/src/landmarks.rs index 94314c7..146b566 100644 --- a/core/dr-face/src/landmarks.rs +++ b/core/dr-face/src/landmarks.rs @@ -123,13 +123,22 @@ pub struct Landmarker { } impl Landmarker { + /// The graph at `path`, or the `.a16w8.onnx` sibling beside it when the + /// device's backend runs that (the Hexagon, inference.md §1.5: 0.25 px + /// from f32 in the 192 crop, where int8 moved the points by 1.5). pub fn from_path(path: impl AsRef) -> Result { + let (path, form) = dr_inference_engine::resolve_model(Role::Landmarks, path.as_ref()); let bytes = std::fs::read(path).map_err(FaceError::ModelRead)?; - Self::from_bytes(&bytes) + Self::from_bytes_in(&bytes, form) } pub fn from_bytes(bytes: &[u8]) -> Result { - let model = dr_inference_engine::open(Role::Landmarks, Form::F32, bytes)?; + Self::from_bytes_in(bytes, Form::F32) + } + + /// `bytes` in a stated numeric form; the output keeps its meaning. + pub fn from_bytes_in(bytes: &[u8], form: Form) -> Result { + let model = dr_inference_engine::open(Role::Landmarks, form, bytes)?; let acquired = model.acquire()?; let session = acquired.lock(); diff --git a/core/dr-inference-engine/examples/ladder.rs b/core/dr-inference-engine/examples/ladder.rs index e7ee7ca..3d5dbb9 100644 --- a/core/dr-inference-engine/examples/ladder.rs +++ b/core/dr-inference-engine/examples/ladder.rs @@ -4,12 +4,14 @@ //! //! DARKROOM_ORT_DIR=/usr/lib \ //! cargo run --release -p dr-inference-engine --features native,tract \ -//! --example ladder -- CACHE_DIR models/face/scrfd_500m_640.onnx [MODEL.onnx ...] +//! --example ladder -- CACHE_DIR models/face/scrfd_500m_640.onnx [ROLE=MODEL.onnx ...] //! -//! Every model named is a `Detector` for the config's purposes, which is -//! enough to see the rung taken, the engines compiled and a session land -//! on it. Delete `CACHE_DIR` to see the first run again; keep it to see the -//! second. +//! A bare path is a `Detector`; `denoiser=…`, `scene=…`, `inpainter=…`, +//! `landmarks=…` (any `Role`, lower case) says otherwise, so a device can +//! show each role taking its own form (inference.md §1.5). Each is opened +//! through `resolve_model`, as the app opens it, and the line says which +//! form and which rung it landed on. Delete `CACHE_DIR` to see the first +//! run again; keep it to see the second. use std::path::PathBuf; use std::time::{Duration, Instant}; @@ -17,7 +19,10 @@ use std::time::{Duration, Instant}; fn main() { env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); let mut args = std::env::args_os().skip(1).map(PathBuf::from); - let (Some(cache_dir), models) = (args.next(), args.collect::>()) else { + let (Some(cache_dir), models) = ( + args.next(), + args.map(|a| role_and_path(&a)).collect::>(), + ) else { eprintln!("usage: ladder CACHE_DIR MODEL.onnx [MODEL.onnx ...]"); std::process::exit(2); }; @@ -34,10 +39,7 @@ fn main() { dr_inference_engine::init(dr_inference_engine::Config { runtime_dirs, cache_dir: cache_dir.clone(), - models: models - .iter() - .map(|p| (dr_inference_engine::Role::Detector, p.clone())) - .collect(), + models: models.clone(), embedded: Vec::new(), ceiling: None, threads: 0, @@ -80,21 +82,39 @@ fn main() { std::thread::sleep(Duration::from_millis(500)); } - for path in &models { - let bytes = std::fs::read(path).expect("read model"); + for (role, path) in &models { + let (path, form) = dr_inference_engine::resolve_model(*role, path); + let bytes = std::fs::read(&path).expect("read model"); let t = Instant::now(); - let model = dr_inference_engine::open( - dr_inference_engine::Role::Detector, - dr_inference_engine::Form::F32, - &bytes, - ) - .expect("open model"); + let model = dr_inference_engine::open(*role, form, &bytes).expect("open model"); let acquired = model.acquire().expect("acquire session"); println!( - "{} on {} in {:.2} s", + "{role:?}: {} ({form:?}) on {} in {:.2} s", path.file_name().unwrap().to_string_lossy(), acquired.rung().label(), t.elapsed().as_secs_f64() ); } } + +/// `denoiser=path` → (Denoiser, path); a bare path is a detector. +fn role_and_path(arg: &std::path::Path) -> (dr_inference_engine::Role, PathBuf) { + use dr_inference_engine::Role::*; + let s = arg.to_string_lossy(); + let Some((name, path)) = s.split_once('=') else { + return (Detector, arg.to_path_buf()); + }; + let role = match name { + "detector" => Detector, + "embedder" => Embedder, + "segmenter" => Segmenter, + "scene" => Scene, + "landmarks" => Landmarks, + "eyes" => EyeClassifier, + "keypoints" => Keypoints, + "inpainter" => Inpainter, + "denoiser" => Denoiser, + other => panic!("no role {other:?}"), + }; + (role, PathBuf::from(path)) +} diff --git a/core/dr-inference-engine/src/engines.rs b/core/dr-inference-engine/src/engines.rs index d752735..31525a3 100644 --- a/core/dr-inference-engine/src/engines.rs +++ b/core/dr-inference-engine/src/engines.rs @@ -8,7 +8,7 @@ use std::path::PathBuf; -use crate::{state, Config, Form, Rung}; +use crate::{state, Config, Rung}; enum Source { File(PathBuf), @@ -82,10 +82,10 @@ pub fn run() { (*role, Source::File(path), size) }) }) - .chain(cfg.embedded.iter().filter_map(|(role, bytes)| { - // An embedded model has no int8 sibling to offer a rung that - // wants one; it runs on that rung's fallback. - (rung.serves(*role) && rung.form(*role) == Form::F32).then_some(( + .chain(cfg.embedded.iter().filter_map(|(role, form, bytes)| { + // The embedded form the rung wants, if the build carries it; + // a build without it runs that model on the rung's fallback. + (rung.serves(*role) && rung.form(*role) == *form).then_some(( *role, Source::Bytes(bytes), bytes.len() as u64, diff --git a/core/dr-inference-engine/src/lib.rs b/core/dr-inference-engine/src/lib.rs index 80d2b4e..9a91c71 100644 --- a/core/dr-inference-engine/src/lib.rs +++ b/core/dr-inference-engine/src/lib.rs @@ -42,25 +42,46 @@ pub enum Role { /// XFeat, the panorama keypoint detector (docs/dev/panorama.md). Keypoints, /// MI-GAN, the panorama border filler (docs/dev/panorama.md §12). Plain - /// convolutions, so any rung serves it; fp16 on TensorRT and int8 on - /// the Hexagon are the point of it. + /// convolutions, so any rung serves it; fp16 on TensorRT and 16-bit + /// activations on the Hexagon (int8 changes the fill, §1.5). Inpainter, /// The learned demosaic and denoise on the raw mosaic (docs/dev/denoise.md). /// fp16 costs it nothing measurable; int8 costs 6–9 dB, because 256 /// levels cannot hold the shadow steps it exists to recover — so the - /// Hexagon does not take it. + /// Hexagon takes it with 16-bit activations and weights (§1.5). Denoiser, } /// Which numeric form of a model a session was built from. /// -/// `Int8` is a different network from `F32` for a detector — it finds a -/// different set of faces — which is why [`form_suffix`] exists and why a +/// The quantised forms are QDQ graphs, per-channel weights, as QNN's HTP +/// takes them (docs/dev/inference.md §1.5): `Int8` is 8-bit activations and +/// weights, `A16W8` 16-bit activations with 8-bit weights, `A16W16` 16-bit +/// both. The Hexagon accepts no float tensor at all, so these are the +/// whole menu; which one a role gets is [`Rung::form`], measured per model. +/// +/// A quantised detector is a different network from the f32 one — it finds +/// a different set of faces — which is why [`form_suffix`] exists and why a /// caller appends it to `model_id`. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum Form { F32, Int8, + A16W8, + A16W16, +} + +impl Form { + /// The infix of the sibling file that holds this form: + /// `scrfd_500m_640.a16w8.onnx` beside `scrfd_500m_640.onnx`. + pub fn file_tag(self) -> Option<&'static str> { + match self { + Form::F32 => None, + Form::Int8 => Some("int8"), + Form::A16W8 => Some("a16w8"), + Form::A16W16 => Some("a16w16"), + } + } } /// A rung of the ladder (§2). Ordered: a user override names the highest rung @@ -81,7 +102,7 @@ pub enum Rung { /// removed in ONNX Runtime 1.23, so there is no non-compiling AMD rung /// to fall back to: this one falls back to the CPU. MiGraphX, - /// Qualcomm's Hexagon NPU through QNN, int8 models only. Android only. + /// Qualcomm's Hexagon NPU through QNN, quantised models only. Android only. Hexagon, /// Apple, through CoreML: the Neural Engine, the GPU or the CPU, as /// CoreML schedules it. macOS only. Compiles an ML Program per model on @@ -121,23 +142,38 @@ impl Rung { } /// The model form this rung wants for a role. - fn form(self, _role: Role) -> Form { + /// + /// On the Hexagon, the narrowest form that held each model's accuracy + /// on the tablet itself (§1.5): int8 lost 5% of the detector's faces at + /// 40–80 px, moved the landmarks by 1.5 px and the segmenter's scores + /// to nothing, and the denoiser by 6–9 dB, so those take 16-bit + /// activations; the segmenter, scene model, filler and denoiser also + /// needed 16-bit weights. Only XFeat keeps int8: its panorama alignment + /// moved by no more than f32's own refits do. + pub fn form(self, role: Role) -> Form { match self { - Rung::Hexagon => Form::Int8, + Rung::Hexagon => match role { + Role::Keypoints => Form::Int8, + Role::Detector | Role::Landmarks => Form::A16W8, + Role::Segmenter | Role::Scene | Role::Inpainter | Role::Denoiser => Form::A16W16, + Role::Embedder | Role::EyeClassifier => Form::F32, + }, _ => Form::F32, } } - /// Whether this rung runs `role` at all. The Hexagon takes int8 graphs - /// only, and the embedder is never int8 (§7) — it runs on the CPU - /// beside a detector on the NPU, so its vectors compare across devices. - /// Nor is the denoiser: its int8 form failed the 0.5 dB gate by 6–9 dB - /// (denoise.md §8), so it runs on the CPU there too. CoreML is kept off - /// the embedder for the same reason as the Hexagon: the Neural Engine is - /// fp16, and which unit runs a graph is CoreML's choice. + /// Whether this rung runs `role` at all. The Hexagon takes quantised + /// graphs only, and the embedder is never quantised (§7) — it runs on + /// the CPU beside a detector on the NPU, so its vectors compare across + /// devices; at A16W16 it still missed the 0.999 cosine gate. The eye + /// classifiers stay on the CPU too: a millisecond there, and the two + /// share one role while only one of them held its readings quantised. + /// CoreML is kept off the embedder for the same reason as the Hexagon: + /// the Neural Engine is fp16, and which unit runs a graph is CoreML's + /// choice. fn serves(self, role: Role) -> bool { match self { - Rung::Hexagon => !matches!(role, Role::Embedder | Role::Denoiser), + Rung::Hexagon => !matches!(role, Role::Embedder | Role::EyeClassifier), Rung::CoreMl => role != Role::Embedder, _ => true, } @@ -161,8 +197,10 @@ pub struct Config { /// The canonical model files on this device, so engines can be compiled /// ahead of the first request for them. pub models: Vec<(Role, PathBuf)>, - /// Models compiled into the binary, for the same reason. - pub embedded: Vec<(Role, &'static [u8])>, + /// Models compiled into the binary, for the same reason, each with the + /// form it is. A build that embeds a quantised sibling lists it here + /// beside the f32 graph, and the compile step takes the one the rung wants. + pub embedded: Vec<(Role, Form, &'static [u8])>, /// The highest rung the user allows; `None` is "the best that works". pub ceiling: Option, /// ONNX Runtime's intra-op pool; 0 picks from the core count. @@ -188,10 +226,10 @@ pub struct Status { } impl Status { - /// "Hexagon NPU · int8 · ONNX Runtime 1.29" — the settings row's text. + /// "Hexagon NPU · quantised · ONNX Runtime 1.29" — the settings row's text. pub fn line(&self) -> String { let form = match self.rung { - Rung::Hexagon => " · int8", + Rung::Hexagon => " · quantised", Rung::TensorRt | Rung::MiGraphX => " · fp16", _ => "", }; @@ -464,26 +502,44 @@ fn current_rung(s: &State) -> Rung { /// The file to load for `role` under the current selection, and its form. /// -/// A rung that wants int8 gets the `.int8.onnx` sibling of the canonical file -/// if it exists; otherwise the canonical file, on the rung's fallback. A -/// caller adds [`form_suffix`] to the `model_id` it records. +/// A rung that wants a quantised form gets that sibling of the canonical +/// file (`.a16w8.onnx` and so on, [`Form::file_tag`]) if it exists; +/// otherwise the canonical file, on the rung's fallback. A caller adds +/// [`form_suffix`] to the `model_id` it records. pub fn resolve_model(role: Role, canonical: &Path) -> (PathBuf, Form) { let rung = current_rung(&state().lock().unwrap()); - if rung.serves(role) && rung.form(role) == Form::Int8 { - let sibling = int8_sibling(canonical); + let want = rung.form(role); + if rung.serves(role) && want != Form::F32 { + let sibling = form_sibling(canonical, want); if sibling.is_file() { - return (sibling, Form::Int8); + return (sibling, want); } } (canonical.to_path_buf(), Form::F32) } -fn int8_sibling(canonical: &Path) -> PathBuf { +/// The same choice for a model compiled into the binary: of the forms +/// `offered`, the one the current rung wants for `role`, else the f32 one. +/// `offered` must hold an `F32` entry. +pub fn choose_embedded(role: Role, offered: &[(Form, &'static [u8])]) -> (&'static [u8], Form) { + let rung = current_rung(&state().lock().unwrap()); + let want = rung.form(role); + let pick = |form| offered.iter().find(|(f, _)| *f == form); + let (form, bytes) = (rung.serves(role).then(|| pick(want)).flatten()) + .or_else(|| pick(Form::F32)) + .expect("an embedded model offers its f32 form"); + (bytes, *form) +} + +fn form_sibling(canonical: &Path, form: Form) -> PathBuf { let stem = canonical .file_stem() .map(|s| s.to_string_lossy().into_owned()) .unwrap_or_default(); - canonical.with_file_name(format!("{stem}.int8.onnx")) + match form.file_tag() { + Some(tag) => canonical.with_file_name(format!("{stem}.{tag}.onnx")), + None => canonical.to_path_buf(), + } } /// What a form appends to a detector's `model_id` (§7). @@ -491,6 +547,8 @@ pub fn form_suffix(form: Form) -> &'static str { match form { Form::F32 => "", Form::Int8 => "_i8", + Form::A16W8 => "_a16", + Form::A16W16 => "_a16w16", } } @@ -520,8 +578,8 @@ pub fn open(role: Role, form: Form, bytes: &[u8]) -> Result { fn effective_rung(s: &State, selected: Rung, role: Role, form: Form, hash: u64) -> Rung { let mut rung = selected; if !rung.serves(role) || rung.form(role) != form { - // The embedder on a Hexagon device, or an f32 detector where the int8 - // sibling was missing: neither can go to the NPU. + // The embedder on a Hexagon device, or an f32 detector where the + // quantised sibling was missing: neither can go to the NPU. rung = rung.fallback(); } if rung.compiles() && !s.cache.compiled.contains(&engines::key_of(rung, hash)) { @@ -612,9 +670,10 @@ mod tests { #[test] fn the_hexagon_never_takes_the_embedder() { assert!(!Rung::Hexagon.serves(Role::Embedder)); - assert!(!Rung::Hexagon.serves(Role::Denoiser)); + assert!(!Rung::Hexagon.serves(Role::EyeClassifier)); assert!(Rung::Hexagon.serves(Role::Detector)); - assert_eq!(Rung::Hexagon.form(Role::Detector), Form::Int8); + assert!(Rung::Hexagon.serves(Role::Denoiser)); + assert_eq!(Rung::Hexagon.form(Role::Detector), Form::A16W8); // A detector offered in f32 on a Hexagon device lands on the CPU. let s = State { config: Config::default(), @@ -625,36 +684,57 @@ mod tests { probing: false, wanted: 0, }; + let on = |role, form| effective_rung(&s, Rung::Hexagon, role, form, engines::hash(b"")); + assert_eq!(on(Role::Embedder, Form::F32), Rung::Cpu); + assert_eq!(on(Role::Detector, Form::F32), Rung::Cpu); + // A form other than the one the role wants is not the NPU's either: + // an int8 detector left over from an older install stays off it. + assert_eq!(on(Role::Detector, Form::Int8), Rung::Cpu); + // The wanted form whose context is not compiled yet: also the CPU. + assert_eq!(on(Role::Detector, Form::A16W8), Rung::Cpu); + } + + /// The form each role gets on the Hexagon is the one measured to hold + /// its accuracy there (§1.5); a change to this table is a change to + /// what the tablet computes, and must come with a measurement. + #[test] + fn each_role_has_its_measured_form_on_the_hexagon() { + use Form::*; + for (role, form) in [ + (Role::Detector, A16W8), + (Role::Landmarks, A16W8), + (Role::Segmenter, A16W16), + (Role::Scene, A16W16), + (Role::Inpainter, A16W16), + (Role::Denoiser, A16W16), + (Role::Keypoints, Int8), + (Role::Embedder, F32), + (Role::EyeClassifier, F32), + ] { + assert_eq!(Rung::Hexagon.form(role), form, "{role:?}"); + } + for rung in [ + Rung::Cpu, + Rung::Cuda, + Rung::TensorRt, + Rung::MiGraphX, + Rung::CoreMl, + ] { + assert_eq!(rung.form(Role::Detector), F32); + } + } + + #[test] + fn a_form_lives_in_its_tagged_sibling() { + let canonical = Path::new("/m/scrfd_500m_640.onnx"); + assert_eq!(form_sibling(canonical, Form::F32), canonical); assert_eq!( - effective_rung( - &s, - Rung::Hexagon, - Role::Embedder, - Form::F32, - engines::hash(b"") - ), - Rung::Cpu + form_sibling(canonical, Form::A16W8), + Path::new("/m/scrfd_500m_640.a16w8.onnx") ); assert_eq!( - effective_rung( - &s, - Rung::Hexagon, - Role::Detector, - Form::F32, - engines::hash(b"") - ), - Rung::Cpu - ); - // An int8 detector whose context is not compiled yet: also the CPU. - assert_eq!( - effective_rung( - &s, - Rung::Hexagon, - Role::Detector, - Form::Int8, - engines::hash(b"") - ), - Rung::Cpu + form_sibling(canonical, Form::Int8), + Path::new("/m/scrfd_500m_640.int8.onnx") ); } diff --git a/core/dr-inference-engine/src/probe.rs b/core/dr-inference-engine/src/probe.rs index e3a866c..167a8a8 100644 --- a/core/dr-inference-engine/src/probe.rs +++ b/core/dr-inference-engine/src/probe.rs @@ -174,10 +174,9 @@ pub fn attempt(cfg: &Config, what: &str, f: impl FnOnce() -> T) -> Result Option<(Role, PathBuf)> { let smallest = |want: Option| { cfg.models @@ -203,16 +202,14 @@ fn time_rung( cfg: &Config, ) -> Result<(f64, Option), String> { let want = rung.form(role); - let path = match want { - Form::Int8 => { - let p = crate::int8_sibling(canonical); - if !p.is_file() { - return Err(format!("no int8 form of {}", canonical.display())); - } - p - } - Form::F32 => canonical.to_path_buf(), - }; + let path = crate::form_sibling(canonical, want); + if want != Form::F32 && !path.is_file() { + return Err(format!( + "no {} form of {}", + want.file_tag().unwrap_or("f32"), + canonical.display() + )); + } let bytes = std::fs::read(&path).map_err(|e| e.to_string())?; let started = Instant::now(); let mut session = @@ -295,9 +292,9 @@ fn fingerprint(runtime: &Runtime, cfg: &Config) -> String { }, device_identity(), ]; - for (role, bytes) in &cfg.embedded { + for (role, form, bytes) in &cfg.embedded { parts.push(format!( - "{role:?} embedded {:016x}", + "{role:?} embedded {form:?} {:016x}", crate::engines::hash(bytes) )); } @@ -306,9 +303,13 @@ fn fingerprint(runtime: &Runtime, cfg: &Config) -> String { .map(|b| crate::engines::hash(&b)) .unwrap_or(0); parts.push(format!("{role:?} {hash:016x}")); - let int8 = crate::int8_sibling(path); - if let Ok(b) = std::fs::read(&int8) { - parts.push(format!("{role:?} int8 {:016x}", crate::engines::hash(&b))); + for form in [Form::Int8, Form::A16W8, Form::A16W16] { + if let Ok(b) = std::fs::read(crate::form_sibling(path, form)) { + parts.push(format!( + "{role:?} {form:?} {:016x}", + crate::engines::hash(&b) + )); + } } } parts.join("\n") diff --git a/core/dr-pano/build.rs b/core/dr-pano/build.rs index 2d03f8f..286f0c3 100644 --- a/core/dr-pano/build.rs +++ b/core/dr-pano/build.rs @@ -13,8 +13,13 @@ const MODELS: &[&str] = &[ "../../models/keypoints/xfeat-768.onnx", ]; +const QUANTISED: &[&str] = &[ + "../../models/keypoints/xfeat-1024.int8.onnx", + "../../models/keypoints/xfeat-768.int8.onnx", +]; + fn main() { - for m in MODELS { + for m in MODELS.iter().chain(QUANTISED) { println!("cargo:rerun-if-changed={m}"); } println!("cargo:rerun-if-changed=build.rs"); @@ -26,6 +31,12 @@ fn main() { for model in MODELS.iter().copied() { check(model); } + // The Hexagon's int8 forms ride only in an Android build. + if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("android") { + for model in QUANTISED.iter().copied() { + check(model); + } + } } fn check(model: &str) { diff --git a/core/dr-pano/src/migan.rs b/core/dr-pano/src/migan.rs index 40cc708..d6d86b4 100644 --- a/core/dr-pano/src/migan.rs +++ b/core/dr-pano/src/migan.rs @@ -30,7 +30,8 @@ pub struct MiGan { impl MiGan { /// From the model file, in whichever form the engine's rung wants - /// (`resolve_model` picks an int8 sibling for the Hexagon). + /// (`resolve_model` picks the `.a16w16.onnx` sibling on the Hexagon: + /// int8 moved the fill 16 dB from f32's, 16-bit about 41). pub fn from_path(path: &std::path::Path) -> Result { use dr_inference_engine::{resolve_model, Role}; let (path, form) = resolve_model(Role::Inpainter, path); diff --git a/core/dr-pano/src/xfeat.rs b/core/dr-pano/src/xfeat.rs index 28a135f..d756f9d 100644 --- a/core/dr-pano/src/xfeat.rs +++ b/core/dr-pano/src/xfeat.rs @@ -36,18 +36,49 @@ pub struct XFeat { pub options: DecodeOptions, } -/// The bytes of both exports compiled into the binary, for whoever compiles -/// engines ahead of the first request (docs/dev/inference.md §6). +/// The Hexagon's forms (docs/dev/inference.md §1.5): int8, from the same +/// network spelled for the HTP (the unfold as SpaceToDepth, the bilinear +/// resizes as matrix products). Only Android has a Hexagon. +#[cfg(all(feature = "embedded-model", target_os = "android"))] +const EMBEDDED_LANDSCAPE_INT8: &[u8] = + include_bytes!("../../../models/keypoints/xfeat-1024.int8.onnx"); +#[cfg(all(feature = "embedded-model", target_os = "android"))] +const EMBEDDED_PORTRAIT_INT8: &[u8] = + include_bytes!("../../../models/keypoints/xfeat-768.int8.onnx"); + +/// Every form of both exports compiled into the binary, landscape then +/// portrait, for whoever compiles engines ahead of the first request +/// (docs/dev/inference.md §6). #[cfg(feature = "embedded-model")] -pub fn embedded_model_bytes() -> [&'static [u8]; 2] { - [EMBEDDED_LANDSCAPE, EMBEDDED_PORTRAIT] +pub fn embedded_models() -> [Vec<(dr_inference_engine::Form, &'static [u8])>; 2] { + use dr_inference_engine::Form; + #[allow(unused_mut)] + let mut forms = [ + vec![(Form::F32, EMBEDDED_LANDSCAPE)], + vec![(Form::F32, EMBEDDED_PORTRAIT)], + ]; + #[cfg(target_os = "android")] + { + forms[0].push((Form::Int8, EMBEDDED_LANDSCAPE_INT8)); + forms[1].push((Form::Int8, EMBEDDED_PORTRAIT_INT8)); + } + forms } impl XFeat { - /// The weights compiled into the binary. + /// The weights compiled into the binary, in the form the device's + /// backend runs. #[cfg(feature = "embedded-model")] pub fn embedded() -> Result { - Self::from_bytes(EMBEDDED_LANDSCAPE, EMBEDDED_PORTRAIT) + use dr_inference_engine::{choose_embedded, open, Role}; + let [l, p] = embedded_models(); + let (l, lf) = choose_embedded(Role::Keypoints, &l); + let (p, pf) = choose_embedded(Role::Keypoints, &p); + Ok(XFeat { + landscape: open(Role::Keypoints, lf, l)?, + portrait: open(Role::Keypoints, pf, p)?, + options: DecodeOptions::default(), + }) } /// From the two exports on disk. diff --git a/core/dr-segment/build.rs b/core/dr-segment/build.rs index 276ff85..e17e6c7 100644 --- a/core/dr-segment/build.rs +++ b/core/dr-segment/build.rs @@ -13,9 +13,11 @@ use std::path::Path; const MODEL: &str = "../../models/segment/yolo26n-seg.onnx"; +const QUANTISED: &str = "../../models/segment/yolo26n-seg.a16w16.onnx"; fn main() { println!("cargo:rerun-if-changed={MODEL}"); + println!("cargo:rerun-if-changed={QUANTISED}"); println!("cargo:rerun-if-changed=build.rs"); // Only the embedded path needs the file present; a build without it is @@ -24,10 +26,18 @@ fn main() { return; } - let path = Path::new(MODEL); + check(MODEL); + // The Hexagon's quantised form rides only in an Android build. + if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("android") { + check(QUANTISED); + } +} + +fn check(model: &str) { + let path = Path::new(model); let Ok(bytes) = std::fs::read(path) else { panic!( - "\n\n{MODEL} is missing.\n\ + "\n\n{model} is missing.\n\ It ships in Git LFS. Run `git lfs install && git lfs pull`, or build \ with `--no-default-features` for a watershed-only build.\n" ); @@ -40,7 +50,7 @@ fn main() { // happens in practice. if bytes.starts_with(b"version https://git-lfs") { panic!( - "\n\n{MODEL} is a Git LFS pointer, not the model ({} bytes).\n\ + "\n\n{model} is a Git LFS pointer, not the model ({} bytes).\n\ Run `git lfs install && git lfs pull` to fetch the real file.\n", bytes.len() ); @@ -50,7 +60,7 @@ fn main() { // export is ~11 MB; anything under a megabyte is a truncated checkout. if bytes.len() < 1_000_000 { panic!( - "\n\n{MODEL} is only {} bytes — expected ~11 MB.\n\ + "\n\n{model} is only {} bytes — expected several MB.\n\ The checkout looks incomplete; try `git lfs pull`.\n", bytes.len() ); diff --git a/core/dr-segment/src/lib.rs b/core/dr-segment/src/lib.rs index b918ce5..c6374a4 100644 --- a/core/dr-segment/src/lib.rs +++ b/core/dr-segment/src/lib.rs @@ -72,7 +72,7 @@ pub use refine::{ #[cfg(feature = "semantic")] pub use scene::{Category, Scene, SceneModel}; #[cfg(feature = "embedded-model")] -pub use semantic::embedded_model_bytes; +pub use semantic::embedded_models; #[cfg(feature = "semantic")] pub use semantic::{Instance, SemanticModel, SemanticOptions, Tiling}; diff --git a/core/dr-segment/src/scene.rs b/core/dr-segment/src/scene.rs index f92fe73..5d53379 100644 --- a/core/dr-segment/src/scene.rs +++ b/core/dr-segment/src/scene.rs @@ -123,21 +123,29 @@ impl SceneModel { classes: impl AsRef, categories: impl AsRef, ) -> Result { + // The form the device's backend runs: the `.a16w16.onnx` sibling on + // the Hexagon (attention left in float, inference.md §1.5), else this. + let (model, form) = + dr_inference_engine::resolve_model(dr_inference_engine::Role::Scene, model.as_ref()); let bytes = std::fs::read(model).map_err(SegmentError::ModelRead)?; let classes = std::fs::read_to_string(classes).map_err(SegmentError::ModelRead)?; let categories = std::fs::read_to_string(categories).map_err(SegmentError::ModelRead)?; let classes = crate::semantic::parse_classes(&classes); let categories = parse_categories(&categories, &classes)?; - Self::from_bytes(&bytes, categories) + Self::from_bytes_in(&bytes, form, categories) } pub fn from_bytes(bytes: &[u8], categories: Vec) -> Result { - // f32, as for `SemanticModel`; see there. - let session = dr_inference_engine::open( - dr_inference_engine::Role::Scene, - dr_inference_engine::Form::F32, - bytes, - )?; + Self::from_bytes_in(bytes, dr_inference_engine::Form::F32, categories) + } + + /// `bytes` in a stated numeric form; the outputs keep their shape. + pub fn from_bytes_in( + bytes: &[u8], + form: dr_inference_engine::Form, + categories: Vec, + ) -> Result { + let session = dr_inference_engine::open(dr_inference_engine::Role::Scene, form, bytes)?; Ok(Self { session, diff --git a/core/dr-segment/src/semantic.rs b/core/dr-segment/src/semantic.rs index 88b92a9..c337f68 100644 --- a/core/dr-segment/src/semantic.rs +++ b/core/dr-segment/src/semantic.rs @@ -208,18 +208,32 @@ const EMBEDDED_MODEL: &[u8] = include_bytes!("../../../models/segment/yolo26n-se #[cfg(feature = "embedded-model")] const EMBEDDED_CLASSES: &str = include_str!("../../../models/segment/yolo26n-seg.classes.json"); -/// The bytes of the model that ships with this crate, for whoever compiles +/// The Hexagon's form (docs/dev/inference.md §1.5): 16-bit activations and +/// weights, the rows' tail left in float. Only Android has a Hexagon, so only +/// Android carries it. +#[cfg(all(feature = "embedded-model", target_os = "android"))] +const EMBEDDED_A16W16: &[u8] = include_bytes!("../../../models/segment/yolo26n-seg.a16w16.onnx"); + +/// Every form of the model that ships with this crate, for whoever compiles /// engines ahead of the first request (docs/dev/inference.md §6). #[cfg(feature = "embedded-model")] -pub fn embedded_model_bytes() -> &'static [u8] { - EMBEDDED_MODEL +pub fn embedded_models() -> Vec<(dr_inference_engine::Form, &'static [u8])> { + #[allow(unused_mut)] + let mut forms = vec![(dr_inference_engine::Form::F32, EMBEDDED_MODEL)]; + #[cfg(target_os = "android")] + forms.push((dr_inference_engine::Form::A16W16, EMBEDDED_A16W16)); + forms } impl SemanticModel { - /// Load the model that ships with this crate. + /// Load the model that ships with this crate, in the form the device's + /// backend runs. #[cfg(feature = "embedded-model")] pub fn embedded() -> Result { - Self::from_bytes(EMBEDDED_MODEL, parse_classes(EMBEDDED_CLASSES)) + let forms = embedded_models(); + let (bytes, form) = + dr_inference_engine::choose_embedded(dr_inference_engine::Role::Segmenter, &forms); + Self::from_bytes_in(bytes, form, parse_classes(EMBEDDED_CLASSES)) } /// Load a model from an ONNX file, with `classes` supplying its vocabulary. @@ -236,14 +250,18 @@ impl SemanticModel { } pub fn from_bytes(bytes: &[u8], classes: Vec>) -> Result { - // The f32 graph on whatever the device's backend is. An int8 form - // for the Hexagon waits on docs/dev/inference.md §10 M7 — the mask - // boundary has to be measured before it moves. - let session = dr_inference_engine::open( - dr_inference_engine::Role::Segmenter, - dr_inference_engine::Form::F32, - bytes, - )?; + Self::from_bytes_in(bytes, dr_inference_engine::Form::F32, classes) + } + + /// `bytes` in a stated numeric form. The quantised one keeps the same + /// outputs (the rows' tail stays float), so decoding does not change; the + /// masks it draws were measured against f32's (inference.md §1.5). + pub fn from_bytes_in( + bytes: &[u8], + form: dr_inference_engine::Form, + classes: Vec>, + ) -> Result { + let session = dr_inference_engine::open(dr_inference_engine::Role::Segmenter, form, bytes)?; Ok(Self { session, classes }) } diff --git a/core/dr-types/src/settings.rs b/core/dr-types/src/settings.rs index 156e3a1..dab8230 100644 --- a/core/dr-types/src/settings.rs +++ b/core/dr-types/src/settings.rs @@ -275,13 +275,26 @@ impl FaceDetector { } } - /// Both ids this detector writes under — the f32 form and the int8 one — - /// for a question that is about the detector and not about which form - /// of it a device happened to run: "has the chosen detector been over - /// this image", asked by a re-index that must not ping-pong between a - /// desktop that runs it in f32 and a tablet that runs it on the Hexagon. - pub fn model_ids(self) -> [&'static str; 2] { - [self.model_id(), self.model_id_int8()] + /// The id when the detector runs with 16-bit activations and 8-bit + /// weights, the Hexagon's form since the int8 one lost faces at 40–80 px + /// (docs/dev/inference.md §1.5). Different again from both, for the same + /// reason as [`Self::model_id_int8`]; the embedder half is unchanged. + pub fn model_id_a16w8(self) -> &'static str { + match self { + FaceDetector::Scrfd500m => "scrfd_500m_a16+w600k_mbf", + FaceDetector::Scrfd2_5g => "scrfd_2.5g_a16+w600k_mbf", + FaceDetector::Scrfd10g => "scrfd_10g_a16+w600k_mbf", + } + } + + /// Every id this detector writes under — the f32 form and each quantised + /// one a device has run — for a question that is about the detector and + /// not about which form of it a device happened to run: "has the chosen + /// detector been over this image", asked by a re-index that must not + /// ping-pong between a desktop that runs it in f32 and a tablet that + /// runs it on the Hexagon. + pub fn model_ids(self) -> [&'static str; 3] { + [self.model_id(), self.model_id_int8(), self.model_id_a16w8()] } /// The detector that writes under a pipeline id, if it is one of these. @@ -1769,11 +1782,16 @@ mod tests { #[test] fn a_detectors_two_spellings_share_its_embedder_and_nothing_else() { for d in FaceDetector::ALL { - let [f32_id, int8_id] = d.model_ids(); + let [f32_id, int8_id, a16_id] = d.model_ids(); assert_eq!(f32_id, d.model_id()); assert_eq!(int8_id, d.model_id_int8()); + assert_eq!(a16_id, d.model_id_a16w8()); assert_ne!(f32_id, int8_id); - assert_eq!(f32_id.rsplit('+').next(), int8_id.rsplit('+').next()); + assert_ne!(f32_id, a16_id); + assert_ne!(int8_id, a16_id); + for q in [int8_id, a16_id] { + assert_eq!(f32_id.rsplit('+').next(), q.rsplit('+').next()); + } } } @@ -1797,6 +1815,7 @@ mod tests { } assert_eq!(FaceDetector::for_model_id(d.model_id()), Some(d)); assert_eq!(FaceDetector::for_model_id(d.model_id_int8()), Some(d)); + assert_eq!(FaceDetector::for_model_id(d.model_id_a16w8()), Some(d)); } assert_eq!(FaceDetector::for_model_id("scrfd_10g+other"), None); } diff --git a/docker/windows/package.sh b/docker/windows/package.sh index 5d3e122..ed04618 100755 --- a/docker/windows/package.sh +++ b/docker/windows/package.sh @@ -54,10 +54,14 @@ sed 's/$/\r/' "${REPO}/LICENSE" > "${STAGE}/LICENSE" # with its two descriptors, the panorama border filler and the denoiser. The installer # smoke test counts the same directories, so a model added here is expected # there without a number to update. +# +# Not the quantised siblings (`*.int8.onnx`, `*.a16w8.onnx`, `*.a16w16.onnx`): +# they are the Hexagon's forms (docs/dev/inference.md §1.5), and a Windows +# machine has no Hexagon to load them. for dir in face scene inpaint denoise; do for f in "${REPO}/models/${dir}"/*; do case "$(basename "${f}")" in - README.md) continue ;; + README.md | *.int8.onnx | *.a16w8.onnx | *.a16w16.onnx) continue ;; esac if [[ "${f}" == *.onnx && "$(stat -c%s "${f}")" -lt 100000 ]]; then echo "error: $(basename "${f}") is $(stat -c%s "${f}") bytes — an LFS pointer, not a model." >&2 diff --git a/docs/dev/denoise.md b/docs/dev/denoise.md index 161e85d..10e9154 100644 --- a/docs/dev/denoise.md +++ b/docs/dev/denoise.md @@ -331,6 +331,17 @@ If neither holds S's quality within 0.5 dB of fp32 on the real pairs, **v1 is de tablet shows the classical path. The sidecar still records the intent, so a desktop can render the learned result for a photograph edited on the tablet. +**Measured 2026-10-04 (inference.md §1.5): the second way holds, without the first.** The shipped +network, with its Bayer packing re-spelled as `SpaceToDepth` so QNN can hold it (the 6-D reshape +it replaces is exact but past the HTP's rank limit), at A16W16 — 16-bit activations and weights — +scores within 0.00 dB of f32 at ISO 400–25600 on the tablet's own HTP, and within 0.09 dB with the +6D's noise model scaled ×0.5, ×2 and ×4 to stand in for other sensors. A16W8 holds the 6D (worst +−0.19 dB at ISO 25600) but not ×4 noise at 25600 (−0.52 dB), so A16W16 is what ships. int8 loses +4.7–9.2 dB and fp16 is refused outright. A 1408 tile takes 95 ms on the Hexagon against 1510 ms on +the tablet's CPU: about 2.3 s for a 20 MP frame. Calibration ranges come from 96 training-day +tiles across every ISO, a third of them with that scaled noise; coverage of other bodies is that +synthetic bracket, not their raws. + ## 9. X-Trans The requirements tie this stage to FR-RAW-5, and the library has no Fuji raws. What we can do diff --git a/docs/dev/inference.md b/docs/dev/inference.md index df66133..3d7d1ad 100644 --- a/docs/dev/inference.md +++ b/docs/dev/inference.md @@ -127,7 +127,8 @@ Three things the table settles. five of six models. A whole-library face index on the tablet goes from ~100 ms + 39 ms per face to ~1.4 ms + 12 ms per face, and the "Thorough" detector — 3× the cost of "Fast" today — becomes free. Its price is that the models must be **quantised to int8**, which is an accuracy question - §5 has to answer before it is believed. + §5 has to answer before it is believed. (§1.5 answered it: int8 lost faces, and every model but + XFeat ships with 16-bit activations, at about three times these timings.) - **The embedder does not gain from either accelerator.** 112×112 input, per-op overhead dominates; it is 9 ms on the tablet's CPU and 12 ms on its NPU. It stays float, which §7 turns from a performance footnote into a correctness rule. @@ -137,6 +138,60 @@ Three things the table settles. - **On AMD, MIGraphX fp16 is 4–17× the CPU provider** on the detectors and 60× on the inpainter, with the same first-run compile cost as TensorRT and no rung between it and the CPU. +### 1.5 The Hexagon at every bit width · 2026-10-04 + +§1.1's Hexagon column is int8 calibrated on noise: timing only. This is the follow-up — every +model, every bit width the HTP offers, calibrated on real photographs and **scored on the tablet +itself** (ORT 1.29 + QNN 2.42, `htp_arch` 73), against the f32 model on the same inputs. The +"Form shipped" column is the files in `models/`, re-scored on the tablet after +`tools/quantise-models.sh` wrote them. The +calibration and scoring photographs are 800 from the public COCO val2017 set (CC-BY); the face +models' numbers are over the faces in them of at least 32 px. The tools are `tools/quantise-models.sh` +and the scratch harness described with it. + +**What the HTP accepts.** fp16: nothing — every fp16 operator fails validation (3110), on QNN +2.42 and 2.50, with `htp_arch` and every `soc_model` tried; the fp16 rung stays off the table until +someone has Qualcomm's own SDK to say why. 4-bit weights (A8W4, A16W4): load, and wreck accuracy +(SCRFD finds 25–35% of f32's faces). What is left: **A8W8 (int8), A16W8 and A16W16**, all running +the whole graph. 16-bit activations cost about 3× int8's time, A16W16 about 4×. + +| Model | ORT CPU f32 | Form shipped | Hexagon | On the tablet, against f32 | int8 for comparison | +|---|---|---|---|---|---| +| scrfd_500m / 2.5g / 10g | 17 / 56 / 198 ms | **A16W8** | 4.2 / 5.1 / 9.0 ms | 100% of faces found in every size band; keypoints 0.3–0.6% of the box | 94–95% of faces at 40–80 px | +| 2d106det (landmarks) | 2.8 ms | **A16W8** | 0.5 ms | 0.25 px in the 192 crop (eye points 0.20) | 1.5 px, and 29 partitions at 7.3 ms | +| yolo26n-seg | 90 ms | **A16W16**, tail in float | 12.9 ms | 98.2% of objects, mask IoU 0.994 | 74% (simulated) | +| yolo26s-sem-ade20k | 151 ms | **A16W16**, attention in float | 15 ms | 98.9% of cells agree on the class, TV 0.009 | 67% | +| migan-512 | 488 ms | **A16W16** | 87 ms | 41 dB from f32 in the fill (worst 1%: 30 dB) | 16 dB (simulated) | +| xfeat-1024 / 768 | 58 ms | **int8**, rewritten graph | 6.5 ms | panorama alignment 0.45 px from f32's — f32's own refit on 90% of its matches is 0.41 | — | +| mosaic-1408 (denoiser) | 1510 ms a tile | **A16W16**, rewritten graph | 95 ms a tile | 0.00 dB at every ISO; ≤ 0.09 dB with the noise scaled ×0.5–×4 | −4.7 to −9.2 dB | +| arcface_mbf (embedder) | 8.5 ms | f32, CPU | — | A16W16: cosine 0.9995, p1 0.9967 — misses §7's 0.999 gate | — | +| ocec, sgc (eyes) | 1, 1.7 ms | f32, CPU | — | sgc flips 1.45% of views even at A16W16; not worth a millisecond | — | + +Four things the table needed that the f32 graphs did not have, all in `tools/htp_graph.py` and all +checked exact against the f32 graph before they are used: + +- **Rank ≤ 5.** QNN's tensors stop at rank 5, and the denoiser packs the mosaic through a 6-D + reshape (6007 at compose). For one channel that reshape is `SpaceToDepth(2)`. XFeat's 8×8 unfold + is 224 Slices and 6-D Concats; it is `SpaceToDepth(8)` (736 nodes to 60). +- **No bilinear Resize at XFeat's sizes** (3110). A half-pixel bilinear resize between fixed sizes + is two constant matrices, so it is two MatMuls. +- **One scale per tensor.** The segmenter's output rows carry boxes in pixels beside scores in + 0..1; quantised as one tensor the scores vanish. Everything from the Concat that builds the rows + stays float, on the CPU, where the top-300 selection costs nothing. +- **Float where the HTP's 16-bit arithmetic drifts.** The scene model's one attention block + (two MatMuls and a Softmax over 400 tokens) moved its agreement from 98.7% to 96.7%; it stays + float. + +**ORT's CPU simulation of a QDQ graph is not the tablet.** It matched to the hundredth of a dB for +the denoiser and to rounding for the detectors, landmarks and XFeat, and it overstated MI-GAN by +27 dB and the scene model by three points. Every number above is the device's; a new form is not +measured until it has run there. + +**XFeat's int8 loses keypoints and not the panorama.** 83% of f32's keypoints come back within +1.5 px; but over the twelve-frame `fixtures/pano/2025-08-05` sweep, the homographies fitted from +int8's matches land 0.45 px from f32's in the overlaps — the spread f32 shows against itself +(0.41). + --- ## 2. The shape of the answer @@ -146,7 +201,7 @@ winning: | Platform | 1st | 2nd | 3rd | Floor | |---|---|---|---|---| -| Android, Qualcomm with a Hexagon the shipped QNN skel covers (V68–V81) | QNN HTP, int8 model | ORT CPU, f32 model | — | tract | +| Android, Qualcomm with a Hexagon the shipped QNN skel covers (V68–V81) | QNN HTP, each model's quantised form (§1.5) | ORT CPU, f32 model | — | tract | | Android, any other SoC | ORT CPU, f32 | — | — | tract | | Linux / Windows, NVIDIA GPU | TensorRT, f32 model, fp16 engine | CUDA provider, f32 | ORT CPU, f32 | tract | | Linux, AMD GPU with ROCm | MIGraphX, f32 model, fp16 program | ORT CPU, f32 | — | tract | @@ -298,10 +353,10 @@ of which form they load: | Form | Who produces it | When | Needed by | |---|---|---|---| | f32 ONNX, shape-fixed, **opset ≥ 13** | `tools/fix-face-model-shapes.sh`, `tools/export-seg-model.sh` | Release time, once | Every rung except Hexagon | -| int8 QDQ ONNX, per-channel, uint8 activations | `tools/quantise-models.sh` (new) | Release time, once, **calibrated on real photographs** | Hexagon | +| QDQ ONNX, per-channel — int8, A16W8 or A16W16 per model (§1.5) | `tools/quantise-models.sh` | Release time, once, **calibrated on real photographs**, scored on the tablet | Hexagon | | TensorRT engine (`.engine`, per GPU architecture and TensorRT version) | The app, from the f32 file | First run on that device, in the background | TensorRT rung | | MIGraphX program (`.mxr`, per GPU architecture, MIGraphX version and precision) | The app, from the f32 file | First run on that device, in the background | MIGraphX rung | -| QNN context binary | The app, from the int8 file | First run on that device, in the background | Hexagon rung | +| QNN context binary | The app, from the quantised file | First run on that device, in the background | Hexagon rung | Two rules. @@ -380,6 +435,11 @@ already the rule for the detector and because §5 is the gate on whether the int enough to be *offered* at all. f32 on tract, ORT CPU, CUDA and TensorRT-f32 are one identity: the same graph, the same arithmetic, differences at the last bit. +After §1.5 the Hexagon runs the detectors in **A16W8**, and that is a third spelling: +`scrfd_500m_a16+w600k_mbf` and its two siblings. Same rule, same reconciliation; a tablet that +indexed under `_i8` keeps those rows, and `FaceDetector::model_ids` answers "has this detector been +over this image" for all three forms. + **The embedder** is where comparability across devices is the whole point, and it is the one model that no accelerator helps (§1.4). So: **the embedder runs in f32 on every rung.** On TensorRT that means the embedder's engine is built without fp16 while the detector's is built with it; on diff --git a/docs/dev/panorama.md b/docs/dev/panorama.md index 025b162..44408d2 100644 --- a/docs/dev/panorama.md +++ b/docs/dev/panorama.md @@ -467,9 +467,11 @@ the tablet. Three ways to make it viable, none built: 1. **Fill at a quarter of the resolution and upsample.** Sky and scree tolerate it; twenty-odd tiles, about three minutes on the desktop CPU. A background job with the outbox's patience, not an interactive one. -2. **int8 on the tablet's Hexagon through QNN**, where the plain-conv design - is the point and the whole graph should run in milliseconds. The setup - exists from the eye-state work; MI-GAN is a candidate for the same path. +2. **The tablet's Hexagon through QNN**, where the plain-conv design is the + point. Measured 2026-10-04 (inference.md §1.5): int8 changes the fill + (16 dB from f32's), so it ships with 16-bit activations and weights — + 87 ms a tile against 488 ms on the tablet's CPU, the whole graph on the + NPU, 41 dB from f32 in the hole. 3. **A WGSL runtime for those six operators.** A project of its own, and the only route that would make it interactive on the desktop. diff --git a/docs/dev/traceability.md b/docs/dev/traceability.md index 802a80b..ba8d9bc 100644 --- a/docs/dev/traceability.md +++ b/docs/dev/traceability.md @@ -35,7 +35,7 @@ _None._ | ID | Tagged in | |---|---| | FR-CAT-1 | [`core/dr-catalog/src/scan.rs:1`](../../core/dr-catalog/src/scan.rs#L1), [`core/dr-catalog/src/walk.rs:108`](../../core/dr-catalog/src/walk.rs#L108), [`core/dr-catalog/src/walk.rs:161`](../../core/dr-catalog/src/walk.rs#L161), [`core/dr-catalog/src/walk.rs:1`](../../core/dr-catalog/src/walk.rs#L1), [`core/dr-sync/src/scan.rs:129`](../../core/dr-sync/src/scan.rs#L129), [`core/dr-types/src/lib.rs:206`](../../core/dr-types/src/lib.rs#L206), [`core/dr-types/src/lib.rs:275`](../../core/dr-types/src/lib.rs#L275), [`core/dr-types/src/lib.rs:308`](../../core/dr-types/src/lib.rs#L308), [`platform/dr-plat/src/storage.rs:1`](../../platform/dr-plat/src/storage.rs#L1), [`platform/dr-plat/src/storage.rs:216`](../../platform/dr-plat/src/storage.rs#L216), [`ui/dr-ui/src/activity.rs:1`](../../ui/dr-ui/src/activity.rs#L1), [`ui/dr-ui/src/library/mod.rs:1`](../../ui/dr-ui/src/library/mod.rs#L1) | -| FR-CAT-10 | [`core/dr-ingest/src/layout.rs:1`](../../core/dr-ingest/src/layout.rs#L1), [`core/dr-ingest/src/lib.rs:1`](../../core/dr-ingest/src/lib.rs#L1), [`core/dr-ingest/src/lib.rs:733`](../../core/dr-ingest/src/lib.rs#L733), [`core/dr-types/src/settings.rs:377`](../../core/dr-types/src/settings.rs#L377), [`platform/dr-plat/src/storage.rs:287`](../../platform/dr-plat/src/storage.rs#L287), [`platform/dr-plat/src/storage.rs:586`](../../platform/dr-plat/src/storage.rs#L586), [`platform/dr-plat/src/volumes.rs:1`](../../platform/dr-plat/src/volumes.rs#L1), [`platform/dr-plat/src/volumes.rs:62`](../../platform/dr-plat/src/volumes.rs#L62), [`ui/dr-ui/src/cards.rs:1`](../../ui/dr-ui/src/cards.rs#L1), [`ui/dr-ui/src/import.rs:1`](../../ui/dr-ui/src/import.rs#L1), [`ui/dr-ui/src/import.rs:345`](../../ui/dr-ui/src/import.rs#L345), [`ui/dr-ui/src/import_ui.rs:1`](../../ui/dr-ui/src/import_ui.rs#L1), [`ui/dr-ui/src/lib.rs:1946`](../../ui/dr-ui/src/lib.rs#L1946), [`ui/dr-ui/ui/import.slint:6`](../../ui/dr-ui/ui/import.slint#L6), [`ui/dr-ui/ui/library.slint:1370`](../../ui/dr-ui/ui/library.slint#L1370), [`ui/dr-ui/ui/library.slint:1424`](../../ui/dr-ui/ui/library.slint#L1424), [`ui/dr-ui/ui/library.slint:1686`](../../ui/dr-ui/ui/library.slint#L1686) | +| FR-CAT-10 | [`core/dr-ingest/src/layout.rs:1`](../../core/dr-ingest/src/layout.rs#L1), [`core/dr-ingest/src/lib.rs:1`](../../core/dr-ingest/src/lib.rs#L1), [`core/dr-ingest/src/lib.rs:733`](../../core/dr-ingest/src/lib.rs#L733), [`core/dr-types/src/settings.rs:390`](../../core/dr-types/src/settings.rs#L390), [`platform/dr-plat/src/storage.rs:287`](../../platform/dr-plat/src/storage.rs#L287), [`platform/dr-plat/src/storage.rs:586`](../../platform/dr-plat/src/storage.rs#L586), [`platform/dr-plat/src/volumes.rs:1`](../../platform/dr-plat/src/volumes.rs#L1), [`platform/dr-plat/src/volumes.rs:62`](../../platform/dr-plat/src/volumes.rs#L62), [`ui/dr-ui/src/cards.rs:1`](../../ui/dr-ui/src/cards.rs#L1), [`ui/dr-ui/src/import.rs:1`](../../ui/dr-ui/src/import.rs#L1), [`ui/dr-ui/src/import.rs:345`](../../ui/dr-ui/src/import.rs#L345), [`ui/dr-ui/src/import_ui.rs:1`](../../ui/dr-ui/src/import_ui.rs#L1), [`ui/dr-ui/src/lib.rs:1946`](../../ui/dr-ui/src/lib.rs#L1946), [`ui/dr-ui/ui/import.slint:6`](../../ui/dr-ui/ui/import.slint#L6), [`ui/dr-ui/ui/library.slint:1370`](../../ui/dr-ui/ui/library.slint#L1370), [`ui/dr-ui/ui/library.slint:1424`](../../ui/dr-ui/ui/library.slint#L1424), [`ui/dr-ui/ui/library.slint:1686`](../../ui/dr-ui/ui/library.slint#L1686) | | FR-CAT-11 | [`core/dr-catalog/src/dedup.rs:1`](../../core/dr-catalog/src/dedup.rs#L1), [`core/dr-ingest/src/lib.rs:1`](../../core/dr-ingest/src/lib.rs#L1), [`core/dr-ingest/src/lib.rs:392`](../../core/dr-ingest/src/lib.rs#L392), [`core/dr-sync/src/upload.rs:40`](../../core/dr-sync/src/upload.rs#L40), [`ui/dr-ui/src/import.rs:1`](../../ui/dr-ui/src/import.rs#L1), [`ui/dr-ui/src/import_ui.rs:1`](../../ui/dr-ui/src/import_ui.rs#L1), [`ui/dr-ui/src/lib.rs:1946`](../../ui/dr-ui/src/lib.rs#L1946), [`ui/dr-ui/src/library/filters.rs:12`](../../ui/dr-ui/src/library/filters.rs#L12), [`ui/dr-ui/src/library/thumbnails_gen.rs:608`](../../ui/dr-ui/src/library/thumbnails_gen.rs#L608), [`ui/dr-ui/ui/import.slint:6`](../../ui/dr-ui/ui/import.slint#L6) | | FR-CAT-11a | [`core/dr-catalog/src/duplicates.rs:1`](../../core/dr-catalog/src/duplicates.rs#L1), [`core/dr-catalog/src/error.rs:110`](../../core/dr-catalog/src/error.rs#L110), [`core/dr-catalog/src/faces.rs:1662`](../../core/dr-catalog/src/faces.rs#L1662), [`core/dr-catalog/src/faces.rs:1680`](../../core/dr-catalog/src/faces.rs#L1680), [`ui/dr-ui/src/duplicates.rs:1`](../../ui/dr-ui/src/duplicates.rs#L1), [`ui/dr-ui/src/duplicates_ui.rs:1`](../../ui/dr-ui/src/duplicates_ui.rs#L1), [`ui/dr-ui/src/duplicates_ui.rs:79`](../../ui/dr-ui/src/duplicates_ui.rs#L79), [`ui/dr-ui/src/lib.rs:1976`](../../ui/dr-ui/src/lib.rs#L1976), [`ui/dr-ui/src/lib.rs:3613`](../../ui/dr-ui/src/lib.rs#L3613), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:671`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L671), [`ui/dr-ui/ui/app.slint:83`](../../ui/dr-ui/ui/app.slint#L83), [`ui/dr-ui/ui/collections.slint:897`](../../ui/dr-ui/ui/collections.slint#L897), [`ui/dr-ui/ui/duplicates.slint:1`](../../ui/dr-ui/ui/duplicates.slint#L1), [`ui/dr-ui/ui/settings.slint:108`](../../ui/dr-ui/ui/settings.slint#L108), [`ui/dr-ui/ui/settings.slint:512`](../../ui/dr-ui/ui/settings.slint#L512) | | FR-CAT-12 | [`core/dr-pipeline/src/sidecar.rs:140`](../../core/dr-pipeline/src/sidecar.rs#L140) | @@ -87,7 +87,7 @@ _None._ | FR-DEV-3j | [`core/dr-gpu/src/adjust.rs:1301`](../../core/dr-gpu/src/adjust.rs#L1301), [`core/dr-gpu/src/adjust.rs:1539`](../../core/dr-gpu/src/adjust.rs#L1539), [`core/dr-gpu/src/adjust.rs:3396`](../../core/dr-gpu/src/adjust.rs#L3396), [`core/dr-gpu/tests/camera_profile.rs:197`](../../core/dr-gpu/tests/camera_profile.rs#L197), [`core/dr-gpu/tests/scene_referred.rs:145`](../../core/dr-gpu/tests/scene_referred.rs#L145), [`core/dr-gpu/tests/scene_referred.rs:1`](../../core/dr-gpu/tests/scene_referred.rs#L1), [`core/dr-gpu/tests/view_transform.rs:114`](../../core/dr-gpu/tests/view_transform.rs#L114), [`core/dr-gpu/tests/view_transform.rs:137`](../../core/dr-gpu/tests/view_transform.rs#L137), [`core/dr-gpu/tests/view_transform.rs:163`](../../core/dr-gpu/tests/view_transform.rs#L163), [`core/dr-gpu/tests/view_transform.rs:1`](../../core/dr-gpu/tests/view_transform.rs#L1), [`core/dr-gpu/tests/view_transform.rs:201`](../../core/dr-gpu/tests/view_transform.rs#L201), [`core/dr-gpu/tests/view_transform.rs:63`](../../core/dr-gpu/tests/view_transform.rs#L63), [`core/dr-pipeline/src/camera_raw.rs:1`](../../core/dr-pipeline/src/camera_raw.rs#L1), [`core/dr-pipeline/src/camera_raw.rs:56`](../../core/dr-pipeline/src/camera_raw.rs#L56), [`core/dr-pipeline/src/operation.rs:1414`](../../core/dr-pipeline/src/operation.rs#L1414), [`core/dr-pipeline/src/operation.rs:2377`](../../core/dr-pipeline/src/operation.rs#L2377), [`core/dr-pipeline/src/operation.rs:2493`](../../core/dr-pipeline/src/operation.rs#L2493), [`core/dr-pipeline/src/operation.rs:2510`](../../core/dr-pipeline/src/operation.rs#L2510), [`core/dr-pipeline/src/operation.rs:2700`](../../core/dr-pipeline/src/operation.rs#L2700), [`core/dr-pipeline/src/operation.rs:2738`](../../core/dr-pipeline/src/operation.rs#L2738), [`core/dr-pipeline/src/operation.rs:580`](../../core/dr-pipeline/src/operation.rs#L580), [`core/dr-pipeline/src/operation.rs:880`](../../core/dr-pipeline/src/operation.rs#L880), [`core/dr-pipeline/src/operation.rs:930`](../../core/dr-pipeline/src/operation.rs#L930), [`core/dr-pipeline/src/ops/camera_profile.rs:384`](../../core/dr-pipeline/src/ops/camera_profile.rs#L384), [`core/dr-pipeline/src/ops/film_sim.rs:315`](../../core/dr-pipeline/src/ops/film_sim.rs#L315), [`core/dr-pipeline/src/ops/view_transform.rs:1`](../../core/dr-pipeline/src/ops/view_transform.rs#L1), [`core/dr-pipeline/src/ops/view_transform.rs:224`](../../core/dr-pipeline/src/ops/view_transform.rs#L224), [`core/dr-pipeline/src/ops/view_transform.rs:36`](../../core/dr-pipeline/src/ops/view_transform.rs#L36), [`core/dr-pipeline/src/view.rs:195`](../../core/dr-pipeline/src/view.rs#L195), [`core/dr-pipeline/src/view.rs:1`](../../core/dr-pipeline/src/view.rs#L1), [`core/dr-pipeline/src/view.rs:208`](../../core/dr-pipeline/src/view.rs#L208), [`core/dr-pipeline/src/view.rs:222`](../../core/dr-pipeline/src/view.rs#L222), [`core/dr-pipeline/src/view.rs:238`](../../core/dr-pipeline/src/view.rs#L238), [`core/dr-pipeline/src/view.rs:254`](../../core/dr-pipeline/src/view.rs#L254), [`core/dr-pipeline/src/view.rs:265`](../../core/dr-pipeline/src/view.rs#L265), [`core/dr-types/src/tone.rs:1`](../../core/dr-types/src/tone.rs#L1) | | FR-DEV-4 | [`core/dr-gpu/src/adjust.rs:1107`](../../core/dr-gpu/src/adjust.rs#L1107), [`core/dr-gpu/src/lib.rs:370`](../../core/dr-gpu/src/lib.rs#L370), [`ui/dr-ui/ui/crop.slint:1`](../../ui/dr-ui/ui/crop.slint#L1) | | FR-DEV-5 | [`core/dr-pipeline/src/graph.rs:1604`](../../core/dr-pipeline/src/graph.rs#L1604), [`core/dr-pipeline/src/graph.rs:710`](../../core/dr-pipeline/src/graph.rs#L710), [`core/dr-pipeline/src/graph.rs:770`](../../core/dr-pipeline/src/graph.rs#L770), [`core/dr-pipeline/src/history.rs:102`](../../core/dr-pipeline/src/history.rs#L102), [`core/dr-pipeline/src/history.rs:110`](../../core/dr-pipeline/src/history.rs#L110), [`core/dr-pipeline/src/history.rs:127`](../../core/dr-pipeline/src/history.rs#L127), [`core/dr-pipeline/src/history.rs:184`](../../core/dr-pipeline/src/history.rs#L184), [`core/dr-pipeline/src/history.rs:1`](../../core/dr-pipeline/src/history.rs#L1), [`core/dr-pipeline/src/history.rs:214`](../../core/dr-pipeline/src/history.rs#L214), [`core/dr-pipeline/src/history.rs:234`](../../core/dr-pipeline/src/history.rs#L234), [`core/dr-pipeline/src/history.rs:293`](../../core/dr-pipeline/src/history.rs#L293), [`core/dr-pipeline/src/history.rs:479`](../../core/dr-pipeline/src/history.rs#L479), [`core/dr-pipeline/src/history.rs:489`](../../core/dr-pipeline/src/history.rs#L489), [`core/dr-pipeline/src/history.rs:499`](../../core/dr-pipeline/src/history.rs#L499), [`core/dr-pipeline/src/history.rs:526`](../../core/dr-pipeline/src/history.rs#L526), [`core/dr-pipeline/src/history.rs:86`](../../core/dr-pipeline/src/history.rs#L86), [`core/dr-pipeline/src/sidecar.rs:1067`](../../core/dr-pipeline/src/sidecar.rs#L1067), [`core/dr-pipeline/src/sidecar.rs:147`](../../core/dr-pipeline/src/sidecar.rs#L147), [`core/dr-pipeline/src/sidecar.rs:2515`](../../core/dr-pipeline/src/sidecar.rs#L2515), [`core/dr-pipeline/src/sidecar.rs:2551`](../../core/dr-pipeline/src/sidecar.rs#L2551), [`core/dr-pipeline/src/sidecar.rs:2563`](../../core/dr-pipeline/src/sidecar.rs#L2563), [`core/dr-pipeline/src/sidecar.rs:318`](../../core/dr-pipeline/src/sidecar.rs#L318), [`core/dr-pipeline/src/sidecar.rs:713`](../../core/dr-pipeline/src/sidecar.rs#L713), [`core/dr-pipeline/src/sidecar.rs:729`](../../core/dr-pipeline/src/sidecar.rs#L729), [`core/dr-pipeline/src/sidecar.rs:876`](../../core/dr-pipeline/src/sidecar.rs#L876), [`core/dr-pipeline/src/state.rs:1`](../../core/dr-pipeline/src/state.rs#L1), [`core/dr-pipeline/src/state.rs:75`](../../core/dr-pipeline/src/state.rs#L75), [`ui/dr-ui/src/develop/framing.rs:945`](../../ui/dr-ui/src/develop/framing.rs#L945), [`ui/dr-ui/src/develop/history.rs:11`](../../ui/dr-ui/src/develop/history.rs#L11), [`ui/dr-ui/src/develop/history.rs:152`](../../ui/dr-ui/src/develop/history.rs#L152), [`ui/dr-ui/src/develop/history.rs:165`](../../ui/dr-ui/src/develop/history.rs#L165), [`ui/dr-ui/src/develop/history.rs:178`](../../ui/dr-ui/src/develop/history.rs#L178), [`ui/dr-ui/src/develop/history.rs:190`](../../ui/dr-ui/src/develop/history.rs#L190), [`ui/dr-ui/src/develop/history.rs:206`](../../ui/dr-ui/src/develop/history.rs#L206), [`ui/dr-ui/src/develop/history.rs:238`](../../ui/dr-ui/src/develop/history.rs#L238), [`ui/dr-ui/src/develop/history.rs:242`](../../ui/dr-ui/src/develop/history.rs#L242), [`ui/dr-ui/src/develop/history.rs:261`](../../ui/dr-ui/src/develop/history.rs#L261), [`ui/dr-ui/src/develop/history.rs:277`](../../ui/dr-ui/src/develop/history.rs#L277), [`ui/dr-ui/src/develop/history.rs:299`](../../ui/dr-ui/src/develop/history.rs#L299), [`ui/dr-ui/src/develop/history.rs:29`](../../ui/dr-ui/src/develop/history.rs#L29), [`ui/dr-ui/src/develop/history.rs:359`](../../ui/dr-ui/src/develop/history.rs#L359), [`ui/dr-ui/src/develop/history.rs:68`](../../ui/dr-ui/src/develop/history.rs#L68), [`ui/dr-ui/src/develop/history.rs:82`](../../ui/dr-ui/src/develop/history.rs#L82), [`ui/dr-ui/src/develop/history.rs:93`](../../ui/dr-ui/src/develop/history.rs#L93), [`ui/dr-ui/src/develop/render.rs:1079`](../../ui/dr-ui/src/develop/render.rs#L1079), [`ui/dr-ui/src/develop/render.rs:1115`](../../ui/dr-ui/src/develop/render.rs#L1115), [`ui/dr-ui/src/develop/render.rs:859`](../../ui/dr-ui/src/develop/render.rs#L859), [`ui/dr-ui/src/develop/session.rs:100`](../../ui/dr-ui/src/develop/session.rs#L100), [`ui/dr-ui/src/develop/session.rs:91`](../../ui/dr-ui/src/develop/session.rs#L91), [`ui/dr-ui/src/develop/white_balance.rs:11`](../../ui/dr-ui/src/develop/white_balance.rs#L11), [`ui/dr-ui/src/develop/white_balance.rs:162`](../../ui/dr-ui/src/develop/white_balance.rs#L162), [`ui/dr-ui/src/develop_ui.rs:852`](../../ui/dr-ui/src/develop_ui.rs#L852), [`ui/dr-ui/src/develop_ui.rs:872`](../../ui/dr-ui/src/develop_ui.rs#L872), [`ui/dr-ui/src/labels.rs:12`](../../ui/dr-ui/src/labels.rs#L12), [`ui/dr-ui/src/labels.rs:312`](../../ui/dr-ui/src/labels.rs#L312), [`ui/dr-ui/src/labels.rs:34`](../../ui/dr-ui/src/labels.rs#L34), [`ui/dr-ui/src/lib.rs:2205`](../../ui/dr-ui/src/lib.rs#L2205), [`ui/dr-ui/src/lib.rs:2210`](../../ui/dr-ui/src/lib.rs#L2210), [`ui/dr-ui/src/lib.rs:2242`](../../ui/dr-ui/src/lib.rs#L2242), [`ui/dr-ui/src/lib.rs:2251`](../../ui/dr-ui/src/lib.rs#L2251), [`ui/dr-ui/src/lib.rs:2268`](../../ui/dr-ui/src/lib.rs#L2268), [`ui/dr-ui/src/library/sidecar.rs:351`](../../ui/dr-ui/src/library/sidecar.rs#L351), [`ui/dr-ui/src/library/sidecar.rs:61`](../../ui/dr-ui/src/library/sidecar.rs#L61), [`ui/dr-ui/src/presets.rs:2155`](../../ui/dr-ui/src/presets.rs#L2155), [`ui/dr-ui/src/presets.rs:373`](../../ui/dr-ui/src/presets.rs#L373), [`ui/dr-ui/src/presets.rs:437`](../../ui/dr-ui/src/presets.rs#L437), [`ui/dr-ui/src/presets.rs:557`](../../ui/dr-ui/src/presets.rs#L557), [`ui/dr-ui/ui/history.slint:192`](../../ui/dr-ui/ui/history.slint#L192), [`ui/dr-ui/ui/history.slint:1`](../../ui/dr-ui/ui/history.slint#L1), [`ui/dr-ui/ui/history.slint:219`](../../ui/dr-ui/ui/history.slint#L219), [`ui/dr-ui/ui/history.slint:304`](../../ui/dr-ui/ui/history.slint#L304), [`ui/dr-ui/ui/history.slint:43`](../../ui/dr-ui/ui/history.slint#L43), [`ui/dr-ui/ui/history.slint:53`](../../ui/dr-ui/ui/history.slint#L53) | -| FR-DEV-6 | [`core/dr-pipeline/src/bundled.rs:1`](../../core/dr-pipeline/src/bundled.rs#L1), [`core/dr-pipeline/src/preset.rs:1343`](../../core/dr-pipeline/src/preset.rs#L1343), [`core/dr-pipeline/src/preset.rs:1353`](../../core/dr-pipeline/src/preset.rs#L1353), [`core/dr-pipeline/src/preset.rs:1362`](../../core/dr-pipeline/src/preset.rs#L1362), [`core/dr-pipeline/src/preset.rs:1378`](../../core/dr-pipeline/src/preset.rs#L1378), [`core/dr-pipeline/src/preset.rs:1390`](../../core/dr-pipeline/src/preset.rs#L1390), [`core/dr-pipeline/src/preset.rs:1406`](../../core/dr-pipeline/src/preset.rs#L1406), [`core/dr-pipeline/src/preset.rs:1432`](../../core/dr-pipeline/src/preset.rs#L1432), [`core/dr-pipeline/src/preset.rs:1454`](../../core/dr-pipeline/src/preset.rs#L1454), [`core/dr-pipeline/src/preset.rs:1468`](../../core/dr-pipeline/src/preset.rs#L1468), [`core/dr-pipeline/src/preset.rs:1486`](../../core/dr-pipeline/src/preset.rs#L1486), [`core/dr-pipeline/src/preset.rs:1670`](../../core/dr-pipeline/src/preset.rs#L1670), [`core/dr-pipeline/src/preset.rs:1685`](../../core/dr-pipeline/src/preset.rs#L1685), [`core/dr-pipeline/src/preset.rs:1`](../../core/dr-pipeline/src/preset.rs#L1), [`core/dr-pipeline/src/preset.rs:501`](../../core/dr-pipeline/src/preset.rs#L501), [`core/dr-pipeline/src/preset.rs:534`](../../core/dr-pipeline/src/preset.rs#L534), [`core/dr-pipeline/src/preset.rs:678`](../../core/dr-pipeline/src/preset.rs#L678), [`core/dr-preset-xmp/src/lib.rs:148`](../../core/dr-preset-xmp/src/lib.rs#L148), [`core/dr-preset-xmp/src/lib.rs:1`](../../core/dr-preset-xmp/src/lib.rs#L1), [`core/dr-preset-xmp/src/lib.rs:355`](../../core/dr-preset-xmp/src/lib.rs#L355), [`core/dr-preset-xmp/src/lib.rs:561`](../../core/dr-preset-xmp/src/lib.rs#L561), [`core/dr-preset-xmp/src/lib.rs:583`](../../core/dr-preset-xmp/src/lib.rs#L583), [`core/dr-preset-xmp/src/lib.rs:729`](../../core/dr-preset-xmp/src/lib.rs#L729), [`core/dr-types/src/settings.rs:443`](../../core/dr-types/src/settings.rs#L443), [`ui/dr-ui/src/derived_sync.rs:1142`](../../ui/dr-ui/src/derived_sync.rs#L1142), [`ui/dr-ui/src/derived_sync.rs:1160`](../../ui/dr-ui/src/derived_sync.rs#L1160), [`ui/dr-ui/src/derived_sync.rs:2134`](../../ui/dr-ui/src/derived_sync.rs#L2134), [`ui/dr-ui/src/derived_sync.rs:2169`](../../ui/dr-ui/src/derived_sync.rs#L2169), [`ui/dr-ui/src/derived_sync.rs:254`](../../ui/dr-ui/src/derived_sync.rs#L254), [`ui/dr-ui/src/derived_sync.rs:99`](../../ui/dr-ui/src/derived_sync.rs#L99), [`ui/dr-ui/src/develop/session.rs:619`](../../ui/dr-ui/src/develop/session.rs#L619), [`ui/dr-ui/src/develop/session.rs:625`](../../ui/dr-ui/src/develop/session.rs#L625), [`ui/dr-ui/src/develop/session.rs:732`](../../ui/dr-ui/src/develop/session.rs#L732), [`ui/dr-ui/src/develop/session.rs:753`](../../ui/dr-ui/src/develop/session.rs#L753), [`ui/dr-ui/src/develop/session.rs:79`](../../ui/dr-ui/src/develop/session.rs#L79), [`ui/dr-ui/src/develop/session.rs:926`](../../ui/dr-ui/src/develop/session.rs#L926), [`ui/dr-ui/src/develop_ui.rs:247`](../../ui/dr-ui/src/develop_ui.rs#L247), [`ui/dr-ui/src/develop_ui.rs:276`](../../ui/dr-ui/src/develop_ui.rs#L276), [`ui/dr-ui/src/export.rs:1322`](../../ui/dr-ui/src/export.rs#L1322), [`ui/dr-ui/src/labels.rs:30`](../../ui/dr-ui/src/labels.rs#L30), [`ui/dr-ui/src/lib.rs:1335`](../../ui/dr-ui/src/lib.rs#L1335), [`ui/dr-ui/src/lib.rs:2662`](../../ui/dr-ui/src/lib.rs#L2662), [`ui/dr-ui/src/lib.rs:337`](../../ui/dr-ui/src/lib.rs#L337), [`ui/dr-ui/src/lib.rs:880`](../../ui/dr-ui/src/lib.rs#L880), [`ui/dr-ui/src/lib.rs:898`](../../ui/dr-ui/src/lib.rs#L898), [`ui/dr-ui/src/library/paths.rs:42`](../../ui/dr-ui/src/library/paths.rs#L42), [`ui/dr-ui/src/library/sidecar.rs:12`](../../ui/dr-ui/src/library/sidecar.rs#L12), [`ui/dr-ui/src/library/sidecar.rs:40`](../../ui/dr-ui/src/library/sidecar.rs#L40), [`ui/dr-ui/src/library/thumbnails_fetch.rs:414`](../../ui/dr-ui/src/library/thumbnails_fetch.rs#L414), [`ui/dr-ui/src/library/thumbnails_fetch.rs:549`](../../ui/dr-ui/src/library/thumbnails_fetch.rs#L549), [`ui/dr-ui/src/library_ui/controller.rs:293`](../../ui/dr-ui/src/library_ui/controller.rs#L293), [`ui/dr-ui/src/library_ui/controller.rs:704`](../../ui/dr-ui/src/library_ui/controller.rs#L704), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:495`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L495), [`ui/dr-ui/src/library_ui/sync.rs:246`](../../ui/dr-ui/src/library_ui/sync.rs#L246), [`ui/dr-ui/src/library_ui/window.rs:835`](../../ui/dr-ui/src/library_ui/window.rs#L835), [`ui/dr-ui/src/preset_store.rs:1`](../../ui/dr-ui/src/preset_store.rs#L1), [`ui/dr-ui/src/presets.rs:1036`](../../ui/dr-ui/src/presets.rs#L1036), [`ui/dr-ui/src/presets.rs:1933`](../../ui/dr-ui/src/presets.rs#L1933), [`ui/dr-ui/src/presets.rs:1`](../../ui/dr-ui/src/presets.rs#L1), [`ui/dr-ui/src/presets.rs:2020`](../../ui/dr-ui/src/presets.rs#L2020), [`ui/dr-ui/src/presets.rs:2043`](../../ui/dr-ui/src/presets.rs#L2043), [`ui/dr-ui/src/presets.rs:707`](../../ui/dr-ui/src/presets.rs#L707), [`ui/dr-ui/src/presets.rs:754`](../../ui/dr-ui/src/presets.rs#L754), [`ui/dr-ui/src/presets.rs:783`](../../ui/dr-ui/src/presets.rs#L783), [`ui/dr-ui/ui/adjust.slint:937`](../../ui/dr-ui/ui/adjust.slint#L937), [`ui/dr-ui/ui/adjust.slint:962`](../../ui/dr-ui/ui/adjust.slint#L962), [`ui/dr-ui/ui/app.slint:1094`](../../ui/dr-ui/ui/app.slint#L1094), [`ui/dr-ui/ui/app.slint:1518`](../../ui/dr-ui/ui/app.slint#L1518), [`ui/dr-ui/ui/app.slint:1802`](../../ui/dr-ui/ui/app.slint#L1802), [`ui/dr-ui/ui/app.slint:2550`](../../ui/dr-ui/ui/app.slint#L2550), [`ui/dr-ui/ui/app.slint:3483`](../../ui/dr-ui/ui/app.slint#L3483), [`ui/dr-ui/ui/app.slint:3529`](../../ui/dr-ui/ui/app.slint#L3529), [`ui/dr-ui/ui/app.slint:527`](../../ui/dr-ui/ui/app.slint#L527), [`ui/dr-ui/ui/app.slint:538`](../../ui/dr-ui/ui/app.slint#L538), [`ui/dr-ui/ui/app.slint:698`](../../ui/dr-ui/ui/app.slint#L698), [`ui/dr-ui/ui/develop.slint:257`](../../ui/dr-ui/ui/develop.slint#L257), [`ui/dr-ui/ui/icons.slint:390`](../../ui/dr-ui/ui/icons.slint#L390), [`ui/dr-ui/ui/library.slint:2026`](../../ui/dr-ui/ui/library.slint#L2026), [`ui/dr-ui/ui/library.slint:2032`](../../ui/dr-ui/ui/library.slint#L2032), [`ui/dr-ui/ui/library.slint:3229`](../../ui/dr-ui/ui/library.slint#L3229), [`ui/dr-ui/ui/library.slint:5167`](../../ui/dr-ui/ui/library.slint#L5167), [`ui/dr-ui/ui/library.slint:5185`](../../ui/dr-ui/ui/library.slint#L5185), [`ui/dr-ui/ui/presets.slint:114`](../../ui/dr-ui/ui/presets.slint#L114), [`ui/dr-ui/ui/presets.slint:155`](../../ui/dr-ui/ui/presets.slint#L155), [`ui/dr-ui/ui/presets.slint:203`](../../ui/dr-ui/ui/presets.slint#L203), [`ui/dr-ui/ui/presets.slint:211`](../../ui/dr-ui/ui/presets.slint#L211), [`ui/dr-ui/ui/presets.slint:21`](../../ui/dr-ui/ui/presets.slint#L21), [`ui/dr-ui/ui/presets.slint:33`](../../ui/dr-ui/ui/presets.slint#L33), [`ui/dr-ui/ui/presets.slint:394`](../../ui/dr-ui/ui/presets.slint#L394), [`ui/dr-ui/ui/presets.slint:432`](../../ui/dr-ui/ui/presets.slint#L432), [`ui/dr-ui/ui/presets.slint:467`](../../ui/dr-ui/ui/presets.slint#L467), [`ui/dr-ui/ui/presets.slint:62`](../../ui/dr-ui/ui/presets.slint#L62), [`ui/dr-ui/ui/presets.slint:7`](../../ui/dr-ui/ui/presets.slint#L7), [`ui/dr-ui/ui/settings.slint:126`](../../ui/dr-ui/ui/settings.slint#L126), [`ui/dr-ui/ui/settings.slint:690`](../../ui/dr-ui/ui/settings.slint#L690), [`ui/dr-ui/ui/toolrail.slint:123`](../../ui/dr-ui/ui/toolrail.slint#L123), [`ui/dr-ui/ui/toolrail.slint:453`](../../ui/dr-ui/ui/toolrail.slint#L453) | +| FR-DEV-6 | [`core/dr-pipeline/src/bundled.rs:1`](../../core/dr-pipeline/src/bundled.rs#L1), [`core/dr-pipeline/src/preset.rs:1343`](../../core/dr-pipeline/src/preset.rs#L1343), [`core/dr-pipeline/src/preset.rs:1353`](../../core/dr-pipeline/src/preset.rs#L1353), [`core/dr-pipeline/src/preset.rs:1362`](../../core/dr-pipeline/src/preset.rs#L1362), [`core/dr-pipeline/src/preset.rs:1378`](../../core/dr-pipeline/src/preset.rs#L1378), [`core/dr-pipeline/src/preset.rs:1390`](../../core/dr-pipeline/src/preset.rs#L1390), [`core/dr-pipeline/src/preset.rs:1406`](../../core/dr-pipeline/src/preset.rs#L1406), [`core/dr-pipeline/src/preset.rs:1432`](../../core/dr-pipeline/src/preset.rs#L1432), [`core/dr-pipeline/src/preset.rs:1454`](../../core/dr-pipeline/src/preset.rs#L1454), [`core/dr-pipeline/src/preset.rs:1468`](../../core/dr-pipeline/src/preset.rs#L1468), [`core/dr-pipeline/src/preset.rs:1486`](../../core/dr-pipeline/src/preset.rs#L1486), [`core/dr-pipeline/src/preset.rs:1670`](../../core/dr-pipeline/src/preset.rs#L1670), [`core/dr-pipeline/src/preset.rs:1685`](../../core/dr-pipeline/src/preset.rs#L1685), [`core/dr-pipeline/src/preset.rs:1`](../../core/dr-pipeline/src/preset.rs#L1), [`core/dr-pipeline/src/preset.rs:501`](../../core/dr-pipeline/src/preset.rs#L501), [`core/dr-pipeline/src/preset.rs:534`](../../core/dr-pipeline/src/preset.rs#L534), [`core/dr-pipeline/src/preset.rs:678`](../../core/dr-pipeline/src/preset.rs#L678), [`core/dr-preset-xmp/src/lib.rs:148`](../../core/dr-preset-xmp/src/lib.rs#L148), [`core/dr-preset-xmp/src/lib.rs:1`](../../core/dr-preset-xmp/src/lib.rs#L1), [`core/dr-preset-xmp/src/lib.rs:355`](../../core/dr-preset-xmp/src/lib.rs#L355), [`core/dr-preset-xmp/src/lib.rs:561`](../../core/dr-preset-xmp/src/lib.rs#L561), [`core/dr-preset-xmp/src/lib.rs:583`](../../core/dr-preset-xmp/src/lib.rs#L583), [`core/dr-preset-xmp/src/lib.rs:729`](../../core/dr-preset-xmp/src/lib.rs#L729), [`core/dr-types/src/settings.rs:456`](../../core/dr-types/src/settings.rs#L456), [`ui/dr-ui/src/derived_sync.rs:1142`](../../ui/dr-ui/src/derived_sync.rs#L1142), [`ui/dr-ui/src/derived_sync.rs:1160`](../../ui/dr-ui/src/derived_sync.rs#L1160), [`ui/dr-ui/src/derived_sync.rs:2134`](../../ui/dr-ui/src/derived_sync.rs#L2134), [`ui/dr-ui/src/derived_sync.rs:2169`](../../ui/dr-ui/src/derived_sync.rs#L2169), [`ui/dr-ui/src/derived_sync.rs:254`](../../ui/dr-ui/src/derived_sync.rs#L254), [`ui/dr-ui/src/derived_sync.rs:99`](../../ui/dr-ui/src/derived_sync.rs#L99), [`ui/dr-ui/src/develop/session.rs:619`](../../ui/dr-ui/src/develop/session.rs#L619), [`ui/dr-ui/src/develop/session.rs:625`](../../ui/dr-ui/src/develop/session.rs#L625), [`ui/dr-ui/src/develop/session.rs:732`](../../ui/dr-ui/src/develop/session.rs#L732), [`ui/dr-ui/src/develop/session.rs:753`](../../ui/dr-ui/src/develop/session.rs#L753), [`ui/dr-ui/src/develop/session.rs:79`](../../ui/dr-ui/src/develop/session.rs#L79), [`ui/dr-ui/src/develop/session.rs:926`](../../ui/dr-ui/src/develop/session.rs#L926), [`ui/dr-ui/src/develop_ui.rs:247`](../../ui/dr-ui/src/develop_ui.rs#L247), [`ui/dr-ui/src/develop_ui.rs:276`](../../ui/dr-ui/src/develop_ui.rs#L276), [`ui/dr-ui/src/export.rs:1322`](../../ui/dr-ui/src/export.rs#L1322), [`ui/dr-ui/src/labels.rs:30`](../../ui/dr-ui/src/labels.rs#L30), [`ui/dr-ui/src/lib.rs:1335`](../../ui/dr-ui/src/lib.rs#L1335), [`ui/dr-ui/src/lib.rs:2662`](../../ui/dr-ui/src/lib.rs#L2662), [`ui/dr-ui/src/lib.rs:337`](../../ui/dr-ui/src/lib.rs#L337), [`ui/dr-ui/src/lib.rs:880`](../../ui/dr-ui/src/lib.rs#L880), [`ui/dr-ui/src/lib.rs:898`](../../ui/dr-ui/src/lib.rs#L898), [`ui/dr-ui/src/library/paths.rs:42`](../../ui/dr-ui/src/library/paths.rs#L42), [`ui/dr-ui/src/library/sidecar.rs:12`](../../ui/dr-ui/src/library/sidecar.rs#L12), [`ui/dr-ui/src/library/sidecar.rs:40`](../../ui/dr-ui/src/library/sidecar.rs#L40), [`ui/dr-ui/src/library/thumbnails_fetch.rs:414`](../../ui/dr-ui/src/library/thumbnails_fetch.rs#L414), [`ui/dr-ui/src/library/thumbnails_fetch.rs:549`](../../ui/dr-ui/src/library/thumbnails_fetch.rs#L549), [`ui/dr-ui/src/library_ui/controller.rs:293`](../../ui/dr-ui/src/library_ui/controller.rs#L293), [`ui/dr-ui/src/library_ui/controller.rs:704`](../../ui/dr-ui/src/library_ui/controller.rs#L704), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:495`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L495), [`ui/dr-ui/src/library_ui/sync.rs:246`](../../ui/dr-ui/src/library_ui/sync.rs#L246), [`ui/dr-ui/src/library_ui/window.rs:835`](../../ui/dr-ui/src/library_ui/window.rs#L835), [`ui/dr-ui/src/preset_store.rs:1`](../../ui/dr-ui/src/preset_store.rs#L1), [`ui/dr-ui/src/presets.rs:1036`](../../ui/dr-ui/src/presets.rs#L1036), [`ui/dr-ui/src/presets.rs:1933`](../../ui/dr-ui/src/presets.rs#L1933), [`ui/dr-ui/src/presets.rs:1`](../../ui/dr-ui/src/presets.rs#L1), [`ui/dr-ui/src/presets.rs:2020`](../../ui/dr-ui/src/presets.rs#L2020), [`ui/dr-ui/src/presets.rs:2043`](../../ui/dr-ui/src/presets.rs#L2043), [`ui/dr-ui/src/presets.rs:707`](../../ui/dr-ui/src/presets.rs#L707), [`ui/dr-ui/src/presets.rs:754`](../../ui/dr-ui/src/presets.rs#L754), [`ui/dr-ui/src/presets.rs:783`](../../ui/dr-ui/src/presets.rs#L783), [`ui/dr-ui/ui/adjust.slint:937`](../../ui/dr-ui/ui/adjust.slint#L937), [`ui/dr-ui/ui/adjust.slint:962`](../../ui/dr-ui/ui/adjust.slint#L962), [`ui/dr-ui/ui/app.slint:1094`](../../ui/dr-ui/ui/app.slint#L1094), [`ui/dr-ui/ui/app.slint:1518`](../../ui/dr-ui/ui/app.slint#L1518), [`ui/dr-ui/ui/app.slint:1802`](../../ui/dr-ui/ui/app.slint#L1802), [`ui/dr-ui/ui/app.slint:2550`](../../ui/dr-ui/ui/app.slint#L2550), [`ui/dr-ui/ui/app.slint:3483`](../../ui/dr-ui/ui/app.slint#L3483), [`ui/dr-ui/ui/app.slint:3529`](../../ui/dr-ui/ui/app.slint#L3529), [`ui/dr-ui/ui/app.slint:527`](../../ui/dr-ui/ui/app.slint#L527), [`ui/dr-ui/ui/app.slint:538`](../../ui/dr-ui/ui/app.slint#L538), [`ui/dr-ui/ui/app.slint:698`](../../ui/dr-ui/ui/app.slint#L698), [`ui/dr-ui/ui/develop.slint:257`](../../ui/dr-ui/ui/develop.slint#L257), [`ui/dr-ui/ui/icons.slint:390`](../../ui/dr-ui/ui/icons.slint#L390), [`ui/dr-ui/ui/library.slint:2026`](../../ui/dr-ui/ui/library.slint#L2026), [`ui/dr-ui/ui/library.slint:2032`](../../ui/dr-ui/ui/library.slint#L2032), [`ui/dr-ui/ui/library.slint:3229`](../../ui/dr-ui/ui/library.slint#L3229), [`ui/dr-ui/ui/library.slint:5167`](../../ui/dr-ui/ui/library.slint#L5167), [`ui/dr-ui/ui/library.slint:5185`](../../ui/dr-ui/ui/library.slint#L5185), [`ui/dr-ui/ui/presets.slint:114`](../../ui/dr-ui/ui/presets.slint#L114), [`ui/dr-ui/ui/presets.slint:155`](../../ui/dr-ui/ui/presets.slint#L155), [`ui/dr-ui/ui/presets.slint:203`](../../ui/dr-ui/ui/presets.slint#L203), [`ui/dr-ui/ui/presets.slint:211`](../../ui/dr-ui/ui/presets.slint#L211), [`ui/dr-ui/ui/presets.slint:21`](../../ui/dr-ui/ui/presets.slint#L21), [`ui/dr-ui/ui/presets.slint:33`](../../ui/dr-ui/ui/presets.slint#L33), [`ui/dr-ui/ui/presets.slint:394`](../../ui/dr-ui/ui/presets.slint#L394), [`ui/dr-ui/ui/presets.slint:432`](../../ui/dr-ui/ui/presets.slint#L432), [`ui/dr-ui/ui/presets.slint:467`](../../ui/dr-ui/ui/presets.slint#L467), [`ui/dr-ui/ui/presets.slint:62`](../../ui/dr-ui/ui/presets.slint#L62), [`ui/dr-ui/ui/presets.slint:7`](../../ui/dr-ui/ui/presets.slint#L7), [`ui/dr-ui/ui/settings.slint:126`](../../ui/dr-ui/ui/settings.slint#L126), [`ui/dr-ui/ui/settings.slint:690`](../../ui/dr-ui/ui/settings.slint#L690), [`ui/dr-ui/ui/toolrail.slint:123`](../../ui/dr-ui/ui/toolrail.slint#L123), [`ui/dr-ui/ui/toolrail.slint:453`](../../ui/dr-ui/ui/toolrail.slint#L453) | | FR-DEV-7 | [`core/dr-pipeline/src/history.rs:214`](../../core/dr-pipeline/src/history.rs#L214), [`core/dr-pipeline/src/history.rs:499`](../../core/dr-pipeline/src/history.rs#L499), [`core/dr-pipeline/src/history.rs:526`](../../core/dr-pipeline/src/history.rs#L526), [`ui/dr-ui/src/develop/history.rs:107`](../../ui/dr-ui/src/develop/history.rs#L107), [`ui/dr-ui/src/develop/history.rs:124`](../../ui/dr-ui/src/develop/history.rs#L124), [`ui/dr-ui/src/develop/history.rs:206`](../../ui/dr-ui/src/develop/history.rs#L206), [`ui/dr-ui/src/develop/history.rs:238`](../../ui/dr-ui/src/develop/history.rs#L238), [`ui/dr-ui/src/develop/history.rs:359`](../../ui/dr-ui/src/develop/history.rs#L359), [`ui/dr-ui/src/develop/render.rs:1115`](../../ui/dr-ui/src/develop/render.rs#L1115), [`ui/dr-ui/src/develop/render.rs:586`](../../ui/dr-ui/src/develop/render.rs#L586), [`ui/dr-ui/src/develop/render.rs:627`](../../ui/dr-ui/src/develop/render.rs#L627), [`ui/dr-ui/src/develop/session.rs:116`](../../ui/dr-ui/src/develop/session.rs#L116), [`ui/dr-ui/src/develop_ui.rs:793`](../../ui/dr-ui/src/develop_ui.rs#L793), [`ui/dr-ui/src/develop_ui.rs:852`](../../ui/dr-ui/src/develop_ui.rs#L852), [`ui/dr-ui/src/develop_ui.rs:924`](../../ui/dr-ui/src/develop_ui.rs#L924), [`ui/dr-ui/src/lib.rs:2251`](../../ui/dr-ui/src/lib.rs#L2251), [`ui/dr-ui/src/lib.rs:2337`](../../ui/dr-ui/src/lib.rs#L2337), [`ui/dr-ui/src/lib.rs:2351`](../../ui/dr-ui/src/lib.rs#L2351), [`ui/dr-ui/src/lib.rs:464`](../../ui/dr-ui/src/lib.rs#L464), [`ui/dr-ui/ui/app.slint:224`](../../ui/dr-ui/ui/app.slint#L224), [`ui/dr-ui/ui/app.slint:2417`](../../ui/dr-ui/ui/app.slint#L2417), [`ui/dr-ui/ui/app.slint:2463`](../../ui/dr-ui/ui/app.slint#L2463), [`ui/dr-ui/ui/app.slint:3027`](../../ui/dr-ui/ui/app.slint#L3027), [`ui/dr-ui/ui/history.slint:103`](../../ui/dr-ui/ui/history.slint#L103), [`ui/dr-ui/ui/history.slint:1`](../../ui/dr-ui/ui/history.slint#L1), [`ui/dr-ui/ui/history.slint:227`](../../ui/dr-ui/ui/history.slint#L227), [`ui/dr-ui/ui/history.slint:53`](../../ui/dr-ui/ui/history.slint#L53), [`ui/dr-ui/ui/widgets.slint:72`](../../ui/dr-ui/ui/widgets.slint#L72) | | FR-DEV-8 | [`core/dr-gpu/src/detail.rs:397`](../../core/dr-gpu/src/detail.rs#L397), [`core/dr-gpu/src/detail.rs:609`](../../core/dr-gpu/src/detail.rs#L609), [`core/dr-gpu/tests/detail_instances.rs:1`](../../core/dr-gpu/tests/detail_instances.rs#L1), [`core/dr-gpu/tests/spot_removal.rs:1`](../../core/dr-gpu/tests/spot_removal.rs#L1), [`core/dr-pipeline/src/detail.rs:449`](../../core/dr-pipeline/src/detail.rs#L449), [`core/dr-pipeline/src/detail.rs:491`](../../core/dr-pipeline/src/detail.rs#L491), [`core/dr-pipeline/src/detail.rs:526`](../../core/dr-pipeline/src/detail.rs#L526), [`core/dr-pipeline/src/detail.rs:620`](../../core/dr-pipeline/src/detail.rs#L620), [`core/dr-pipeline/src/graph.rs:122`](../../core/dr-pipeline/src/graph.rs#L122), [`core/dr-pipeline/src/graph.rs:1243`](../../core/dr-pipeline/src/graph.rs#L1243), [`core/dr-pipeline/src/graph.rs:269`](../../core/dr-pipeline/src/graph.rs#L269), [`core/dr-pipeline/src/operation.rs:377`](../../core/dr-pipeline/src/operation.rs#L377), [`core/dr-pipeline/src/operation.rs:869`](../../core/dr-pipeline/src/operation.rs#L869), [`core/dr-pipeline/src/sidecar.rs:1104`](../../core/dr-pipeline/src/sidecar.rs#L1104), [`core/dr-pipeline/src/sidecar.rs:1158`](../../core/dr-pipeline/src/sidecar.rs#L1158), [`core/dr-pipeline/src/sidecar.rs:1188`](../../core/dr-pipeline/src/sidecar.rs#L1188), [`core/dr-pipeline/src/sidecar.rs:233`](../../core/dr-pipeline/src/sidecar.rs#L233), [`core/dr-pipeline/src/sidecar.rs:410`](../../core/dr-pipeline/src/sidecar.rs#L410), [`core/dr-pipeline/src/sidecar.rs:910`](../../core/dr-pipeline/src/sidecar.rs#L910), [`core/dr-pipeline/src/spot.rs:115`](../../core/dr-pipeline/src/spot.rs#L115), [`core/dr-pipeline/src/spot.rs:151`](../../core/dr-pipeline/src/spot.rs#L151), [`core/dr-pipeline/src/spot.rs:1`](../../core/dr-pipeline/src/spot.rs#L1), [`core/dr-pipeline/src/spot.rs:207`](../../core/dr-pipeline/src/spot.rs#L207), [`core/dr-pipeline/src/spot.rs:387`](../../core/dr-pipeline/src/spot.rs#L387), [`core/dr-pipeline/src/spot.rs:472`](../../core/dr-pipeline/src/spot.rs#L472), [`core/dr-pipeline/src/spot.rs:582`](../../core/dr-pipeline/src/spot.rs#L582), [`core/dr-pipeline/src/spot.rs:673`](../../core/dr-pipeline/src/spot.rs#L673), [`core/dr-pipeline/src/state.rs:103`](../../core/dr-pipeline/src/state.rs#L103), [`core/dr-pipeline/tests/spot_sidecar.rs:1`](../../core/dr-pipeline/tests/spot_sidecar.rs#L1), [`core/dr-pipeline/tests/spots.rs:1`](../../core/dr-pipeline/tests/spots.rs#L1), [`ui/dr-ui/src/develop/repairs.rs:114`](../../ui/dr-ui/src/develop/repairs.rs#L114), [`ui/dr-ui/src/develop/repairs.rs:11`](../../ui/dr-ui/src/develop/repairs.rs#L11), [`ui/dr-ui/src/develop/repairs.rs:197`](../../ui/dr-ui/src/develop/repairs.rs#L197), [`ui/dr-ui/src/develop/repairs.rs:211`](../../ui/dr-ui/src/develop/repairs.rs#L211), [`ui/dr-ui/src/develop/repairs.rs:49`](../../ui/dr-ui/src/develop/repairs.rs#L49), [`ui/dr-ui/src/develop/session.rs:244`](../../ui/dr-ui/src/develop/session.rs#L244), [`ui/dr-ui/src/develop_ui.rs:1081`](../../ui/dr-ui/src/develop_ui.rs#L1081), [`ui/dr-ui/src/labels.rs:83`](../../ui/dr-ui/src/labels.rs#L83), [`ui/dr-ui/src/lib.rs:2295`](../../ui/dr-ui/src/lib.rs#L2295), [`ui/dr-ui/src/lib.rs:507`](../../ui/dr-ui/src/lib.rs#L507), [`ui/dr-ui/src/spots_ui.rs:19`](../../ui/dr-ui/src/spots_ui.rs#L19), [`ui/dr-ui/src/spots_ui.rs:1`](../../ui/dr-ui/src/spots_ui.rs#L1), [`ui/dr-ui/src/spots_ui.rs:265`](../../ui/dr-ui/src/spots_ui.rs#L265), [`ui/dr-ui/ui/app.slint:2270`](../../ui/dr-ui/ui/app.slint#L2270), [`ui/dr-ui/ui/app.slint:2513`](../../ui/dr-ui/ui/app.slint#L2513), [`ui/dr-ui/ui/app.slint:3376`](../../ui/dr-ui/ui/app.slint#L3376), [`ui/dr-ui/ui/icons.slint:378`](../../ui/dr-ui/ui/icons.slint#L378), [`ui/dr-ui/ui/session.slint:102`](../../ui/dr-ui/ui/session.slint#L102), [`ui/dr-ui/ui/session.slint:52`](../../ui/dr-ui/ui/session.slint#L52), [`ui/dr-ui/ui/spots.slint:191`](../../ui/dr-ui/ui/spots.slint#L191), [`ui/dr-ui/ui/spots.slint:49`](../../ui/dr-ui/ui/spots.slint#L49), [`ui/dr-ui/ui/spots.slint:6`](../../ui/dr-ui/ui/spots.slint#L6), [`ui/dr-ui/ui/spots.slint:91`](../../ui/dr-ui/ui/spots.slint#L91) | | FR-DSP-1 | [`core/dr-gpu/src/adjust.rs:1107`](../../core/dr-gpu/src/adjust.rs#L1107), [`core/dr-gpu/src/adjust.rs:2883`](../../core/dr-gpu/src/adjust.rs#L2883), [`core/dr-gpu/src/adjust.rs:2960`](../../core/dr-gpu/src/adjust.rs#L2960), [`core/dr-gpu/src/adjust.rs:3045`](../../core/dr-gpu/src/adjust.rs#L3045), [`core/dr-gpu/src/adjust.rs:44`](../../core/dr-gpu/src/adjust.rs#L44), [`core/dr-gpu/src/lib.rs:176`](../../core/dr-gpu/src/lib.rs#L176), [`core/dr-gpu/src/lib.rs:196`](../../core/dr-gpu/src/lib.rs#L196), [`core/dr-gpu/src/lib.rs:74`](../../core/dr-gpu/src/lib.rs#L74), [`core/dr-gpu/src/lib.rs:97`](../../core/dr-gpu/src/lib.rs#L97), [`core/dr-gpu/tests/capture_sharpen.rs:199`](../../core/dr-gpu/tests/capture_sharpen.rs#L199), [`core/dr-gpu/tests/detail_stage.rs:327`](../../core/dr-gpu/tests/detail_stage.rs#L327), [`core/dr-gpu/tests/local_contrast.rs:263`](../../core/dr-gpu/tests/local_contrast.rs#L263), [`core/dr-gpu/tests/noise_reduction.rs:377`](../../core/dr-gpu/tests/noise_reduction.rs#L377), [`core/dr-pipeline/src/detail.rs:142`](../../core/dr-pipeline/src/detail.rs#L142), [`core/dr-pipeline/src/detail.rs:231`](../../core/dr-pipeline/src/detail.rs#L231), [`core/dr-pipeline/src/detail.rs:592`](../../core/dr-pipeline/src/detail.rs#L592), [`core/dr-pipeline/src/detail.rs:980`](../../core/dr-pipeline/src/detail.rs#L980), [`core/dr-pipeline/src/graph.rs:1084`](../../core/dr-pipeline/src/graph.rs#L1084), [`core/dr-pipeline/src/graph.rs:1114`](../../core/dr-pipeline/src/graph.rs#L1114), [`core/dr-pipeline/src/ops/capture_sharpen.rs:1`](../../core/dr-pipeline/src/ops/capture_sharpen.rs#L1), [`core/dr-pipeline/src/ops/capture_sharpen.rs:628`](../../core/dr-pipeline/src/ops/capture_sharpen.rs#L628), [`core/dr-pipeline/src/ops/dehaze.rs:1`](../../core/dr-pipeline/src/ops/dehaze.rs#L1), [`core/dr-pipeline/src/ops/dehaze.rs:564`](../../core/dr-pipeline/src/ops/dehaze.rs#L564), [`core/dr-pipeline/src/ops/local_contrast.rs:1`](../../core/dr-pipeline/src/ops/local_contrast.rs#L1), [`core/dr-pipeline/src/ops/local_contrast.rs:970`](../../core/dr-pipeline/src/ops/local_contrast.rs#L970), [`core/dr-pipeline/src/ops/noise_reduction.rs:694`](../../core/dr-pipeline/src/ops/noise_reduction.rs#L694), [`core/dr-pipeline/src/spot.rs:673`](../../core/dr-pipeline/src/spot.rs#L673), [`ui/dr-ui/src/develop/framing.rs:640`](../../ui/dr-ui/src/develop/framing.rs#L640), [`ui/dr-ui/src/develop/mod.rs:58`](../../ui/dr-ui/src/develop/mod.rs#L58), [`ui/dr-ui/src/develop/render.rs:1172`](../../ui/dr-ui/src/develop/render.rs#L1172), [`ui/dr-ui/src/develop/render.rs:1206`](../../ui/dr-ui/src/develop/render.rs#L1206), [`ui/dr-ui/src/develop/render.rs:1344`](../../ui/dr-ui/src/develop/render.rs#L1344), [`ui/dr-ui/src/develop/render.rs:315`](../../ui/dr-ui/src/develop/render.rs#L315), [`ui/dr-ui/src/develop/render.rs:97`](../../ui/dr-ui/src/develop/render.rs#L97), [`ui/dr-ui/src/lib.rs:1137`](../../ui/dr-ui/src/lib.rs#L1137), [`ui/dr-ui/src/lib.rs:118`](../../ui/dr-ui/src/lib.rs#L118) | @@ -99,14 +99,14 @@ _None._ | FR-DSP-7 | [`core/dr-gpu/src/histogram.rs:147`](../../core/dr-gpu/src/histogram.rs#L147), [`core/dr-gpu/src/histogram.rs:1`](../../core/dr-gpu/src/histogram.rs#L1), [`core/dr-gpu/src/histogram.rs:281`](../../core/dr-gpu/src/histogram.rs#L281), [`core/dr-gpu/src/histogram.rs:50`](../../core/dr-gpu/src/histogram.rs#L50), [`core/dr-gpu/src/shaders/histogram.wgsl:1`](../../core/dr-gpu/src/shaders/histogram.wgsl#L1), [`ui/dr-ui/src/develop/render.rs:1541`](../../ui/dr-ui/src/develop/render.rs#L1541), [`ui/dr-ui/src/develop/render.rs:1573`](../../ui/dr-ui/src/develop/render.rs#L1573), [`ui/dr-ui/src/develop/render.rs:378`](../../ui/dr-ui/src/develop/render.rs#L378), [`ui/dr-ui/src/develop/session.rs:144`](../../ui/dr-ui/src/develop/session.rs#L144), [`ui/dr-ui/src/histogram.rs:1`](../../ui/dr-ui/src/histogram.rs#L1), [`ui/dr-ui/src/lib.rs:2399`](../../ui/dr-ui/src/lib.rs#L2399), [`ui/dr-ui/src/lib.rs:484`](../../ui/dr-ui/src/lib.rs#L484), [`ui/dr-ui/ui/histogram.slint:191`](../../ui/dr-ui/ui/histogram.slint#L191), [`ui/dr-ui/ui/histogram.slint:1`](../../ui/dr-ui/ui/histogram.slint#L1) | | FR-DSP-8 | [`platform/dr-plat/src/display.rs:1`](../../platform/dr-plat/src/display.rs#L1), [`platform/dr-plat/src/display/icc.rs:1`](../../platform/dr-plat/src/display/icc.rs#L1), [`platform/dr-plat/src/display/wayland.rs:1`](../../platform/dr-plat/src/display/wayland.rs#L1), [`platform/dr-plat/src/display/x11.rs:1`](../../platform/dr-plat/src/display/x11.rs#L1), [`ui/dr-ui/src/develop/render.rs:1305`](../../ui/dr-ui/src/develop/render.rs#L1305), [`ui/dr-ui/src/develop/render.rs:1752`](../../ui/dr-ui/src/develop/render.rs#L1752), [`ui/dr-ui/src/develop/render.rs:1798`](../../ui/dr-ui/src/develop/render.rs#L1798), [`ui/dr-ui/src/develop/render.rs:1817`](../../ui/dr-ui/src/develop/render.rs#L1817), [`ui/dr-ui/src/develop/render.rs:291`](../../ui/dr-ui/src/develop/render.rs#L291), [`ui/dr-ui/src/develop/render.rs:350`](../../ui/dr-ui/src/develop/render.rs#L350), [`ui/dr-ui/src/develop/render.rs:65`](../../ui/dr-ui/src/develop/render.rs#L65), [`ui/dr-ui/src/develop/session.rs:297`](../../ui/dr-ui/src/develop/session.rs#L297), [`ui/dr-ui/src/display_ui.rs:192`](../../ui/dr-ui/src/display_ui.rs#L192), [`ui/dr-ui/src/display_ui.rs:1`](../../ui/dr-ui/src/display_ui.rs#L1), [`ui/dr-ui/src/display_ui.rs:325`](../../ui/dr-ui/src/display_ui.rs#L325), [`ui/dr-ui/src/display_ui.rs:346`](../../ui/dr-ui/src/display_ui.rs#L346), [`ui/dr-ui/src/display_ui.rs:379`](../../ui/dr-ui/src/display_ui.rs#L379), [`ui/dr-ui/src/lib.rs:1357`](../../ui/dr-ui/src/lib.rs#L1357), [`ui/dr-ui/src/lib.rs:2227`](../../ui/dr-ui/src/lib.rs#L2227), [`ui/dr-ui/src/lib.rs:3092`](../../ui/dr-ui/src/lib.rs#L3092), [`ui/dr-ui/src/lib.rs:3115`](../../ui/dr-ui/src/lib.rs#L3115), [`ui/dr-ui/ui/app.slint:130`](../../ui/dr-ui/ui/app.slint#L130), [`ui/dr-ui/ui/app.slint:1849`](../../ui/dr-ui/ui/app.slint#L1849), [`ui/dr-ui/ui/settings.slint:176`](../../ui/dr-ui/ui/settings.slint#L176), [`ui/dr-ui/ui/settings.slint:864`](../../ui/dr-ui/ui/settings.slint#L864) | | FR-EXP-1 | [`core/dr-export/src/encode.rs:1`](../../core/dr-export/src/encode.rs#L1), [`core/dr-export/src/lib.rs:1`](../../core/dr-export/src/lib.rs#L1), [`core/dr-types/src/settings.rs:1`](../../core/dr-types/src/settings.rs#L1), [`ui/dr-ui/src/settings_ui.rs:1`](../../ui/dr-ui/src/settings_ui.rs#L1) | -| FR-EXP-10 | [`core/dr-catalog/src/albums.rs:1`](../../core/dr-catalog/src/albums.rs#L1), [`core/dr-types/src/settings.rs:731`](../../core/dr-types/src/settings.rs#L731), [`ui/dr-ui/src/albums_ui.rs:1`](../../ui/dr-ui/src/albums_ui.rs#L1), [`ui/dr-ui/src/collections_ui/controller.rs:129`](../../ui/dr-ui/src/collections_ui/controller.rs#L129), [`ui/dr-ui/src/collections_ui/controller.rs:432`](../../ui/dr-ui/src/collections_ui/controller.rs#L432), [`ui/dr-ui/src/develop_ui.rs:353`](../../ui/dr-ui/src/develop_ui.rs#L353), [`ui/dr-ui/src/export.rs:100`](../../ui/dr-ui/src/export.rs#L100), [`ui/dr-ui/src/export.rs:240`](../../ui/dr-ui/src/export.rs#L240), [`ui/dr-ui/src/export.rs:679`](../../ui/dr-ui/src/export.rs#L679), [`ui/dr-ui/src/export.rs:949`](../../ui/dr-ui/src/export.rs#L949), [`ui/dr-ui/src/lib.rs:1737`](../../ui/dr-ui/src/lib.rs#L1737), [`ui/dr-ui/src/library_ui/controller.rs:1001`](../../ui/dr-ui/src/library_ui/controller.rs#L1001), [`ui/dr-ui/src/library_ui/controller.rs:224`](../../ui/dr-ui/src/library_ui/controller.rs#L224), [`ui/dr-ui/src/library_ui/controller.rs:961`](../../ui/dr-ui/src/library_ui/controller.rs#L961), [`ui/dr-ui/src/library_ui/window.rs:400`](../../ui/dr-ui/src/library_ui/window.rs#L400), [`ui/dr-ui/src/saf.rs:1`](../../ui/dr-ui/src/saf.rs#L1), [`ui/dr-ui/ui/albums.slint:1`](../../ui/dr-ui/ui/albums.slint#L1), [`ui/dr-ui/ui/app.slint:3610`](../../ui/dr-ui/ui/app.slint#L3610), [`ui/dr-ui/ui/collections.slint:789`](../../ui/dr-ui/ui/collections.slint#L789), [`ui/dr-ui/ui/export.slint:204`](../../ui/dr-ui/ui/export.slint#L204) | -| FR-EXP-2 | [`core/dr-export/src/encode.rs:1`](../../core/dr-export/src/encode.rs#L1), [`core/dr-export/src/error.rs:26`](../../core/dr-export/src/error.rs#L26), [`core/dr-export/src/icc.rs:1`](../../core/dr-export/src/icc.rs#L1), [`core/dr-export/src/lib.rs:157`](../../core/dr-export/src/lib.rs#L157), [`core/dr-export/src/lib.rs:1`](../../core/dr-export/src/lib.rs#L1), [`core/dr-export/src/lib.rs:57`](../../core/dr-export/src/lib.rs#L57), [`core/dr-gpu/src/adjust.rs:3318`](../../core/dr-gpu/src/adjust.rs#L3318), [`core/dr-pipeline/src/graph.rs:997`](../../core/dr-pipeline/src/graph.rs#L997), [`core/dr-pipeline/src/operation.rs:676`](../../core/dr-pipeline/src/operation.rs#L676), [`core/dr-types/src/colour.rs:1`](../../core/dr-types/src/colour.rs#L1), [`core/dr-types/src/settings.rs:1`](../../core/dr-types/src/settings.rs#L1), [`core/dr-types/src/settings.rs:976`](../../core/dr-types/src/settings.rs#L976), [`ui/dr-ui/src/develop/render.rs:1817`](../../ui/dr-ui/src/develop/render.rs#L1817), [`ui/dr-ui/src/settings_ui.rs:1`](../../ui/dr-ui/src/settings_ui.rs#L1) | -| FR-EXP-3 | [`core/dr-export/src/lib.rs:186`](../../core/dr-export/src/lib.rs#L186), [`core/dr-export/src/lib.rs:1`](../../core/dr-export/src/lib.rs#L1), [`core/dr-export/src/size.rs:136`](../../core/dr-export/src/size.rs#L136), [`core/dr-export/src/size.rs:1`](../../core/dr-export/src/size.rs#L1), [`core/dr-export/src/size.rs:25`](../../core/dr-export/src/size.rs#L25), [`core/dr-export/src/size.rs:84`](../../core/dr-export/src/size.rs#L84), [`core/dr-types/src/settings.rs:1021`](../../core/dr-types/src/settings.rs#L1021), [`core/dr-types/src/settings.rs:1029`](../../core/dr-types/src/settings.rs#L1029), [`core/dr-types/src/settings.rs:1045`](../../core/dr-types/src/settings.rs#L1045), [`core/dr-types/src/settings.rs:1114`](../../core/dr-types/src/settings.rs#L1114), [`core/dr-types/src/settings.rs:1142`](../../core/dr-types/src/settings.rs#L1142), [`core/dr-types/src/settings.rs:1153`](../../core/dr-types/src/settings.rs#L1153), [`core/dr-types/src/settings.rs:1159`](../../core/dr-types/src/settings.rs#L1159), [`core/dr-types/src/settings.rs:1`](../../core/dr-types/src/settings.rs#L1), [`ui/dr-ui/src/settings_ui.rs:1`](../../ui/dr-ui/src/settings_ui.rs#L1), [`ui/dr-ui/src/settings_ui.rs:256`](../../ui/dr-ui/src/settings_ui.rs#L256), [`ui/dr-ui/src/settings_ui.rs:269`](../../ui/dr-ui/src/settings_ui.rs#L269), [`ui/dr-ui/src/settings_ui.rs:288`](../../ui/dr-ui/src/settings_ui.rs#L288), [`ui/dr-ui/src/settings_ui.rs:701`](../../ui/dr-ui/src/settings_ui.rs#L701), [`ui/dr-ui/ui/export.slint:138`](../../ui/dr-ui/ui/export.slint#L138), [`ui/dr-ui/ui/export.slint:148`](../../ui/dr-ui/ui/export.slint#L148) | +| FR-EXP-10 | [`core/dr-catalog/src/albums.rs:1`](../../core/dr-catalog/src/albums.rs#L1), [`core/dr-types/src/settings.rs:744`](../../core/dr-types/src/settings.rs#L744), [`ui/dr-ui/src/albums_ui.rs:1`](../../ui/dr-ui/src/albums_ui.rs#L1), [`ui/dr-ui/src/collections_ui/controller.rs:129`](../../ui/dr-ui/src/collections_ui/controller.rs#L129), [`ui/dr-ui/src/collections_ui/controller.rs:432`](../../ui/dr-ui/src/collections_ui/controller.rs#L432), [`ui/dr-ui/src/develop_ui.rs:353`](../../ui/dr-ui/src/develop_ui.rs#L353), [`ui/dr-ui/src/export.rs:100`](../../ui/dr-ui/src/export.rs#L100), [`ui/dr-ui/src/export.rs:240`](../../ui/dr-ui/src/export.rs#L240), [`ui/dr-ui/src/export.rs:679`](../../ui/dr-ui/src/export.rs#L679), [`ui/dr-ui/src/export.rs:949`](../../ui/dr-ui/src/export.rs#L949), [`ui/dr-ui/src/lib.rs:1737`](../../ui/dr-ui/src/lib.rs#L1737), [`ui/dr-ui/src/library_ui/controller.rs:1001`](../../ui/dr-ui/src/library_ui/controller.rs#L1001), [`ui/dr-ui/src/library_ui/controller.rs:224`](../../ui/dr-ui/src/library_ui/controller.rs#L224), [`ui/dr-ui/src/library_ui/controller.rs:961`](../../ui/dr-ui/src/library_ui/controller.rs#L961), [`ui/dr-ui/src/library_ui/window.rs:400`](../../ui/dr-ui/src/library_ui/window.rs#L400), [`ui/dr-ui/src/saf.rs:1`](../../ui/dr-ui/src/saf.rs#L1), [`ui/dr-ui/ui/albums.slint:1`](../../ui/dr-ui/ui/albums.slint#L1), [`ui/dr-ui/ui/app.slint:3610`](../../ui/dr-ui/ui/app.slint#L3610), [`ui/dr-ui/ui/collections.slint:789`](../../ui/dr-ui/ui/collections.slint#L789), [`ui/dr-ui/ui/export.slint:204`](../../ui/dr-ui/ui/export.slint#L204) | +| FR-EXP-2 | [`core/dr-export/src/encode.rs:1`](../../core/dr-export/src/encode.rs#L1), [`core/dr-export/src/error.rs:26`](../../core/dr-export/src/error.rs#L26), [`core/dr-export/src/icc.rs:1`](../../core/dr-export/src/icc.rs#L1), [`core/dr-export/src/lib.rs:157`](../../core/dr-export/src/lib.rs#L157), [`core/dr-export/src/lib.rs:1`](../../core/dr-export/src/lib.rs#L1), [`core/dr-export/src/lib.rs:57`](../../core/dr-export/src/lib.rs#L57), [`core/dr-gpu/src/adjust.rs:3318`](../../core/dr-gpu/src/adjust.rs#L3318), [`core/dr-pipeline/src/graph.rs:997`](../../core/dr-pipeline/src/graph.rs#L997), [`core/dr-pipeline/src/operation.rs:676`](../../core/dr-pipeline/src/operation.rs#L676), [`core/dr-types/src/colour.rs:1`](../../core/dr-types/src/colour.rs#L1), [`core/dr-types/src/settings.rs:1`](../../core/dr-types/src/settings.rs#L1), [`core/dr-types/src/settings.rs:989`](../../core/dr-types/src/settings.rs#L989), [`ui/dr-ui/src/develop/render.rs:1817`](../../ui/dr-ui/src/develop/render.rs#L1817), [`ui/dr-ui/src/settings_ui.rs:1`](../../ui/dr-ui/src/settings_ui.rs#L1) | +| FR-EXP-3 | [`core/dr-export/src/lib.rs:186`](../../core/dr-export/src/lib.rs#L186), [`core/dr-export/src/lib.rs:1`](../../core/dr-export/src/lib.rs#L1), [`core/dr-export/src/size.rs:136`](../../core/dr-export/src/size.rs#L136), [`core/dr-export/src/size.rs:1`](../../core/dr-export/src/size.rs#L1), [`core/dr-export/src/size.rs:25`](../../core/dr-export/src/size.rs#L25), [`core/dr-export/src/size.rs:84`](../../core/dr-export/src/size.rs#L84), [`core/dr-types/src/settings.rs:1034`](../../core/dr-types/src/settings.rs#L1034), [`core/dr-types/src/settings.rs:1042`](../../core/dr-types/src/settings.rs#L1042), [`core/dr-types/src/settings.rs:1058`](../../core/dr-types/src/settings.rs#L1058), [`core/dr-types/src/settings.rs:1127`](../../core/dr-types/src/settings.rs#L1127), [`core/dr-types/src/settings.rs:1155`](../../core/dr-types/src/settings.rs#L1155), [`core/dr-types/src/settings.rs:1166`](../../core/dr-types/src/settings.rs#L1166), [`core/dr-types/src/settings.rs:1172`](../../core/dr-types/src/settings.rs#L1172), [`core/dr-types/src/settings.rs:1`](../../core/dr-types/src/settings.rs#L1), [`ui/dr-ui/src/settings_ui.rs:1`](../../ui/dr-ui/src/settings_ui.rs#L1), [`ui/dr-ui/src/settings_ui.rs:256`](../../ui/dr-ui/src/settings_ui.rs#L256), [`ui/dr-ui/src/settings_ui.rs:269`](../../ui/dr-ui/src/settings_ui.rs#L269), [`ui/dr-ui/src/settings_ui.rs:288`](../../ui/dr-ui/src/settings_ui.rs#L288), [`ui/dr-ui/src/settings_ui.rs:701`](../../ui/dr-ui/src/settings_ui.rs#L701), [`ui/dr-ui/ui/export.slint:138`](../../ui/dr-ui/ui/export.slint#L138), [`ui/dr-ui/ui/export.slint:148`](../../ui/dr-ui/ui/export.slint#L148) | | FR-EXP-4 | [`core/dr-export/src/lib.rs:1`](../../core/dr-export/src/lib.rs#L1), [`core/dr-export/src/sharpen.rs:1`](../../core/dr-export/src/sharpen.rs#L1), [`core/dr-export/src/size.rs:1`](../../core/dr-export/src/size.rs#L1), [`ui/dr-ui/src/settings_ui.rs:1`](../../ui/dr-ui/src/settings_ui.rs#L1) | | FR-EXP-5 | [`ui/dr-ui/src/settings_store.rs:1`](../../ui/dr-ui/src/settings_store.rs#L1) | | FR-EXP-6 | [`core/dr-catalog/src/albums.rs:1`](../../core/dr-catalog/src/albums.rs#L1), [`core/dr-export/src/lib.rs:1`](../../core/dr-export/src/lib.rs#L1), [`core/dr-export/src/name.rs:1`](../../core/dr-export/src/name.rs#L1), [`core/dr-types/src/settings.rs:1`](../../core/dr-types/src/settings.rs#L1), [`ui/dr-ui/src/albums_ui.rs:1`](../../ui/dr-ui/src/albums_ui.rs#L1), [`ui/dr-ui/src/export.rs:1`](../../ui/dr-ui/src/export.rs#L1), [`ui/dr-ui/src/export.rs:621`](../../ui/dr-ui/src/export.rs#L621), [`ui/dr-ui/src/export.rs:959`](../../ui/dr-ui/src/export.rs#L959), [`ui/dr-ui/src/lib.rs:668`](../../ui/dr-ui/src/lib.rs#L668), [`ui/dr-ui/src/settings_ui.rs:1`](../../ui/dr-ui/src/settings_ui.rs#L1), [`ui/dr-ui/ui/albums.slint:1`](../../ui/dr-ui/ui/albums.slint#L1) | | FR-EXP-7 | [`ui/dr-ui/src/activity.rs:83`](../../ui/dr-ui/src/activity.rs#L83), [`ui/dr-ui/src/develop_ui.rs:457`](../../ui/dr-ui/src/develop_ui.rs#L457), [`ui/dr-ui/src/export.rs:1518`](../../ui/dr-ui/src/export.rs#L1518), [`ui/dr-ui/src/export.rs:1`](../../ui/dr-ui/src/export.rs#L1), [`ui/dr-ui/src/lib.rs:295`](../../ui/dr-ui/src/lib.rs#L295), [`ui/dr-ui/src/lib.rs:668`](../../ui/dr-ui/src/lib.rs#L668), [`ui/dr-ui/src/lib.rs:714`](../../ui/dr-ui/src/lib.rs#L714), [`ui/dr-ui/src/lib.rs:744`](../../ui/dr-ui/src/lib.rs#L744), [`ui/dr-ui/src/library_ui/controller.rs:1126`](../../ui/dr-ui/src/library_ui/controller.rs#L1126), [`ui/dr-ui/src/library_ui/controller.rs:1138`](../../ui/dr-ui/src/library_ui/controller.rs#L1138), [`ui/dr-ui/src/library_ui/controller.rs:1150`](../../ui/dr-ui/src/library_ui/controller.rs#L1150), [`ui/dr-ui/src/library_ui/controller.rs:795`](../../ui/dr-ui/src/library_ui/controller.rs#L795), [`ui/dr-ui/src/library_ui/controller.rs:895`](../../ui/dr-ui/src/library_ui/controller.rs#L895), [`ui/dr-ui/src/library_ui/controller.rs:952`](../../ui/dr-ui/src/library_ui/controller.rs#L952), [`ui/dr-ui/src/library_ui/sync.rs:101`](../../ui/dr-ui/src/library_ui/sync.rs#L101), [`ui/dr-ui/src/settings_ui.rs:394`](../../ui/dr-ui/src/settings_ui.rs#L394), [`ui/dr-ui/ui/app.slint:1094`](../../ui/dr-ui/ui/app.slint#L1094), [`ui/dr-ui/ui/app.slint:1528`](../../ui/dr-ui/ui/app.slint#L1528), [`ui/dr-ui/ui/app.slint:2606`](../../ui/dr-ui/ui/app.slint#L2606), [`ui/dr-ui/ui/app.slint:3510`](../../ui/dr-ui/ui/app.slint#L3510), [`ui/dr-ui/ui/app.slint:691`](../../ui/dr-ui/ui/app.slint#L691), [`ui/dr-ui/ui/app.slint:708`](../../ui/dr-ui/ui/app.slint#L708), [`ui/dr-ui/ui/app.slint:944`](../../ui/dr-ui/ui/app.slint#L944), [`ui/dr-ui/ui/export.slint:1`](../../ui/dr-ui/ui/export.slint#L1), [`ui/dr-ui/ui/export.slint:246`](../../ui/dr-ui/ui/export.slint#L246), [`ui/dr-ui/ui/library.slint:2035`](../../ui/dr-ui/ui/library.slint#L2035), [`ui/dr-ui/ui/library.slint:2044`](../../ui/dr-ui/ui/library.slint#L2044), [`ui/dr-ui/ui/library.slint:3210`](../../ui/dr-ui/ui/library.slint#L3210), [`ui/dr-ui/ui/library.slint:5240`](../../ui/dr-ui/ui/library.slint#L5240) | -| FR-EXP-8 | [`core/dr-decode/src/lib.rs:373`](../../core/dr-decode/src/lib.rs#L373), [`core/dr-decode/src/lib.rs:397`](../../core/dr-decode/src/lib.rs#L397), [`core/dr-decode/src/lib.rs:411`](../../core/dr-decode/src/lib.rs#L411), [`core/dr-decode/src/lib.rs:76`](../../core/dr-decode/src/lib.rs#L76), [`core/dr-decode/src/lib.rs:84`](../../core/dr-decode/src/lib.rs#L84), [`core/dr-decode/src/lib.rs:87`](../../core/dr-decode/src/lib.rs#L87), [`core/dr-decode/src/locate.rs:1233`](../../core/dr-decode/src/locate.rs#L1233), [`core/dr-decode/src/locate.rs:1292`](../../core/dr-decode/src/locate.rs#L1292), [`core/dr-decode/src/locate.rs:359`](../../core/dr-decode/src/locate.rs#L359), [`core/dr-decode/src/locate.rs:556`](../../core/dr-decode/src/locate.rs#L556), [`core/dr-decode/src/locate.rs:640`](../../core/dr-decode/src/locate.rs#L640), [`core/dr-decode/src/locate.rs:653`](../../core/dr-decode/src/locate.rs#L653), [`core/dr-decode/src/locate.rs:736`](../../core/dr-decode/src/locate.rs#L736), [`core/dr-export/examples/export.rs:99`](../../core/dr-export/examples/export.rs#L99), [`core/dr-export/src/encode.rs:117`](../../core/dr-export/src/encode.rs#L117), [`core/dr-export/src/encode.rs:161`](../../core/dr-export/src/encode.rs#L161), [`core/dr-export/src/encode.rs:1`](../../core/dr-export/src/encode.rs#L1), [`core/dr-export/src/encode.rs:206`](../../core/dr-export/src/encode.rs#L206), [`core/dr-export/src/encode.rs:235`](../../core/dr-export/src/encode.rs#L235), [`core/dr-export/src/encode.rs:311`](../../core/dr-export/src/encode.rs#L311), [`core/dr-export/src/encode.rs:325`](../../core/dr-export/src/encode.rs#L325), [`core/dr-export/src/encode.rs:408`](../../core/dr-export/src/encode.rs#L408), [`core/dr-export/src/encode.rs:456`](../../core/dr-export/src/encode.rs#L456), [`core/dr-export/src/encode.rs:70`](../../core/dr-export/src/encode.rs#L70), [`core/dr-export/src/encode.rs:795`](../../core/dr-export/src/encode.rs#L795), [`core/dr-export/src/encode.rs:809`](../../core/dr-export/src/encode.rs#L809), [`core/dr-export/src/encode.rs:850`](../../core/dr-export/src/encode.rs#L850), [`core/dr-export/src/encode.rs:898`](../../core/dr-export/src/encode.rs#L898), [`core/dr-export/src/exif.rs:1`](../../core/dr-export/src/exif.rs#L1), [`core/dr-export/src/lib.rs:140`](../../core/dr-export/src/lib.rs#L140), [`core/dr-export/src/metadata.rs:1`](../../core/dr-export/src/metadata.rs#L1), [`core/dr-export/src/metadata.rs:41`](../../core/dr-export/src/metadata.rs#L41), [`core/dr-export/src/metadata.rs:74`](../../core/dr-export/src/metadata.rs#L74), [`core/dr-types/src/lib.rs:658`](../../core/dr-types/src/lib.rs#L658), [`core/dr-types/src/settings.rs:671`](../../core/dr-types/src/settings.rs#L671), [`ui/dr-ui/src/decoder_seam.rs:257`](../../ui/dr-ui/src/decoder_seam.rs#L257), [`ui/dr-ui/src/develop/session.rs:37`](../../ui/dr-ui/src/develop/session.rs#L37), [`ui/dr-ui/src/develop/session.rs:494`](../../ui/dr-ui/src/develop/session.rs#L494), [`ui/dr-ui/src/develop/session.rs:701`](../../ui/dr-ui/src/develop/session.rs#L701), [`ui/dr-ui/src/export.rs:1127`](../../ui/dr-ui/src/export.rs#L1127), [`ui/dr-ui/src/export.rs:1173`](../../ui/dr-ui/src/export.rs#L1173), [`ui/dr-ui/src/export.rs:1201`](../../ui/dr-ui/src/export.rs#L1201), [`ui/dr-ui/src/export.rs:1238`](../../ui/dr-ui/src/export.rs#L1238), [`ui/dr-ui/src/export.rs:1344`](../../ui/dr-ui/src/export.rs#L1344), [`ui/dr-ui/src/export.rs:1362`](../../ui/dr-ui/src/export.rs#L1362), [`ui/dr-ui/src/export.rs:916`](../../ui/dr-ui/src/export.rs#L916), [`ui/dr-ui/src/lib.rs:295`](../../ui/dr-ui/src/lib.rs#L295), [`ui/dr-ui/src/lib.rs:319`](../../ui/dr-ui/src/lib.rs#L319), [`ui/dr-ui/src/lib.rs:700`](../../ui/dr-ui/src/lib.rs#L700), [`ui/dr-ui/src/settings_ui.rs:1`](../../ui/dr-ui/src/settings_ui.rs#L1) | +| FR-EXP-8 | [`core/dr-decode/src/lib.rs:373`](../../core/dr-decode/src/lib.rs#L373), [`core/dr-decode/src/lib.rs:397`](../../core/dr-decode/src/lib.rs#L397), [`core/dr-decode/src/lib.rs:411`](../../core/dr-decode/src/lib.rs#L411), [`core/dr-decode/src/lib.rs:76`](../../core/dr-decode/src/lib.rs#L76), [`core/dr-decode/src/lib.rs:84`](../../core/dr-decode/src/lib.rs#L84), [`core/dr-decode/src/lib.rs:87`](../../core/dr-decode/src/lib.rs#L87), [`core/dr-decode/src/locate.rs:1233`](../../core/dr-decode/src/locate.rs#L1233), [`core/dr-decode/src/locate.rs:1292`](../../core/dr-decode/src/locate.rs#L1292), [`core/dr-decode/src/locate.rs:359`](../../core/dr-decode/src/locate.rs#L359), [`core/dr-decode/src/locate.rs:556`](../../core/dr-decode/src/locate.rs#L556), [`core/dr-decode/src/locate.rs:640`](../../core/dr-decode/src/locate.rs#L640), [`core/dr-decode/src/locate.rs:653`](../../core/dr-decode/src/locate.rs#L653), [`core/dr-decode/src/locate.rs:736`](../../core/dr-decode/src/locate.rs#L736), [`core/dr-export/examples/export.rs:99`](../../core/dr-export/examples/export.rs#L99), [`core/dr-export/src/encode.rs:117`](../../core/dr-export/src/encode.rs#L117), [`core/dr-export/src/encode.rs:161`](../../core/dr-export/src/encode.rs#L161), [`core/dr-export/src/encode.rs:1`](../../core/dr-export/src/encode.rs#L1), [`core/dr-export/src/encode.rs:206`](../../core/dr-export/src/encode.rs#L206), [`core/dr-export/src/encode.rs:235`](../../core/dr-export/src/encode.rs#L235), [`core/dr-export/src/encode.rs:311`](../../core/dr-export/src/encode.rs#L311), [`core/dr-export/src/encode.rs:325`](../../core/dr-export/src/encode.rs#L325), [`core/dr-export/src/encode.rs:408`](../../core/dr-export/src/encode.rs#L408), [`core/dr-export/src/encode.rs:456`](../../core/dr-export/src/encode.rs#L456), [`core/dr-export/src/encode.rs:70`](../../core/dr-export/src/encode.rs#L70), [`core/dr-export/src/encode.rs:795`](../../core/dr-export/src/encode.rs#L795), [`core/dr-export/src/encode.rs:809`](../../core/dr-export/src/encode.rs#L809), [`core/dr-export/src/encode.rs:850`](../../core/dr-export/src/encode.rs#L850), [`core/dr-export/src/encode.rs:898`](../../core/dr-export/src/encode.rs#L898), [`core/dr-export/src/exif.rs:1`](../../core/dr-export/src/exif.rs#L1), [`core/dr-export/src/lib.rs:140`](../../core/dr-export/src/lib.rs#L140), [`core/dr-export/src/metadata.rs:1`](../../core/dr-export/src/metadata.rs#L1), [`core/dr-export/src/metadata.rs:41`](../../core/dr-export/src/metadata.rs#L41), [`core/dr-export/src/metadata.rs:74`](../../core/dr-export/src/metadata.rs#L74), [`core/dr-types/src/lib.rs:658`](../../core/dr-types/src/lib.rs#L658), [`core/dr-types/src/settings.rs:684`](../../core/dr-types/src/settings.rs#L684), [`ui/dr-ui/src/decoder_seam.rs:257`](../../ui/dr-ui/src/decoder_seam.rs#L257), [`ui/dr-ui/src/develop/session.rs:37`](../../ui/dr-ui/src/develop/session.rs#L37), [`ui/dr-ui/src/develop/session.rs:494`](../../ui/dr-ui/src/develop/session.rs#L494), [`ui/dr-ui/src/develop/session.rs:701`](../../ui/dr-ui/src/develop/session.rs#L701), [`ui/dr-ui/src/export.rs:1127`](../../ui/dr-ui/src/export.rs#L1127), [`ui/dr-ui/src/export.rs:1173`](../../ui/dr-ui/src/export.rs#L1173), [`ui/dr-ui/src/export.rs:1201`](../../ui/dr-ui/src/export.rs#L1201), [`ui/dr-ui/src/export.rs:1238`](../../ui/dr-ui/src/export.rs#L1238), [`ui/dr-ui/src/export.rs:1344`](../../ui/dr-ui/src/export.rs#L1344), [`ui/dr-ui/src/export.rs:1362`](../../ui/dr-ui/src/export.rs#L1362), [`ui/dr-ui/src/export.rs:916`](../../ui/dr-ui/src/export.rs#L916), [`ui/dr-ui/src/lib.rs:295`](../../ui/dr-ui/src/lib.rs#L295), [`ui/dr-ui/src/lib.rs:319`](../../ui/dr-ui/src/lib.rs#L319), [`ui/dr-ui/src/lib.rs:700`](../../ui/dr-ui/src/lib.rs#L700), [`ui/dr-ui/src/settings_ui.rs:1`](../../ui/dr-ui/src/settings_ui.rs#L1) | | FR-EXP-9 | [`core/dr-decode/src/lib.rs:564`](../../core/dr-decode/src/lib.rs#L564), [`core/dr-export/src/lib.rs:132`](../../core/dr-export/src/lib.rs#L132), [`core/dr-export/src/lib.rs:1`](../../core/dr-export/src/lib.rs#L1), [`core/dr-gpu/src/adjust.rs:1557`](../../core/dr-gpu/src/adjust.rs#L1557), [`ui/dr-ui/examples/face_native.rs:1`](../../ui/dr-ui/examples/face_native.rs#L1), [`ui/dr-ui/src/decoder_seam.rs:257`](../../ui/dr-ui/src/decoder_seam.rs#L257), [`ui/dr-ui/src/develop/mask_ops.rs:1130`](../../ui/dr-ui/src/develop/mask_ops.rs#L1130), [`ui/dr-ui/src/develop/render.rs:681`](../../ui/dr-ui/src/develop/render.rs#L681), [`ui/dr-ui/src/develop/render.rs:723`](../../ui/dr-ui/src/develop/render.rs#L723), [`ui/dr-ui/src/lib.rs:668`](../../ui/dr-ui/src/lib.rs#L668), [`ui/dr-ui/src/library/sweep.rs:563`](../../ui/dr-ui/src/library/sweep.rs#L563), [`ui/dr-ui/tests/export_ignores_the_viewport.rs:1`](../../ui/dr-ui/tests/export_ignores_the_viewport.rs#L1) | | FR-INF-1 | [`ui/dr-ui/src/lib.rs:1537`](../../ui/dr-ui/src/lib.rs#L1537), [`ui/dr-ui/ui/settings.slint:824`](../../ui/dr-ui/ui/settings.slint#L824) | | FR-MRG-1 | [`core/dr-pano/src/align.rs:1`](../../core/dr-pano/src/align.rs#L1), [`core/dr-pano/src/lib.rs:1`](../../core/dr-pano/src/lib.rs#L1), [`ui/dr-ui/src/lib.rs:2000`](../../ui/dr-ui/src/lib.rs#L2000), [`ui/dr-ui/src/merge.rs:1`](../../ui/dr-ui/src/merge.rs#L1), [`ui/dr-ui/src/merge_ui.rs:1`](../../ui/dr-ui/src/merge_ui.rs#L1), [`ui/dr-ui/ui/app.slint:1533`](../../ui/dr-ui/ui/app.slint#L1533), [`ui/dr-ui/ui/app.slint:592`](../../ui/dr-ui/ui/app.slint#L592), [`ui/dr-ui/ui/library.slint:2049`](../../ui/dr-ui/ui/library.slint#L2049), [`ui/dr-ui/ui/library.slint:5197`](../../ui/dr-ui/ui/library.slint#L5197), [`ui/dr-ui/ui/merge.slint:1`](../../ui/dr-ui/ui/merge.slint#L1) | @@ -133,7 +133,7 @@ _None._ | FR-NC-6c | [`core/dr-catalog/src/cache.rs:225`](../../core/dr-catalog/src/cache.rs#L225), [`core/dr-sync-folder/src/borrow.rs:1`](../../core/dr-sync-folder/src/borrow.rs#L1), [`core/dr-sync-folder/src/lib.rs:197`](../../core/dr-sync-folder/src/lib.rs#L197), [`core/dr-sync-folder/src/lib.rs:73`](../../core/dr-sync-folder/src/lib.rs#L73), [`core/dr-sync-folder/src/lib.rs:818`](../../core/dr-sync-folder/src/lib.rs#L818), [`core/dr-sync-folder/src/lib.rs:857`](../../core/dr-sync-folder/src/lib.rs#L857), [`core/dr-sync-folder/src/vfs.rs:1`](../../core/dr-sync-folder/src/vfs.rs#L1), [`core/dr-sync-nextcloud/src/desktop_client.rs:172`](../../core/dr-sync-nextcloud/src/desktop_client.rs#L172), [`core/dr-sync-nextcloud/src/desktop_client.rs:35`](../../core/dr-sync-nextcloud/src/desktop_client.rs#L35), [`core/dr-sync/src/capability.rs:41`](../../core/dr-sync/src/capability.rs#L41), [`core/dr-sync/src/error.rs:39`](../../core/dr-sync/src/error.rs#L39), [`core/dr-sync/src/lib.rs:178`](../../core/dr-sync/src/lib.rs#L178), [`core/dr-sync/src/types.rs:101`](../../core/dr-sync/src/types.rs#L101), [`core/dr-types/src/lib.rs:125`](../../core/dr-types/src/lib.rs#L125), [`core/dr-types/src/lib.rs:207`](../../core/dr-types/src/lib.rs#L207), [`ui/dr-ui/src/activity.rs:1`](../../ui/dr-ui/src/activity.rs#L1), [`ui/dr-ui/src/collections_ui/tree_sync.rs:116`](../../ui/dr-ui/src/collections_ui/tree_sync.rs#L116), [`ui/dr-ui/src/collections_ui/tree_sync.rs:392`](../../ui/dr-ui/src/collections_ui/tree_sync.rs#L392), [`ui/dr-ui/src/collections_ui/tree_sync.rs:43`](../../ui/dr-ui/src/collections_ui/tree_sync.rs#L43), [`ui/dr-ui/src/derived_sync.rs:1348`](../../ui/dr-ui/src/derived_sync.rs#L1348), [`ui/dr-ui/src/derived_sync.rs:686`](../../ui/dr-ui/src/derived_sync.rs#L686), [`ui/dr-ui/src/library/sidecar.rs:539`](../../ui/dr-ui/src/library/sidecar.rs#L539), [`ui/dr-ui/src/library/sweep.rs:738`](../../ui/dr-ui/src/library/sweep.rs#L738), [`ui/dr-ui/src/library/thumbnails_fetch.rs:216`](../../ui/dr-ui/src/library/thumbnails_fetch.rs#L216), [`ui/dr-ui/src/library/thumbnails_fetch.rs:306`](../../ui/dr-ui/src/library/thumbnails_fetch.rs#L306), [`ui/dr-ui/src/library_ui/offline.rs:20`](../../ui/dr-ui/src/library_ui/offline.rs#L20), [`ui/dr-ui/src/library_ui/offline.rs:310`](../../ui/dr-ui/src/library_ui/offline.rs#L310), [`ui/dr-ui/src/library_ui/offline.rs:93`](../../ui/dr-ui/src/library_ui/offline.rs#L93), [`ui/dr-ui/src/remote.rs:40`](../../ui/dr-ui/src/remote.rs#L40), [`ui/dr-ui/src/repairs.rs:1155`](../../ui/dr-ui/src/repairs.rs#L1155), [`ui/dr-ui/ui/collections.slint:290`](../../ui/dr-ui/ui/collections.slint#L290), [`ui/dr-ui/ui/collections.slint:978`](../../ui/dr-ui/ui/collections.slint#L978), [`ui/dr-ui/ui/icons.slint:263`](../../ui/dr-ui/ui/icons.slint#L263) | | FR-NC-6d | [`core/dr-sync-folder/src/borrow.rs:1`](../../core/dr-sync-folder/src/borrow.rs#L1), [`core/dr-sync-folder/src/lib.rs:1`](../../core/dr-sync-folder/src/lib.rs#L1), [`core/dr-sync-folder/src/vfs.rs:1`](../../core/dr-sync-folder/src/vfs.rs#L1) | | FR-NC-7 | [`core/dr-catalog/src/face_shard.rs:1`](../../core/dr-catalog/src/face_shard.rs#L1), [`core/dr-sync-nextcloud/src/lib.rs:120`](../../core/dr-sync-nextcloud/src/lib.rs#L120), [`ui/dr-ui/src/derived_sync.rs:1`](../../ui/dr-ui/src/derived_sync.rs#L1), [`ui/dr-ui/src/library/sweep.rs:615`](../../ui/dr-ui/src/library/sweep.rs#L615), [`ui/dr-ui/src/library_ui/sync.rs:397`](../../ui/dr-ui/src/library_ui/sync.rs#L397), [`ui/dr-ui/ui/settings.slint:375`](../../ui/dr-ui/ui/settings.slint#L375) | -| FR-NC-7a | [`core/dr-ingest/src/layout.rs:1`](../../core/dr-ingest/src/layout.rs#L1), [`core/dr-sync/src/upload.rs:1`](../../core/dr-sync/src/upload.rs#L1), [`core/dr-sync/src/upload.rs:40`](../../core/dr-sync/src/upload.rs#L40), [`core/dr-types/src/settings.rs:377`](../../core/dr-types/src/settings.rs#L377), [`ui/dr-ui/src/import.rs:101`](../../ui/dr-ui/src/import.rs#L101), [`ui/dr-ui/src/import.rs:1`](../../ui/dr-ui/src/import.rs#L1), [`ui/dr-ui/src/import_ui.rs:1`](../../ui/dr-ui/src/import_ui.rs#L1), [`ui/dr-ui/src/lib.rs:1946`](../../ui/dr-ui/src/lib.rs#L1946), [`ui/dr-ui/ui/import.slint:6`](../../ui/dr-ui/ui/import.slint#L6) | +| FR-NC-7a | [`core/dr-ingest/src/layout.rs:1`](../../core/dr-ingest/src/layout.rs#L1), [`core/dr-sync/src/upload.rs:1`](../../core/dr-sync/src/upload.rs#L1), [`core/dr-sync/src/upload.rs:40`](../../core/dr-sync/src/upload.rs#L40), [`core/dr-types/src/settings.rs:390`](../../core/dr-types/src/settings.rs#L390), [`ui/dr-ui/src/import.rs:101`](../../ui/dr-ui/src/import.rs#L101), [`ui/dr-ui/src/import.rs:1`](../../ui/dr-ui/src/import.rs#L1), [`ui/dr-ui/src/import_ui.rs:1`](../../ui/dr-ui/src/import_ui.rs#L1), [`ui/dr-ui/src/lib.rs:1946`](../../ui/dr-ui/src/lib.rs#L1946), [`ui/dr-ui/ui/import.slint:6`](../../ui/dr-ui/ui/import.slint#L6) | | FR-NC-7b | [`core/dr-ingest/src/lib.rs:733`](../../core/dr-ingest/src/lib.rs#L733), [`core/dr-sync/src/upload.rs:1`](../../core/dr-sync/src/upload.rs#L1), [`ui/dr-ui/src/import.rs:101`](../../ui/dr-ui/src/import.rs#L101), [`ui/dr-ui/src/import.rs:126`](../../ui/dr-ui/src/import.rs#L126), [`ui/dr-ui/src/import.rs:345`](../../ui/dr-ui/src/import.rs#L345), [`ui/dr-ui/src/import.rs:599`](../../ui/dr-ui/src/import.rs#L599), [`ui/dr-ui/src/import_ui.rs:1`](../../ui/dr-ui/src/import_ui.rs#L1), [`ui/dr-ui/src/lib.rs:1946`](../../ui/dr-ui/src/lib.rs#L1946) | | FR-NC-8 | [`core/dr-catalog/src/rating.rs:1225`](../../core/dr-catalog/src/rating.rs#L1225), [`core/dr-catalog/src/rating.rs:210`](../../core/dr-catalog/src/rating.rs#L210), [`core/dr-catalog/src/rating.rs:72`](../../core/dr-catalog/src/rating.rs#L72), [`core/dr-catalog/src/schema.rs:330`](../../core/dr-catalog/src/schema.rs#L330), [`core/dr-pipeline/src/sidecar.rs:140`](../../core/dr-pipeline/src/sidecar.rs#L140), [`core/dr-pipeline/src/sidecar.rs:3066`](../../core/dr-pipeline/src/sidecar.rs#L3066), [`core/dr-pipeline/src/sidecar.rs:679`](../../core/dr-pipeline/src/sidecar.rs#L679), [`core/dr-pipeline/src/sidecar.rs:754`](../../core/dr-pipeline/src/sidecar.rs#L754), [`core/dr-pipeline/src/sidecar.rs:99`](../../core/dr-pipeline/src/sidecar.rs#L99), [`ui/dr-ui/src/lib.rs:2809`](../../ui/dr-ui/src/lib.rs#L2809), [`ui/dr-ui/src/library/sidecar.rs:12`](../../ui/dr-ui/src/library/sidecar.rs#L12), [`ui/dr-ui/src/library/sidecar.rs:274`](../../ui/dr-ui/src/library/sidecar.rs#L274), [`ui/dr-ui/src/library/sidecar.rs:581`](../../ui/dr-ui/src/library/sidecar.rs#L581), [`ui/dr-ui/src/library/thumbnails_fetch.rs:514`](../../ui/dr-ui/src/library/thumbnails_fetch.rs#L514), [`ui/dr-ui/src/library_ui/controller.rs:704`](../../ui/dr-ui/src/library_ui/controller.rs#L704), [`ui/dr-ui/src/presets.rs:288`](../../ui/dr-ui/src/presets.rs#L288), [`ui/dr-ui/src/presets.rs:336`](../../ui/dr-ui/src/presets.rs#L336) | | FR-NC-9 | [`core/dr-catalog/src/merge.rs:1`](../../core/dr-catalog/src/merge.rs#L1), [`core/dr-catalog/src/rating.rs:1225`](../../core/dr-catalog/src/rating.rs#L1225), [`core/dr-catalog/src/rating.rs:210`](../../core/dr-catalog/src/rating.rs#L210), [`core/dr-catalog/src/rating.rs:72`](../../core/dr-catalog/src/rating.rs#L72), [`core/dr-catalog/src/schema.rs:1164`](../../core/dr-catalog/src/schema.rs#L1164), [`core/dr-catalog/src/schema.rs:330`](../../core/dr-catalog/src/schema.rs#L330), [`core/dr-catalog/src/schema.rs:779`](../../core/dr-catalog/src/schema.rs#L779), [`core/dr-catalog/src/sync.rs:1`](../../core/dr-catalog/src/sync.rs#L1), [`core/dr-pipeline/src/sidecar.rs:206`](../../core/dr-pipeline/src/sidecar.rs#L206), [`core/dr-pipeline/src/sidecar.rs:233`](../../core/dr-pipeline/src/sidecar.rs#L233), [`core/dr-pipeline/src/sidecar.rs:2563`](../../core/dr-pipeline/src/sidecar.rs#L2563), [`core/dr-pipeline/src/sidecar.rs:2800`](../../core/dr-pipeline/src/sidecar.rs#L2800), [`core/dr-pipeline/src/sidecar.rs:3066`](../../core/dr-pipeline/src/sidecar.rs#L3066), [`core/dr-pipeline/src/sidecar.rs:410`](../../core/dr-pipeline/src/sidecar.rs#L410), [`core/dr-pipeline/src/sidecar.rs:508`](../../core/dr-pipeline/src/sidecar.rs#L508), [`core/dr-pipeline/src/sidecar.rs:679`](../../core/dr-pipeline/src/sidecar.rs#L679), [`core/dr-pipeline/src/sidecar.rs:729`](../../core/dr-pipeline/src/sidecar.rs#L729), [`core/dr-pipeline/src/sidecar.rs:754`](../../core/dr-pipeline/src/sidecar.rs#L754), [`core/dr-pipeline/src/spot.rs:245`](../../core/dr-pipeline/src/spot.rs#L245), [`core/dr-pipeline/tests/mask_sidecar.rs:1075`](../../core/dr-pipeline/tests/mask_sidecar.rs#L1075), [`core/dr-pipeline/tests/spot_sidecar.rs:1`](../../core/dr-pipeline/tests/spot_sidecar.rs#L1), [`core/dr-sync-nextcloud/src/lib.rs:139`](../../core/dr-sync-nextcloud/src/lib.rs#L139), [`core/dr-sync/src/scan.rs:31`](../../core/dr-sync/src/scan.rs#L31), [`core/dr-sync/src/scan.rs:56`](../../core/dr-sync/src/scan.rs#L56), [`core/dr-sync/src/scan.rs:926`](../../core/dr-sync/src/scan.rs#L926), [`core/dr-xmp/src/lib.rs:733`](../../core/dr-xmp/src/lib.rs#L733), [`core/dr-xmp/src/lib.rs:746`](../../core/dr-xmp/src/lib.rs#L746), [`ui/dr-ui/src/derived_sync.rs:1376`](../../ui/dr-ui/src/derived_sync.rs#L1376), [`ui/dr-ui/src/derived_sync.rs:657`](../../ui/dr-ui/src/derived_sync.rs#L657), [`ui/dr-ui/src/derived_sync.rs:686`](../../ui/dr-ui/src/derived_sync.rs#L686), [`ui/dr-ui/src/derived_sync.rs:719`](../../ui/dr-ui/src/derived_sync.rs#L719), [`ui/dr-ui/src/derived_sync.rs:866`](../../ui/dr-ui/src/derived_sync.rs#L866), [`ui/dr-ui/src/library/scan.rs:227`](../../ui/dr-ui/src/library/scan.rs#L227), [`ui/dr-ui/src/library/scan.rs:262`](../../ui/dr-ui/src/library/scan.rs#L262), [`ui/dr-ui/src/library/scan.rs:35`](../../ui/dr-ui/src/library/scan.rs#L35), [`ui/dr-ui/src/library/sidecar.rs:274`](../../ui/dr-ui/src/library/sidecar.rs#L274), [`ui/dr-ui/src/library/sidecar.rs:351`](../../ui/dr-ui/src/library/sidecar.rs#L351), [`ui/dr-ui/src/library/sidecar.rs:435`](../../ui/dr-ui/src/library/sidecar.rs#L435), [`ui/dr-ui/src/library/sidecar.rs:581`](../../ui/dr-ui/src/library/sidecar.rs#L581), [`ui/dr-ui/src/library/sidecar.rs:601`](../../ui/dr-ui/src/library/sidecar.rs#L601), [`ui/dr-ui/src/library/thumbnails_fetch.rs:514`](../../ui/dr-ui/src/library/thumbnails_fetch.rs#L514), [`ui/dr-ui/src/library_ui/open.rs:519`](../../ui/dr-ui/src/library_ui/open.rs#L519), [`ui/dr-ui/src/presets.rs:288`](../../ui/dr-ui/src/presets.rs#L288), [`ui/dr-ui/src/presets.rs:336`](../../ui/dr-ui/src/presets.rs#L336), [`ui/dr-ui/src/xmp_sync.rs:1`](../../ui/dr-ui/src/xmp_sync.rs#L1), [`ui/dr-ui/src/xmp_sync.rs:287`](../../ui/dr-ui/src/xmp_sync.rs#L287) | @@ -141,7 +141,7 @@ _None._ | FR-PLAT-AND-3 | [`core/dr-catalog/src/jobs.rs:1`](../../core/dr-catalog/src/jobs.rs#L1), [`core/dr-catalog/src/runner.rs:1`](../../core/dr-catalog/src/runner.rs#L1), [`core/dr-catalog/tests/every_queued_kind_has_a_consumer.rs:1`](../../core/dr-catalog/tests/every_queued_kind_has_a_consumer.rs#L1) | | FR-PLAT-AND-4 | [`core/dr-catalog/src/runner.rs:1`](../../core/dr-catalog/src/runner.rs#L1) | | FR-PLAT-AND-5 | [`apps/darkroom-android/src/lib.rs:166`](../../apps/darkroom-android/src/lib.rs#L166), [`core/dr-gpu/src/adjust.rs:1472`](../../core/dr-gpu/src/adjust.rs#L1472), [`core/dr-gpu/src/detail.rs:161`](../../core/dr-gpu/src/detail.rs#L161), [`core/dr-gpu/src/detail.rs:533`](../../core/dr-gpu/src/detail.rs#L533), [`core/dr-gpu/tests/detail_stage.rs:415`](../../core/dr-gpu/tests/detail_stage.rs#L415), [`ui/dr-ui/src/develop/render.rs:654`](../../ui/dr-ui/src/develop/render.rs#L654), [`ui/dr-ui/src/identity_ui.rs:125`](../../ui/dr-ui/src/identity_ui.rs#L125), [`ui/dr-ui/src/lib.rs:1309`](../../ui/dr-ui/src/lib.rs#L1309), [`ui/dr-ui/src/lib.rs:1745`](../../ui/dr-ui/src/lib.rs#L1745), [`ui/dr-ui/src/memory.rs:163`](../../ui/dr-ui/src/memory.rs#L163), [`ui/dr-ui/src/memory.rs:1`](../../ui/dr-ui/src/memory.rs#L1) | -| FR-PLAT-AND-6 | [`apps/darkroom-android/src/lib.rs:444`](../../apps/darkroom-android/src/lib.rs#L444) | +| FR-PLAT-AND-6 | [`apps/darkroom-android/src/lib.rs:457`](../../apps/darkroom-android/src/lib.rs#L457) | | FR-PLAT-LIN-1 | [`core/dr-types/src/settings.rs:1`](../../core/dr-types/src/settings.rs#L1), [`platform/dr-plat/src/dirs.rs:1`](../../platform/dr-plat/src/dirs.rs#L1), [`platform/dr-plat/src/storage.rs:344`](../../platform/dr-plat/src/storage.rs#L344), [`ui/dr-ui/src/lib.rs:1486`](../../ui/dr-ui/src/lib.rs#L1486), [`ui/dr-ui/src/preset_store.rs:1`](../../ui/dr-ui/src/preset_store.rs#L1), [`ui/dr-ui/src/settings_store.rs:1`](../../ui/dr-ui/src/settings_store.rs#L1) | | FR-PLAT-LIN-2 | [`platform/dr-plat/src/display.rs:1`](../../platform/dr-plat/src/display.rs#L1), [`platform/dr-plat/src/display/wayland.rs:1`](../../platform/dr-plat/src/display/wayland.rs#L1), [`platform/dr-plat/src/display/x11.rs:1`](../../platform/dr-plat/src/display/x11.rs#L1) | | FR-PLAT-WIN-1 | [`platform/dr-plat/src/dirs.rs:1`](../../platform/dr-plat/src/dirs.rs#L1) | @@ -152,13 +152,13 @@ _None._ | FR-RAW-3 | [`core/dr-decode/src/lib.rs:162`](../../core/dr-decode/src/lib.rs#L162), [`core/dr-decode/src/lib.rs:564`](../../core/dr-decode/src/lib.rs#L564), [`core/dr-decode/src/locate.rs:1435`](../../core/dr-decode/src/locate.rs#L1435), [`core/dr-gpu/src/demosaic.rs:1063`](../../core/dr-gpu/src/demosaic.rs#L1063), [`core/dr-gpu/src/demosaic.rs:1461`](../../core/dr-gpu/src/demosaic.rs#L1461), [`core/dr-gpu/src/demosaic.rs:868`](../../core/dr-gpu/src/demosaic.rs#L868), [`core/dr-gpu/tests/hot_pixels.rs:1`](../../core/dr-gpu/tests/hot_pixels.rs#L1) | | FR-RAW-4 | [`core/dr-decode/src/error.rs:1`](../../core/dr-decode/src/error.rs#L1), [`core/dr-decode/src/error.rs:30`](../../core/dr-decode/src/error.rs#L30), [`ui/dr-ui/src/lib.rs:295`](../../ui/dr-ui/src/lib.rs#L295) | | FR-RAW-5 | [`core/dr-decode/src/lib.rs:190`](../../core/dr-decode/src/lib.rs#L190), [`core/dr-gpu/src/demosaic.rs:1256`](../../core/dr-gpu/src/demosaic.rs#L1256), [`core/dr-gpu/src/demosaic.rs:1335`](../../core/dr-gpu/src/demosaic.rs#L1335), [`core/dr-gpu/src/demosaic.rs:1459`](../../core/dr-gpu/src/demosaic.rs#L1459), [`core/dr-gpu/src/demosaic.rs:34`](../../core/dr-gpu/src/demosaic.rs#L34) | -| FR-UI-1 | [`core/dr-types/src/settings.rs:1390`](../../core/dr-types/src/settings.rs#L1390), [`core/dr-types/src/settings.rs:492`](../../core/dr-types/src/settings.rs#L492), [`core/dr-types/src/settings.rs:512`](../../core/dr-types/src/settings.rs#L512), [`ui/dr-ui/src/develop_ui.rs:284`](../../ui/dr-ui/src/develop_ui.rs#L284), [`ui/dr-ui/src/lib.rs:126`](../../ui/dr-ui/src/lib.rs#L126), [`ui/dr-ui/src/lib.rs:141`](../../ui/dr-ui/src/lib.rs#L141), [`ui/dr-ui/src/lib.rs:163`](../../ui/dr-ui/src/lib.rs#L163), [`ui/dr-ui/src/lib.rs:3387`](../../ui/dr-ui/src/lib.rs#L3387), [`ui/dr-ui/src/lib.rs:3406`](../../ui/dr-ui/src/lib.rs#L3406), [`ui/dr-ui/src/lib.rs:3433`](../../ui/dr-ui/src/lib.rs#L3433), [`ui/dr-ui/src/lib.rs:3484`](../../ui/dr-ui/src/lib.rs#L3484), [`ui/dr-ui/src/lib.rs:3517`](../../ui/dr-ui/src/lib.rs#L3517), [`ui/dr-ui/src/library_ui/grid.rs:242`](../../ui/dr-ui/src/library_ui/grid.rs#L242), [`ui/dr-ui/src/masks_ui.rs:1608`](../../ui/dr-ui/src/masks_ui.rs#L1608), [`ui/dr-ui/src/settings_ui.rs:143`](../../ui/dr-ui/src/settings_ui.rs#L143), [`ui/dr-ui/ui/adjust.slint:1000`](../../ui/dr-ui/ui/adjust.slint#L1000), [`ui/dr-ui/ui/app.slint:3147`](../../ui/dr-ui/ui/app.slint#L3147), [`ui/dr-ui/ui/app.slint:670`](../../ui/dr-ui/ui/app.slint#L670), [`ui/dr-ui/ui/app.slint:815`](../../ui/dr-ui/ui/app.slint#L815), [`ui/dr-ui/ui/identity.slint:249`](../../ui/dr-ui/ui/identity.slint#L249), [`ui/dr-ui/ui/library.slint:1469`](../../ui/dr-ui/ui/library.slint#L1469), [`ui/dr-ui/ui/settings.slint:129`](../../ui/dr-ui/ui/settings.slint#L129), [`ui/dr-ui/ui/settings.slint:660`](../../ui/dr-ui/ui/settings.slint#L660), [`ui/dr-ui/ui/toolrail.slint:180`](../../ui/dr-ui/ui/toolrail.slint#L180), [`ui/dr-ui/ui/toolrail.slint:92`](../../ui/dr-ui/ui/toolrail.slint#L92), [`ui/dr-ui/ui/widgets.slint:1117`](../../ui/dr-ui/ui/widgets.slint#L1117) | +| FR-UI-1 | [`core/dr-types/src/settings.rs:1403`](../../core/dr-types/src/settings.rs#L1403), [`core/dr-types/src/settings.rs:505`](../../core/dr-types/src/settings.rs#L505), [`core/dr-types/src/settings.rs:525`](../../core/dr-types/src/settings.rs#L525), [`ui/dr-ui/src/develop_ui.rs:284`](../../ui/dr-ui/src/develop_ui.rs#L284), [`ui/dr-ui/src/lib.rs:126`](../../ui/dr-ui/src/lib.rs#L126), [`ui/dr-ui/src/lib.rs:141`](../../ui/dr-ui/src/lib.rs#L141), [`ui/dr-ui/src/lib.rs:163`](../../ui/dr-ui/src/lib.rs#L163), [`ui/dr-ui/src/lib.rs:3387`](../../ui/dr-ui/src/lib.rs#L3387), [`ui/dr-ui/src/lib.rs:3406`](../../ui/dr-ui/src/lib.rs#L3406), [`ui/dr-ui/src/lib.rs:3433`](../../ui/dr-ui/src/lib.rs#L3433), [`ui/dr-ui/src/lib.rs:3484`](../../ui/dr-ui/src/lib.rs#L3484), [`ui/dr-ui/src/lib.rs:3517`](../../ui/dr-ui/src/lib.rs#L3517), [`ui/dr-ui/src/library_ui/grid.rs:242`](../../ui/dr-ui/src/library_ui/grid.rs#L242), [`ui/dr-ui/src/masks_ui.rs:1608`](../../ui/dr-ui/src/masks_ui.rs#L1608), [`ui/dr-ui/src/settings_ui.rs:143`](../../ui/dr-ui/src/settings_ui.rs#L143), [`ui/dr-ui/ui/adjust.slint:1000`](../../ui/dr-ui/ui/adjust.slint#L1000), [`ui/dr-ui/ui/app.slint:3147`](../../ui/dr-ui/ui/app.slint#L3147), [`ui/dr-ui/ui/app.slint:670`](../../ui/dr-ui/ui/app.slint#L670), [`ui/dr-ui/ui/app.slint:815`](../../ui/dr-ui/ui/app.slint#L815), [`ui/dr-ui/ui/identity.slint:249`](../../ui/dr-ui/ui/identity.slint#L249), [`ui/dr-ui/ui/library.slint:1469`](../../ui/dr-ui/ui/library.slint#L1469), [`ui/dr-ui/ui/settings.slint:129`](../../ui/dr-ui/ui/settings.slint#L129), [`ui/dr-ui/ui/settings.slint:660`](../../ui/dr-ui/ui/settings.slint#L660), [`ui/dr-ui/ui/toolrail.slint:180`](../../ui/dr-ui/ui/toolrail.slint#L180), [`ui/dr-ui/ui/toolrail.slint:92`](../../ui/dr-ui/ui/toolrail.slint#L92), [`ui/dr-ui/ui/widgets.slint:1117`](../../ui/dr-ui/ui/widgets.slint#L1117) | | FR-UI-2 | [`ui/dr-ui/src/collections_ui/controller.rs:45`](../../ui/dr-ui/src/collections_ui/controller.rs#L45), [`ui/dr-ui/src/collections_ui/controller.rs:83`](../../ui/dr-ui/src/collections_ui/controller.rs#L83), [`ui/dr-ui/src/collections_ui/drag.rs:246`](../../ui/dr-ui/src/collections_ui/drag.rs#L246), [`ui/dr-ui/src/collections_ui/drag.rs:256`](../../ui/dr-ui/src/collections_ui/drag.rs#L256), [`ui/dr-ui/src/collections_ui/press.rs:453`](../../ui/dr-ui/src/collections_ui/press.rs#L453), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:121`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L121), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:138`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L138), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:415`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L415), [`ui/dr-ui/src/collections_ui/wiring_tree.rs:287`](../../ui/dr-ui/src/collections_ui/wiring_tree.rs#L287), [`ui/dr-ui/src/gestures.rs:1`](../../ui/dr-ui/src/gestures.rs#L1), [`ui/dr-ui/src/lib.rs:126`](../../ui/dr-ui/src/lib.rs#L126), [`ui/dr-ui/src/library_ui/filter_bar.rs:351`](../../ui/dr-ui/src/library_ui/filter_bar.rs#L351), [`ui/dr-ui/src/library_ui/grid.rs:242`](../../ui/dr-ui/src/library_ui/grid.rs#L242), [`ui/dr-ui/src/library_ui/window.rs:1164`](../../ui/dr-ui/src/library_ui/window.rs#L1164), [`ui/dr-ui/tests/scroll_cue.rs:1`](../../ui/dr-ui/tests/scroll_cue.rs#L1), [`ui/dr-ui/ui/app.slint:137`](../../ui/dr-ui/ui/app.slint#L137), [`ui/dr-ui/ui/app.slint:3545`](../../ui/dr-ui/ui/app.slint#L3545), [`ui/dr-ui/ui/app.slint:799`](../../ui/dr-ui/ui/app.slint#L799), [`ui/dr-ui/ui/collections.slint:1354`](../../ui/dr-ui/ui/collections.slint#L1354), [`ui/dr-ui/ui/controls.slint:626`](../../ui/dr-ui/ui/controls.slint#L626), [`ui/dr-ui/ui/gestures.slint:1`](../../ui/dr-ui/ui/gestures.slint#L1), [`ui/dr-ui/ui/library.slint:1356`](../../ui/dr-ui/ui/library.slint#L1356), [`ui/dr-ui/ui/library.slint:1389`](../../ui/dr-ui/ui/library.slint#L1389), [`ui/dr-ui/ui/library.slint:1717`](../../ui/dr-ui/ui/library.slint#L1717), [`ui/dr-ui/ui/library.slint:1724`](../../ui/dr-ui/ui/library.slint#L1724), [`ui/dr-ui/ui/library.slint:1825`](../../ui/dr-ui/ui/library.slint#L1825), [`ui/dr-ui/ui/library.slint:3168`](../../ui/dr-ui/ui/library.slint#L3168), [`ui/dr-ui/ui/library.slint:586`](../../ui/dr-ui/ui/library.slint#L586), [`ui/dr-ui/ui/presets.slint:114`](../../ui/dr-ui/ui/presets.slint#L114), [`ui/dr-ui/ui/settings.slint:143`](../../ui/dr-ui/ui/settings.slint#L143), [`ui/dr-ui/ui/widgets.slint:1117`](../../ui/dr-ui/ui/widgets.slint#L1117) | | FR-UI-3 | [`ui/dr-ui/src/collections_ui/controller.rs:143`](../../ui/dr-ui/src/collections_ui/controller.rs#L143), [`ui/dr-ui/src/collections_ui/controller.rs:348`](../../ui/dr-ui/src/collections_ui/controller.rs#L348), [`ui/dr-ui/src/collections_ui/controller.rs:361`](../../ui/dr-ui/src/collections_ui/controller.rs#L361), [`ui/dr-ui/src/collections_ui/wiring_tree.rs:287`](../../ui/dr-ui/src/collections_ui/wiring_tree.rs#L287), [`ui/dr-ui/src/develop/masks.rs:361`](../../ui/dr-ui/src/develop/masks.rs#L361), [`ui/dr-ui/src/develop/repairs.rs:49`](../../ui/dr-ui/src/develop/repairs.rs#L49), [`ui/dr-ui/src/library_ui/grid.rs:228`](../../ui/dr-ui/src/library_ui/grid.rs#L228), [`ui/dr-ui/src/masks_ui.rs:1700`](../../ui/dr-ui/src/masks_ui.rs#L1700), [`ui/dr-ui/src/masks_ui.rs:1722`](../../ui/dr-ui/src/masks_ui.rs#L1722), [`ui/dr-ui/src/masks_ui.rs:339`](../../ui/dr-ui/src/masks_ui.rs#L339), [`ui/dr-ui/src/spots_ui.rs:19`](../../ui/dr-ui/src/spots_ui.rs#L19), [`ui/dr-ui/ui/app.slint:2356`](../../ui/dr-ui/ui/app.slint#L2356), [`ui/dr-ui/ui/collections.slint:1228`](../../ui/dr-ui/ui/collections.slint#L1228), [`ui/dr-ui/ui/collections.slint:4`](../../ui/dr-ui/ui/collections.slint#L4), [`ui/dr-ui/ui/collections.slint:78`](../../ui/dr-ui/ui/collections.slint#L78), [`ui/dr-ui/ui/collections.slint:978`](../../ui/dr-ui/ui/collections.slint#L978), [`ui/dr-ui/ui/masks.slint:1246`](../../ui/dr-ui/ui/masks.slint#L1246), [`ui/dr-ui/ui/widgets.slint:72`](../../ui/dr-ui/ui/widgets.slint#L72) | | FR-UI-4 | [`tools/traceability/src/gestures.rs:1`](../../tools/traceability/src/gestures.rs#L1), [`tools/traceability/src/keymap.rs:1`](../../tools/traceability/src/keymap.rs#L1), [`ui/dr-ui/src/collections_ui/controller.rs:143`](../../ui/dr-ui/src/collections_ui/controller.rs#L143), [`ui/dr-ui/src/collections_ui/controller.rs:348`](../../ui/dr-ui/src/collections_ui/controller.rs#L348), [`ui/dr-ui/src/collections_ui/controller.rs:361`](../../ui/dr-ui/src/collections_ui/controller.rs#L361), [`ui/dr-ui/src/collections_ui/controller.rs:45`](../../ui/dr-ui/src/collections_ui/controller.rs#L45), [`ui/dr-ui/src/collections_ui/controller.rs:46`](../../ui/dr-ui/src/collections_ui/controller.rs#L46), [`ui/dr-ui/src/collections_ui/controller.rs:55`](../../ui/dr-ui/src/collections_ui/controller.rs#L55), [`ui/dr-ui/src/collections_ui/controller.rs:83`](../../ui/dr-ui/src/collections_ui/controller.rs#L83), [`ui/dr-ui/src/collections_ui/drag.rs:246`](../../ui/dr-ui/src/collections_ui/drag.rs#L246), [`ui/dr-ui/src/collections_ui/drag.rs:256`](../../ui/dr-ui/src/collections_ui/drag.rs#L256), [`ui/dr-ui/src/collections_ui/press.rs:258`](../../ui/dr-ui/src/collections_ui/press.rs#L258), [`ui/dr-ui/src/collections_ui/press.rs:278`](../../ui/dr-ui/src/collections_ui/press.rs#L278), [`ui/dr-ui/src/collections_ui/press.rs:453`](../../ui/dr-ui/src/collections_ui/press.rs#L453), [`ui/dr-ui/src/collections_ui/press.rs:490`](../../ui/dr-ui/src/collections_ui/press.rs#L490), [`ui/dr-ui/src/collections_ui/press.rs:54`](../../ui/dr-ui/src/collections_ui/press.rs#L54), [`ui/dr-ui/src/collections_ui/tree_sync.rs:344`](../../ui/dr-ui/src/collections_ui/tree_sync.rs#L344), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:121`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L121), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:138`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L138), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:264`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L264), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:415`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L415), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:444`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L444), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:577`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L577), [`ui/dr-ui/src/collections_ui/wiring_tree.rs:287`](../../ui/dr-ui/src/collections_ui/wiring_tree.rs#L287), [`ui/dr-ui/src/collections_ui/wiring_tree.rs:344`](../../ui/dr-ui/src/collections_ui/wiring_tree.rs#L344), [`ui/dr-ui/src/develop/framing.rs:1070`](../../ui/dr-ui/src/develop/framing.rs#L1070), [`ui/dr-ui/src/develop/framing.rs:1110`](../../ui/dr-ui/src/develop/framing.rs#L1110), [`ui/dr-ui/src/develop/framing.rs:1140`](../../ui/dr-ui/src/develop/framing.rs#L1140), [`ui/dr-ui/src/develop/framing.rs:257`](../../ui/dr-ui/src/develop/framing.rs#L257), [`ui/dr-ui/src/develop/framing.rs:591`](../../ui/dr-ui/src/develop/framing.rs#L591), [`ui/dr-ui/src/develop/framing.rs:640`](../../ui/dr-ui/src/develop/framing.rs#L640), [`ui/dr-ui/src/develop/framing.rs:665`](../../ui/dr-ui/src/develop/framing.rs#L665), [`ui/dr-ui/src/develop/framing.rs:676`](../../ui/dr-ui/src/develop/framing.rs#L676), [`ui/dr-ui/src/develop/framing.rs:690`](../../ui/dr-ui/src/develop/framing.rs#L690), [`ui/dr-ui/src/develop/framing.rs:763`](../../ui/dr-ui/src/develop/framing.rs#L763), [`ui/dr-ui/src/develop/framing.rs:893`](../../ui/dr-ui/src/develop/framing.rs#L893), [`ui/dr-ui/src/develop/framing.rs:924`](../../ui/dr-ui/src/develop/framing.rs#L924), [`ui/dr-ui/src/develop/framing.rs:945`](../../ui/dr-ui/src/develop/framing.rs#L945), [`ui/dr-ui/src/develop/render.rs:1257`](../../ui/dr-ui/src/develop/render.rs#L1257), [`ui/dr-ui/src/develop/render.rs:1282`](../../ui/dr-ui/src/develop/render.rs#L1282), [`ui/dr-ui/src/develop/render.rs:1305`](../../ui/dr-ui/src/develop/render.rs#L1305), [`ui/dr-ui/src/develop/render.rs:1344`](../../ui/dr-ui/src/develop/render.rs#L1344), [`ui/dr-ui/src/develop/render.rs:65`](../../ui/dr-ui/src/develop/render.rs#L65), [`ui/dr-ui/src/develop/render.rs:84`](../../ui/dr-ui/src/develop/render.rs#L84), [`ui/dr-ui/src/develop/render.rs:97`](../../ui/dr-ui/src/develop/render.rs#L97), [`ui/dr-ui/src/develop_ui.rs:1001`](../../ui/dr-ui/src/develop_ui.rs#L1001), [`ui/dr-ui/src/duplicates_ui.rs:1`](../../ui/dr-ui/src/duplicates_ui.rs#L1), [`ui/dr-ui/src/gesture_book.rs:3`](../../ui/dr-ui/src/gesture_book.rs#L3), [`ui/dr-ui/src/gestures.rs:127`](../../ui/dr-ui/src/gestures.rs#L127), [`ui/dr-ui/src/gestures.rs:139`](../../ui/dr-ui/src/gestures.rs#L139), [`ui/dr-ui/src/gestures.rs:1`](../../ui/dr-ui/src/gestures.rs#L1), [`ui/dr-ui/src/lib.rs:1376`](../../ui/dr-ui/src/lib.rs#L1376), [`ui/dr-ui/src/lib.rs:1710`](../../ui/dr-ui/src/lib.rs#L1710), [`ui/dr-ui/src/lib.rs:2279`](../../ui/dr-ui/src/lib.rs#L2279), [`ui/dr-ui/src/lib.rs:2668`](../../ui/dr-ui/src/lib.rs#L2668), [`ui/dr-ui/src/lib.rs:2961`](../../ui/dr-ui/src/lib.rs#L2961), [`ui/dr-ui/src/lib.rs:3032`](../../ui/dr-ui/src/lib.rs#L3032), [`ui/dr-ui/src/lib.rs:3197`](../../ui/dr-ui/src/lib.rs#L3197), [`ui/dr-ui/src/lib.rs:3599`](../../ui/dr-ui/src/lib.rs#L3599), [`ui/dr-ui/src/lib.rs:3643`](../../ui/dr-ui/src/lib.rs#L3643), [`ui/dr-ui/src/lib.rs:514`](../../ui/dr-ui/src/lib.rs#L514), [`ui/dr-ui/src/lib.rs:543`](../../ui/dr-ui/src/lib.rs#L543), [`ui/dr-ui/src/library/thumbnails_fetch.rs:792`](../../ui/dr-ui/src/library/thumbnails_fetch.rs#L792), [`ui/dr-ui/src/library/thumbnails_fetch.rs:807`](../../ui/dr-ui/src/library/thumbnails_fetch.rs#L807), [`ui/dr-ui/src/library_ui/controller.rs:426`](../../ui/dr-ui/src/library_ui/controller.rs#L426), [`ui/dr-ui/src/library_ui/controller.rs:573`](../../ui/dr-ui/src/library_ui/controller.rs#L573), [`ui/dr-ui/src/library_ui/controller.rs:678`](../../ui/dr-ui/src/library_ui/controller.rs#L678), [`ui/dr-ui/src/library_ui/filter_bar.rs:500`](../../ui/dr-ui/src/library_ui/filter_bar.rs#L500), [`ui/dr-ui/src/library_ui/grid.rs:228`](../../ui/dr-ui/src/library_ui/grid.rs#L228), [`ui/dr-ui/src/library_ui/grid.rs:252`](../../ui/dr-ui/src/library_ui/grid.rs#L252), [`ui/dr-ui/src/library_ui/grid.rs:273`](../../ui/dr-ui/src/library_ui/grid.rs#L273), [`ui/dr-ui/src/library_ui/grid.rs:347`](../../ui/dr-ui/src/library_ui/grid.rs#L347), [`ui/dr-ui/src/library_ui/grid.rs:517`](../../ui/dr-ui/src/library_ui/grid.rs#L517), [`ui/dr-ui/src/library_ui/grid.rs:547`](../../ui/dr-ui/src/library_ui/grid.rs#L547), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1178`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1178), [`ui/dr-ui/src/manual.rs:1`](../../ui/dr-ui/src/manual.rs#L1), [`ui/dr-ui/src/settings_ui.rs:121`](../../ui/dr-ui/src/settings_ui.rs#L121), [`ui/dr-ui/src/settings_ui.rs:508`](../../ui/dr-ui/src/settings_ui.rs#L508), [`ui/dr-ui/ui/app.slint:1836`](../../ui/dr-ui/ui/app.slint#L1836), [`ui/dr-ui/ui/app.slint:2044`](../../ui/dr-ui/ui/app.slint#L2044), [`ui/dr-ui/ui/app.slint:2170`](../../ui/dr-ui/ui/app.slint#L2170), [`ui/dr-ui/ui/app.slint:2456`](../../ui/dr-ui/ui/app.slint#L2456), [`ui/dr-ui/ui/app.slint:2618`](../../ui/dr-ui/ui/app.slint#L2618), [`ui/dr-ui/ui/app.slint:2648`](../../ui/dr-ui/ui/app.slint#L2648), [`ui/dr-ui/ui/app.slint:264`](../../ui/dr-ui/ui/app.slint#L264), [`ui/dr-ui/ui/app.slint:2793`](../../ui/dr-ui/ui/app.slint#L2793), [`ui/dr-ui/ui/app.slint:2988`](../../ui/dr-ui/ui/app.slint#L2988), [`ui/dr-ui/ui/app.slint:3545`](../../ui/dr-ui/ui/app.slint#L3545), [`ui/dr-ui/ui/app.slint:3572`](../../ui/dr-ui/ui/app.slint#L3572), [`ui/dr-ui/ui/app.slint:702`](../../ui/dr-ui/ui/app.slint#L702), [`ui/dr-ui/ui/collections.slint:1354`](../../ui/dr-ui/ui/collections.slint#L1354), [`ui/dr-ui/ui/collections.slint:78`](../../ui/dr-ui/ui/collections.slint#L78), [`ui/dr-ui/ui/develop.slint:142`](../../ui/dr-ui/ui/develop.slint#L142), [`ui/dr-ui/ui/develop.slint:328`](../../ui/dr-ui/ui/develop.slint#L328), [`ui/dr-ui/ui/develop.slint:66`](../../ui/dr-ui/ui/develop.slint#L66), [`ui/dr-ui/ui/duplicates.slint:1`](../../ui/dr-ui/ui/duplicates.slint#L1), [`ui/dr-ui/ui/gestures.slint:1`](../../ui/dr-ui/ui/gestures.slint#L1), [`ui/dr-ui/ui/icons.slint:367`](../../ui/dr-ui/ui/icons.slint#L367), [`ui/dr-ui/ui/labels.slint:74`](../../ui/dr-ui/ui/labels.slint#L74), [`ui/dr-ui/ui/library.slint:1356`](../../ui/dr-ui/ui/library.slint#L1356), [`ui/dr-ui/ui/library.slint:1383`](../../ui/dr-ui/ui/library.slint#L1383), [`ui/dr-ui/ui/library.slint:1389`](../../ui/dr-ui/ui/library.slint#L1389), [`ui/dr-ui/ui/library.slint:1444`](../../ui/dr-ui/ui/library.slint#L1444), [`ui/dr-ui/ui/library.slint:1643`](../../ui/dr-ui/ui/library.slint#L1643), [`ui/dr-ui/ui/library.slint:1694`](../../ui/dr-ui/ui/library.slint#L1694), [`ui/dr-ui/ui/library.slint:1717`](../../ui/dr-ui/ui/library.slint#L1717), [`ui/dr-ui/ui/library.slint:1724`](../../ui/dr-ui/ui/library.slint#L1724), [`ui/dr-ui/ui/library.slint:1760`](../../ui/dr-ui/ui/library.slint#L1760), [`ui/dr-ui/ui/library.slint:1818`](../../ui/dr-ui/ui/library.slint#L1818), [`ui/dr-ui/ui/library.slint:1825`](../../ui/dr-ui/ui/library.slint#L1825), [`ui/dr-ui/ui/library.slint:3168`](../../ui/dr-ui/ui/library.slint#L3168), [`ui/dr-ui/ui/library.slint:3273`](../../ui/dr-ui/ui/library.slint#L3273), [`ui/dr-ui/ui/library.slint:3424`](../../ui/dr-ui/ui/library.slint#L3424), [`ui/dr-ui/ui/library.slint:5073`](../../ui/dr-ui/ui/library.slint#L5073), [`ui/dr-ui/ui/library.slint:5111`](../../ui/dr-ui/ui/library.slint#L5111), [`ui/dr-ui/ui/library.slint:5215`](../../ui/dr-ui/ui/library.slint#L5215), [`ui/dr-ui/ui/library.slint:581`](../../ui/dr-ui/ui/library.slint#L581), [`ui/dr-ui/ui/library.slint:591`](../../ui/dr-ui/ui/library.slint#L591), [`ui/dr-ui/ui/library.slint:693`](../../ui/dr-ui/ui/library.slint#L693), [`ui/dr-ui/ui/library.slint:762`](../../ui/dr-ui/ui/library.slint#L762), [`ui/dr-ui/ui/settings.slint:121`](../../ui/dr-ui/ui/settings.slint#L121), [`ui/dr-ui/ui/settings.slint:172`](../../ui/dr-ui/ui/settings.slint#L172), [`ui/dr-ui/ui/settings.slint:635`](../../ui/dr-ui/ui/settings.slint#L635) | | FR-UI-5 | [`core/dr-types/src/place.rs:118`](../../core/dr-types/src/place.rs#L118), [`tools/traceability/src/chord.rs:1`](../../tools/traceability/src/chord.rs#L1), [`tools/traceability/src/keymap.rs:1`](../../tools/traceability/src/keymap.rs#L1), [`ui/dr-ui/src/collections_ui/mod.rs:1`](../../ui/dr-ui/src/collections_ui/mod.rs#L1), [`ui/dr-ui/src/develop_ui.rs:1046`](../../ui/dr-ui/src/develop_ui.rs#L1046), [`ui/dr-ui/src/gestures.rs:139`](../../ui/dr-ui/src/gestures.rs#L139), [`ui/dr-ui/src/identity_ui.rs:1643`](../../ui/dr-ui/src/identity_ui.rs#L1643), [`ui/dr-ui/src/identity_ui.rs:802`](../../ui/dr-ui/src/identity_ui.rs#L802), [`ui/dr-ui/src/lib.rs:3581`](../../ui/dr-ui/src/lib.rs#L3581), [`ui/dr-ui/src/lib.rs:3599`](../../ui/dr-ui/src/lib.rs#L3599), [`ui/dr-ui/src/lib.rs:3613`](../../ui/dr-ui/src/lib.rs#L3613), [`ui/dr-ui/src/lib.rs:3794`](../../ui/dr-ui/src/lib.rs#L3794), [`ui/dr-ui/src/library/filters.rs:79`](../../ui/dr-ui/src/library/filters.rs#L79), [`ui/dr-ui/src/library_ui/filter_bar.rs:57`](../../ui/dr-ui/src/library_ui/filter_bar.rs#L57), [`ui/dr-ui/src/library_ui/grid.rs:367`](../../ui/dr-ui/src/library_ui/grid.rs#L367), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1035`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1035), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1154`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1154), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1215`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1215), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1285`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1285), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1311`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1311), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:464`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L464), [`ui/dr-ui/src/masks_ui.rs:1655`](../../ui/dr-ui/src/masks_ui.rs#L1655), [`ui/dr-ui/src/place.rs:331`](../../ui/dr-ui/src/place.rs#L331), [`ui/dr-ui/src/settings_ui.rs:394`](../../ui/dr-ui/src/settings_ui.rs#L394), [`ui/dr-ui/tests/back_closes_the_presets_menu.rs:1`](../../ui/dr-ui/tests/back_closes_the_presets_menu.rs#L1), [`ui/dr-ui/ui/app.slint:1094`](../../ui/dr-ui/ui/app.slint#L1094), [`ui/dr-ui/ui/app.slint:1686`](../../ui/dr-ui/ui/app.slint#L1686), [`ui/dr-ui/ui/app.slint:2456`](../../ui/dr-ui/ui/app.slint#L2456), [`ui/dr-ui/ui/app.slint:2606`](../../ui/dr-ui/ui/app.slint#L2606), [`ui/dr-ui/ui/app.slint:2748`](../../ui/dr-ui/ui/app.slint#L2748), [`ui/dr-ui/ui/app.slint:2793`](../../ui/dr-ui/ui/app.slint#L2793), [`ui/dr-ui/ui/app.slint:2846`](../../ui/dr-ui/ui/app.slint#L2846), [`ui/dr-ui/ui/app.slint:2877`](../../ui/dr-ui/ui/app.slint#L2877), [`ui/dr-ui/ui/app.slint:3510`](../../ui/dr-ui/ui/app.slint#L3510), [`ui/dr-ui/ui/app.slint:3529`](../../ui/dr-ui/ui/app.slint#L3529), [`ui/dr-ui/ui/app.slint:424`](../../ui/dr-ui/ui/app.slint#L424), [`ui/dr-ui/ui/app.slint:691`](../../ui/dr-ui/ui/app.slint#L691), [`ui/dr-ui/ui/app.slint:698`](../../ui/dr-ui/ui/app.slint#L698), [`ui/dr-ui/ui/app.slint:702`](../../ui/dr-ui/ui/app.slint#L702), [`ui/dr-ui/ui/app.slint:708`](../../ui/dr-ui/ui/app.slint#L708), [`ui/dr-ui/ui/app.slint:764`](../../ui/dr-ui/ui/app.slint#L764), [`ui/dr-ui/ui/collections.slint:111`](../../ui/dr-ui/ui/collections.slint#L111), [`ui/dr-ui/ui/collections.slint:4`](../../ui/dr-ui/ui/collections.slint#L4), [`ui/dr-ui/ui/develop.slint:168`](../../ui/dr-ui/ui/develop.slint#L168), [`ui/dr-ui/ui/develop.slint:55`](../../ui/dr-ui/ui/develop.slint#L55), [`ui/dr-ui/ui/export.slint:1`](../../ui/dr-ui/ui/export.slint#L1), [`ui/dr-ui/ui/export.slint:246`](../../ui/dr-ui/ui/export.slint#L246), [`ui/dr-ui/ui/identity.slint:294`](../../ui/dr-ui/ui/identity.slint#L294), [`ui/dr-ui/ui/identity.slint:400`](../../ui/dr-ui/ui/identity.slint#L400), [`ui/dr-ui/ui/keys.slint:1`](../../ui/dr-ui/ui/keys.slint#L1), [`ui/dr-ui/ui/library.slint:1066`](../../ui/dr-ui/ui/library.slint#L1066), [`ui/dr-ui/ui/library.slint:1640`](../../ui/dr-ui/ui/library.slint#L1640), [`ui/dr-ui/ui/library.slint:1649`](../../ui/dr-ui/ui/library.slint#L1649), [`ui/dr-ui/ui/library.slint:1654`](../../ui/dr-ui/ui/library.slint#L1654), [`ui/dr-ui/ui/library.slint:1666`](../../ui/dr-ui/ui/library.slint#L1666), [`ui/dr-ui/ui/library.slint:1914`](../../ui/dr-ui/ui/library.slint#L1914), [`ui/dr-ui/ui/library.slint:2044`](../../ui/dr-ui/ui/library.slint#L2044), [`ui/dr-ui/ui/library.slint:2691`](../../ui/dr-ui/ui/library.slint#L2691), [`ui/dr-ui/ui/library.slint:3134`](../../ui/dr-ui/ui/library.slint#L3134), [`ui/dr-ui/ui/library.slint:3210`](../../ui/dr-ui/ui/library.slint#L3210), [`ui/dr-ui/ui/library.slint:3229`](../../ui/dr-ui/ui/library.slint#L3229), [`ui/dr-ui/ui/library.slint:3235`](../../ui/dr-ui/ui/library.slint#L3235), [`ui/dr-ui/ui/library.slint:3242`](../../ui/dr-ui/ui/library.slint#L3242), [`ui/dr-ui/ui/library.slint:3262`](../../ui/dr-ui/ui/library.slint#L3262), [`ui/dr-ui/ui/library.slint:3273`](../../ui/dr-ui/ui/library.slint#L3273), [`ui/dr-ui/ui/library.slint:3351`](../../ui/dr-ui/ui/library.slint#L3351), [`ui/dr-ui/ui/library.slint:3424`](../../ui/dr-ui/ui/library.slint#L3424), [`ui/dr-ui/ui/library.slint:4031`](../../ui/dr-ui/ui/library.slint#L4031), [`ui/dr-ui/ui/library.slint:5128`](../../ui/dr-ui/ui/library.slint#L5128), [`ui/dr-ui/ui/library.slint:669`](../../ui/dr-ui/ui/library.slint#L669), [`ui/dr-ui/ui/library.slint:687`](../../ui/dr-ui/ui/library.slint#L687), [`ui/dr-ui/ui/library.slint:708`](../../ui/dr-ui/ui/library.slint#L708), [`ui/dr-ui/ui/presets.slint:467`](../../ui/dr-ui/ui/presets.slint#L467), [`ui/dr-ui/ui/session.slint:75`](../../ui/dr-ui/ui/session.slint#L75) | | FR-UI-6 | [`ui/dr-ui/ui/widgets.slint:1`](../../ui/dr-ui/ui/widgets.slint#L1) | -| FR-UI-7 | [`core/dr-pipeline/src/descriptor.rs:195`](../../core/dr-pipeline/src/descriptor.rs#L195), [`core/dr-pipeline/src/framing.rs:568`](../../core/dr-pipeline/src/framing.rs#L568), [`core/dr-types/src/settings.rs:492`](../../core/dr-types/src/settings.rs#L492), [`ui/dr-ui/src/develop/rows.rs:118`](../../ui/dr-ui/src/develop/rows.rs#L118), [`ui/dr-ui/src/lib.rs:3406`](../../ui/dr-ui/src/lib.rs#L3406), [`ui/dr-ui/ui/adjust.slint:1000`](../../ui/dr-ui/ui/adjust.slint#L1000), [`ui/dr-ui/ui/app.slint:2294`](../../ui/dr-ui/ui/app.slint#L2294), [`ui/dr-ui/ui/settings.slint:660`](../../ui/dr-ui/ui/settings.slint#L660), [`ui/dr-ui/ui/toolrail.slint:92`](../../ui/dr-ui/ui/toolrail.slint#L92) | +| FR-UI-7 | [`core/dr-pipeline/src/descriptor.rs:195`](../../core/dr-pipeline/src/descriptor.rs#L195), [`core/dr-pipeline/src/framing.rs:568`](../../core/dr-pipeline/src/framing.rs#L568), [`core/dr-types/src/settings.rs:505`](../../core/dr-types/src/settings.rs#L505), [`ui/dr-ui/src/develop/rows.rs:118`](../../ui/dr-ui/src/develop/rows.rs#L118), [`ui/dr-ui/src/lib.rs:3406`](../../ui/dr-ui/src/lib.rs#L3406), [`ui/dr-ui/ui/adjust.slint:1000`](../../ui/dr-ui/ui/adjust.slint#L1000), [`ui/dr-ui/ui/app.slint:2294`](../../ui/dr-ui/ui/app.slint#L2294), [`ui/dr-ui/ui/settings.slint:660`](../../ui/dr-ui/ui/settings.slint#L660), [`ui/dr-ui/ui/toolrail.slint:92`](../../ui/dr-ui/ui/toolrail.slint#L92) | | FR-UI-8 | [`core/dr-catalog/src/collections.rs:654`](../../core/dr-catalog/src/collections.rs#L654), [`core/dr-catalog/src/collections.rs:675`](../../core/dr-catalog/src/collections.rs#L675), [`core/dr-types/src/place.rs:1`](../../core/dr-types/src/place.rs#L1), [`ui/dr-ui/src/derived_sync.rs:1292`](../../ui/dr-ui/src/derived_sync.rs#L1292), [`ui/dr-ui/src/derived_sync.rs:260`](../../ui/dr-ui/src/derived_sync.rs#L260), [`ui/dr-ui/src/derived_sync.rs:85`](../../ui/dr-ui/src/derived_sync.rs#L85), [`ui/dr-ui/src/derived_sync.rs:939`](../../ui/dr-ui/src/derived_sync.rs#L939), [`ui/dr-ui/src/derived_sync.rs:949`](../../ui/dr-ui/src/derived_sync.rs#L949), [`ui/dr-ui/src/library/cells.rs:270`](../../ui/dr-ui/src/library/cells.rs#L270), [`ui/dr-ui/src/library/cells.rs:641`](../../ui/dr-ui/src/library/cells.rs#L641), [`ui/dr-ui/src/library/cells.rs:707`](../../ui/dr-ui/src/library/cells.rs#L707), [`ui/dr-ui/src/library/paths.rs:22`](../../ui/dr-ui/src/library/paths.rs#L22), [`ui/dr-ui/src/library_ui/controller.rs:1046`](../../ui/dr-ui/src/library_ui/controller.rs#L1046), [`ui/dr-ui/src/library_ui/controller.rs:229`](../../ui/dr-ui/src/library_ui/controller.rs#L229), [`ui/dr-ui/src/library_ui/filter_bar.rs:793`](../../ui/dr-ui/src/library_ui/filter_bar.rs#L793), [`ui/dr-ui/src/library_ui/grid.rs:172`](../../ui/dr-ui/src/library_ui/grid.rs#L172), [`ui/dr-ui/src/library_ui/grid.rs:296`](../../ui/dr-ui/src/library_ui/grid.rs#L296), [`ui/dr-ui/src/library_ui/grid.rs:338`](../../ui/dr-ui/src/library_ui/grid.rs#L338), [`ui/dr-ui/src/library_ui/grid.rs:715`](../../ui/dr-ui/src/library_ui/grid.rs#L715), [`ui/dr-ui/src/library_ui/grid.rs:841`](../../ui/dr-ui/src/library_ui/grid.rs#L841), [`ui/dr-ui/src/library_ui/open.rs:1019`](../../ui/dr-ui/src/library_ui/open.rs#L1019), [`ui/dr-ui/src/library_ui/open.rs:135`](../../ui/dr-ui/src/library_ui/open.rs#L135), [`ui/dr-ui/src/library_ui/open.rs:182`](../../ui/dr-ui/src/library_ui/open.rs#L182), [`ui/dr-ui/src/library_ui/open.rs:37`](../../ui/dr-ui/src/library_ui/open.rs#L37), [`ui/dr-ui/src/library_ui/open.rs:904`](../../ui/dr-ui/src/library_ui/open.rs#L904), [`ui/dr-ui/src/library_ui/open.rs:921`](../../ui/dr-ui/src/library_ui/open.rs#L921), [`ui/dr-ui/src/place.rs:1`](../../ui/dr-ui/src/place.rs#L1) | | NFR-A11Y-2 | [`ui/dr-ui/tests/ui_controls_are_accessible.rs:1`](../../ui/dr-ui/tests/ui_controls_are_accessible.rs#L1), [`ui/dr-ui/ui/adjust.slint:173`](../../ui/dr-ui/ui/adjust.slint#L173), [`ui/dr-ui/ui/app.slint:2988`](../../ui/dr-ui/ui/app.slint#L2988), [`ui/dr-ui/ui/app.slint:3027`](../../ui/dr-ui/ui/app.slint#L3027) | | NFR-A11Y-3 | [`ui/dr-ui/src/histogram.rs:356`](../../ui/dr-ui/src/histogram.rs#L356), [`ui/dr-ui/tests/status_is_not_colour_alone.rs:1`](../../ui/dr-ui/tests/status_is_not_colour_alone.rs#L1), [`ui/dr-ui/ui/app.slint:2877`](../../ui/dr-ui/ui/app.slint#L2877), [`ui/dr-ui/ui/develop.slint:142`](../../ui/dr-ui/ui/develop.slint#L142), [`ui/dr-ui/ui/develop.slint:168`](../../ui/dr-ui/ui/develop.slint#L168), [`ui/dr-ui/ui/histogram.slint:137`](../../ui/dr-ui/ui/histogram.slint#L137), [`ui/dr-ui/ui/labels.slint:19`](../../ui/dr-ui/ui/labels.slint#L19), [`ui/dr-ui/ui/labels.slint:1`](../../ui/dr-ui/ui/labels.slint#L1), [`ui/dr-ui/ui/labels.slint:40`](../../ui/dr-ui/ui/labels.slint#L40), [`ui/dr-ui/ui/labels.slint:74`](../../ui/dr-ui/ui/labels.slint#L74), [`ui/dr-ui/ui/library.slint:1066`](../../ui/dr-ui/ui/library.slint#L1066), [`ui/dr-ui/ui/library.slint:1137`](../../ui/dr-ui/ui/library.slint#L1137), [`ui/dr-ui/ui/library.slint:1300`](../../ui/dr-ui/ui/library.slint#L1300), [`ui/dr-ui/ui/library.slint:2736`](../../ui/dr-ui/ui/library.slint#L2736), [`ui/dr-ui/ui/library.slint:3351`](../../ui/dr-ui/ui/library.slint#L3351), [`ui/dr-ui/ui/library.slint:3967`](../../ui/dr-ui/ui/library.slint#L3967), [`ui/dr-ui/ui/peaking.slint:1`](../../ui/dr-ui/ui/peaking.slint#L1), [`ui/dr-ui/ui/widgets.slint:271`](../../ui/dr-ui/ui/widgets.slint#L271) | diff --git a/docs/dev/windows.md b/docs/dev/windows.md index 720e44e..7a20c31 100644 --- a/docs/dev/windows.md +++ b/docs/dev/windows.md @@ -287,7 +287,6 @@ $LOCALAPPDATA\Programs\DarkRoom\ models\ scrfd_500m_640.onnx scrfd_2.5g_640.onnx scrfd_10g_640.onnx arcface_mbf_b1.onnx 2d106det_b1.onnx ocec_s_b1.onnx sgc_l_48_b1.onnx - scrfd_500m_640.int8.onnx scrfd_2.5g_640.int8.onnx scrfd_10g_640.int8.onnx yolo26s-sem-ade20k.onnx yolo26s-sem-ade20k.classes.json categories.txt migan-512.onnx manual\ @@ -297,7 +296,8 @@ $LOCALAPPDATA\Programs\DarkRoom\ ``` Plus a Start Menu shortcut, and nothing on the desktop unless the user ticks it. The models are -the same ten files the APK bundles and the PKGBUILD installs; `models\` beside the executable is +the f32 files the APK bundles and the PKGBUILD installs — not the APK's quantised siblings, which +only a Hexagon runs; `models\` beside the executable is where §3.2's lookup finds them. **No `LICENSE` yet**: the repository has no licence file at its root (the Arch package points at the system's shared GPL text), so the installer has no licence page until one is added — a one-file change, and the `.nsi` says where the page then goes. The face weights carry the research-only grant that diff --git a/models/LICENCE.md b/models/LICENCE.md index df68400..2f376c1 100644 --- a/models/LICENCE.md +++ b/models/LICENCE.md @@ -14,6 +14,13 @@ Both come from `https://huggingface.co/Ultralytics/YOLO26`. The face weights in The keypoint weights in `keypoints/` and the border filler in `inpaint/` are the other two, and the easiest — see the last two sections. +Every quantised sibling — `*.int8.onnx`, `*.a16w8.onnx`, `*.a16w16.onnx`, the +forms the tablet's Hexagon runs (`tools/quantise-models.sh`) — is the same +weights rounded, and carries exactly the grant of the file it was made from. +What calibration adds is one minimum and maximum per tensor: from public COCO +val2017 photographs (CC-BY 4.0) for the image models, and for the denoiser from +the same training tiles its weights were learned from. No image is in the files. + ## The grant **Ultralytics releases YOLO under AGPL-3.0**, and the weights carry the same diff --git a/models/denoise/mosaic-1408.a16w16.onnx b/models/denoise/mosaic-1408.a16w16.onnx new file mode 100644 index 0000000..a4241f5 --- /dev/null +++ b/models/denoise/mosaic-1408.a16w16.onnx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4b4de28646546279e8c4e70fc3707d089aee846a504ba163e4bf2a33724d5db3 +size 6412449 diff --git a/models/face/2d106det_b1.a16w8.onnx b/models/face/2d106det_b1.a16w8.onnx new file mode 100644 index 0000000..e51e9cd --- /dev/null +++ b/models/face/2d106det_b1.a16w8.onnx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6e27cebaf42befe29900c4eda734bbaffbc1f0506e765c2d5ab3addc7248ef79 +size 1444351 diff --git a/models/face/README.md b/models/face/README.md index b253ba4..c53aab0 100644 --- a/models/face/README.md +++ b/models/face/README.md @@ -28,15 +28,21 @@ every reader treats "never read" as unknown, never as closed. They are found in directory as the pair, so a hand-placed pair does not pick up a package's eye models from a directory it otherwise outranks. - scrfd_500m_640.int8.onnx 0.8 MB the same three, in the form the Hexagon NPU takes - scrfd_2.5g_640.int8.onnx 0.9 MB (docs/dev/inference.md §5) — opset 17, per-channel int8 - scrfd_10g_640.int8.onnx 4.3 MB weights, uint8 activations, calibrated on 96 photographs + scrfd_500m_640.a16w8.onnx the same three, in the form the Hexagon NPU takes + scrfd_2.5g_640.a16w8.onnx (docs/dev/inference.md §1.5) — 16-bit activations, + scrfd_10g_640.a16w8.onnx per-channel 8-bit weights, calibrated on 300 photographs + 2d106det_b1.a16w8.onnx the landmarks, likewise -The int8 files are **derived** by `tools/quantise-models.sh` from the f32 ones beside them and -travel with them: the engine loads the `.int8.onnx` sibling when the device's backend wants it and -the canonical file otherwise, and a library indexed on the int8 form records it as a different -detector (`scrfd_500m_i8+w600k_mbf`), because it finds a different set of faces. Every other -platform ignores them. The embedder has no int8 form and never will (§7 of the same document). +These are **derived** by `tools/quantise-models.sh` from the f32 files beside them and travel with +them: the engine loads the `.a16w8.onnx` sibling when the device's backend wants it and the +canonical file otherwise, and a library indexed on that form records it as a different detector +(`scrfd_500m_a16+w600k_mbf`), because it finds a different set of faces. Every other platform +ignores them, and the Windows installer leaves them out. The embedder and the eye classifiers have +no quantised form: the embedder's vectors must compare across devices (inference.md §7), and the +classifiers cost a millisecond on the CPU. + +The detectors were int8 until §1.5 measured them on the tablet: int8 found 94–95% of f32's faces +at 40–80 px, A16W8 all of them. A tablet that indexed under the int8 ids keeps those rows. **A clone without git-lfs gets a ~130-byte pointer where each model should be.** Both packagers check for exactly that and refuse, rather than shipping the pointer and failing inside tract on the user's diff --git a/models/face/scrfd_10g_640.a16w8.onnx b/models/face/scrfd_10g_640.a16w8.onnx new file mode 100644 index 0000000..e320a57 --- /dev/null +++ b/models/face/scrfd_10g_640.a16w8.onnx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:062244085f92a0533e847ff8298ad94ab4b339e7c19ba412590d2a70960e2b07 +size 4388394 diff --git a/models/face/scrfd_10g_640.int8.onnx b/models/face/scrfd_10g_640.int8.onnx deleted file mode 100644 index 8a0396d..0000000 --- a/models/face/scrfd_10g_640.int8.onnx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5fca4624192a2b73d00cc60367b96ca140448af3d27e9930b4d8f29ae80d2ac6 -size 4382606 diff --git a/models/face/scrfd_2.5g_640.a16w8.onnx b/models/face/scrfd_2.5g_640.a16w8.onnx new file mode 100644 index 0000000..642db86 --- /dev/null +++ b/models/face/scrfd_2.5g_640.a16w8.onnx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:837ae627c6f187973b15852c190fa7faf810157aae593ec11305a332651a7700 +size 940265 diff --git a/models/face/scrfd_2.5g_640.int8.onnx b/models/face/scrfd_2.5g_640.int8.onnx deleted file mode 100644 index da498ff..0000000 --- a/models/face/scrfd_2.5g_640.int8.onnx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e2f4482ffcd398c50068a196468bdfeb7e77faed76392675f436a2f6a60b74d1 -size 934472 diff --git a/models/face/scrfd_500m_640.a16w8.onnx b/models/face/scrfd_500m_640.a16w8.onnx new file mode 100644 index 0000000..0aff6d4 --- /dev/null +++ b/models/face/scrfd_500m_640.a16w8.onnx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9c039f0688e20d90dc0d65854eee4fd00b525246035fcdb2f82bcbd8b74a6b05 +size 792792 diff --git a/models/face/scrfd_500m_640.int8.onnx b/models/face/scrfd_500m_640.int8.onnx deleted file mode 100644 index ec9af9b..0000000 --- a/models/face/scrfd_500m_640.int8.onnx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7796596ea665f8cef53e0809557168a17a12d3969c45813d3ce70da7e0a11804 -size 788801 diff --git a/models/inpaint/migan-512.a16w16.onnx b/models/inpaint/migan-512.a16w16.onnx new file mode 100644 index 0000000..c64bd43 --- /dev/null +++ b/models/inpaint/migan-512.a16w16.onnx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d8d98d1d5729aa12f6203f19cd190a3e3883c8ceb69ecd18f46afbd3deab67a7 +size 15378985 diff --git a/models/keypoints/xfeat-1024.int8.onnx b/models/keypoints/xfeat-1024.int8.onnx new file mode 100644 index 0000000..1646169 --- /dev/null +++ b/models/keypoints/xfeat-1024.int8.onnx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:715e20e45b7dabc42830faba3651c4487b055f6cca310139b0af7cbf052d29e9 +size 765301 diff --git a/models/keypoints/xfeat-768.int8.onnx b/models/keypoints/xfeat-768.int8.onnx new file mode 100644 index 0000000..5854447 --- /dev/null +++ b/models/keypoints/xfeat-768.int8.onnx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:88fd68593b5f03bfaa0cd19889999449286e657d86b62414cfc92662df11e2eb +size 765297 diff --git a/models/scene/yolo26s-sem-ade20k.a16w16.onnx b/models/scene/yolo26s-sem-ade20k.a16w16.onnx new file mode 100644 index 0000000..3e19659 --- /dev/null +++ b/models/scene/yolo26s-sem-ade20k.a16w16.onnx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:21f8d15506b7e4f1ca0b2b97a1210570fd3470c74ec68b90dece8f5d4582daa9 +size 12836893 diff --git a/models/segment/yolo26n-seg.a16w16.onnx b/models/segment/yolo26n-seg.a16w16.onnx new file mode 100644 index 0000000..fb5a5a2 --- /dev/null +++ b/models/segment/yolo26n-seg.a16w16.onnx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a7089b768ccb4e198d4a7c9c7adb2f019bca9c841ed0b9c2b5f90a3e06f21774 +size 6224820 diff --git a/ui/dr-ui/src/inference.rs b/ui/dr-ui/src/inference.rs index 3c944d4..3a99725 100644 --- a/ui/dr-ui/src/inference.rs +++ b/ui/dr-ui/src/inference.rs @@ -46,12 +46,14 @@ pub fn init(runtime_dirs: Vec) { cache_dir: crate::library::inference_cache_dir(), models, embedded: { - let [landscape, portrait] = dr_pano::xfeat::embedded_model_bytes(); - vec![ - (Role::Segmenter, dr_segment::embedded_model_bytes()), - (Role::Keypoints, landscape), - (Role::Keypoints, portrait), - ] + let [landscape, portrait] = dr_pano::xfeat::embedded_models(); + let tag = |role| move |(form, bytes)| (role, form, bytes); + dr_segment::embedded_models() + .into_iter() + .map(tag(Role::Segmenter)) + .chain(landscape.into_iter().map(tag(Role::Keypoints))) + .chain(portrait.into_iter().map(tag(Role::Keypoints))) + .collect() }, ceiling: None, threads: 0, @@ -81,7 +83,7 @@ pub fn user_runtime_dir() -> PathBuf { /// /// Reads the shared and system directories only. An account-private model /// directory can override the file `library::face_models` loads, but not -/// which form the backend wants, and the int8 sibling is something a +/// which form the backend wants, and the quantised sibling is something a /// packager ships, not something a user drops in. pub fn detector_form(detector: FaceDetector) -> Form { let canonical = crate::library::shared_model(detector.file_name()) @@ -92,8 +94,11 @@ pub fn detector_form(detector: FaceDetector) -> Form { /// The `faces.model_id` this device indexes under with `detector`. pub fn model_id(detector: FaceDetector) -> &'static str { match detector_form(detector) { - Form::F32 => detector.model_id(), Form::Int8 => detector.model_id_int8(), + Form::A16W8 => detector.model_id_a16w8(), + // No detector is offered in A16W16 (inference.md §1.5); were one, it + // would be the network f32 is to the last bit that a person can see. + Form::F32 | Form::A16W16 => detector.model_id(), } }