color_dissimilarity

Measure the similarity of colors in a palette, and choose dissimilar colors from them.
git clone https://git.eamoncaddigan.net/color_dissimilarity.git
Log | Files | Refs | README | LICENSE

commit 9aa42913b9d189ab39a987ee5936be23256ac138
parent 4858784e2426eaf719cc3b206c07c3aa27510ace
Author: Eamon Caddigan <eamon.caddigan@gmail.com>
Date:   Sat,  4 Sep 2021 19:14:30 -0400

Doing the whole dissimilarity matrix

Diffstat:
M.gitignore | 4++++
MREADME.md | 2+-
Acolor_utils.R | 15+++++++++++++++
Ademo.Rmd | 48++++++++++++++++++++++++++++++++++++++++++++++++
Mdissimilarity.R | 85++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
5 files changed, 131 insertions(+), 23 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -37,3 +37,7 @@ vignettes/*.pdf # R Environment Variables .Renviron + +# Generated files (for this) +*.html + diff --git a/README.md b/README.md @@ -1,7 +1,7 @@ # color_dissimilarity Measure the similarity of colors in a palette, and choose dissimilar colors from them -I'm not developing an entire package here, just sketching out ways to do some things that I often want to do. +I'm not developing an entire package here (I'm using the MIT Lincense intentionally to make it easy for you to use it in _your_ package), just sketching out ways to do some things that I often want to do. Some goals: * Measure the (dis)similarity of colors is a perceptually scaled space (CIELAB) with a couple different options for dealing with colorblindness. diff --git a/color_utils.R b/color_utils.R @@ -0,0 +1,15 @@ +#' Convert a colorspace RGB object into a vector of hex codes. +#' +#' I thought the function `colorspace::hex()` would do this but it doesn't seem +#' to work. Maybe I'm doing something wrong? +#' +#' @param colors_rgb A colorspace RGB object +#' +#' @return A vector of hex codes. +RGB2hex <- function(colors_rgb) { + grDevices::rgb(pmin(colors_rgb@coords[, 'R'] / 255, 1), + pmin(colors_rgb@coords[, 'G'] / 255, 1), + pmin(colors_rgb@coords[, 'B'] / 255, 1)) +} + + diff --git a/demo.Rmd b/demo.Rmd @@ -0,0 +1,48 @@ +--- +title: "Demonstrations" +author: "Eamon Caddigan" +date: "9/4/2021" +output: html_document +--- + +```{r setup, include=FALSE} +knitr::opts_chunk$set(echo = TRUE) +``` + +First, make sure we have the libraries we need and import the R scripts. + +```{r} +library(colorspace) +library(grDevices) +``` +```{r} +source('dissimilarity.R') +source('color_utils.R') +``` +```{r} +compare_colors <- function(cols, cvd_function = identity) { + colorspace::swatchplot(cols, + RGB2hex(apply_cvd(col2RGB(cols), cvd_function))) +} +``` + +Next let's grab some random colors and show them. + +```{r} +some_colors <- c('red', 'black', 'white', 'khaki', 'gray50', + '#a6cee3', '#1f78b4', '#b2df8a', '#33a02c') +compare_colors(some_colors) +``` + +Here's the mild deuteranomaly we're using for the default color comparison (it's more extreme than my own). + +```{r} +compare_colors(some_colors, function(x) colorspace::deutan(x, 0.5)) +``` + +And here's the dissimilarity matrix of the colors. + +```{r} +image(cvd_dissimilarity(some_colors)) +``` + diff --git a/dissimilarity.R b/dissimilarity.R @@ -2,23 +2,28 @@ loadNamespace('colorspace') loadNamespace('grDevices') -# Some colors as a matrix -some_colors_mat <- grDevices::col2rgb(c('red', 'black', 'white', 'khaki', 'gray50', - '#a6cee3', '#1f78b4', '#b2df8a', '#33a02c')) - -# Turn them into colorspace RGB objects -some_colors_rgb <- colorspace::RGB(some_colors_mat['red', ], - some_colors_mat['green', ], - some_colors_mat['blue', ]) - -# Convert a colorspace RGB object to LAB and calculate a dissimilarity matrix +#' Find the dissimilarity of colors in a colorspace RGB object. +#' +#' This estimates the "dissimilarity" of pairs of colors by measuring their +#' Euclidean distance in CIELAB space. +#' +#' @param colors_rgb A set of colors represented by a colorspace RGB object or +#' other color object +#' +#' @return A triangular numeric matrix of dissimilarity values lab_dist <- function(colors_rgb) { colors_lab <- as(colors_rgb, 'LAB') - as.matrix(stats::dist(some_colors_lab@coords, method = 'euclidean')) + as.matrix(stats::dist(colors_lab@coords, method = 'euclidean')) } -# Apply the given colorspace CVD transformation to the colorspace RGB object -# and return a colorspace object + +#' Apply a CVD transformation to a colorspace RGB object. +#' +#' @param colors_rgb A set of colors represented by a colorspace RGB object or +#' other color object +#' @param cvd_function A colorspace CVD transformation +#' +#' @return A transformed set of colors represented by a colorspace RGB object apply_cvd <- function(colors_rgb, cvd_function = identity) { colors_cvd_mat <- cvd_function(t(colors_rgb@coords)) colorspace::RGB(colors_cvd_mat['R', ], @@ -26,17 +31,53 @@ apply_cvd <- function(colors_rgb, cvd_function = identity) { colors_cvd_mat['B', ]) } -# Turn a colorspace RGB object into a vector of hex codes (`colorspace::hex()` -# should do this but it doesn't work) -RGB2hex <- function(colors_rgb) { - grDevices::rgb(pmin(colors_rgb@coords[, 'R'] / 255, 1), - pmin(colors_rgb@coords[, 'G'] / 255, 1), - pmin(colors_rgb@coords[, 'B'] / 255, 1)) + +#' `R` color to colorspace RGB object conversion. +#' +#' @param cols A character vector of colors, can be names returned by `colors()` +#' or hex codes +#' +#' @return A colorspace RGB color object +col2RGB <- function(cols) { + cols_mat <- grDevices::col2rgb(cols) + colorspace::RGB(cols_mat['red', ], cols_mat['green', ], cols_mat['blue', ]) } +#' Find the dissimilarity of a vector of `R` colors. +#' +#' @param cols A character vector of colors, can be names returned by `colors()` +#' or hex codes +#' @param cvd A color vision deficiency to apply to the colors before measuring +#' dissimilarity. Must be one of 'deuteranomaly', 'monochromacy', or 'none'. +#' +#' @return A triangular numeric matrix of dissimilarity values +#' @export +#' +#' @examples +#' # A good set of colors from ColorBrewer +#' cvd_dissimilarity(c('#a6cee3', '#1f78b4', '#b2df8a', '#33a02c'), 'monochromacy') +#' +#' # Red and green are easy to differentiate for people with normal color +#' # vision, but much harder for those of us with deuteranomaly. +#' cvd_dissimilarity(c('red1', 'green'), cvd = 'none') +#' cvd_dissimilarity(c('red1', 'green'), cvd = 'deuteranomaly') +cvd_dissimilarity <- function(cols, cvd = "deuteranomaly") { + # Find the CVD transform + if (cvd == "deuteranomaly") { + cvd_fn <- function(x) colorspace::deutan(x, 0.5) + } else if (cvd == "monochromacy") { + cvd_fn <- colorspace::desaturate + } else if (cvd == "none") { + cvd_fn <- identity + } else { + stop("cvd must be one of 'deuteranomaly', 'monochromacy', or 'none'") + } + + # Apply the transformation and get the dissimilarity matrix + dist_mat <- lab_dist(apply_cvd(col2RGB(cols), cvd_fn)) + colnames(dist_mat) <- cols + rownames(dist_mat) <- cols -compare_colors <- function(colors_rgb, cvd_function = identity) { - colorspace::swatchplot(RGB2hex(colors_rgb), - RGB2hex(apply_cvd(colors_rgb, cvd_function))) + dist_mat }