Read the true white level, and clamp the sensor stage at both ends
Two corrections to the sensor stage, found while chasing magenta highlights. Neither is the cause of that — see below — but both are wrong on their own terms. `white_level` took the *first* of rawler's per-channel saturation points. On a Canon 6D that reports 15070 while the data reaches 16383, so every sample above it was treated as brighter than white. It takes the maximum now. The normalisation clamped its floor and not its ceiling, so those over-white samples passed through as values above 1.0. Clamped at both ends. **This does not fix the pink.** Measured on _MG_8596.CR2, exported and looked at: the subject renders correctly and only the blown sky is magenta. A fully clipped pixel is (1,1,1) in raw, the as-shot balance multiplies it to (1.93, 1.00, 1.68), and the camera matrix turns that into R 2.88, G 0.51, B 2.03 — red and blue clip at one, green does not, and the result is magenta. It is correct white balance applied to already-saturated data, which is the classic highlight-clipping cast and needs highlight desaturation to fix: a pixel at saturation carries no colour information and must be rendered neutral, not balanced. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -481,11 +481,17 @@ pub fn decode(bytes: &[u8]) -> Result<RawImage, DecodeError> {
|
||||
data,
|
||||
cfa_pattern: cfa,
|
||||
black_level,
|
||||
// The **maximum** of the per-channel saturation points, not the
|
||||
// first. Taking one channel's value under-reports the others', and
|
||||
// every sample above the level it is normalised against becomes a
|
||||
// highlight the pipeline treats as brighter than white.
|
||||
white_level: image
|
||||
.whitelevel
|
||||
.0
|
||||
.first()
|
||||
.map(|v| *v as u16)
|
||||
.iter()
|
||||
.copied()
|
||||
.max()
|
||||
.map(|v| v as u16)
|
||||
.unwrap_or(u16::MAX),
|
||||
wb_coeffs,
|
||||
color_matrix,
|
||||
|
||||
Reference in New Issue
Block a user