An ISCC-NBS Colour List for 'roloc'

by Paul Murrell http://orcid.org/0000-0002-3224-8858

Version 1: Wednesday 07 February 2018


Creative Commons License
This document by Paul Murrell is licensed under a Creative Commons Attribution 4.0 International License.


This report describes the development of a colour list and colour metric for the 'roloc' package that is based on the ISCC-NBS System of Color Designation.

Table of Contents:

1. Introduction

The 'roloc' package for R (Murrell, 2018a, Murrell, 2018b) was created to provide tools for transforming inscrutable colour specifications into more recognisable colour names. The colourName() function in the 'roloc' package can take a hexadecimal string colour specification, like "#123456", and produce a matching colour name.

library(roloc)
colourName("#123456")
  [1] "steelblue4"

However, the colour names that the colourName function produces are not always easily comprehensible themselves. For example, in the following result, the colour name "chartreuse" may not be meaningful to everyone, let alone the significance of "chartreuse4" compared to "chartreuse3".

cols <- hcl(c(0, 120, 240), 80, 60)
cols
  [1] "#E16A86" "#50A315" "#009ADE"
colourName(cols)
  [1] "palevioletred2" "chartreuse4"    "steelblue2"

Fortunately, the colourName function allows us to specify our own list of colour names and our own colour metric for deciding which colour name matches a colour specification. For example, the HTMLcolours colour list provides a much smaller set of colour names and the euclideanRGB colour metric determines colour matches based on euclidean distance in the sRGB colour space (Wikipedia, 2018g) (rather than the default euclidean distance in CIE LUV colour space; Wikipedia, 2018b).

colourName(cols, colourList=HTMLcolours, colourMetric=euclideanRGB)
  [1] "gray"  "olive" "teal"

While these colours are much easier to interpret, there is a danger of obtaining a misleading result. For example, it is not clear that "grey" is a good description of a colour that we previously identified as some sort of light red (palevioletred2).

What we need is a list of colours that is sufficiently large so as to provide a reasonable level of accuracy in describing colours, but uses relatively plain descriptive colour names.

This document describes the development of a colour list and colour metric based on the ISCC-NBS System of Color Designation (Wikipedia, 2018d, Judd and Kelly, 1939, Kelly and Judd, 1955), which produces colour names that have simple interpretations.

library(rolocISCCNBS)
match <- colourMatch(cols, colourList=ISCCNBScolours, colourMetric=ISCCNBSblock)
colourName(match)
  [1] "deep pink"             "vivid yellowish green" "brilliant blue"
colourSwatch(match)
plot of chunk unnamed-chunk-7

2. The ISCC-NBS System of Color Designation

The ISCC-NBS System of Color Designation comes from a collaboration between the Inter-Society Color Council (ISCC) and the National Bureau of Standards (NBS) in the 1930s. The aim was to produce a way of designating colours that would be rigorous and reliable enough to be used across a broad range of disciplines and applications, but still be easily understood by the general public.

The ISCC-NBS System describes colours in terms of their hue, using words like pink, red, orange, brown, etc, plus modifiers that relate to the saturation or chroma and the lightness or darkness of the colour, e.g., vivid, brilliant, light, dark, pale, etc.

There are 267 distinct colour names in the ISCC-NBS system, with each colour name corresponding to a "block" of colours (a region of colour space), so that all colours fall into exactly one block and hence correspond to exactly one ISCC-NBS colour name. More precisely, the ISCC-NBS System is defined in terms of the Munsell colour space (Wikipedia, 2018f), so that any Munsell colour specification falls into exactly one ISCC-NBS block and corresponds to exactly one ISCC-NBS colour name.

The images below show two views of a subset of the ISCC-NBS colour blocks in Munsell colour space. Each ISCC-NBS block is filled with a single colour (based on the centroid of the block) to indicate that all Munsell colours within that block correspond to the same ISCC-NBS colour name.

3. An ISCC-NBS colour list

