Leave the profile's look table off by default

Every raw rendered through a camera profile — the library's DNGs with an
embedded profile, and CR2s given one — now renders differently: more
colourful in near-neutral tones. The profile's look table is no longer
applied unless its slider is raised; PROFILE_LOOK names the strength the
profile states.

Against the photographer's earlier exports with no look applied, the default
rendering scores the same with the look table at 100, 50 or 0 (held-out MSE
140, 140, 143), and is 9 % more colourful at 0: the table lowers the
saturation of near-neutral tones, which is exactly where the default
rendering was short of those exports. The user chose more colour.
This commit is contained in:
2026-10-04 05:28:15 -04:00
parent 5a8c3e4c40
commit 5ffd54ba43
4 changed files with 31 additions and 10 deletions
+16 -4
View File
@@ -18,7 +18,7 @@ use dr_decode::{CfaPattern, CropRect, RawImage};
use dr_gpu::{AdjustPass, Demosaicer, GpuContext};
use dr_pipeline::descriptor::{Attribute, LocalizedKey, OpDescriptor, OpId, ParamId};
use dr_pipeline::operation::{Operation, Stage, Uniform};
use dr_pipeline::ops::camera_profile::{apply_reference, CameraProfile, APPLY, LOOK};
use dr_pipeline::ops::camera_profile::{apply_reference, CameraProfile, APPLY, LOOK, PROFILE_LOOK};
use dr_types::{HueSatTable, ProfileOrigin, ProfileTables, Transfer};
const SIZE: u32 = 16;
@@ -151,6 +151,14 @@ fn render(ctx: &GpuContext, raw: &RawImage, op: CameraProfile) -> Vec<[u8; 3]> {
pixels.chunks_exact(4).map(|p| [p[0], p[1], p[2]]).collect()
}
/// The profile at the strength it states — the look table on, as the
/// reference applies it at 1.0. Not the default, which leaves it off (D21).
fn as_stated() -> CameraProfile {
let mut op = CameraProfile::new();
op.set_param(LOOK, PROFILE_LOOK);
op
}
fn encode(c: [f32; 3]) -> [i32; 3] {
c.map(|v| (Transfer::Srgb.encode(v.clamp(0.0, 1.0)) * 255.0).round() as i32)
}
@@ -183,8 +191,12 @@ fn the_shader_agrees_with_the_cpu_reference() {
return;
};
let tables = strong_tables();
let got = render(&ctx, &frame(Some(tables.clone())), CameraProfile::new());
assert_agrees(&got, |c| apply_reference(&tables, c, 1.0), "at defaults");
let got = render(&ctx, &frame(Some(tables.clone())), as_stated());
assert_agrees(
&got,
|c| apply_reference(&tables, c, 1.0),
"as the profile states it",
);
let mut doubled = CameraProfile::new();
doubled.set_param(LOOK, 200.0);
@@ -279,7 +291,7 @@ fn the_libraries_adobe_standard_renders_as_the_reference_does() {
let tables = dr_decode::dcp::embedded_in(&bytes)
.expect("Adobe Standard")
.tables(5000.0, ProfileOrigin::Embedded);
let got = render(&ctx, &frame(Some(tables.clone())), CameraProfile::new());
let got = render(&ctx, &frame(Some(tables.clone())), as_stated());
for (i, (c, g)) in colours().into_iter().zip(&got).enumerate() {
let want = encode(apply_reference(&tables, c, 1.0));
let g = g.map(i32::from);
+11 -2
View File
@@ -47,9 +47,18 @@ pub const LOOK: ParamId = ParamId("look");
/// The look's strength at which the LookTable is applied as the profile
/// states it, in percent.
pub const DEFAULT_LOOK: f32 = 100.0;
pub const PROFILE_LOOK: f32 = 100.0;
/// The look's default strength: off.
///
/// Measured, not chosen. Against the photographer's earlier exports with no
/// look applied, the default rendering scores the same with the table at 100,
/// 50 or 0 (held-out MSE 140, 140, 143), and is 9 % more colourful without
/// it: the table desaturates near-neutral tones, which is exactly where the
/// default rendering was short of those exports. The table stays one slider
/// away for anyone who wants the profile's look.
pub const DEFAULT_LOOK: f32 = 0.0;
/// Twice the profile's look.
pub const MAX_LOOK: f32 = 200.0;
pub const MAX_LOOK: f32 = 2.0 * PROFILE_LOOK;
/// Entries of the buffer's header, before the entries themselves: one
/// `vec4` describing each table — `(hue divisions, saturation divisions,
+1 -1
View File
@@ -2801,7 +2801,7 @@ curve a power of 1.5/1.4 about grey (`REFERENCE_CONTRAST` is where the curve is
brightness needs nothing: baseline exposure and the curve together land where the earlier exports do. The
profile's look strength, vibrance and saturation bought nothing measurable on those exports. The
user chose to change every photograph rather than keep edited ones on the old rendering. The
fitting tools live outside the repository (`darkroom-lrfit`).
fitting tools live outside the repository (`darkroom-lrfit`). *Amended 2026-10-04:* the look strength now defaults to 0. It scored the same at 100, 50 and 0 (held-out MSE 140, 140, 143) and the rendering is 9 % more colourful without it — the table desaturates near-neutral tones, where the default was short of those exports; the user chose more colour.
*Amended earlier the same day:* the default was **not** decided. The measurement below was against
Lightroom previews of photographs carrying the user's Lightroom edits — a house look of HSL
File diff suppressed because one or more lines are too long