Skip to main content

adaptation_matrix

Function adaptation_matrix 

Source
pub fn adaptation_matrix<T, I, O, M>(
    input_wp: Option<Xyz<I, T>>,
    output_wp: Option<Xyz<O, T>>,
) -> Matrix3<Xyz<I, T>, Xyz<O, T>>
where T: Zero + Arithmetics + Clone, I: WhitePoint<T> + HasXyzMeta<XyzMeta = I>, O: WhitePoint<T> + HasXyzMeta<XyzMeta = O>, M: XyzToLms<T> + LmsToXyz<T>, Xyz<I, T>: IntoColorUnclamped<Lms<WithLmsMatrix<I, M>, T>>, Xyz<O, T>: IntoColorUnclamped<Lms<WithLmsMatrix<O, M>, T>>,
Expand description

Construct a one-step chromatic adaptation matrix.

The matrix uses the von Kries method to fully adapt a color from an input white point to an output white point, using a provided LMS matrix. See the chromatic_adaptation module for more details.

§Static White Points

The input_wp and output_wp parameters represent the color “white” for the input and output colors, respectively. Passing None will make it use I and O to calculate the white points:

use palette::{
    chromatic_adaptation::adaptation_matrix,
    lms::matrix::Bradford,
    convert::Convert,
    white_point::{A, C},
    Xyz,
};
use approx::assert_relative_eq;

// Adapts from white point A to white point C:
let matrix = adaptation_matrix::<f32, A, C, Bradford>(None, None);

// Explicit types added for illustration.
let input: Xyz<A> = Xyz::new(0.315756, 0.162732, 0.015905);
let output: Xyz<C> = matrix.convert(input);

let expected = Xyz::new(0.257963, 0.139776, 0.058825);
assert_relative_eq!(output, expected, epsilon = 0.0001);

§Dynamic White Points

It’s also possible to use arbitrary colors as white points, as long as they are brighter than black. This can be useful for white balancing a photo, where we may want to use the same static white point for both the input and the output:

use palette::{
    chromatic_adaptation::adaptation_matrix,
    lms::matrix::Bradford,
    convert::{FromColorUnclampedMut, Convert},
    Srgb, Xyz,
};
use approx::assert_relative_eq;

fn simple_white_balance(image: &mut [Srgb<f32>]) {
    // Temporarily convert to Xyz:
    let mut image = <[Xyz<_, f32>]>::from_color_unclamped_mut(image);

    // Find the average Xyz color:
    let sum = image.iter().fold(Xyz::new(0.0, 0.0, 0.0), |sum, &c| sum + c);
    let average = sum / image.len() as f32;

    // Considering the average color to be "white", this matrix adapts from the
    // average to default sRGB white, D65:
    let matrix = adaptation_matrix::<_, _, _, Bradford>(Some(average), None);

    for pixel in &mut *image {
        *pixel = matrix.convert(*pixel);
    }
}

// Minimal test case. This one pixel becomes gray after white balancing:
let mut image = [Srgb::new(0.8, 0.3, 0.9)];
simple_white_balance(&mut image);

let expected = Srgb::new(0.524706, 0.524706, 0.524706);
assert_relative_eq!(image[0], expected, epsilon = 0.00001);

See also Wikipedia - Von Kries transform.