# [R] measuring distances between colours?

Martin Maechler maechler at stat.math.ethz.ch
Thu May 30 15:18:00 CEST 2013

```Dear John,

>>>>> John Fox <jfox at mcmaster.ca>
>>>>>     on Thu, 30 May 2013 08:13:19 -0400 writes:

> Dear r-helpers,
> I'm interested in locating the named colour that's "closest" to an arbitrary RGB colour.

Hmm, maybe I was not really marketing well enough

------------------------------------------------------------------------
r61127 | maechler | 2012-11-17 20:00:58 +0100 (Sat, 17 Nov 2012) | 1 line

new option colors(distinct=TRUE); new demo(colors) & demo(hclColors)
------------------------------------------------------------------------

demo(colors)  contains a few niceties,  some originating from
------------  Marius Hofert,
notably for you the function  nearRcolor()

which has the nice extra that you can specify the color *space*
in which to measure nearness.

##' Find close R colors() to a given color {original by Marius Hofert)
##' using Euclidean norm in (HSV / RGB / ...) color space
nearRcolor <- function(rgb, cSpace = c("hsv", "rgb255", "Luv", "Lab"),
dist = switch(cSpace, "hsv" = 0.10, "rgb255" = 30,
"Luv" = 15, "Lab" = 12))
{
if(is.character(rgb)) rgb <- col2rgb(rgb)
stopifnot(length(rgb <- as.vector(rgb)) == 3)
Rcol <- col2rgb(.cc <- colors())
uniqC <- !duplicated(t(Rcol)) # gray9 == grey9 (etc)
Rcol <- Rcol[, uniqC] ; .cc <- .cc[uniqC]
cSpace <- match.arg(cSpace)
convRGB2 <- function(Rgb, to)
t(convertColor(t(Rgb), from="sRGB", to=to, scale.in=255))
## the transformation,  rgb{0..255} --> cSpace :
TransF <- switch(cSpace,
"rgb255" = identity,
"hsv" = rgb2hsv,
"Luv" = function(RGB) convRGB2(RGB, "Luv"),
"Lab" = function(RGB) convRGB2(RGB, "Lab"))
d <- sqrt(colSums((TransF(Rcol) - as.vector(TransF(rgb)))^2))
iS <- sort.list(d[near <- d <= dist])# sorted: closest first
setNames(.cc[near][iS], format(d[near][iS], digits=3))
}

You should use the full

demo(colors)

or browse

https://svn.r-project.org/R/trunk/src/library/grDevices/demo/colors.R

to also get a few nice examples..

Martin

> The best that I've been able to come up is the following, which uses HSV colours for the comparison:

>
> r2c <- function(){
>     hexnumerals <- 0:15
>     names(hexnumerals) <- c(0:9, LETTERS[1:6])
>     hex2decimal <- function(hexnums){
>         hexnums <- strsplit(hexnums, "")
>         decimals <- matrix(0, 3, length(hexnums))
>         decimals[1, ] <- sapply(hexnums, function(x)
>                                  sum(hexnumerals[x[1:2]] * c(16, 1)))
>         decimals[2, ] <- sapply(hexnums, function(x)
>                                  sum(hexnumerals[x[3:4]] * c(16, 1)))
>         decimals[3, ] <- sapply(hexnums, function(x)
>                                  sum(hexnumerals[x[5:6]] * c(16, 1)))
>         decimals
>     }
>     colors <- colors()
>     hsv <- rgb2hsv(col2rgb(colors))
>     function(cols){
>         cols <- sub("^#", "", toupper(cols))
>         dec.cols <- rgb2hsv(hex2decimal(cols))
>         colors[apply(dec.cols, 2, function(dec.col)
>             which.min(colSums((hsv - dec.col)^2)))]
>     }
> }
>
> rgb2col <- r2c()
>
> I've programmed this with a closure so that hsv gets computed only once.
>
> Examples:
>
> > rgb2col(c("AA0000", "002200", "000099", "333300", "BB00BB", "#005555"))
> [1] "darkred"   "darkgreen" "blue4"     "darkgreen" "magenta3"  "darkgreen"
> > rgb2col(c("AAAA00", "#00AAAA"))
> [1] "darkgoldenrod" "cyan4"
>
> Some of these colour matches, e.g., "#005555" -> "darkgreen" seem poor to me. Even if the approach is sound, I'd like to be able to detect that there is no sufficiently close match in the vector of named colours. That is, can I establish a maximum acceptable distance in the HSV (or some other) colour space?
>
> I vaguely recall a paper or discussion concerning colour representation in R but can't locate it.
>
> Any suggestions would be appreciated.
>
> John
>
> ------------------------------------------------
> John Fox
> Sen. William McMaster Prof. of Social Statistics
> Department of Sociology
> McMaster University