Skip to content
Prev 324559 / 398503 Next

measuring distances between colours?

Hi Michael,

Thanks for the Wikipedia tip -- I'd looked there but didn't find this
article. The article explains that the Lab colour space was formulated to
provide uniform perceptual differences between colours, with a JND of
approximately of 2.3. Ken Knoblauch made a similar point. The article goes
on to describe relatively complicated adjustments meant to improve the LAV
distance metric, which are probably overkill for my application.

I've programmed Lab colour matching as follows, using Euclidean distances
and adapting Kevin Wright's modification of my original code. I used
convertColor(), which Martin Maechler pointed out to me.

----------- snip --------------

r2c <- function(){
    all.names <- colors()
    all.lab <- t(convertColor(t(col2rgb(all.names)), from="sRGB", to="Lab",
scale.in=255))
    find.near <- function(x.lab) {
        sq.dist <- colSums((all.lab - x.lab)^2)
        rbind(all.names[which.min(sq.dist)], min(sq.dist))
    }
    function(cols.hex, near=2.3){
        cols.lab <- t(convertColor(t(col2rgb(cols.hex)), from="sRGB",
to="Lab", scale.in=255))
        cols.near <- apply(cols.lab, 2, find.near)
        ifelse(cols.near[2, ] < near^2, cols.near[1, ], cols.hex)
    }
}

rgb2col <- r2c()

----------- snip --------------

A bit of experimentation suggests that this works better than using (as I
did previously) direct RGB distances, matching more colours to names and
providing (to my eye, with my monitor) perceptually closer matches, though
sometimes with (again to my eye) perceptible differences. Here's an
illustration, adapting one of Kevin's examples:

----------- snip --------------

cols <- c("#010101", "#EEEEEE", "#AA0000", "#00AA00", "#0000AA", "#AAAA00",
"#AA00AA", "#00AAAA")
(nms <- rgb2col(cols))
pie(rep(1, 2*length(cols)), labels=c(cols, nms), col=c(cols, nms))

----------- snip --------------

Thanks again to everyone who responded to my original, na?ve, question.

Best,
 John