示例#1
0
def Luv_to_XYZ(cobj, *args, **kwargs):
    """
    Convert from Luv to XYZ.
    """
    illum = cobj.get_illuminant_xyz()
    # Without Light, there is no color. Short-circuit this and avoid some
    # zero division errors in the var_a_frac calculation.
    if cobj.luv_l <= 0.0:
        xyz_x = 0.0
        xyz_y = 0.0
        xyz_z = 0.0
        return XYZColor(
            xyz_x, xyz_y, xyz_z,
            observer=cobj.observer, illuminant=cobj.illuminant)

    # Various variables used throughout the conversion.
    cie_k_times_e = color_constants.CIE_K * color_constants.CIE_E
    u_sub_0 = (4.0 * illum["X"]) / (illum["X"] + 15.0 * illum["Y"] + 3.0 * illum["Z"])
    v_sub_0 = (9.0 * illum["Y"]) / (illum["X"] + 15.0 * illum["Y"] + 3.0 * illum["Z"])
    var_u = cobj.luv_u / (13.0 * cobj.luv_l) + u_sub_0
    var_v = cobj.luv_v / (13.0 * cobj.luv_l) + v_sub_0

    # Y-coordinate calculations.
    if cobj.luv_l > cie_k_times_e:
        xyz_y = math.pow((cobj.luv_l + 16.0) / 116.0, 3.0)
    else:
        xyz_y = cobj.luv_l / color_constants.CIE_K

    # X-coordinate calculation.
    xyz_x = xyz_y * 9.0 * var_u / (4.0 * var_v)
    # Z-coordinate calculation.
    xyz_z = xyz_y * (12.0 - 3.0 * var_u - 20.0 * var_v) / (4.0 * var_v)

    return XYZColor(
        xyz_x, xyz_y, xyz_z, illuminant=cobj.illuminant, observer=cobj.observer)
示例#2
0
    def test_srgb_conversion_to_xyz_d65(self):
        """
        sRGB's native illuminant is D65. This is a straightforward conversion.
        """

        xyz = convert_color(self.color, XYZColor)
        self.assertColorMatch(xyz, XYZColor(0.294, 0.457, 0.103))
    def test_observer_guard(self):
        xyz = XYZColor(1, 1, 1, illuminant="d65", observer="10")

        def _ipt_conversion():
            return convert_color(xyz, IPTColor)

        self.assertRaises(ValueError, _ipt_conversion)
示例#4
0
def Lab_to_XYZ(cobj, *args, **kwargs):
    """
    Convert from Lab to XYZ
    """
    illum = cobj.get_illuminant_xyz()
    xyz_y = (cobj.lab_l + 16.0) / 116.0
    xyz_x = cobj.lab_a / 500.0 + xyz_y
    xyz_z = xyz_y - cobj.lab_b / 200.0

    if math.pow(xyz_y, 3) > color_constants.CIE_E:
        xyz_y = math.pow(xyz_y, 3)
    else:
        xyz_y = (xyz_y - 16.0 / 116.0) / 7.787

    if math.pow(xyz_x, 3) > color_constants.CIE_E:
        xyz_x = math.pow(xyz_x, 3)
    else:
        xyz_x = (xyz_x - 16.0 / 116.0) / 7.787

    if math.pow(xyz_z, 3) > color_constants.CIE_E:
        xyz_z = math.pow(xyz_z, 3)
    else:
        xyz_z = (xyz_z - 16.0 / 116.0) / 7.787

    xyz_x = (illum["X"] * xyz_x)
    xyz_y = (illum["Y"] * xyz_y)
    xyz_z = (illum["Z"] * xyz_z)

    return XYZColor(
        xyz_x, xyz_y, xyz_z, observer=cobj.observer, illuminant=cobj.illuminant)
示例#5
0
def RGB_SS():
    X = np.sum(SunShine * CIEx)
    Y = np.sum(SunShine * CIEy)
    Z = np.sum(SunShine * CIEz)
    tmp=XYZColor(X*100/Y,Y*100/Y,Z*100/Y)
    rgb=convert_color(tmp,sRGBColor)
    return rgb   