In terms of the 'roloc' package, a colour list consists of a set of colour names, plus a set of sRGB colour specifications, one per colour name. Each ISCC-NBS colour name corresponds to a block of colours, rather than a single colour specification, however, there are published lists of sRGB centroids for the ISCC-NBS colour blocks (Nickerson and Newhall, 1941, Texas Precancel Club, 2018). These sRGB centroids were used to generate the ISCCNBScolours colour list in the 'rolocISCCNBS' package.

This colour list allows ISCC-NBS colour names to be used with existing colour metrics, like euclideanLUV, to convert colour specifications to ISCC-NBS colour names based on the distance from the colour specification to the ISCC-NBS centroids.

match <- colourMatch(cols, colourList=ISCCNBScolours)
colourName(match)
  [1] "deep pink"             "vivid yellowish green" "brilliant blue"

We can also produce colour swatches to help visualise the colour match, although this is a bit misleading because the colour of the swatch just represents the centroid of the relevant ISCC-NBS colour block.

colourSwatch(match)
plot of chunk unnamed-chunk-9

4. An ISCC-NBS colour metric

If we use a euclidean metric with the ISCC-NBS colour block centroids to determine distances from colour specifications, we ignore the colour block boundaries, and this can produce some unsatisfactory results. For example, the following code obtains colour names for a set of grey colour specifications, but the resulting names are not all neutral greys.

colourName(grey(1:4/5), colourList=ISCCNBScolours)
  [1] "dark grayish olive green" "dark gray"                "purplish gray"           
  [4] "light purplish gray"

Because ISCC-NBS colour names actually correspond to blocks of colour space, a more appropriate colour metric involves determining which colour block a colour specification falls within.

This is the purpose of the ISCCNBSblock colour metric function in the 'rolocISCCNBS' package. Conceptually, this function converts each colour specification into a colour block and then converts each colour in the colour list into a colour block. The distance between a colour specification and a colour name is 0 if they are in the same block or infinity if they are in different blocks.

When combined with the ISCC-NBS colour list, the ISCCNBSblock colour metric can be more efficient and just convert each colour specification into a colour block (because the colour list already corresponds to colour blocks).

If we use both the ISCC-NBS colour list and the ISCC-NBS colour metric, each colour specification corresponds to a single ISCC-NBS colour block and we obtain the ISCC-NBS colour name for that colour block. For example, the ISCC-NBS colour names for a set of sRGB greys are all neutral greys (because they all fall within neutral grey colour blocks in the ISCC-NBS System).

colourName(grey(1:4/5), colourList=ISCCNBScolours, colourMetric=ISCCNBSblock)
  [1] "black"       "dark gray"   "medium gray" "light gray"

The ISCC-NBS colour metric can also be used with other colour lists, although the results may be more interesting than useful. For example, the following code performs a colour match using the ISCC-NBS colour metric with the HTMLcolours colour list. There are 3 colour specifications and the colour list contains 16 colours, so the result is a 3-by-16 matrix, with one row per colour specification and one column per colour on the colour list. Two of the colour specifications have no match (all values on the row are Inf or NA) and one colour specification has two matches (there are two zeroes in the second row). In the former case, this means that the colour specification does not lie within the same ISCC-NBS colour block as any of the colours on the colour list. In the latter case, this means that the colour specification is in the same ISCC-NBS block as two of the colours from the colour list. There are NA values in the matrix because some colours in the colour list have not converted successfully to Munsell colours (the colour could not be represented in Munsell colour space). For example, neither "yellow", "navy", nor "blue" have converted properly. This is partly a limitation of the tools used to perform the conversion, which will be discussed more later.

