[R] measuring distances between colours?
John Fox
jfox at mcmaster.ca
Thu May 30 23:14:06 CEST 2013
Dear all,
My thanks to everyone who addressed my question. I've incorporated Eik
Vettorazzi's suggestion for improved conversion of hexadecimal RGB colours
to decimal numbers, and Martin Maechler's hint to look at demo(colors). I've
loosened the default definition of "close enough" from the latter, since the
following seems to work well for my purposes.
r2c <- function(){
hex2dec <- function(hexnums) {
# suggestion of Eik Vettorazzi
sapply(strtoi(hexnums, 16L), function(x) x %/% 256^(2:0) %% 256)
}
findMatch <- function(dec.col) {
sq.dist <- colSums((hsv - dec.col)^2)
rbind(which.min(sq.dist), min(sq.dist))
}
colors <- colors()
hsv <- rgb2hsv(col2rgb(colors))
function(cols, near=0.25){
cols <- sub("^#", "", toupper(cols))
dec.cols <- rgb2hsv(hex2dec(cols))
which.col <- apply(dec.cols, 2, findMatch)
matches <- colors[which.col[1, ]]
unmatched <- which.col[2, ] > near^2
matches[unmatched] <- paste("#", cols[unmatched], sep="")
matches
}
}
rgb2col <- r2c()
For example,
> rgb2col(c("010101", "EEEEEE", "AA0000", "00AA00", "0000AA", "AAAA00",
"AA00AA", "00AAAA"))
[1] "black" "gray93" "darkred" "green4"
[5] "blue4" "darkgoldenrod" "darkmagenta" "cyan4"
> rgb2col(c("010101", "090909", "090000", "000900", "000009", "090900",
"090009", "000909"))
[1] "black" "gray3" "#090000" "#000900" "#000009" "#090900"
[7] "#090009" "#000909"
Thanks again,
John
> -----Original Message-----
> From: r-help-bounces at r-project.org [mailto:r-help-bounces at r-
> project.org] On Behalf Of John Fox
> Sent: Thursday, May 30, 2013 8:13 AM
> To: r-help at r-project.org
> Subject: [R] measuring distances between colours?
>
> Dear r-helpers,
>
> I'm interested in locating the named colour that's "closest" to an
> arbitrary RGB colour. 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/
>
> ______________________________________________
> R-help at r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-help
> PLEASE do read the posting guide http://www.R-project.org/posting-
> guide.html
> and provide commented, minimal, self-contained, reproducible code.
More information about the R-help
mailing list