示例#6
0
    def test_srgb_conversion_to_xyz_d50(self):
        """
        sRGB's native illuminant is D65. Test the XYZ adaptations by setting
        a target illuminant to something other than D65.
        """

        xyz = convert_color(self.color, XYZColor, target_illuminant='D50')
        self.assertColorMatch(xyz, XYZColor(0.313, 0.460, 0.082))
示例#7
0
def convert_xyz_to_rgb(
        xyz_pixel: ndarray) \
        -> ndarray:
    """ Convert color in numpy.ndarray from XYZ to RGB color space.
    """
    # ----------------------------------------------------------------------- #
    return array(
        convert_color(XYZColor(*xyz_pixel, illuminant='d65', observer='2'),
                      sRGBColor).get_value_tuple())
def RGB_to_XYZ(cobj, target_illuminant=None, *args, **kwargs):
    """
    RGB to XYZ conversion. Expects RGB values between 0 and 255.

    Based off of: http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html
    """
    # Will contain linearized RGB channels (removed the gamma func).
    linear_channels = {}

    if isinstance(cobj, sRGBColor):
        for channel in ["r", "g", "b"]:
            V = getattr(cobj, "rgb_" + channel)
            if V <= 0.04045:
                linear_channels[channel] = V / 12.92
            else:
                linear_channels[channel] = math.pow((V + 0.055) / 1.055, 2.4)
    elif isinstance(cobj, BT2020Color):
        if kwargs.get("is_12_bits_system"):
            a, b, c = 1.0993, 0.0181, 0.081697877417347  # noqa
        else:
            a, b, c = 1.099, 0.018, 0.08124794403514049  # noqa
        for channel in ["r", "g", "b"]:
            V = getattr(cobj, "rgb_" + channel)
            if V <= c:
                linear_channels[channel] = V / 4.5
            else:
                linear_channels[channel] = math.pow((V + (a - 1)) / a, 1 / 0.45)
    else:
        # If it's not sRGB...
        gamma = cobj.rgb_gamma

        for channel in ["r", "g", "b"]:
            V = getattr(cobj, "rgb_" + channel)
            linear_channels[channel] = math.pow(V, gamma)

    # Apply an RGB working space matrix to the XYZ values (matrix mul).
    xyz_x, xyz_y, xyz_z = apply_RGB_matrix(
        linear_channels["r"],
        linear_channels["g"],
        linear_channels["b"],
        rgb_type=cobj,
        convtype="rgb_to_xyz",
    )

    if target_illuminant is None:
        target_illuminant = cobj.native_illuminant

    # The illuminant of the original RGB object. This will always match
    # the RGB colorspace's native illuminant.
    illuminant = cobj.native_illuminant
    xyzcolor = XYZColor(xyz_x, xyz_y, xyz_z, illuminant=illuminant)
    # This will take care of any illuminant changes for us (if source
    # illuminant != target illuminant).
    xyzcolor.apply_adaptation(target_illuminant)

    return xyzcolor
示例#9
0
def run():
    # Assign configuration variables.
    # The configuration check takes care they are present.
    conf = load_json(CONFIG_FILE)

    try:
        identity = conf[args.host].get('identity')
        psk = conf[args.host].get('key')
        api_factory = APIFactory(host=args.host, psk_id=identity, psk=psk)
    except KeyError:
        identity = uuid.uuid4().hex
        api_factory = APIFactory(host=args.host, psk_id=identity)

        try:
            psk = yield from api_factory.generate_psk(args.key)
            print('Generated PSK: ', psk)

            conf[args.host] = {'identity': identity,
                               'key': psk}
            save_json(CONFIG_FILE, conf)
        except AttributeError:
            raise PytradfriError("Please provide your Key")

    api = api_factory.request

    gateway = Gateway()

    devices_command = gateway.get_devices()
    devices_commands = yield from api(devices_command)
    devices = yield from api(devices_commands)

    lights = [dev for dev in devices if dev.has_light_control]

    rgb = (0, 0, 102)

    # Convert RGB to XYZ using a D50 illuminant.
    xyz = convert_color(sRGBColor(rgb[0], rgb[1], rgb[2]), XYZColor,
                        observer='2', target_illuminant='d65')
    xy = int(xyz.xyz_x), int(xyz.xyz_y)

    #  Assuming lights[3] is a RGB bulb
    xy_command = lights[3].light_control.set_xy_color(xy[0], xy[1])
    yield from api(xy_command)

    #  Assuming lights[3] is a RGB bulb
    xy = lights[3].light_control.lights[0].xy_color

    #  Normalize Z
    Z = int(lights[3].light_control.lights[0].dimmer/254*65535)
    xyZ = xy+(Z,)
    rgb = convert_color(XYZColor(xyZ[0], xyZ[1], xyZ[2]), sRGBColor,
                        observer='2', target_illuminant='d65')
    rgb = (int(rgb.rgb_r), int(rgb.rgb_g), int(rgb.rgb_b))
    print(rgb)

    yield from asyncio.sleep(120)
