BoxDragon / kolor

Color space conversion math made simple
38 stars 7 forks source link

Non-standard WhitePoint values #14

Open quietvoid opened 5 months ago

quietvoid commented 5 months ago

Hi. I noticed that the WhitePoint values have 5 decimals of precision, whereas most other implementations are truncated to 4. Is there some documentation on how they were derived? I'm asking because it's complicated to compare my results with others due to the precision being different and sometimes rounding wrong if the precision is > 4 decimals.

For example:

XYZ_to_xyY(WhitePoint::D65.values().clone().into(), WhitePoint::D65)
=> 0.3127266146810121, 0.32902313032606195, 1

Whereas most other implementations would result in 0.3127, 0.3290, 1

On Wikipedia, D65 is defined as xy 0.31272, 0.32903 but those numbers are not obtained when rounding to 5 decimals from the conversion in kolor. Instead I would end up with xy 0.31273, 0.32902.

D65 in kolor is XYZ 0.95047, 1.0, 1.08883 While if we use 4 decimal places: 0.95045593, 1.0 , 1.08905775 It's not really close and to me it seems like kolor is wrong.

kabergstrom commented 4 months ago

Hi, happy to see kolor is being used for stuff! Sorry for the late reply, I seem to have missed this GitHub notification.

I got my whitepoint definitions from Bruce Lindbloom's page, where he says they come from ASTM E308-01 except B which comes from Wyszecki & Stiles, p. 769. http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html

I'm happy to correct the constants if they prove to be wrong, though it would be good to know which source should be considered more authoritative. Btw, were you using kolor-64 to calculate these? kolor uses 32-bit floats by default. https://docs.rs/crate/kolor-64/latest

quietvoid commented 4 months ago

Thanks for the link.

I did attempt to change the constant here: https://github.com/quietvoid/kolor/commit/908568665404034e5e4447f6f4f8ae7d337a8c40, but it ended up producing different unexpected values for some conversions. So I just left it as it is and it seemed fine.

I am using kolor-64 but the result I took from colour's calculations. Which are based on D65 being x=0.3127, y=0.3290

For example:

let rgb = Vec3::new(0.5, 0.5, 0.5);
let target_rgb_to_xyz = ColorConversion::new(kolor_64::spaces::LINEAR_SRGB, CIE_XYZ);

let xyz = target_rgb_to_xyz.convert(rgb);
let lab = XYZ_to_CIELAB(xyz, WhitePoint::D65);

With current constants:

XYZ = [0.4752350000000001, 0.5, 0.5444150000000001]
Lab = [76.06926101415557, 0, 0]

With 4 decimals

XYZ = [0.4752350000000001, 0.5, 0.5444150000000001]
Lab = [76.06926101415557, 0.001958237610621971, 0.01106631839304928]

As I'd expect the AB values to be 0.0, I guess the current constants are OK. I'm not quite sure why there's a large error for CIELAB here.

In Python with colour, it does return something close:

>>> colour.XYZ_to_Lab([0.4752350000000001, 0.5, 0.5444150000000001], D65)
array([ 76.06926101,   0.00195865,   0.01106636])

So I suppose the Bruce Lindbloom calcs are different but I don't know who is right or wrong here. In colour the CIELAB conversion has different constants too, due to using fractions. But even changing them doesn't do anything.

kabergstrom commented 4 months ago

Interesting, I'll dig into the difference between the implementations. For the XYZ ->LAB conversion, I tried implementing Bruce Lindbloom's math: http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html In this page he does call out some ambiguity in two constants, where there is an "actual standard" and "intent of standard", with some difference in the decimals. But it may be down to some error in my implementation too.