mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-31 21:48:53 -06:00
Merge PR #923
This commit is contained in:
@@ -358,6 +358,9 @@ pub struct Plan<'a> {
|
||||
pub geo: u64,
|
||||
/// Key of the white-balanced, retouched, denoised image (and of the planes computed from it).
|
||||
pub lin_key: u64,
|
||||
/// [`Self::lin_key`] without the noise-reduction settings: draft renders reuse the smooth
|
||||
/// planes of an image that differs from this one only in its noise reduction.
|
||||
pub pre_nr_key: u64,
|
||||
/// Red eye / pet eye corrections with their detected pupils, in output pixels.
|
||||
pub eyes: Vec<redeye::EyeK>,
|
||||
/// The whole frame's dehaze airlight, for a windowed render (which can't see the whole frame).
|
||||
@@ -418,7 +421,7 @@ pub fn plan<'a>(src: &Rgb32f, info: &SourceInfo, s: &'a DevelopSettings, req: &R
|
||||
let (wb_t, wb_tint) = local::effective_wb(info, s);
|
||||
let eyes = redeye::resolve(src, &s.red_eye, s.orientation, &frame, w, h, px_per_long);
|
||||
let d = &s.detail;
|
||||
let lin_key = hash_of((
|
||||
let pre_nr_key = hash_of((
|
||||
geo,
|
||||
[wb_t, wb_tint, info.as_shot_temp, info.as_shot_tint].map(f64::to_bits),
|
||||
format!("{:?}", s.spots),
|
||||
@@ -427,10 +430,10 @@ pub fn plan<'a>(src: &Rgb32f, info: &SourceInfo, s: &'a DevelopSettings, req: &R
|
||||
format!("{:?}", s.optics),
|
||||
// and the local tone mapping
|
||||
local_tone::enabled(info, s),
|
||||
[d.nr_luminance, d.nr_detail, d.nr_color, d.nr_color_detail, d.nr_color_smoothness].map(f64::to_bits),
|
||||
src_long,
|
||||
));
|
||||
Plan { settings, frame, w, h, px_per_long, src_long, geo, lin_key, eyes, fixed_air, keep, mattes: info.mattes.clone() }
|
||||
let lin_key = hash_of((pre_nr_key, [d.nr_luminance, d.nr_detail, d.nr_color, d.nr_color_detail, d.nr_color_smoothness].map(f64::to_bits)));
|
||||
Plan { settings, frame, w, h, px_per_long, src_long, geo, lin_key, pre_nr_key, eyes, fixed_air, keep, mattes: info.mattes.clone() }
|
||||
}
|
||||
|
||||
/// Whether the scene-linear stage needs work only the CPU does (defringe, spot removal).
|
||||
@@ -521,7 +524,12 @@ fn render_impl(src: Src<'_>, info: &SourceInfo, s: &DevelopSettings, req: &Rende
|
||||
lap("wb/spots/nr", &mut t);
|
||||
let mut planes = match cached.map(|e| e.planes) {
|
||||
Some(p) if p.key == lin_key => p,
|
||||
_ => local::Planes { key: lin_key, ..Default::default() },
|
||||
// A draft while noise reduction changes (a slider drag): the wide, edge-aware planes of the
|
||||
// image before the change stand in (noise reduction barely moves a blur that wide); the
|
||||
// per-pixel log luminance and the fine texture band are redone. Keyed so a full-quality
|
||||
// render never takes them.
|
||||
Some(p) if req.quality == Quality::Draft && p.pre_nr == plan.pre_nr_key => p.for_other_nr(),
|
||||
_ => local::Planes { key: lin_key, pre_nr: plan.pre_nr_key, ..Default::default() },
|
||||
};
|
||||
let mut prep = local::prepare(lin.clone(), s, frame, px_per_long, req.quality, &mut planes, plan.mattes.as_deref());
|
||||
if let Some(a) = plan.fixed_air {
|
||||
|
||||
@@ -210,6 +210,8 @@ pub fn log_lum(c: [f32; 3]) -> f32 {
|
||||
#[derive(Clone, Default)]
|
||||
pub(crate) struct Planes {
|
||||
pub key: u64,
|
||||
/// The image's key without its noise reduction (`Plan::pre_nr_key`).
|
||||
pub pre_nr: u64,
|
||||
pub log_l: Option<Arc<Plane>>,
|
||||
pub base: Option<(u32, Arc<Plane>)>,
|
||||
pub clarity: Option<(u32, Arc<Plane>)>,
|
||||
@@ -220,6 +222,15 @@ pub(crate) struct Planes {
|
||||
pub chroma: Option<(u32, Arc<Rgb32f>)>,
|
||||
}
|
||||
|
||||
impl Planes {
|
||||
/// The planes that still fit an image differing from this one only in noise reduction, close
|
||||
/// enough for a draft: the wide blurs (base, clarity, dehaze, chromaticity). Its `key` matches
|
||||
/// no image, so only another draft takes them.
|
||||
pub fn for_other_nr(self) -> Planes {
|
||||
Planes { key: 0, pre_nr: self.pre_nr, log_l: None, texture: None, ..self }
|
||||
}
|
||||
}
|
||||
|
||||
/// Reuse `slot` if it was computed at `sigma`, else compute and store it.
|
||||
fn plane_at(slot: &mut Option<(u32, Arc<Plane>)>, sigma: f32, f: impl FnOnce() -> Plane) -> Arc<Plane> {
|
||||
match slot {
|
||||
|
||||
@@ -206,7 +206,13 @@ fn stage_cache_matches_uncached_render_through_a_slider_session() {
|
||||
for req in [&full, &draft] {
|
||||
let a = render_cached(&src, &info, &s, req, &cache).image;
|
||||
let b = render(&src, &info, &s, req).image;
|
||||
assert_eq!(a, b, "step {i} {:?}", req.quality);
|
||||
if req.quality == Quality::Draft && i == 6 {
|
||||
// a draft while noise reduction changes keeps the wide planes of the image before it
|
||||
let d = max_diff(&a, &b);
|
||||
assert!(d <= 3, "step {i} draft: {d}");
|
||||
} else {
|
||||
assert_eq!(a, b, "step {i} {:?}", req.quality);
|
||||
}
|
||||
}
|
||||
}
|
||||
assert_eq!(cache.len(), 2);
|
||||
@@ -220,6 +226,35 @@ fn stage_cache_matches_uncached_render_through_a_slider_session() {
|
||||
assert_eq!(render_cached(&other, &info, &s, &full, &cache).image, render(&other, &info, &s, &full).image);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drafts_keep_wide_planes_across_a_noise_reduction_change_and_full_renders_do_not() {
|
||||
use crate::{Quality, StageCache, render_cached};
|
||||
use std::sync::Arc;
|
||||
let src = Arc::new(scene());
|
||||
let info = SourceInfo { raw: true, ..Default::default() };
|
||||
let cache = StageCache::default();
|
||||
let draft = RenderRequest { quality: Quality::Draft, ..RenderRequest::fit(160, 160) };
|
||||
let full = RenderRequest { quality: Quality::Full, ..draft };
|
||||
let mut s = typical_edits();
|
||||
s.effects.clarity = 60.0;
|
||||
render_cached(&src, &info, &s, &draft, &cache);
|
||||
let clarity_plane = |c: &StageCache| c.entries.lock().unwrap().first().and_then(|e| e.planes.clarity.clone()).unwrap().1;
|
||||
let first = clarity_plane(&cache);
|
||||
s.detail.nr_luminance = 50.0;
|
||||
render_cached(&src, &info, &s, &draft, &cache);
|
||||
assert!(Arc::ptr_eq(&first, &clarity_plane(&cache)), "a draft redid the clarity plane for a noise-reduction change");
|
||||
for v in [10.0, 45.0, 80.0] {
|
||||
s.detail.nr_luminance = v;
|
||||
s.detail.nr_color = v / 2.0;
|
||||
let a = render_cached(&src, &info, &s, &draft, &cache).image;
|
||||
let d = max_diff(&a, &render(&src, &info, &s, &draft).image);
|
||||
// close to the exact draft (a few levels at edges, where clarity is strong)
|
||||
assert!(d <= 8, "nr {v}: {d}");
|
||||
// a full-quality render after the drafts is exact (it never takes the stand-in planes)
|
||||
assert_eq!(render_cached(&src, &info, &s, &full, &cache).image, render(&src, &info, &s, &full).image, "nr {v}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exposure_after_spatial_filters_equals_exposing_the_source() {
|
||||
// The pipeline filters the image before exposure and applies exposure per pixel; filters on
|
||||
|
||||
Reference in New Issue
Block a user