示例#10
0
    def test_adobe_conversion_to_xyz_d65(self):
        """
        Adobe RGB's native illuminant is D65, like sRGB's. However, sRGB uses
        different conversion math that uses gamma, so test the alternate logic
        route for non-sRGB RGB colors.
        """

        adobe = AdobeRGBColor(0.482, 0.784, 0.196)
        xyz = convert_color(adobe, XYZColor)
        self.assertColorMatch(xyz, XYZColor(0.230, 0.429, 0.074))
示例#11
0
def jch(start, amount, saturation, luminosity):
    k = 360/amount
    ans = ((luminosity, saturation, start + i*k) for i in range(amount))
    ans = (cspace_convert(color, "JCh", "XYZ1") for color in ans)
    ans = (XYZColor(*color.tolist()) for color in ans)
    ans = (convert_color(color, AdobeRGBColor, target_illuminant="d65") for color in ans)
    ans = ((color.clamped_rgb_r, color.clamped_rgb_g, color.clamped_rgb_b) for color in ans)
    ans = ((round(r*255), round(g*255), round(b*255)) for r, g, b in ans)
    ans = (("#%02x%02x%02x" % (r, g, b), r, g, b) for r, g, b in ans)
    return ans
示例#12
0
def RGB(Spec,griglia,depth):
    S_col=ipl.interp1d(griglia,Spec,kind='linear')(lambdas_col)
    zxy=XYZ(S_col, depth)
    rgb=np.empty(zxy.shape)
    rgb_ss=RGB_SS()
    for ii, xyz in enumerate(zxy):
        tmp=XYZColor(xyz[0],xyz[1],xyz[2])
        tmp2=convert_color(tmp,sRGBColor)
        rgb[ii,:]=np.array([tmp2.rgb_r/rgb_ss.rgb_r,tmp2.rgb_g/rgb_ss.rgb_g,tmp2.rgb_b/rgb_ss.rgb_b])
    return rgb
示例#13
0
    def test_adobe_conversion_to_xyz_d50(self):
        """
        Adobe RGB's native illuminant is D65, so an adaptation matrix is
        involved here. However, the math for sRGB and all other RGB types is
        different, so test all of the other types with an adaptation matrix
        here.
        """

        adobe = AdobeRGBColor(0.482, 0.784, 0.196)
        xyz = convert_color(adobe, XYZColor, target_illuminant='D50')
        self.assertColorMatch(xyz, XYZColor(0.247, 0.431, 0.060))
示例#14
0
def Spectral_to_XYZ(cobj, illuminant_override=None, *args, **kwargs):
    """
    Converts spectral readings to XYZ.
    """

    # If the user provides an illuminant_override numpy array, use it.
    if illuminant_override:
        reference_illum = illuminant_override
    else:
        # Otherwise, look up the illuminant from known standards based
        # on the value of 'illuminant' pulled from the SpectralColor object.
        try:
            reference_illum = spectral_constants.REF_ILLUM_TABLE[
                cobj.illuminant]
        except KeyError:
            raise InvalidIlluminantError(cobj.illuminant)

    # Get the spectral distribution of the selected standard observer.
    if cobj.observer == '10':
        std_obs_x = spectral_constants.STDOBSERV_X10
        std_obs_y = spectral_constants.STDOBSERV_Y10
        std_obs_z = spectral_constants.STDOBSERV_Z10
    else:
        # Assume 2 degree, since it is theoretically the only other possibility.
        std_obs_x = spectral_constants.STDOBSERV_X2
        std_obs_y = spectral_constants.STDOBSERV_Y2
        std_obs_z = spectral_constants.STDOBSERV_Z2

    # This is a NumPy array containing the spectral distribution of the color.
    sample = cobj.get_numpy_array()

    # The denominator is constant throughout the entire calculation for X,
    # Y, and Z coordinates. Calculate it once and re-use.
    denom = std_obs_y * reference_illum

    # This is also a common element in the calculation whereby the sample
    # NumPy array is multiplied by the reference illuminant's power distribution
    # (which is also a NumPy array).
    sample_by_ref_illum = sample * reference_illum

    # Calculate the numerator of the equation to find X.
    x_numerator = sample_by_ref_illum * std_obs_x
    y_numerator = sample_by_ref_illum * std_obs_y
    z_numerator = sample_by_ref_illum * std_obs_z

    xyz_x = x_numerator.sum() / denom.sum()
    xyz_y = y_numerator.sum() / denom.sum()
    xyz_z = z_numerator.sum() / denom.sum()

    return XYZColor(xyz_x,
                    xyz_y,
                    xyz_z,
                    observer=cobj.observer,
                    illuminant=cobj.illuminant)