match <- colourMatch(cols, colourList=HTMLcolours, colourMetric=ISCCNBSblock)
match
  $colour
  [1] "#E16A86" "#50A315" "#009ADE"
  
  $colourList
  $names
   [1] "black"   "silver"  "gray"    "white"   "maroon"  "red"     "purple"  "fuschia" "green"  
  [10] "lime"    "olive"   "yellow"  "navy"    "blue"    "teal"    "aqua"   
  
  $colours
                R         G         B
   [1,] 0.0000000 0.0000000 0.0000000
   [2,] 0.7529412 0.7529412 0.7529412
   [3,] 0.5019608 0.5019608 0.5019608
   [4,] 1.0000000 1.0000000 1.0000000
   [5,] 0.5019608 0.0000000 0.0000000
   [6,] 1.0000000 0.0000000 0.0000000
   [7,] 0.5019608 0.0000000 0.5019608
   [8,] 1.0000000 0.0000000 1.0000000
   [9,] 0.0000000 0.5019608 0.0000000
  [10,] 0.0000000 1.0000000 0.0000000
  [11,] 0.5019608 0.5019608 0.0000000
  [12,] 1.0000000 1.0000000 0.0000000
  [13,] 0.0000000 0.0000000 0.5019608
  [14,] 0.0000000 0.0000000 1.0000000
  [15,] 0.0000000 0.5019608 0.5019608
  [16,] 0.0000000 1.0000000 1.0000000
  
  attr(,"class")
  [1] "colourList"
  
  $colourDist
       [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14] [,15] [,16]
  [1,]  Inf  Inf  Inf  Inf  Inf  Inf  Inf  Inf  Inf   Inf   Inf    NA    NA    NA   Inf   Inf
  [2,]  Inf  Inf  Inf  Inf  Inf  Inf  Inf  Inf    0     0   Inf    NA    NA    NA   Inf   Inf
  [3,]  Inf  Inf  Inf  Inf  Inf  Inf  Inf  Inf  Inf   Inf   Inf    NA    NA    NA   Inf   Inf
  
  attr(,"class")
  [1] "colourMatch"

The result of this colour match is that, for two of the colour specifications the colour is unknown, but for the other colour specification there are two colour names that match.

colourNames(match)
  [[1]]
  [1] "unknown"
  
  [[2]]
  [1] "green" "lime" 
  
  [[3]]
  [1] "unknown"
colourSwatches(match)
plot of chunk unnamed-chunk-13

As previously mentioned, this may be more interesting than useful.

5. Developing the ISCC-NBS colour metric

Thanks to the existence of sRGB centroids for the ISCC-NBS System, it was very straightforward to create the ISCCNBScolours colour list. However, the development of the ISCCNBSblock colour metric was less straightforward. This section details the obstacles involved in creating the ISCC-NBS colour metric and how they were overcome.

The main problem is that the ISCC-NBS colour metric works in the ISCC-NBS colour space, which is defined in terms of regions within Munsell colour space, but the ISCCNBSblock function is given as inputs both a set of colour specifications and a colour list in terms of sRGB colour space. This means that we need a conversion from sRGB colours to Munsell colours (and on to ISCC-NBS colour blocks).

One major difficulty with this conversion is that the Munsell colour space is essentially a discrete colour space; there are only known conversions between sRGB and Munsell colours for a finite set of Munsell colours (Rochester Institute of Technology, 2018); there is no general formula.

One way to use this finite set of conversions is to just find the closest match. Two packages were found that performed this sort of sRGB to Munsell conversion: 'munsell' (Wickham, 2016) and 'aqp' (Beaudette et al., 2013). Both of these packages use euclidean distance (within CIE LUV or CIE LAB colour spaces; Wikipedia, 2018e) to determine the nearest Munsell colour match for a given sRGB colour. The problem with this approach is that different sRGB colour specifications will be converted to the same Munsell colour.

colours <- rbind(c(.09, .19, .29),
             c(.10, .20, .30),
             c(.11, .21, .31))
library(munsell)
rgb2mnsl(colours)
  [1] "2.5PB 2/4" "2.5PB 2/4" "2.5PB 2/4"
library(aqp)
rgb2munsell(colours)
      hue value chroma       sigma
  1 2.5PB     2      4 0.015824182
  2 2.5PB     2      4 0.005134962
  3 2.5PB     2      4 0.020058189

