Is there a way to programmatically darken the colo

2019-01-23 06:27发布

问题:

Let's say I have the RGB values like this (in R, for example):

cols <- c("#CDE4F3","#E7F3D3","#F7F0C7","#EFCFE5","#D0D1E7")

Is there any way to programmatically derive another set of colors which is a darkened version of the former?

It doesn't have to be R.

回答1:

library(colorspace)

cols <- c("#CDE4F3","#E7F3D3","#F7F0C7","#EFCFE5","#D0D1E7")

cols1 <- readhex(file = textConnection(paste(cols, collapse = "\n")),
                 class = "RGB")
#transform to hue/lightness/saturation colorspace
cols1 <- as(cols1, "HLS")
cols2 <- cols1
#additive decrease of lightness
cols1@coords[, "L"] <- pmax(0, cols1@coords[, "L"] - 0.3)
#multiplicative decrease of lightness
cols2@coords[, "L"] <- cols2@coords[, "L"] * 0.75
#going via rgb seems to work better  
cols1 <- as(cols1, "RGB")
cols1 <- hex(cols1)
cols2 <- as(cols2, "RGB")
cols2 <- hex(cols2)


plot(x = seq_along(cols), y = rep(1, length(cols)), 
     col = cols, pch = 15, ylim = c(0, 4.5), cex = 5,
     xlab = "", ylab = "")
points(x = seq_along(cols), y = rep(2, length(cols)), 
       col = cols1, pch = 16, cex = 5)
points(x = seq_along(cols), y = rep(3, length(cols)), 
       col = cols2, pch = 17, cex = 5)

legend("top",legend = c("original", "additive", "multipl."), 
       pch = 15:17, ncol = 3)



回答2:

The question asks whether there is any way to programmatically darken colors. The problem is that there are many different ways, and they all yield different results. The specific outcome you obtain depends on the specific algorithm employed and the color space used.

The R package colorspace now provides a built-in function to darken colors, through the darken() function. This function uses a new "combined" colorspace we came up with which is a mix between HLS and HCL. (In brief, adjusts L in HCL space but adjusts C by taking a detour through HLS, while keeping H constant.)

To use this function, you need to install the current development version of colorspace:

install.packages("colorspace", repos = "http://R-Forge.R-project.org")

Then, try the following:

# original colors
cols <- c("#CDE4F3", "#E7F3D3", "#F7F0C7", "#EFCFE5", "#D0D1E7")

# darken 20%
cols_d2 <- darken(cols, 0.2)

# darken 40%
cols_d4 <- darken(cols, 0.4)

# plot
pal <- function(col, border = "light gray") {
  n <- length(col)
  plot(0, 0, type="n", xlim = c(0, 1), ylim = c(0, 1), axes = FALSE,
       xlab = "", ylab = "")
  rect(0:(n-1)/n, 0, 1:n/n, 1, col = col, border = border)
}

par(mfrow = c(3, 1), mar = c(1, 0, 2, 0))
pal(cols); mtext("original")
pal(cols_d2); mtext("20% darker")
pal(cols_d4); mtext("40% darker")

There are a couple of different color spaces and other adjustment options you can try, but the default should work in most cases.

To see the effect of darkening in different color spaces, consider what happens when we darken the same colors in HCL or HLS:

The HCL-darkened colors seem quite gray, and the HLS-darkened colors appear overly bright and colorful. However, depending on your specific application, you might want one of these outcomes.



回答3:

HSV Value Adjustment

This seems much better than my first stab with Munsell colors (below). It's still a bit of a run-around (probably because I'm mixing packages that specify colors in rows and columns and take matrices or not), but it works:

cols.hsv = rgb2hsv(cols.rgb)
# adjust the "value" down to 80% of it's previous level
cols.hsv["v", ] = cols.hsv["v", ] * 0.8

cols.darker = cols
for (i in seq_along(cols)) {
    cols.darker[i] = hsv(cols.hsv[1, i], cols.hsv[2, i], cols.hsv[3, i])
}

par(mfrow = c(1, 2))
scales::show_col(cols)
scales::show_col(cols.darker)

Munsell

I've not used the munsell package before, so I might be making this more complicated than it needs to be, but it has a function darker that "Decreases the value of the Munsell colour by 1."

The hard part is conversions. As near as I can tell, we need to get your hex colors to Munsell colors we have to go via RGB.

cols <- c("#CDE4F3","#E7F3D3","#F7F0C7","#EFCFE5","#D0D1E7")
cols.rgb = col2rgb(cols)

library(munsell)
# munsell expects rgb colors in rows, not columns, and expects the
# values to be between 0 and 1, not 0 and 255
cols.m = rgb2mnsl(t(cols.rgb) / rowSums(t(cols.rgb)))

# make darker
darker.m = darker(cols.m)
# at least converting back to hex is one step!
darker.hex = mnsl2hex(darker.m)


# view the results
par(mfrow = c(2, 1))
scales::show_col(cols)
scales::show_col(darker.hex)

Overall I'm not thrilled with this solution. It made the colors much darker and I don't see a way to adjust that in the darker function.



回答4:

Yes there is.

You will have to specify how darken or lighter you what to go.

Here is a Function done in JavaScript: https://css-tricks.com/snippets/javascript/lighten-darken-color/

function LightenDarkenColor(col, amt) {
    var usePound = false;
    if (col[0] == "#") {
        col = col.slice(1);
        usePound = true;
    }
    var num = parseInt(col,16);
    var r = (num >> 16) + amt;
    if (r > 255) {
      r = 255;
    }else if  (r < 0){ 
      r = 0;
    }
    var b = ((num >> 8) & 0x00FF) + amt;
    if (b > 255) {
      b = 255;
    }else if  (b < 0) {
      b = 0;
    }
    var g = (num & 0x0000FF) + amt;
    if (g > 255) {
      g = 255;
    }else if (g < 0) {
      g = 0;
    }
    return (usePound?"#":"") + (g | (b << 8) | (r << 16)).toString(16);
}

Hope it helps, Cheers.



回答5:

It's not really nice code. The munsell package might be more friendly

library(colorspace)

cols <- c("#CDE4F3","#E7F3D3","#F7F0C7","#EFCFE5","#D0D1E7")

lab = as(hex2RGB(cols),"LAB")
lab@coords[,1] = lab@coords[,1] *0.3 # 
cols1 =  hex(as(lab,"RGB"))
cols1


回答6:

You can actually do this really easily in base graphics using colorRampPalette().

Just make a gradient with your initial color and black, ramp 100 and select "how much darker" you want it (1=initial and 100=black).

I made this in a neat little function :

darker.col = function(color, how.much = 30){
  colorRampPalette(c(color, "black"))(100)[how.much]
}

plot(1:100, rep(1, 100), pch=16, cex=5, col=darker.col("royalblue", 1:100))



回答7:

Here's something simple that works for me and returns the colors in the same format as the original:

library(colorspace)

cols <- c("#CDE4F3","#E7F3D3","#F7F0C7","#EFCFE5","#D0D1E7")    
rgb(hex2RGB(cols)@coords * 0.8)


标签: r colors rgb