示例#15
0
def xyY_to_XYZ(cobj, *args, **kwargs):
    """
    Convert from xyY to XYZ.
    """
   
    xyz_x = (cobj.xyy_x * cobj.xyy_Y) / cobj.xyy_y
    xyz_y = cobj.xyy_Y
    xyz_z = ((1.0 - cobj.xyy_x - cobj.xyy_y) * xyz_y) / cobj.xyy_y
    
    return XYZColor(
        xyz_x, xyz_y, xyz_z, illuminant=cobj.illuminant, observer=cobj.observer)
def IPT_to_XYZ(cobj, *args, **kwargs):
    """
    Converts XYZ to IPT.
    """

    ipt_values = numpy.array(cobj.get_value_tuple())
    lms_values = numpy.dot(numpy.linalg.inv(IPTColor.conversion_matrices['lms_to_ipt']), ipt_values)

    lms_prime = numpy.sign(lms_values) * numpy.abs(lms_values) ** (1 / 0.43)

    xyz_values = numpy.dot(numpy.linalg.inv(IPTColor.conversion_matrices['xyz_to_lms']), lms_prime)
    return XYZColor(*xyz_values, observer='2', illuminant='d65')
示例#17
0
def example_lab_to_ipt():
    """
    This function shows a simple conversion of an XYZ color to an IPT color.
    """

    print("=== Simple Example: XYZ->IPT ===")
    # Instantiate an XYZ color object with the given values.
    xyz = XYZColor(0.5, 0.5, 0.5, illuminant="d65")
    # Show a string representation.
    print(xyz)
    # Convert to IPT.
    ipt = convert_color(xyz, IPTColor)
    print(ipt)
    print("=== End Example ===\n")
示例#18
0
def IPT_to_XYZ(cobj, *args, **kwargs):
    """
    Converts IPT to XYZ.
    """
    ipt_values = numpy.array(cobj.get_value_tuple())
    lms_values = numpy.dot(
        numpy.linalg.inv(IPTColor.conversion_matrices["lms_to_ipt"]), ipt_values
    )

    lms_prime = numpy.sign(lms_values) * numpy.abs(lms_values) ** (1 / 0.43)

    xyz_values = numpy.dot(
        numpy.linalg.inv(IPTColor.conversion_matrices["xyz_to_lms"]), lms_prime
    )
    return XYZColor(*xyz_values, observer="2", illuminant="d65")
示例#19
0
def xyY_to_XYZ(cobj, *args, **kwargs):
    """
    Convert from xyY to XYZ.
    """
    # avoid division by zero
    if cobj.xyy_y == 0.0:
        xyz_x = 0.0
        xyz_y = 0.0
        xyz_z = 0.0
    else:
        xyz_x = (cobj.xyy_x * cobj.xyy_Y) / cobj.xyy_y
        xyz_y = cobj.xyy_Y
        xyz_z = ((1.0 - cobj.xyy_x - cobj.xyy_y) * xyz_y) / cobj.xyy_y

    return XYZColor(
        xyz_x, xyz_y, xyz_z, illuminant=cobj.illuminant, observer=cobj.observer)