Another approach to using this finite set of sRGB-Munsell conversions is to interpolate between the known matches. This approach has been implemented by Paul Centore (Centore, 2012) in several different languages, including R, Octave, and Python. Unfortunately, the R version, the 'munsellinterpol' package (Gama and Centore, 2015), does not appear to work correctly. However, the Python version, the 'colour' package (Mansencal et al., 2017), does work and, with the help of the R package 'reticulate' (Allaire et al., 2017), can be run easily from R.

The function we need from the Python 'colour' package is called xyY_to_munsell_colour which, as the name suggests, requires colours in CIE xyY colour space (Wikipedia, 2018a), so we must first convert from sRGB to xyY. This can be achieved using functions from the 'colorscience' package (Gama, 2017), as shown below (notice that we must also perform chromatic adaptation from the D65 illuminant that is defined for sRGB to the C illuminant that is used for Munsell colours).

library(colorscience)
xyYcolours <- XYZ2xyY(RGB2XYZ(colours, illuminant="C"))

With this approach, different sRGB colours are converted to different Munsell colours; the improvement in resolution of the Munsell colours compared to the nearest-match approach is very clear, particularly in terms of hue.

library(reticulate)
colour <- import("colour")
apply(xyYcolours, 1, colour$xyY_to_munsell_colour)
  [1] "3.0PB 1.9/4.2" "2.9PB 2.0/4.2" "2.9PB 2.1/4.1"

The final step is to convert these Munsell colours to ISCC-NBS blocks, which can be performed using the functions MunsellSpecToHVC and ColorBlockFromMunsell from the 'colorscience' package.

munsellColours <- apply(xyYcolours, 1, colour$xyY_to_munsell_colour)
ColorBlockFromMunsell(MunsellSpecToHVC(munsellColours))
  [1] "dark blue" "dark blue" "dark blue"

Unfortunately, the dependence on 'reticulate', to gain access to the Python package 'colour', is a practical obstacle because it means that installation of the 'rolocISCCNBS' package requires the user to install Python (and the Python package 'colour') on their system. However, we can avoid having 'rolocISCCNBS' depend upon 'reticulate' (and Python) if we pre-calculate all sRGB to Munsell (to ISCC-NBS colour block) conversions.

Pre-calculating these conversions is possible because there are only a finite (if large) set of possible sRGB specifications in R. R stores colours using 8 bits each for red, green, and blue components (24-bit colour). This means that there are 16,777,216 (2 to the power of 24) possible colour specifications in R. Every one of those sRGB colour specifications has been converted to an ISCC-NBS colour name and the result is stored in an R character vector in the 'rolocISCCNBS' package (in a known order).

This means that the conversion from sRGB to ISCC-NBS colour name is just a table look up using the integer red, green, and blue components of the sRGB colour specification as an index into the character vector of ISCC-NBS colour names (red + green*256 + blue*256*256 + 1).

This does also mean that the 'rolocISCCNBS' package contains an R object approximately 2.7 MB in size (which takes a few seconds to load the first time that the ISCCNBSblock function is called), but it removes the need for a dependence on the 'reticulate' package (and Python) and all conversions after the first one are instantaneous.

6. Examples

A good test of how well a colour list performs is to test it on one of the colour scales that different R packages produce. The following example shows that the ISCC-NBS colour list and colour metric perform quite well on a relatively complex colour scale produced by the 'pals' package (Wright, 2017).

options(roloc.colourList=ISCCNBScolours,
    roloc.colourMetric=ISCCNBSblock)
library(pals)
colourSwatch(stepped())
plot of chunk unnamed-chunk-20

7. Discussion

The purpose of the 'rolocISCCNBS' package is to support conversions from R colour specifications to colour names using the ISCC-NBS System of Colour Designation. This works reasonably well, although the package is relatively large because of a character vector of pre-calculated sRGB-to-ISCC-NBS-block conversions and, for the same reason, the first conversion is relatively slow.

One weakness in the conversion is the fact that the sRGB-to-ISCC-NBS-block conversions are not always successful (because the conversions make use of the xyY_to_munsell_colour function from the Python package 'colour' and that function does not always succeed). For example, the conversion does not work for very dark colours ...

