From 5a8c3e4c401e72f93c3d59530f4014eb69628598 Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Sun, 4 Oct 2026 03:44:52 -0400 Subject: [PATCH] Run each model on the Hexagon in the form measured to hold it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The engine knew f32 and int8, and gave the Hexagon int8 for every role it served. Measured on the tablet itself (inference.md §1.5), int8 lost 5% of the detector's faces at 40-80 px, moved the landmarks 1.5 px, emptied the segmenter's scores and cost the denoiser 5-9 dB; fp16 the HTP refuses outright. `Form` gains A16W8 and A16W16, and `Rung::form` now names one per role: detectors and landmarks A16W8, the segmenter, scene model, border filler and denoiser A16W16, XFeat int8. The embedder and the eye classifiers stay on the CPU. Each loader resolves its `.
.onnx` sibling; the segmenter and XFeat, compiled into the binary, embed their quantised forms on Android only and pick through `choose_embedded`. The probe, the compile step and the cache fingerprint follow the form instead of assuming int8. Detectors on the new form write `scrfd_*_a16+w600k_mbf`, and `model_ids` answers for all three spellings. On the tablet (ORT 1.29 + QNN 2.42), each shipped file against f32 on the same inputs, and against the CPU's f32 time: SCRFD 500m/2.5g/10g A16W8 100% of faces in every band 4.2/5.1/9.0 ms vs 17/56/198 landmarks A16W8 0.25 px in the 192 crop 0.5 ms vs 2.8 YOLO26n-seg A16W16 98.2% found, mask IoU 0.994 12.9 ms vs 90 scene model A16W16 98.9% of cells agree 15 ms vs 151 MI-GAN A16W16 41 dB from f32 in the fill 87 ms vs 488 XFeat int8 pano alignment 0.45 px (f32's own spread 0.41) 6.5 ms vs 58 denoiser A16W16 0.00 dB at every ISO 95 ms vs 1510 a tile Face numbers are over public COCO val2017 photographs, not a library. The APK carries the siblings (BUNDLED 15 -> 19; the old int8 detectors removed), about 43 MB more. The Windows installer and its CI count skip them; the Arch and Flatpak packages list their files and never had them. The ladder example takes a role per model, which is how the per-role forms above were seen landing on the NPU from the real probe. --- .gitea/workflows/build-and-test.yml | 9 +- apps/darkroom-android/src/lib.rs | 31 ++- core/dr-denoise/src/onnx.rs | 6 +- core/dr-face/src/detect.rs | 6 +- core/dr-face/src/landmarks.rs | 13 +- core/dr-inference-engine/examples/ladder.rs | 58 ++++-- core/dr-inference-engine/src/engines.rs | 10 +- core/dr-inference-engine/src/lib.rs | 198 ++++++++++++++------ core/dr-inference-engine/src/probe.rs | 39 ++-- core/dr-pano/build.rs | 13 +- core/dr-pano/src/migan.rs | 3 +- core/dr-pano/src/xfeat.rs | 43 ++++- core/dr-segment/build.rs | 18 +- core/dr-segment/src/lib.rs | 2 +- core/dr-segment/src/scene.rs | 22 ++- core/dr-segment/src/semantic.rs | 44 +++-- core/dr-types/src/settings.rs | 37 +++- docker/windows/package.sh | 6 +- docs/dev/denoise.md | 11 ++ docs/dev/inference.md | 68 ++++++- docs/dev/panorama.md | 8 +- docs/dev/traceability.md | 20 +- docs/dev/windows.md | 4 +- models/LICENCE.md | 7 + models/denoise/mosaic-1408.a16w16.onnx | 3 + models/face/2d106det_b1.a16w8.onnx | 3 + models/face/README.md | 22 ++- models/face/scrfd_10g_640.a16w8.onnx | 3 + models/face/scrfd_10g_640.int8.onnx | 3 - models/face/scrfd_2.5g_640.a16w8.onnx | 3 + models/face/scrfd_2.5g_640.int8.onnx | 3 - models/face/scrfd_500m_640.a16w8.onnx | 3 + models/face/scrfd_500m_640.int8.onnx | 3 - models/inpaint/migan-512.a16w16.onnx | 3 + models/keypoints/xfeat-1024.int8.onnx | 3 + models/keypoints/xfeat-768.int8.onnx | 3 + models/scene/yolo26s-sem-ade20k.a16w16.onnx | 3 + models/segment/yolo26n-seg.a16w16.onnx | 3 + ui/dr-ui/src/inference.rs | 21 ++- 39 files changed, 550 insertions(+), 208 deletions(-) create mode 100644 models/denoise/mosaic-1408.a16w16.onnx create mode 100644 models/face/2d106det_b1.a16w8.onnx create mode 100644 models/face/scrfd_10g_640.a16w8.onnx delete mode 100644 models/face/scrfd_10g_640.int8.onnx create mode 100644 models/face/scrfd_2.5g_640.a16w8.onnx delete mode 100644 models/face/scrfd_2.5g_640.int8.onnx create mode 100644 models/face/scrfd_500m_640.a16w8.onnx delete mode 100644 models/face/scrfd_500m_640.int8.onnx create mode 100644 models/inpaint/migan-512.a16w16.onnx create mode 100644 models/keypoints/xfeat-1024.int8.onnx create mode 100644 models/keypoints/xfeat-768.int8.onnx create mode 100644 models/scene/yolo26s-sem-ade20k.a16w16.onnx create mode 100644 models/segment/yolo26n-seg.a16w16.onnx 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(), } }