示例#20
0
    def xyb_to_rgba(self, x, y, dimmer):
        _x = x / 65535
        _y = y / 65535
        _z = (1.0 - _x - _y)
        # _z = (dimmer / 254 * 65535)

        rgb = convert_color(XYZColor(_x, _y, _z),
                            sRGBColor,
                            observer='2',
                            target_illuminant='d65')
        r, g, b = (rgb.rgb_r, rgb.rgb_g, rgb.rgb_b)
        a = dimmer / 254

        # rospy.loginfo('r: {}, g: {}, b: {}, a: {}'.format(r, g, b, a))

        return (r, g, b, a)
示例#21
0
def run():
    # Assign configuration variables.
    # The configuration check takes care they are present.
    api_factory = APIFactory(sys.argv[1])
    with open('gateway_psk.txt', 'a+') as file:
        file.seek(0)
        psk = file.read()
        if psk:
            api_factory.psk = psk.strip()
        else:
            psk = api_factory.generate_psk(sys.argv[2])
            print('Generated PSK: ', psk)
            file.write(psk)
    api = api_factory.request

    gateway = Gateway()

    devices_command = gateway.get_devices()
    devices_commands = api(devices_command)
    devices = api(devices_commands)
    lights = [dev for dev in devices if dev.has_light_control]

    rgb = (0, 0, 102)

    # Convert RGB to XYZ using a D50 illuminant.
    xyz = convert_color(sRGBColor(rgb[0], rgb[1], rgb[2]),
                        XYZColor,
                        observer='2',
                        target_illuminant='d65')
    xy = int(xyz.xyz_x), int(xyz.xyz_y)

    #  Assuming lights[3] is a RGB bulb
    api(lights[3].light_control.set_xy_color(xy[0], xy[1]))

    #  Assuming lights[3] is a RGB bulb
    xy = lights[3].light_control.lights[0].xy_color

    #  Normalize Z
    Z = int(lights[3].light_control.lights[0].dimmer / 254 * 65535)
    xyZ = xy + (Z, )
    rgb = convert_color(XYZColor(xyZ[0], xyZ[1], xyZ[2]),
                        sRGBColor,
                        observer='2',
                        target_illuminant='d65')
    rgb = (int(rgb.rgb_r), int(rgb.rgb_g), int(rgb.rgb_b))
    print(rgb)
示例#22
0
    def color_from_position(self, position):
        """Returns a unique RGB color for each position on the map.

        :position: TODO
        :returns: TODO

        """
        # normalize the position on the map to a position in the colorspace
        x = position[0] / self.size[0]  # / 2
        y = position[1] / self.size[1]  # /2
        z = 1 - x - y

        xyz = XYZColor(x, y, z)
        rgb = convert_color(xyz, sRGBColor)

        rgb_tuple = (rgb.clamped_rgb_r, rgb.clamped_rgb_g, rgb.clamped_rgb_b)

        return rgb_tuple
示例#23
0
def RGB_to_XYZ(cobj, target_illuminant=None, *args, **kwargs):
    """
    RGB to XYZ conversion. Expects 0-255 RGB values.

    Based off of: http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html
    """

    # Will contain linearized RGB channels (removed the gamma func).
    linear_channels = {}

    if isinstance(cobj, sRGBColor):
        for channel in ['r', 'g', 'b']:
            V = getattr(cobj, 'rgb_' + channel)
            if V <= 0.04045:
                linear_channels[channel] = V / 12.92
            else:
                linear_channels[channel] = math.pow((V + 0.055) / 1.055, 2.4)
    else:
        # If it's not sRGB...
        gamma = cobj.rgb_gamma

        for channel in ['r', 'g', 'b']:
            V = getattr(cobj, 'rgb_' + channel)
            linear_channels[channel] = math.pow(V, gamma)

    # Apply an RGB working space matrix to the XYZ values (matrix mul).
    xyz_x, xyz_y, xyz_z = apply_RGB_matrix(linear_channels['r'],
                                           linear_channels['g'],
                                           linear_channels['b'],
                                           rgb_type=cobj,
                                           convtype="rgb_to_xyz")

    if target_illuminant is None:
        target_illuminant = cobj.native_illuminant

    # The illuminant of the original RGB object. This will always match
    # the RGB colorspace's native illuminant.
    illuminant = cobj.native_illuminant
    xyzcolor = XYZColor(xyz_x, xyz_y, xyz_z, illuminant=illuminant)
    # This will take care of any illuminant changes for us (if source
    # illuminant != target illuminant).
    xyzcolor.apply_adaptation(target_illuminant)

    return xyzcolor