colour$xyY_to_munsell_colour(XYZ2xyY(RGB2XYZ(c(0, 0.1, 0.1), illuminant="C")))
  AssertionError: "0.69020652865" value must be in domain [1, 10]!

... in other cases the iterative search within xyY_to_munsell_colour does not converge on a solution ...

colour$xyY_to_munsell_colour(XYZ2xyY(RGB2XYZ(c(1, 1, 0), illuminant="C")))
  RuntimeError: Maximum inner iterations count reached without convergence!

... and there are some sRGB colours that are outside the MacAdam limits (for illuminant C; Wikipedia, 2018c) ...

colour$xyY_to_munsell_colour(XYZ2xyY(RGB2XYZ(c(0, 0, 1), illuminant="C")))
  ValueError: "[[0.14915498 0.05849607 0.07642525]]" is not within "MacAdam" limits for illuminant "C"!

In these cases, the ISCCNBSblock colour metric will produce an NA value (as we saw in the Section An ISCC-NBS colour metric), which may in turn result in either "unknown" or, in extreme cases, NA as the colour name.

A possible resolution for the MacAdam limit problem is to clamp invalid sRGB values to the nearest Munsell colour (though this is not currently supported by the xyY_to_munsell_colour function). A possible solution for the dark colour problem and for situations where xyY_to_munsell_colour does not converge could be to use a different sRGB-to-Munsell conversion. These include the more approximate conversions provided by the 'munsell' and 'aqp' packages, plus there are other published algorithms without (public/open) software implementations (ASTM, 2014, Simon and Frost, 1987)

8. Summary

The 'rolocISCCNBS' package provides a colour list and a colour metric for the 'roloc' package that allows colour specifications to be converted into colour names that are based on the ISCC-NBS System of Colour Designation. The colour list is provided as the R object 'ISCCNBScolours' and the colour metric is provided as the function 'ISCCNBSblock'; the former can be specified in the 'colourList' argument in calls to the colourName function from the 'roloc' package, and the latter can be specified in the 'colourMetric' argument to that function.

9. Technical requirements

The examples and discussion in this document relate to version 0.1 of the 'rolocISCCNBS' package, version 0.1 of the 'roloc' package, and version 1.0.6 of the 'colorscience' package (which is a fork of the original 'colorscience' package to include a bug fix for XYZ2xyY and a vectorised version of ColorBlockFromMunsell).

This report was generated within a Docker container (see Resources section below).

10. Resources

Acknowledgements

The author would like to thank Ken Knoblauch, Graham Finlayson, and Philip Green for expert pointers and advice on colour, particularly for conversions to and from the Munsell colour system.

How to cite this document

Murrell, P. (2018). "An ISCC-NBS Colour List for 'roloc'" Technical Report 2018-02, Department of Statistics, The University of Auckland. [ bib ]

11. References

