Skip to content

Commit e550b59

Browse files
narrowstacksclaude
andcommitted
fix: ensure GPU/CPU parity for auto-color pipeline stage
- Move auto-color to run before auto-exposure in GPU pipeline to match CPU ordering (was running after exposure, causing different results) - Scale warm scene r_gain by auto_color_strength instead of hard-coding 1.12 boost, so strength=0 produces no change as expected Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 4f58e61 commit e550b59

1 file changed

Lines changed: 12 additions & 11 deletions

File tree

crates/invers-core/src/gpu/pipeline.rs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,14 @@ fn execute_pipeline(
129129
apply_gains(ctx, image, adjusted_gains, [0.0, 0.0, 0.0], pixel_count)?;
130130
}
131131

132-
// Stage 8: Auto-exposure (if enabled)
132+
// Stage 8: Auto-color (if enabled) - runs before auto-exposure to match CPU pipeline
133+
// Uses midtone-weighted means to prioritize neutral midtones
134+
if options.enable_auto_color {
135+
let color_gains = compute_auto_color_gains_fullimage(image, ctx, options)?;
136+
apply_gains(ctx, image, color_gains, [0.0, 0.0, 0.0], pixel_count)?;
137+
}
138+
139+
// Stage 9: Auto-exposure (if enabled)
133140
if options.enable_auto_exposure {
134141
let exposure_gain = compute_exposure_gain_cpu(image, ctx, options)?;
135142
let strength = options.auto_exposure_strength;
@@ -142,13 +149,6 @@ fn execute_pipeline(
142149
apply_exposure(ctx, image, options.exposure_compensation, 1.0, pixel_count)?;
143150
}
144151

145-
// Stage 11: Auto-color (if enabled) - after exposure so it corrects the final tonal distribution
146-
// Uses midtone-weighted means to prioritize neutral midtones
147-
if options.enable_auto_color {
148-
let color_gains = compute_auto_color_gains_fullimage(image, ctx, options)?;
149-
apply_gains(ctx, image, color_gains, [0.0, 0.0, 0.0], pixel_count)?;
150-
}
151-
152152
// Stage 12: Color matrix (if film preset has one)
153153
if !options.skip_color_matrix {
154154
if let Some(ref preset) = options.film_preset {
@@ -1050,10 +1050,11 @@ fn compute_auto_color_gains_fullimage(
10501050
// If R < G (cyan cast), use full strength to correct
10511051
let r_is_warm = avg_r > avg_g;
10521052
let r_gain = if r_is_warm {
1053-
// Warm scene: boost warmth significantly for pleasing film look
1054-
// Film inversions typically lose warmth - compensate with ~12% boost
1053+
// Warm scene: boost warmth for pleasing film look, scaled by strength
1054+
// Film inversions typically lose warmth - compensate with up to ~12% boost
10551055
// This mimics the warmer tones of professional scanner output
1056-
1.12 // 12% boost when scene is warm
1056+
// At strength=0, no change (1.0); at strength=1.0, full boost (1.12)
1057+
1.0 + strength * 0.12
10571058
} else {
10581059
// Cyan cast: boost R toward G
10591060
1.0 + strength * (r_to_neutral - 1.0)

0 commit comments

Comments
 (0)