[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Colours, or how to make dilute cyanide

>From: bloch@informatics.wustl.edu (Charlene Bloch Abrams)
>Newsgroups: comp.lang.lisp.mcl
>Subject: Colours
>Date: 23 Apr 92 18:59:58 GMT
>Organization: Washington University, St. Louis
>My question is this: does anyone have soem sort of a mapping (or know where 
>I can find said animal) which will tell how to alter the r-g-b components
>of a colour in order to decrease its intensity?
>Any help gratefully appreciated.

First of all, I need to make a big disclaimer that color representation
and color perception is a big, fascinating, yet often confusing area. 
For more extended flamage, just ask the same question on comp.graphics. 
For the sake of this discussion, I'm representing all colors as RGB triples, 
where each number lies between 0 and 1. So (0 0 0)=black, (1 1 1)=white, 
and (1 0 0)=red.

A simplified rule of thumb: to get the luminosity (greyscale brightness) 
of a color, take  (.2R + .7G + .1B). The eye is much more sensitive
to brightness changes in green than in red or blue, so that's what a 
black and white TV does when it displays a color video signal.

Saturation is different from luminosity - it's a measure of how "pure"
or "diluted" the color is. Saturated colors look like bright berries
or children's plastic toys. Desaturated colors look like washed-out
and faded pastels.

One simple (but not perfect) way to make a saturation scale is to 
interpolate between a color and a grey of equal luminosity. 
So for example, suppose you start with a saturated color like (0 1 1),
which is cyan (the green-blue that cyanide gets its name from).
Here's how you can get various desaturated versions of it.

? (defconstant *luminosity-weights* '(2/10 7/10 1/10))

? (defun luminosity (rgb) 
      "RGB is a list of red, green, and blue values"
      (reduce '+ (mapcar '*  *luminosity-weights* rgb)))

? (luminosity '(0 1 1))   ; Cyan's luminosity = 80% of pure white.

? (defun interpolate (x start end) 
       "X is a fraction between 0 and 1. 
        Returns a value interpolated between START and END by FRACTION"
       (+ (* x end) (* (- 1 x) start)))

? (interpolate 1/3 100 400)

? (defun desaturate (rgb saturation)
    "SATURATION is a fraction from 0 (desaturated) to 1 (saturated)"
    (let* ((luminosity (luminosity rgb))
           (grey (list luminosity luminosity luminosity)))
       (interpolate saturation (first grey) (first rgb))
       (interpolate saturation (second grey) (second rgb))
       (interpolate saturation (third grey) (third rgb)))))

? (desaturate '(0 1 1) 1)    ;; Full-strength cyan
(0 1 1)
? (desaturate '(0 1 1) .5)   ;; Cyan at 50% saturation
(0.4 0.9 0.9)
? (desaturate '(0 1 1) .001) ;; 0.1% saturated cyan = almost 80% grey
(0.7992 0.8002 0.8002)