[Allaire et al., 2017]
Allaire, J., Tang, Y., and Geelnard, M. (2017). reticulate: R Interface to Python. R package version 1.2. [ bib | http ]
[ASTM, 2014]
ASTM (2014). D1535-14, standard practice for specifying color by the Munsell system. www.astm.org. [ bib ]
[Beaudette et al., 2013]
Beaudette, D., Roudier, P., and O'Geen, A. (2013). Algorithms for Quantitative Pedology: A Toolkit for Soil Scientists. [ bib | www: ]
[Centore, 2012]
Centore, P. (2012). An open-source inversion algorithm for the munsell renotation. Color Research & Application, 37(6):455--464. [ bib | DOI | http ]
[Gama, 2017]
Gama, J. (2017). colorscience: Color Science Methods and Data. R package version 1.0.5. [ bib | http ]
[Gama and Centore, 2015]
Gama, J. and Centore, P. (2015). munsellinterpol: Interpolate Munsell Renotation Data from Hue/Chroma to CIE/sRGB. R package version 1.0.2. [ bib | http ]
[Ihaka et al., 2016]
Ihaka, R., Murrell, P., Hornik, K., Fisher, J. C., and Zeileis, A. (2016). colorspace: Color Space Manipulation. R package version 1.3-2. [ bib | http ]
[Judd and Kelly, 1939]
Judd, D. B. and Kelly, K. L. (1939). Method of designating colors and a dictionary. Journal of Research of the National Bureau of Standards, 23:355. [ bib ]
[Kelly and Judd, 1955]
Kelly, K. L. and Judd, D. B. (1955). The ISCC-NBS method of designating colors and a dictionary of color names. [ bib ]
[Mansencal et al., 2017]
Mansencal, T., Mauderer, M., Parsons, M., Canavan, L., Cooper, S., Shaw, N., Wheatley, K., and Crowson, K. (2017). Colour 0.3.10. [ bib | DOI | http ]
[Murrell, 2018a]
Murrell, P. (2018a). Generating colour names: The 'roloc' package for R. Technical Report 2018-01, Department of Statistics, The University of Auckland. [ bib ]
[Murrell, 2018b]
Murrell, P. (2018b). roloc: Convert colour specification to colour name. R package version 0.1. [ bib ]
[Nickerson and Newhall, 1941]
Nickerson, D. and Newhall, S. (1941). Central notations for ISCC-NBS color names. Journal of the Optical Society of America, 31:587. [ bib ]
[Rochester Institute of Technology, 2018]
Rochester Institute of Technology (2018). Munsell renotation data. https://www.rit.edu/cos/colorscience/rc_munsell_renotation.php. Accessed: 2018-02-07. [ bib ]
[Simon and Frost, 1987]
Simon, F. T. and Frost, J. A. (1987). A new method for the conversion of CIE colorimetric data to munsell notations. Color Research & Application, 12(5):256--260. [ bib | DOI | http ]
[Texas Precancel Club, 2018]
Texas Precancel Club (2018). NBS/ISCC color system. http://tx4.us/nbs-iscc.htm. Accessed: 2018-02-07. [ bib ]
[Wickham, 2016]
Wickham, C. (2016). munsell: Utilities for Using Munsell Colours. R package version 0.4.3. [ bib | www: ]
[Wikipedia, 2018a]
Wikipedia (2018a). CIE 1931 color space. https://en.wikipedia.org/wiki/CIE_1931_color_space. Accessed: 2018-02-07. [ bib ]
[Wikipedia, 2018b]
Wikipedia (2018b). CIELUV Color Space. http://en.wikipedia.org/wiki/CIELUV_color_space. Accessed: 2018-02-07. [ bib ]
[Wikipedia, 2018c]
Wikipedia (2018c). Gamut. http://en.wikipedia.org/wiki/Gamut. Accessed: 2018-02-07. [ bib ]
[Wikipedia, 2018d]
Wikipedia (2018d). ISCC-NBS system. https://en.wikipedia.org/wiki/ISCC-NBS_system. Accessed: 2018-02-07. [ bib ]
[Wikipedia, 2018e]
Wikipedia (2018e). Lab color space. http://en.wikipedia.org/wiki/Lab_color_space. Accessed: 2018-02-07. [ bib ]
[Wikipedia, 2018f]
Wikipedia (2018f). Munsell color system. http://en.wikipedia.org/wiki/Munsell_color_system. Accessed: 2018-02-07. [ bib ]
[Wikipedia, 2018g]
Wikipedia (2018g). sRGB. http://en.wikipedia.org/wiki/SRGB. Accessed: 2018-02-07. [ bib ]
[Wright, 2017]
Wright, K. (2017). pals: Color Palettes, Colormaps, and Tools to Evaluate Them. R package version 1.4. [ bib | http ]
[Zeileis et al., 2009]
Zeileis, A., Hornik, K., and Murrell, P. (2009). Escaping RGBland: Selecting colors for statistical graphics. Computational Statistics & Data Analysis, 53(9):3259--3270. [ bib | DOI ]

Creative Commons License
This document by Paul Murrell is licensed under a Creative Commons Attribution 4.0 International License.