Color (also colour, from celare, "to cover") is the perceived visual quality of light that's associated with its wavelength/frequency (or mixture of several); for example red, blue and yellow are colors. Electromagnetic waves with wavelength from about 380 to 750 nm (about 400 to 790 THz) form the visible spectrum, i.e. waves our eyes can see -- combining such waves with different intensities and letting them fall on the retina of our eyes gives rise to the perception of color in our brain. Without a question colors play immensely important role in our daily lives and thus the study of such an essential physical quality is very profound: there is a deep and complex color theory concerned with the concept of color (its definition, description, reproduction, psychological effect etc.). Needless to say colors are intimately related to any visual information such as art, computer graphics, astrophysics, various visualizations or just everyday perception of our world. Color support is sometimes used as the opposite of systems that are extremely limited in the number of colors they can handle, which may be called monochromatic, 1bit (distinguishing only two colors), black&white or grayscale. Color can be thought of as having a similar relationship to visual information as pitch has to auditory information.
Fun fact: in the past some colors were officially called nigger.
How many colors are there? The total count of colors humans can distinguish is of course individual (color blindness makes people see fewer colors but there are also conditions that make some people be able to perceive more colors), then also we can ask what color really means (see below) but -- approximately speaking -- various sources state we are able to distinguish millions or even over 10 million different colors on average. In computer technology we talk about color depth which says the number of bits we use to represent color -- the more bits, the more colors we can represent. 24 bits are nowadays mostly used to record color (8 bits for each red, green and blue component, so called true color), which allows for 16777216 distinct colors, though even something like 16 bits (65536 colors) is mostly enough for many use cases. Some advanced systems however support many more colors than true color, especially extremely bright and dim ones -- see HDR.
What gives physical objects their color? Most everyday objects get their color from reflecting only specific parts of the white light (usually sunlight), while absorbing the opposite part of the spectrum, i.e. for example a white object reflects all incoming light, a black one absorbs all incoming light (that's why black things get hot in sunlight), a red one reflects the red light and absorbs the rest etc. This is determined by the qualities of the object's surface, such as the structure of its atoms or its microscopic geometry.
This is actually a non-trivial question, or rather there exist many varying definitions of it and furthermore it is a matter of subjective experience, perception of colors may differ between people. When asking what color really is, consider the following:
Provided it's so hard to even define color, it's no surprise that color theory is kind of complicated as fuck. This section will only poke on essential stuff, mostly in relation to programming.
To preface we must briefly mention that colors are divided in many ways, for example there are spectral colors (ones that can be describes by a single wavelength, i.e. those found in the rainbow), primary colors (a set of a few basic colors whose mixing can produce other colors, for example red, green and blue; this depends on color model), secondary colors (equal mixing of two primary ones), tertiary colors (mix of primary and secondary) etc.
How do our eyes perceive color? Even if by chance we hated biology, it's useful to know the basics of what's going on in our eyes and brains. In they eye (on the retina) we have two kinds of bitches (cells): rods and cones. Rods perceive "brightness", i.e. "how many photons per second" there are -- these bitches can't see color, only "intensity" of light, but they're useful at night when there is low light. Cones on the other hand make us see actual colors, and they are separated to three types: ones detecting red, green and blue light. Each type of cone gets excited when bombarded with photons of its respective desired FREQUENCY (i.e. color), and it also reacts a little less to frequencies close to that key frequency. So for example the "green color" cones react violently when a photon with the frequency of 555 * 10^12 Hz (wavelength of 540 nm) hits them, and they also somewhat react to 600 * 10^12 Hz, but they remain very chill when hit by 680 * 10^12 Hz wave -- but here the blue cone gets excited very much. The brain sees the amount of excitement for each of the three cone types and based on that decides what exact color we are seeing.
Now to begin with the programming theory, let's start with the term color model. As could be guessed by the name, it means "mathematical model of color", i.e. a way of representing colors with numbers. Even though this is probably not 100% correct, as programmers we can more or less equate color model with "color representation in memory". There exist several widely used color models, most of which consist of 3 numeric components. This means that color to us can be abstractly though of as a point in 3D space (unless we're dealing with more limited colors, for example shades of gray, which is then of course a 1 dimensional value). Color models can be further divided into additive, subtractive etc.
Let's list some of the most common color models/representations (conversions between then will be shown later):
Now given a model such as RGB, a mathematician will like to represent each of the components as a real number in the range between 0 and 1, i.e. for example the red color would be represented as [1,0,0]. As programmers, however, we'll eventually have to quantize the values and thus we have to also talk about so called color depth, a value saying how many bits we allocate for a color representation -- the term bits per pixel (BPP) is frequently encountered as a unit here. For example the standard for the RGB model is nowadays 8 bits per component, i.e. 24 bits in total, and so it is sometimes called RGB24 (this frequently gets extended to RGB32 by adding another 8bit alpha component, which expresses transparency; this is convenient as 32bit values nicely align in memory). 24bit RGB values are commonly expressed in hexadecimal where, very conveniently, each pair of digits represents one component: for example the color green might be written as #00ff00
(sometimes even shorter forms are allowed, e.g. CSS also supports #0f0
). Color depth, naturally, will imply how many colors in total we'll be able to represent. Some devices possess higher color depth (see mainly HDR) and some have lower (e.g. RGB332 uses 8, RGB565 uses 16 etc.). In case we can't split the number of bits evenly, we should allocate more bits for the components that "matter more" in terms of human vision -- for example RGB565 allocates 5 bits to red and blue and 6 bits to green, as human eye is most sensitive to green. Especially with lower color depths tricks such as dithering can be used to visually simulate more colors.
Another essential term is color space, practically denoting a set of "physical" colors. Color space is oftentimes related to and/or confused with color model, but they are different things: whereas color model says how we represent color, color space just defines a set of colors in the real world. Color space may also define a correspondence with some specific color model(s), i.e. for example the color of the Sun in the sky may correspond to the RGB value [1.0, 1.0, 1.0] or something -- this is why they may get confused. There exist standardized color spaces such as sRGB. And then there is also the term gamut which signifies a subset of given colorspace. Gamut is used in context of physical devices such as monitors, printers or cameras, to express which exact physical colors the device can reproduce and/or capture. Color spaces and gamuts are important when you're calibrating devices, for example if you want the colors on your monitor match colors that come out of your printer etc.
Finally let's quickly go over other concepts related to colors. Gamma correction is a non-linear function very often used on the "brightness" component of recorded colors -- this is because human sight is more sensitive to darker colors than lighter ones, and so to increase perceptual image quality it is good to allocate more bits for darker tones on the detriment of lighter ones. HDR (high dynamic range) means that a device is capable of handling "brightness" values in very wide range, i.e. for instance an HDR camera will be able to simultaneously capture details in both very bright and very dark areas of a scene (whereas in traditional cameras a bright sky will for example turn out all white) -- for this a very high color depth is used (typically the RGB components are represented as floating point values). Complementary color to given color is one that "cancels" it out when mixed with it.
The following is a table of some common colors:
color name | red | green | blue | cyan | magenta | yellow | hue | chroma | sat.(V) | sat.(L) | value | light. | grayscale | RGB24 | RGB565 | RGB332 | comment |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
white | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | ffffff |
ffff |
ff |
all frequencies, complementary to black |
light gray | 0.75 | 0.75 | 0.75 | 0.25 | 0.25 | 0.25 | 0 | 0 | 0 | 0 | 0.75 | 0.75 | 0.75 | c0c0c0 |
c618 |
db |
complementary to dark gray |
gray | 0.5 | 0.5 | 0.5 | 0.5 | 0.5 | 0.5 | 0 | 0 | 0 | 0 | 0.5 | 0.5 | 0.5 | 808080 |
8410 |
92 |
complementary to self |
dark gray | 0.25 | 0.25 | 0.25 | 0.75 | 0.75 | 0.75 | 0 | 0 | 0 | 0 | 0.25 | 0.25 | 0.25 | 404040 |
4208 |
49 |
complementary to light gray |
black | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 000000 |
0000 |
00 |
lack of light, complementary to white |
red | 1 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0.5 | 0.29 | ff0000 |
f800 |
e0 |
~685 nm, RGB primary, complementary to cyan |
orange | 1 | 0.5 | 0 | 0 | 0.5 | 1 | 0.08 | 1 | 1 | 1 | 1 | 0.5 | 0.59 | ff8000 |
fc00 |
f0 |
~605 nm, RGB tertiary, AKA light brown |
yellow | 1 | 1 | 0 | 0 | 0 | 1 | 0.16 | 1 | 1 | 1 | 1 | 0.5 | 0.88 | ffff00 |
ffe0 |
fc |
~580 nm, RGB secondary, complementary to blue |
green | 0 | 1 | 0 | 1 | 0 | 1 | 0.33 | 1 | 1 | 1 | 1 | 0.5 | 0.58 | 00ff00 |
07e0 |
1c |
~532 nm, RGB primary, complementary to pink |
cyan | 0 | 1 | 1 | 1 | 0 | 0 | 0.5 | 1 | 1 | 1 | 1 | 0.5 | 0.7 | 00ffff |
07ff |
1f |
~512 nm, RGB secondary, complementary to red |
blue | 0 | 0 | 1 | 1 | 1 | 0 | 0.66 | 1 | 1 | 1 | 1 | 0.5 | 0.11 | 0000ff |
001f |
03 |
~472 nm, RGB primary, complementary to yellow, most favorite in the world |
violet | 0.5 | 0 | 1 | 0.5 | 1 | 0 | 0.75 | 1 | 1 | 1 | 1 | 0.5 | 0.26 | 8000ff |
801f |
83 |
~415 nm, RGB tertiary |
pink | 1 | 0 | 1 | 0 | 1 | 0 | 0.83 | 1 | 1 | 1 | 1 | 0.5 | 0.41 | ff00ff |
f81f |
e3 |
RGB secondary, complementary to green |
light red | 1 | 0.5 | 0.5 | 0 | 0.5 | 0.5 | 0 | 0.5 | 0.5 | 1 | 1 | 0.75 | 0.64 | ff8080 |
fc10 |
f2 |
|
light orange | 1 | 0.75 | 0.5 | 0 | 0.25 | 0.5 | 0.08 | 0.5 | 0.5 | 1 | 1 | 0.75 | 0.79 | ffc040 |
fe10 |
fa |
|
light yellow | 1 | 1 | 0.5 | 0 | 0 | 0.5 | 0.16 | 0.5 | 0.5 | 1 | 1 | 0.75 | 0.94 | ffff80 |
fff0 |
fe |
|
light green | 0.5 | 1 | 0.5 | 0.5 | 0 | 0.5 | 0.33 | 0.5 | 0.5 | 1 | 1 | 0.75 | 0.79 | 80ff80 |
87f0 |
9e |
|
light cyan | 0.5 | 1 | 1 | 0.5 | 0 | 0 | 0.5 | 0.5 | 0.5 | 1 | 1 | 0.75 | 0.85 | 80ffff |
87ff |
9f |
|
light blue | 0.5 | 0.5 | 1 | 0.5 | 0.5 | 0 | 0.66 | 0.5 | 0.5 | 1 | 1 | 0.75 | 0.55 | 8080ff |
841f |
93 |
|
light violet | 0.75 | 0.5 | 1 | 0.25 | 0.5 | 0 | 0.75 | 0.5 | 0.5 | 1 | 1 | 0.75 | 0.63 | c08040 |
c41f |
d3 |
|
light pink | 1 | 0.5 | 1 | 0 | 0.5 | 0 | 0.83 | 0.5 | 0.5 | 1 | 1 | 0.75 | 0.7 | ff80ff |
fc1f |
f3 |
|
dark red | 0.5 | 0 | 0 | 0.5 | 1 | 1 | 0 | 0.5 | 1 | 1 | 0.5 | 0.25 | 0.14 | 800000 |
8000 |
80 |
|
brown | 0.5 | 0.25 | 0 | 0.5 | 0.75 | 1 | 0.08 | 0.5 | 1 | 1 | 0.5 | 0.25 | 0.29 | 804000 |
8200 |
88 |
AKA dark orange |
dark yellow | 0.5 | 0.5 | 0 | 0.5 | 0.5 | 1 | 0.16 | 0.5 | 1 | 1 | 0.5 | 0.25 | 0.44 | 808000 |
8400 |
90 |
|
dark green | 0 | 0.5 | 0 | 1 | 0.5 | 1 | 0.33 | 0.5 | 1 | 1 | 0.5 | 0.25 | 0.29 | 008000 |
0400 |
10 |
|
dark cyan | 0 | 0.5 | 0.5 | 1 | 0.5 | 0.5 | 0.5 | 0.5 | 1 | 1 | 0.5 | 0.25 | 0.35 | 008080 |
0410 |
12 |
|
dark blue | 0 | 0 | 0.5 | 1 | 1 | 0.5 | 0.66 | 0.5 | 1 | 1 | 0.5 | 0.25 | 0.05 | 000080 |
0010 |
02 |
|
dark violet | 0.25 | 0 | 0.5 | 0.75 | 1 | 0.5 | 0.75 | 0.5 | 1 | 1 | 0.5 | 0.25 | 0.13 | c00080 |
4010 |
42 |
|
dark pink | 0.5 | 0 | 0.5 | 0.5 | 1 | 0.5 | 0.83 | 0.5 | 1 | 1 | 0.5 | 0.25 | 0.2 | 800080 |
8010 |
82 |
Below is a C code implementing some functions for conversion between different color representations, may it serve as a reference of how to convert between them.
#define u8 unsigned char
u8 rgbToGray(u8 r, u8 g, u8 b)
{
// eye has diff. sens. to components, formula: gray ~= 0.3 R + 0.6 G + 0.1 B
return (5 * ((unsigned) r) + 8 * ((unsigned) g) + 3 * ((unsigned) b)) / 16;
}
void rgbToCmy(u8 r, u8 g, u8 b, u8 *c, u8 *m, u8 *y)
{
*c = 255 - r; *m = 255 - g; *y = 255 - b;
}
void cmyToRgb(u8 c, u8 m, u8 y, u8 *r, u8 *g, u8 *b)
{
*r = 255 - c; *g = 255 - m; *b = 255 - y;
}
void hsvlToRgb(u8 h, u8 s, u8 vl, u8 *r, u8 *g, u8 *b, u8 hsv)
{
*r = hsv ? ((((int) vl) * s) / 256) :
(((int) (256 - 2 * ((vl > 127) ? (vl - 127) : (127 - vl)))) * s) / 256;
int c = (((((int) h) * 256) / 42) % 512) - 255;
*g = (((int) *r) * (255 + (c >= 0 ? -1 * c : c))) / 256;
*b = hsv ? (vl - *r) : (((int) vl) - *r / 2);
*r += *b;
*g += *b;
switch (h / 42)
{
case 0: break;
case 1: r ^= g; g ^= r; r ^= g; break; // swap
case 2: r ^= g; g ^= r; r ^= g; r ^= b; b ^= r; r ^= b; break;
case 3: r ^= b; b ^= r; r ^= b; break;
case 4: g ^= b; b ^= g; g ^= b; r ^= b; b ^= r; r ^= b; break;
default: g ^= b; b ^= g; g ^= b; break;
}
}
void rgbToHcsvl(u8 r, u8 g, u8 b, u8 *h, u8 *c, u8 *sv, u8 *sl, u8 *v, u8 *l)
{
int min = r < g ? (r < b ? r : b) : (g < b ? g : b); // min of 3
*v = r > g ? (r > b ? r : b) : (g > b ? g : b); // max of 3
*c = *v - min;
*l = (min + *v) / 2;
*sl = (*l != 0 && *l != 255) ?
((511 * (((int) *v) - *l)) / (256 - 2 * ((*l > 127) ?
(*l - 127) : (127 - *l)))) : 0;
min = *c;
*sv = (*v != 0) ? (255 * min) / *v : 0;
if (*c == 0)
*h = 0;
else if (*v == r)
*h = (256 + (42 * (((int) g) - b)) / min) % 256;
else if (*v == g)
*h = ((42 * ((2 * min + b) - r)) / min) % 256;
else if (*v == b)
*h = ((42 * ((4 * min + r) - g)) / min) % 256;
}
void hsvToRgb(u8 h, u8 s, u8 v, u8 *r, u8 *g, u8 *b)
{
hsvlToRgb(h,s,v,r,g,b,1);
}
void hslToRgb(u8 h, u8 s, u8 l, u8 *r, u8 *g, u8 *b)
{
hsvlToRgb(h,s,l,r,g,b,0);
}
void rgbToHsv(u8 r, u8 g, u8 b, u8 *h, u8 *s, u8 *v)
{
u8 sl, l, c;
rgbToHcsvl(r,g,b,h,&c,s,&sl,v,&sl);
}
void rgbToHsl(u8 r, u8 g, u8 b, u8 *h, u8 *s, u8 *l)
{
u8 sv, v, c;
rgbToHcsvl(r,g,b,h,&c,&sv,s,&v,l);
}
void rgbToHcv(u8 r, u8 g, u8 b, u8 *h, u8 *c, u8 *v)
{
u8 sv, sl, l;
rgbToHcsvl(r,g,b,h,c,&sv,&sl,v,&l);
}
void rgbToHcl(u8 r, u8 g, u8 b, u8 *h, u8 *c, u8 *l)
{
u8 sv, sl, v;
rgbToHcsvl(r,g,b,h,c,&sv,&sl,&v,l);
}
Powered by nothing. All content available under CC0 1.0 (public domain). Send comments and corrections to drummyfish at disroot dot org.