DATE: 2022-12-23
AUTHOR: John L. Godlee
I wrote an R function to find the nearest named colour to a given colour hexcode.
#' Find the closest named colour to a given six character hex colour #' #' @param x vector of six digit hex colours #' @param method colour space, either "rgb" or "hsv". #' @param metric distance metric, either "euclidean" or "manhattan". #' #' @return vector of nearest named colour #' #' @details If metric is "euclidean", distances are root sum-of-squares #' differences. "manhattan" distances are the sum of absolute differences. #' The named colours come from the list of 657 named colours stored in R, #' accessible using the \code{colors()} function. #' #' @examples #' hexName(c("#117733", "#b58900", "#855C75")) #' #' @export #' hexName <- function(x, method = "rgb", metric = "euclidean") { # Check input is valid if (any(nchar(x) != 7) | any(!grepl("^#", x))) { stop("Hex code(s) invalid") } if (!method %in% c("rgb", "hsv")) { stop("method must be 'rgb' or 'hsv'") } if (!metric %in% c("euclidean", "manhattan")) { stop("metric must be 'euclidean' or 'manhattan'") } # Convert hex string to RGB x <- col2rgb(x) # Create matrix of named colours as RGB values coltab <- col2rgb(colors()) # If HSV if (method == "hsv") { x <- rgb2hsv(x) coltab <- rgb2hsv(coltab) } # Find nearest named colour by metric if (metric == "euclidean") { out <- colors()[apply(x, 2, function(y) { which.min(apply(apply(coltab, 2, "-", y)^2, 2, sum)) })] } else if (metric == "manhattan") { out <- colors()[apply(x, 2, function(y) { which.min(apply(abs(apply(coltab, 2, "-", y)), 2, sum)) })] } # Return return(out) }
The function lets you choose between either the RGB (Red, Green, Blue) or HSV (Hue, Saturation, Value) colour space. HSV is an alternative representation of the RGB colour space which more closely aligns with the way the human eye perceives colour differences. You can also choose between using Manhattan or Euclidean distances to find the nearest neighbour named colour.
R stores a list of 657 named colours, accessible from the colors() function.
Bonus: while learning about colour spaces I also wrote a function which takes a six digit colour hexcode and then finds the nearest three digit hexcode:
#' Find the closest hex triplet to a given six character hex colour #' #' @param x vector of six digit hex colours #' #' @return vector of nearest hex colour represented as a triplet #' #' @examples #' hexTrip(c("#117733", "#b58900", "#855C75")) #' hexTrip("#fffffff") #' hexTrip("855C75") #' #' @export #' hexTrip <- function(x) { if (any(nchar(x) != 7) | any(!grepl("^#", x))) { stop ("Hex code(s) invalid") } unlist(lapply(x, function(y) { paste(c("#", sprintf("%x", (col2rgb(y) + 8) %/% 17)), collapse = "") })) }