[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