Closed notevenstickmen closed 1 month ago
The 5 principal and 5 intermediate hues form 10 arcs on the hue circle. The arcs do not intersect (except at the endpoints) and cover the hue circle. Each arc is parameterized from 0 to 10. Hues 10R and 0YR are endpoints of adjacent arcs and denote exactly the same hue. The Munsell system prefers the former notation. The 4'th number 8 in the assertion is the Hue Index of intermediate hue RP.
I claim that XYZ should really be chromatically adapted from Illuminant D65 (the sRGB standard) to Illuminant C (with which the Munsell system was designed). So there should be another conversion (a chromatic adaptation transform) between sRGB_to_XYZ() and XYZ_to_xyY(). As a test, when R=G=B, then xy should be about (0.3101,0.3163), and not (0.3127,0.3290).
I do not know anything about the apparent requirement that Chroma is in [2,50].
I am having the same problems even using the Illuminant C mentioned by glenndavis52, my code is the following:
def RGB2Munsell(RGB):
# RGB is expected to have the following format: (R, G, B), where RGB values range from 0 to 1
# e.g: RGB = (0.96820063, 0.74966853, 0.60617991)
C = colour.CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["C"]
Munsell = colour.xyY_to_munsell_colour(colour.XYZ_to_xyY(colour.sRGB_to_XYZ(RGB, C)))
return Munsell
By introducing as sRGB the following array: array([ 0.5, 0.5, 0.5]) (Trying to achieve the testing of R=G=B) I get the same error AssertionError: "array([ 5.42874897e+00, 5.23531097e+00, 1.08571452e-03, 7.00000000e+00])" specification chroma must be normalised to domain [2, 50]!
@notevenstickmen I've been looking at it (https://colour.readthedocs.io/en/develop/_modules/colour/notation/munsell.html) and it seems that our error is comming from the fact that on the colour.xyY_to_munsell_colour() step, the library internally converts xyY to munsell specification and then munsell specification to munsell colour:
`
def xyY_to_munsell_colour( xyY: ArrayLike, hue_decimals: int = 1, value_decimals: int = 1, chroma_decimals: int = 1, ) -> str | NDArrayStr: """ Convert from CIE xyY colourspace to Munsell colour.
Parameters
----------
xyY
*CIE xyY* colourspace array.
hue_decimals
Hue formatting decimals.
value_decimals
Value formatting decimals.
chroma_decimals
Chroma formatting decimals.
Returns
-------
:class:`str` or :class:`numpy.NDArrayFloat`
*Munsell* colour.
Notes
-----
+------------+-----------------------+---------------+
| **Domain** | **Scale - Reference** | **Scale - 1** |
+============+=======================+===============+
| ``xyY`` | [0, 1] | [0, 1] |
+------------+-----------------------+---------------+
References
----------
:cite:`Centorea`, :cite:`Centore2012a`
Examples
--------
>>> xyY = np.array([0.38736945, 0.35751656, 0.59362000])
>>> xyY_to_munsell_colour(xyY)
'4.2YR 8.1/5.3'
"""
specification = to_domain_10(
xyY_to_munsell_specification(xyY), _munsell_scale_factor()
)
shape = list(specification.shape)
decimals = (hue_decimals, value_decimals, chroma_decimals)
munsell_colour = np.reshape(
np.array(
[
munsell_specification_to_munsell_colour(a, *decimals)
for a in np.reshape(specification, (-1, 4))
]
),
shape[:-1],
)
return str(munsell_colour) if shape == [4] else munsell_colour
`
If we see the function munsell_specification_to_munsell_colour in detail we will find where the error is popping:
`
def munsell_specification_to_munsell_colour( specification: ArrayLike, hue_decimals: int = 1, value_decimals: int = 1, chroma_decimals: int = 1, ) -> str: """ Convert from Munsell Colorlab specification to given Munsell colour.
Parameters
----------
specification
*Munsell* *Colorlab* specification.
hue_decimals
Hue formatting decimals.
value_decimals
Value formatting decimals.
chroma_decimals
Chroma formatting decimals.
Returns
-------
:class:`str`
*Munsell* colour.
Examples
--------
>>> munsell_specification_to_munsell_colour(np.array([np.nan, 5.2, np.nan, np.nan]))
'N5.2'
>>> munsell_specification_to_munsell_colour(np.array([10, 2.0, 4.0, 7]))
'10.0R 2.0/4.0'
"""
hue, value, chroma, code = tsplit(normalise_munsell_specification(specification))
if is_grey_munsell_colour(specification):
return MUNSELL_GRAY_EXTENDED_FORMAT.format(value, value_decimals)
else:
hue = round(hue, hue_decimals)
attest(
0 <= hue <= 10,
f'"{specification!r}" specification hue must be normalised to '
f"domain [0, 10]!",
)
value = round(value, value_decimals)
attest(
0 <= value <= 10,
f'"{specification!r}" specification value must be normalised to '
f"domain [0, 10]!",
)
chroma = round(chroma, chroma_decimals)
attest(
2 <= chroma <= 50,
f'"{specification!r}" specification chroma must be normalised to '
f"domain [2, 50]!",
)
code_values = MUNSELL_HUE_LETTER_CODES.values()
code = round(code, 1)
attest(
code in code_values,
f'"{specification!r}" specification code must one of "{code_values}"!',
)
if value == 0:
return MUNSELL_GRAY_EXTENDED_FORMAT.format(value, value_decimals)
else:
hue_letter = MUNSELL_HUE_LETTER_CODES.first_key_from_value(code)
return MUNSELL_COLOUR_EXTENDED_FORMAT.format(
hue,
hue_decimals,
hue_letter,
value,
value_decimals,
chroma,
chroma_decimals,
)
`
Specifically the error is produced here:
`
chroma = round(chroma, chroma_decimals) attest( 2 <= chroma <= 50, f'"{specification!r}" specification chroma must be normalised to ' f"domain [2, 50]!", )
`
But I will need further assistance with why that is happening when going from sRGB to Munsell colour, some help with it wil be much appreciated. I am using the colour.CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["C"] illuminant. There was a ColourWarning regarding ColourUsageWarning: "array([ 0.37326798, 0.37285041, 0.09701717])" is not within "MacAdam" limits for illuminant "C"! For the moment what I am doing is the following modification since it solves the problem of a Chroma under 2 e.g: Chroma = 1.8, but I would like to have a better solution that requires less original colours modification:
` def RGB2Munsell(RGB):
# RGB is expected to have the following format: (R, G, B), where RGB values range from 0 to 1
# e.g: RGB = (0.96820063, 0.74966853, 0.60617991)
C = colour.CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["C"] # C or D65
XYZ = colour.sRGB_to_XYZ(RGB, C)
xyY = colour.XYZ_to_xyY(XYZ)
MunsellSpecification = colour.notation.munsell.xyY_to_munsell_specification(xyY)
if MunsellSpecification[2]<2:
MunsellSpecification[2] = np.ceil(MunsellSpecification[2])
Munsell = colour.notation.munsell.munsell_specification_to_munsell_colour(MunsellSpecification)
return Munsell
`
Hello,
This is a side effect of using the colour.colour.sRGB_to_XYZ
definition which introduces minor precision issues because our sRGB colourspace uses the 4 decimal places rounded matrices from IEC 61966-2-1:1999: https://github.com/colour-science/colour/blob/develop/colour/models/rgb/datasets/srgb.py#L67
The consequence is a loss of precision and the CIE xyY value becomes [ 0.31007559 0.31616194 0.21404116]
instead of [ 0.31006 0.31616 0.21404114]
which is perfectly neutral. This fails under the grey threshold test here: https://github.com/colour-science/colour/blob/develop/colour/notation/munsell.py#L1076 I think I will increase the threshold value and make it a global that can be changed also.
For the time being you could to the sRGB conversion as follows because BT.709 uses a computed matrix and will not suffer from precision issues:
xyY = colour.XYZ_to_xyY(colour.RGB_to_XYZ(colour.cctf_decoding([0.5, 0.5, 0.5]), colour.RGB_COLOURSPACES["ITU-R BT.709"], C))
The develop
branch should now have reduced threshold value.
Hi all, I tried the workaround presented in the recent response, but still get the same error. I am using version 0.4.4 of Colour-Science. My original RGB value on a 255-scale is [94, 89, 88]. I use that array as a replacement for the entirety of the "colour.cctf_decoding([0.5, 0.5, 0.5])" term in the recent response, but am still receiving the "specification chroma must be normalised to domain [2, 50]".
For added context, the input array for "colour.notation.munsell.munsell_specification_to_munsell_colour" is [ 8.71423617, 6.49948406, 1. , 7. ].
I'm wondering what I might be doing wrong in implementing this solution? My apologies in advance if there is a self-evident solution I'm missing here, I'm a bit new to using the Colour-Science capabilities. Thanks for any help you can provide!
On Fri, Sep 13, 2024 at 7:11 PM dedores @.***> wrote:
Hi all, I tried the workaround presented in the recent response, but still get the same error. I am using version 0.4.4 of Colour-Science. My original RGB value on a 255-scale is [94, 89, 88]. I use that array as a replacement for the entirety of the "colour.cctf_decoding([0.5, 0.5, 0.5])" term in the recent response, but am still receiving the "specification chroma must be normalised to domain [2, 50]".
Judging by this code fragment from munsell.py chroma = round(chroma, chroma_decimals) attest( 2 <= chroma <= 50, f'"{specification!r}" specification chroma must be normalised to ' f"domain [2, 50]!", ) it appears that Chroma less than 2 is not supported.
Using this online color converter:
http://gluonics.com:85/converter.html
and entering RGB = [94, 89, 88] (then press the "signal RGB" button), I get Munsell HVC = 0.32YR 3.8/0.37 . So the correct Chroma is 0.37, which is less than 2.
Glenn
Hi Glenn, thanks for the quick response! That makes sense. It does appear that these values are not supported. Thanks for taking a look at this.
Closing this one as it should be fixed.
Been watching this in the background as I am having the same issue.
@KelSolaar can you elaborate on "should be"?
Do you mean it has been fixed by other code changes and will appear in the next release?
nvm, figured it out...
Question
I'm having problems converting some colours from sRGB to Munsell. I'm no expert in this field so can well believe I'm doing something daft but I don't know what.
I have a spreadsheet (2547 rows) of the main Munsell colours with their sRGB equivalents (I think this came via Paul Centore's work; the numbers check back to this anyway). I've written routine to convert sRGB to Munsell & have been using this spreadsheet to check my results.
Code: import colour import numpy as np rgb = np.array([141,116,121]) XYZ = colour.sRGB_to_XYZ(rgb / 255) xyY = colour.XYZ_to_xyY(XYZ) munsell = colour.xyY_to_munsell_colour(xyY, chroma_decimals=1)
Issues:
I have about 55 Assertation errors e.g. Most of the colours here seem to be relatively low value & chroma but not all e.g. 02.5RP-9-02 RGB(241,222,239) AssertionError: ""array([ 3.08954081, 9.02276054, 1.84797444, 8. ])"" specification chroma must be normalised to domain [2, 50]! The 4th argument looks v strange to me & all cases have this format i.e. n.spaces.
I'm getting quite a lot of hue discrepances in those areas where one hue family changes to another. e.g.
10.0R -7-08 RGB( 242,150,128) my result: 0.1YR 7.0/8.0 10.0R -7-10 RGB( 254,144,114) my result: 0.1YR 7.0/9.9 10.0R -7-12 RGB( 255,136,97) my result: 0.6YR 6.8/11.0
I most cases, the sub-hue group in my result is < 1 rather than 10.0.
Can anyone advise me?