示例#24
0
 def test_conversion_to_xyz(self):
     xyz = convert_color(self.color, XYZColor)
     self.assertColorMatch(xyz, XYZColor(0.115, 0.099, 0.047))
示例#25
0
def XYZ2Lab(xyz):
    obj = XYZColor(*(xyz))
    lab = convert_color(XYZColor(*(xyz)), LabColor, target_illuminant='d65')

    return lab
示例#26
0
# -*- coding: utf-8 -*-
from colormath.color_appearance_models import CIECAM02
from colormath.color_objects import XYZColor

# Color stimulus
color = XYZColor(19.01, 20, 21.78)

# Illuminant
illuminant_d65 = XYZColor(95.05, 100, 108.88)

# Background relative luminance
y_b_dark = 10
y_b_light = 100

# Adapting luminance
l_a = 328.31

# Surround condition assumed to be average (see CIECAM02 documentation for values)
c = 0.69
n_c = 1
f = 1

model_a = CIECAM02(
    color.xyz_x,
    color.xyz_y,
    color.xyz_z,
    illuminant_d65.xyz_x,
    illuminant_d65.xyz_y,
    illuminant_d65.xyz_z,
    y_b_dark,
    l_a,
示例#27
0
 def test_observer_guard(self):
     xyz = XYZColor(1, 1, 1, illuminant='d65', observer='10')
     ipt_conversion = lambda: convert_color(xyz, IPTColor)
     self.assertRaises(ValueError, ipt_conversion)
 def setUp(self):
     self.color = XYZColor(0.5, 0.4, 0.1, illuminant='C')
示例#29
0
async def run():
    # Assign configuration variables.
    # The configuration check takes care they are present.
    conf = load_json(CONFIG_FILE)

    try:
        identity = conf[args.host].get("identity")
        psk = conf[args.host].get("key")
        api_factory = await APIFactory.init(host=args.host,
                                            psk_id=identity,
                                            psk=psk)
    except KeyError:
        identity = uuid.uuid4().hex
        api_factory = await APIFactory.init(host=args.host, psk_id=identity)

        try:
            psk = await api_factory.generate_psk(args.key)
            print("Generated PSK: ", psk)

            conf[args.host] = {"identity": identity, "key": psk}
            save_json(CONFIG_FILE, conf)
        except AttributeError:
            raise PytradfriError("Please provide the 'Security Code' on the "
                                 "back of your Tradfri gateway using the "
                                 "-K flag.")

    api = api_factory.request

    gateway = Gateway()

    devices_command = gateway.get_devices()
    devices_commands = await api(devices_command)
    devices = await api(devices_commands)

    lights = [dev for dev in devices if dev.has_light_control]

    rgb = (0, 0, 102)

    # Convert RGB to XYZ using a D50 illuminant.
    xyz = convert_color(
        sRGBColor(rgb[0], rgb[1], rgb[2]),
        XYZColor,
        observer="2",
        target_illuminant="d65",
    )
    xy = int(xyz.xyz_x), int(xyz.xyz_y)

    light = None
    # Find a bulb that can set color
    for dev in lights:
        if dev.light_control.can_set_color:
            light = dev
            break

    if not light:
        print("No color bulbs found")
        return

    xy_command = light.light_control.set_xy_color(xy[0], xy[1])
    await api(xy_command)

    xy = light.light_control.lights[0].xy_color

    #  Normalize Z
    Z = int(light.light_control.lights[0].dimmer / 254 * 65535)
    xyZ = xy + (Z, )
    rgb = convert_color(
        XYZColor(xyZ[0], xyZ[1], xyZ[2]),
        sRGBColor,
        observer="2",
        target_illuminant="d65",
    )
    rgb = (int(rgb.rgb_r), int(rgb.rgb_g), int(rgb.rgb_b))
    print(rgb)

    await asyncio.sleep(120)

    await api_factory.shutdown()
示例#30
0
 def test_convert_to_XYZ(self):
     xyz = convert_color(self.color, XYZColor)
     self.assertColorMatch(
         xyz,
         XYZColor(0.4497, 0.2694, 0.0196, illuminant='d65', observer='2'))