[R] measuring distances between colours?

John Fox jfox at mcmaster.ca
Thu May 30 15:58:18 CEST 2013


Dear Martin,

> -----Original Message-----
> From: Martin Maechler [mailto:maechler at stat.math.ethz.ch]
> Sent: Thursday, May 30, 2013 9:18 AM
> To: John Fox
> Cc: r-help at r-project.org
> Subject: Re: [R] measuring distances between colours?
> 
> 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
> what I had added for  R 3.0.0 :

Or I didn't look carefully enough! I think that I would have found
nearRcolor() had it been a function in R rather than in a demo, but I didn't
think to try demo(color).

This provides the solution that I was looking for, in that there's a
criterion for acceptable proximity for colour matches. I see that the
default comparison is in the HSV colour space, so I suppose that Marius
judged that to be best, at least for close colours. Is that right?

Thank you,
 John

> 
> -----------------------------------------------------------------------
> -
> 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
> > Hamilton, Ontario, Canada
> > http://socserv.mcmaster.ca/jfox/



More information about the R-help mailing list