colour-science / colour

Colour Science for Python
https://www.colour-science.org
BSD 3-Clause "New" or "Revised" License
2.14k stars 263 forks source link

[BUG]: sRGB to CAM whitepoint (D65) inconsistent? #1228

Closed mesvam closed 8 months ago

mesvam commented 1 year ago

Description

>>> colour.convert([1,1,1], 'sRGB', 'CIECAM16')
CAM_Specification_CIECAM16(J=1.0000047100604554, C=0.030807836056794494, h=0.58215001241517916, s=0.1341768280812253, Q=1.2370826529555712, M=0.022271720052874228, H=0.6651251954371753, HC=None)

Shouldn't C = M = s = 0 for white?

Likewise, the reverse conversion doesn't return white in sRGB

>>> colour.convert([1,0,0], 'CIECAM16 JMh', 'sRGB')
array([ 1.01413632,  0.99558034,  0.99206694])

This happens even if whitepoint is explicitly specified

>>> colour.convert([1,1,1], 'sRGB', 'CIECAM16',
    XYZ_w=colour.TVS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D65"]/100
)
CAM_Specification_CIECAM16(J=1.0000097376333374, C=0.030820422308728883, h=0.58330827532400575, s=0.13420410236408256, Q=1.2370850728361689, M=0.022280818954825046, H=0.66647572590820925, HC=None)

Environment Information

===============================================================================
*                                                                             *
*   Interpreter :                                                             *
*       python : 3.11.5 | packaged by Anaconda, Inc. | (main, Sep 11 2023,    *
*   13:26:23) [MSC v.1916 64 bit (AMD64)]                                     *
*                                                                             *
*   colour-science.org :                                                      *
*       colour : 0.4.3                                                        *
*                                                                             *
*   Runtime :                                                                 *
*       imageio : 2.31.4                                                      *
*       matplotlib : 3.8.0                                                    *
*       networkx : 3.1                                                        *
*       numpy : 1.25.2                                                        *
*       pandas : 2.1.1                                                        *
*       scipy : 1.11.3                                                        *
*       tqdm : 4.65.0                                                         *
*                                                                             *
===============================================================================

defaultdict(dict,
            {'Interpreter': {'python': '3.11.5 | packaged by Anaconda, Inc. | (main, Sep 11 2023, 13:26:23) [MSC v.1916 64 bit (AMD64)]'},
             'colour-science.org': {'colour': '0.4.3'},
             'Runtime': {'imageio': '2.31.4',
              'matplotlib': '3.8.0',
              'networkx': '3.1',
              'numpy': '1.25.2',
              'pandas': '2.1.1',
              'scipy': '1.11.3',
              'tqdm': '4.65.0'}})
KelSolaar commented 1 year ago

Those are unfortunately expected and related to #1227 as the model fitting does not match exactly yield the input whitepoint which can be verified by converting Lightness J=100 to CIE XYZ:

import colour

print(
    colour.xy_to_XYZ(
        colour.CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D65"]
    )
)

XYZ = colour.sRGB_to_XYZ([1, 1, 1])
print(XYZ)

XYZ = colour.CIECAM02_to_XYZ(
    colour.CAM_Specification_CIECAM02(100, 0, 0),
    **colour.appearance.CAM_KWARGS_CIECAM02_sRGB
)
print(XYZ / XYZ[1])

XYZ = colour.CIECAM16_to_XYZ(
    colour.CAM_Specification_CIECAM16(100, 0, 0),
    **colour.appearance.CAM_KWARGS_CIECAM02_sRGB
)
print(XYZ / XYZ[1])
[ 0.95045593  1.          1.08905775]
[ 0.9505  1.      1.089 ]
[ 0.95854868  1.          1.07335032]
[ 0.95857463  1.          1.07307191]

Cheers,

Thomas

mesvam commented 1 year ago

I don't quite understand. Are you saying this is a problem inherent in the model specification of CIECAM16? or just this specific package's implementation?

KelSolaar commented 1 year ago

The model itself.

mesvam commented 1 year ago

It's actually a pretty big difference, dE2000 > 2

import colour
CAM_white = colour.convert([1,0,0], 'CIECAM16 JMh', 'CIE XYZ')
sRGB_white = colour.convert([1,1,1], 'sRGB', 'CIE XYZ')
colour.difference.delta_E_CIE2000(
    colour.convert(CAM_white, 'CIE XYZ', 'CIE Lab')*100,
    colour.convert(sRGB_white, 'CIE XYZ', 'CIE Lab')*100,
)
2.2151311708854311

And it happens no matter the reference white point.

D50 = colour.TVS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D50']/100
CAM_white = colour.convert([1,0,0], 'CIECAM16 JMh', 'CIE XYZ', XYZ_w=D50)
colour.difference.delta_E_CIE2000(
    colour.convert(D50, 'CIE XYZ', 'CIE Lab')*100,
    colour.convert(CAM_white, 'CIE XYZ', 'CIE Lab')*100
)
1.6821909479182446

I'm pretty unnerved to be honest. Isn't the model supposed to use the given white point as the reference? How could it be so far off?

So what's the correct way of dealing with this if we actually want M=0 to correspond to the given white point? When I use discount_illuminant=True, the value is much closer.

colour.convert([1,0,0], 'CIECAM16 JMh', 'sRGB', discount_illuminant=True)
array([ 0.99993111,  1.00004358,  1.00003272])

But is that the correct way to do it? I'm not sure what the interpretation is here.

tjdcs commented 1 year ago

Try setting D=1 in the cam call.

On Wed, Nov 29, 2023 at 6:46 PM mesvam @.***> wrote:

It's actually a pretty big difference, dE2000 > 2

import colour CAM_white = colour.convert([1,0,0], 'CIECAM16 JMh', 'CIE XYZ') sRGB_white = colour.convert([1,1,1], 'sRGB', 'CIE XYZ') colour.difference.delta_E_CIE2000( colour.convert(CAM_white, 'CIE XYZ', 'CIE Lab')100, colour.convert(sRGB_white, 'CIE XYZ', 'CIE Lab')100, ) 2.2151311708854311

And it happens no matter the reference white point.

D50 = colour.TVS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D50']/100 CAM_white = colour.convert([1,0,0], 'CIECAM16 JMh', 'CIE XYZ', XYZ_w=D50) colour.difference.delta_E_CIE2000( colour.convert(D50, 'CIE XYZ', 'CIE Lab')100, colour.convert(CAM_white, 'CIE XYZ', 'CIE Lab')100 ) 1.6821909479182446

I'm pretty unnerved to be honest. Isn't the model supposed to use the given white point as the reference? How could it be so far off?

So what's the correct way of dealing with this if we actually want M=0 to correspond to the given white point? When I use discount_illuminant=True, the value is much closer.

colour.convert([1,0,0], 'CIECAM16 JMh', 'sRGB', discount_illuminant=True) array([ 0.99993111, 1.00004358, 1.00003272])

But is that the correct way to do it?

— Reply to this email directly, view it on GitHub https://github.com/colour-science/colour/issues/1228#issuecomment-1833020576, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABS4J5UR5OBCQRDSSED2JR3YG7XRPAVCNFSM6AAAAAA7WNFNT2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTQMZTGAZDANJXGY . You are receiving this because you are subscribed to this thread.Message ID: @.***>

KelSolaar commented 8 months ago

Closing this one as there is not much to be done!