Colour Spaces

In what follows are various notes dealing with colour spaces and conversion between them.




RGB colour space

Lists of RGB values for named colours

Written by Paul Bourke
May 1995

Resene RGB Values List

This table is the "standard" set as used by the SGI X windows server. The values are in the range of 0 to 255 inclusive.

Contribution by Robert Rapplean, C++ program that creates an image that can be folded together to form the RGB colour cube.

Colour space

A colour space is a means of uniquely specifying a colour. There are a number of colour spaces in common usage depending on the particular industry and/or application involved. For example as humans we normally determine colour by parameters such as brightness, hue, and colourfulness. On computers it is more common to describe colour by three components, normally red, green, and blue. These are related to the excitation of red, green, and blue phosphors on a computer monitor. Another similar system geared more towards the printing industry uses cyan, magenta, and yellow to specify colour, they are related to the reflectance and absorbency of inks on paper.

Some other major colour spaces are:
  • HSL, Hue Saturation and Lightness.
  • HSI, Hue Saturation and Intensity
  • HSV, Hue Saturation and Value
  • CIE, a colour standard from the for Commission Internationale de l'Eclairage based on brightness, hue, and colourfulness.

There are generally ways of converting (transforming) between different colour spaces although in most cases the transformation is non-linear. Some colour spaces for example can represent colours which cannot be represented in others.

RGB colour cube

The colour space for computer based applications is often visualised by a unit cube. Each colour (red, green, blue) is assigned to one of the three orthogonal coordinate axes in 3D space. An example of such a cube is shown below along with some key colours and their coordinates.

Note:

  • Along each axis of the colour cube the colours range from no contribution of that component to a fully saturated colour.
  • The colour cube is solid, any point (colour) within the cube is specified by three numbers, namely, an r,g,b triple.
  • The diagonal line of the cube from black (0,0,0) to white (1,1,1) represents all the greys, that is, all the red, green, and blue components are the same.
  • In practice different computer hardware/software combinations will use different ranges for the colours, common ones are 0-256 and 0-65536 for each component. This is simply a linear scaling of the unit colour cube described here.
  • This RGB colour space lies within our perceptual space, that is, the RGB cube is smaller and represents fewer colours than we can see.

A more "colourful" view of the shell of the colour cube is shown below.

RGB colour values for "familiar" colours.

The first set below was originally compiled by Steve Hollasch and is organised as follows: the first column is the descriptive name of the colour; the next three columns are the RGB coordinates in the 0 to 255 range as if the components were being stored in one unsigned byte; the last three columns are the RGB colour coordinates in the range of 0 to 1 inclusive.

Whites
antique_white     250   235   215   0.9804   0.9216   0.8431
azure             240   255   255   0.9412   1.0000   1.0000
bisque            255   228   196   1.0000   0.8941   0.7686
blanched_almond   255   235   205   1.0000   0.9216   0.8039
cornsilk          255   248   220   1.0000   0.9725   0.8627
eggshell          252   230   201   0.9900   0.9000   0.7900
floral_white      255   250   240   1.0000   0.9804   0.9412
gainsboro         220   220   220   0.8627   0.8627   0.8627
ghost_white       248   248   255   0.9725   0.9725   1.0000
honeydew          240   255   240   0.9412   1.0000   0.9412
ivory             255   255   240   1.0000   1.0000   0.9412
lavender          230   230   250   0.9020   0.9020   0.9804
lavender_blush    255   240   245   1.0000   0.9412   0.9608
lemon_chiffon     255   250   205   1.0000   0.9804   0.8039
linen             250   240   230   0.9804   0.9412   0.9020
mint_cream        245   255   250   0.9608   1.0000   0.9804
misty_rose        255   228   225   1.0000   0.8941   0.8824
moccasin          255   228   181   1.0000   0.8941   0.7098
navajo_white      255   222   173   1.0000   0.8706   0.6784
old_lace          253   245   230   0.9922   0.9608   0.9020
papaya_whip       255   239   213   1.0000   0.9373   0.8353
peach_puff        255   218   185   1.0000   0.8549   0.7255
seashell          255   245   238   1.0000   0.9608   0.9333
snow              255   250   250   1.0000   0.9804   0.9804
thistle           216   191   216   0.8471   0.7490   0.8471
titanium_white    252   255   240   0.9900   1.0000   0.9400
wheat             245   222   179   0.9608   0.8706   0.7020
white             255   255   255   1.0000   1.0000   1.0000
white_smoke       245   245   245   0.9608   0.9608   0.9608
zinc_white        253   248   255   0.9900   0.9700   1.0000

Greys
cold_grey         128   138   135   0.5000   0.5400   0.5300
dim_grey          105   105   105   0.4118   0.4118   0.4118
grey              192   192   192   0.7529   0.7529   0.7529
light_grey        211   211   211   0.8275   0.8275   0.8275
slate_grey        112   128   144   0.4392   0.5020   0.5647
slate_grey_dark    47    79    79   0.1843   0.3098   0.3098
slate_grey_light  119   136   153   0.4667   0.5333   0.6000
warm_grey         128   128   105   0.5000   0.5000   0.4100

Blacks
black               0     0     0   0.0000   0.0000   0.0000
ivory_black        41    36    33   0.1600   0.1400   0.1300
lamp_black         46    71    59   0.1800   0.2800   0.2300

Reds
alizarin_crimson  227    38    54   0.8900   0.1500   0.2100
brick             156   102    31   0.6100   0.4000   0.1200
cadmium_red_deep  227    23    13   0.8900   0.0900   0.0500
coral             255   127    80   1.0000   0.4980   0.3137
coral_light       240   128   128   0.9412   0.5020   0.5020
deep_pink         255    20   147   1.0000   0.0784   0.5765
english_red       212    61    26   0.8300   0.2400   0.1000
firebrick         178    34    34   0.6980   0.1333   0.1333
geranium_lake     227    18    48   0.8900   0.0700   0.1900
hot_pink          255   105   180   1.0000   0.4118   0.7059
indian_red        176    23    31   0.6900   0.0900   0.1200
light_salmon      255   160   122   1.0000   0.6275   0.4784
madder_lake_deep  227    46    48   0.8900   0.1800   0.1900
maroon            176    48    96   0.6902   0.1882   0.3765
pink              255   192   203   1.0000   0.7529   0.7961
pink_light        255   182   193   1.0000   0.7137   0.7569
raspberry         135    38    87   0.5300   0.1500   0.3400
red               255     0     0   1.0000   0.0000   0.0000
rose_madder       227    54    56   0.8900   0.2100   0.2200
salmon            250   128   114   0.9804   0.5020   0.4471
tomato            255    99    71   1.0000   0.3882   0.2784
venetian_red      212    26    31   0.8300   0.1000   0.1200

Browns
beige             163   148   128   0.6400   0.5800   0.5000
brown             128    42    42   0.5000   0.1647   0.1647
brown_madder      219    41    41   0.8600   0.1600   0.1600
brown_ochre       135    66    31   0.5300   0.2600   0.1200
burlywood         222   184   135   0.8706   0.7216   0.5294
burnt_sienna      138    54    15   0.5400   0.2100   0.0600
burnt_umber       138    51    36   0.5400   0.2000   0.1400
chocolate         210   105    30   0.8235   0.4118   0.1176
deep_ochre        115    61    26   0.4500   0.2400   0.1000
flesh             255   125    64   1.0000   0.4900   0.2500
flesh_ochre       255    87    33   1.0000   0.3400   0.1300
gold_ochre        199   120    38   0.7800   0.4700   0.1500
greenish_umber    255    61    13   1.0000   0.2400   0.0500
khaki             240   230   140   0.9412   0.9020   0.5490
khaki_dark        189   183   107   0.7412   0.7176   0.4196
light_beige       245   245   220   0.9608   0.9608   0.8627
peru              205   133    63   0.8039   0.5216   0.2471
rosy_brown        188   143   143   0.7373   0.5608   0.5608
raw_sienna        199    97    20   0.7800   0.3800   0.0800
raw_umber         115    74    18   0.4500   0.2900   0.0700
sepia              94    38    18   0.3700   0.1500   0.0700
sienna            160    82    45   0.6275   0.3216   0.1765
saddle_brown      139    69    19   0.5451   0.2706   0.0745
sandy_brown       244   164    96   0.9569   0.6431   0.3765
tan               210   180   140   0.8235   0.7059   0.5490
van_dyke_brown     94    38     5   0.3700   0.1500   0.0200

Oranges
cadmium_orange    255    97     3   1.0000   0.3800   0.0100
cadmium_red_light 255     3    13   1.0000   0.0100   0.0500
carrot            237   145    33   0.9300   0.5700   0.1300
dark_orange       255   140     0   1.0000   0.5490   0.0000
mars_orange       150    69    20   0.5900   0.2700   0.0800
mars_yellow       227   112    26   0.8900   0.4400   0.1000
orange            255   128     0   1.0000   0.5000   0.0000
orange_red        255    69     0   1.0000   0.2706   0.0000
yellow_ochre      227   130    23   0.8900   0.5100   0.0900

Yellows
aureoline_yellow  255   168    36   1.0000   0.6600   0.1400
banana            227   207    87   0.8900   0.8100   0.3400
cadmium_lemon     255   227     3   1.0000   0.8900   0.0100
cadmium_yellow    255   153    18   1.0000   0.6000   0.0700
gold              255   215     0   1.0000   0.8431   0.0000
goldenrod         218   165    32   0.8549   0.6471   0.1255
goldenrod_dark    184   134    11   0.7216   0.5255   0.0431
goldenrod_light   250   250   210   0.9804   0.9804   0.8235
goldenrod_pale    238   232   170   0.9333   0.9098   0.6667
light_goldenrod   238   221   130   0.9333   0.8667   0.5098
melon             227   168   105   0.8900   0.6600   0.4100
naplesyellowdeep  255   168    18   1.0000   0.6600   0.0700
yellow            255   255     0   1.0000   1.0000   0.0000
yellow_light      255   255   224   1.0000   1.0000   0.8784

Greens
chartreuse        127   255     0   0.4980   1.0000   0.0000
chromeoxidegreen  102   128    20   0.4000   0.5000   0.0800
cinnabar_green     97   179    41   0.3800   0.7000   0.1600
cobalt_green       61   145    64   0.2400   0.5700   0.2500
emerald_green       0   201    87   0.0000   0.7900   0.3400
forest_green       34   139    34   0.1333   0.5451   0.1333
green               0   255     0   0.0000   1.0000   0.0000
green_dark          0   100     0   0.0000   0.3922   0.0000
green_pale        152   251   152   0.5961   0.9843   0.5961
green_yellow      173   255    47   0.6784   1.0000   0.1843
lawn_green        124   252     0   0.4863   0.9882   0.0000
lime_green         50   205    50   0.1961   0.8039   0.1961
mint              189   252   201   0.7400   0.9900   0.7900
olive              59    94    43   0.2300   0.3700   0.1700
olive_drab        107   142    35   0.4196   0.5569   0.1373
olive_green_dark   85   107    47   0.3333   0.4196   0.1843
permanent_green    10   201    43   0.0400   0.7900   0.1700
sap_green          48   128    20   0.1900   0.5000   0.0800
sea_green          46   139    87   0.1804   0.5451   0.3412
sea_green_dark    143   188   143   0.5608   0.7373   0.5608
sea_green_medium   60   179   113   0.2353   0.7020   0.4431
sea_green_light    32   178   170   0.1255   0.6980   0.6667
spring_green        0   255   127   0.0000   1.0000   0.4980
spring_greenmedium  0   250   154   0.0000   0.9804   0.6039
terre_verte        56    94    15   0.2200   0.3700   0.0600
viridian_light    110   255   112   0.4300   1.0000   0.4400
yellow_green      154   205    50   0.6039   0.8039   0.1961

Cyans
aquamarine        127   255   212   0.4980   1.0000   0.8314
aquamarinemedium  102   205   170   0.4000   0.8039   0.6667
cyan                0   255   255   0.0000   1.0000   1.0000
cyan_white        224   255   255   0.8784   1.0000   1.0000
turquoise          64   224   208   0.2510   0.8784   0.8157
turquoise_dark      0   206   209   0.0000   0.8078   0.8196
turquoise_medium   72   209   204   0.2824   0.8196   0.8000
turquoise_pale    175   238   238   0.6863   0.9333   0.9333

Blues
alice_blue        240   248   255   0.9412   0.9725   1.0000
blue                0     0   255   0.0000   0.0000   1.0000
blue_light        173   216   230   0.6784   0.8471   0.9020
blue_medium         0     0   205   0.0000   0.0000   0.8039
cadet              95   158   160   0.3725   0.6196   0.6275
cobalt             61    89   171   0.2400   0.3500   0.6700
cornflower        100   149   237   0.3922   0.5843   0.9294
cerulean            5   184   204   0.0200   0.7200   0.8000
dodger_blue        30   144   255   0.1176   0.5647   1.0000
indigo              8    46    84   0.0300   0.1800   0.3300
manganese_blue      3   168   158   0.0100   0.6600   0.6200
midnight_blue      25    25   112   0.0980   0.0980   0.4392
navy                0     0   128   0.0000   0.0000   0.5020
peacock            51   161   201   0.2000   0.6300   0.7900
powder_blue       176   224   230   0.6902   0.8784   0.9020
royal_blue         65   105   225   0.2549   0.4118   0.8824
slate_blue        106    90   205   0.4157   0.3529   0.8039
slate_blue_dark    72    61   139   0.2824   0.2392   0.5451
slate_blue_light  132   112   255   0.5176   0.4392   1.0000
slate_blue_medium 123   104   238   0.4824   0.4078   0.9333
sky_blue          135   206   235   0.5294   0.8078   0.9216
sky_blue_deep       0   191   255   0.0000   0.7490   1.0000
sky_blue_light    135   206   250   0.5294   0.8078   0.9804
steel_blue         70   130   180   0.2745   0.5098   0.7059
steel_blue_light  176   196   222   0.6902   0.7686   0.8706
turquoise_blue      0   199   140   0.0000   0.7800   0.5500
ultramarine        18    10   143   0.0700   0.0400   0.5600

Magentas
blue_violet       138    43   226   0.5412   0.1686   0.8863
cobalt_violetdeep 145    33   158   0.5700   0.1300   0.6200
magenta           255     0   255   1.0000   0.0000   1.0000
orchid            218   112   214   0.8549   0.4392   0.8392
orchid_dark       153    50   204   0.6000   0.1961   0.8000
orchid_medium     186    85   211   0.7294   0.3333   0.8275
permanent_violet  219    38    69   0.8600   0.1500   0.2700
plum              221   160   221   0.8667   0.6275   0.8667
purple            160    32   240   0.6275   0.1255   0.9412
purple_medium     147   112   219   0.5765   0.4392   0.8588
ultramarine_violet 92    36   110   0.3600   0.1400   0.4300
violet            143    94   153   0.5600   0.3700   0.6000
violet_dark       148     0   211   0.5804   0.0000   0.8275
violet_red        208    32   144   0.8157   0.1255   0.5647
violet_redmedium  199    21   133   0.7804   0.0824   0.5216
violet_red_pale   219   112   147   0.8588   0.4392   0.5765

References

An inexpensive scheme for calibration of a colour monitor in terms of CIE standard coordinates. W.B. Cowan, Computer Graphics, Vol. 17 No. 3, 1983

Calibration of a computer controlled colour monitor. Brainard, D.H,Color Research & Application, 14, 1, pp 23-34 (1989).

Color Monitor Colorimetry. SMPTE Recommended Practice RP 145-1987

Color Temperature for Color Television Studio Monitors. SMPTE Recommended Practice RP 37

Color Science in Television and Display Systems. Sproson, W, N, Adam Hilger Ltd, 1983. ISBN 0-85274-413-7

CIE Colorimetry. Official recommendations of the International Commission on Illumination, Publication 15.2 1986

CRT Colorimetry: Part 1 Theory and Practice, Part 2 Metrology. Berns, R.S., Motta, R.J. and Gorzynski, M.E., Color Research and Application, 18, (1993)

Effective Color Displays. Theory and Practice. Travis, D, Academic Press, 1991. ISBN 0-12-697690-2

Color and Its Reproduction. Field, G.G., Graphics Arts Technical, Foundation, 1988, pp. 320-9

Gamma and its disguises: The non-linear mappings of intensity in perception, CRT's, Film and Video. C. A. Poynton, SMPTE Journal, December 1993

Measuring Color (second edition). R. W. G. Hunt, Ellis Horwood 1991, ISBN 0-13-567686-x

On the Gun Independence and Phosphor Consistency of Color Video Monitors. W.B. Cowan N. Rowell, Color Research and Application, V.11 Supplement 1986

Precision requirements for digital Color reproduction. M Stokes MD Fairchild RS Berns, ACM Transactions on graphics, v11 n4 1992

The colorimetry of self luminous displays - a bibliography. CIE Publication n.87, Central Bureau of the CIE, Vienna 1990

The Reproduction of Color in Photography, Printing and Television. R. W. G. Hunt, Fountain Press, Tolworth, England, 1987

Fully Utilizing Photo CD Images, Article No. 4, PhotoYCC Color Encoding and Compression Schemes. Eastman Kodak Company, Rochester NY (USA) (1993).

Digital halftoning. Robert Ulichney, MIT Press, 1987, ISBN 0-262-21009-6




WWW Browser colours

Written by Paul Bourke
September 1996

Achieving consistent colour representation within WWW documents between browsers (NetScape, MSIE) and hardware platforms (Macintosh, Windows, UNIX) requires some knowledge of how browsers allocate colours when running in 8 bit mode (256 colours). If 16, 24 or higher colour modes are being used then exact colour representation is achieved without dithering and the following doesn't apply.

Colour in HTML is represented by 3 hexadecimal (hex) pairs, representing the red, green, blue components of the final colour. Each RGB component is stored as 1 byte (unsigned) ranging numerically from 0 to 255, in hex this is "00" to "ff". The colour model is the familiar colour cube used in computer graphics.

Any colour can be requested by a WWW page but the browser will not necessarily use that colour precisely due to hardware limitations. Even if the hardware is capable of 24 bit colour, the actual RGB components are often rounded to multiples of 4, fortunately this is not discernible by humans even if it could be resolved by the monitor. In index colour system such as the most common 256 colour system, the actual colours that can be displayed are quantised into much coarser steps. The operating system generally needs some colours for its own use such as drawing window boundaries, menus, etc. Browsers on Macintosh and MSWindows systems use 216 of the available 256 colours, the remaining 40 are left available for the operating system to allocate as necessary. If colours are used that are not in the palette of 256 colours the browser will either convert the colour to one that is closest in the palette or it will give the best representation of the requested colour by dithering a number of the available colours. If these colours are being used for backgrounds and/or text it can often mean the difference between a readable and unreadable page.

Consider the rectangle on the right. It is created using a colour that is in the standard 216 palette used by browsers (NetScape and MSIE).

 

The rectangle on the right however uses a colour not in the standard palette. It will either look like a solid colour if you have a 16 or 24 bit colour system or dithered if you have a 256 indexed colour system.

 

The set of colours which, if used, will look the same on all 256 palette systems is simply a 6x6x6 (216) partitioning of the colour cube. The discrete steps in each RGB component are

000051102153204255

or in hex

00336699ccff

or as percentages

000%020%040%060%080%100%

This doesn't include any grey scale levels (where r = g = b) but fortunately the 40 system wide colours usually include a good range of grey values.

Colour table - red
00 00 00 00 00 33 00 00 66 00 00 99 00 00 CC 00 00 FF
00 33 00 00 33 33 00 33 66 00 33 99 00 33 CC 00 33 FF
00 66 00 00 66 33 00 66 66 00 66 99 00 66 CC 00 66 FF
00 99 00 00 99 33 00 99 66 00 99 99 00 99 CC 00 99 FF
00 CC 00 00 CC 33 00 CC 66 00 CC 99 00 CC CC 00 CC FF
00 FF 00 00 FF 33 00 FF 66 00 FF 99 00 FF CC 00 FF FF

33 00 00 33 00 33 33 00 66 33 00 99 33 00 CC 33 00 FF
33 33 00 33 33 33 33 33 66 33 33 99 33 33 CC 33 33 FF
33 66 00 33 66 33 33 66 66 33 66 99 33 66 CC 33 66 FF
33 99 00 33 99 33 33 99 66 33 99 99 33 99 CC 33 99 FF
33 CC 00 33 CC 33 33 CC 66 33 CC 99 33 CC CC 33 CC FF
33 FF 00 33 FF 33 33 FF 66 33 FF 99 33 FF CC 33 FF FF

66 00 00 66 00 33 66 00 66 66 00 99 66 00 CC 66 00 FF
66 33 00 66 33 33 66 33 66 66 33 99 66 33 CC 66 33 FF
66 66 00 66 66 33 66 66 66 66 66 99 66 66 CC 66 66 FF
66 99 00 66 99 33 66 99 66 66 99 99 66 99 CC 66 99 FF
66 CC 00 66 CC 33 66 CC 66 66 CC 99 66 CC CC 66 CC FF
66 FF 00 66 FF 33 66 FF 66 66 FF 99 66 FF CC 66 FF FF

99 00 00 99 00 33 99 00 66 99 00 99 99 00 CC 99 00 FF
99 33 00 99 33 33 99 33 66 99 33 99 99 33 CC 99 33 FF
99 66 00 99 66 33 99 66 66 99 66 99 99 66 CC 99 66 FF
99 99 00 99 99 33 99 99 66 99 99 99 99 99 CC 99 99 FF
99 CC 00 99 CC 33 99 CC 66 99 CC 99 99 CC CC 99 CC FF
99 FF 00 99 FF 33 99 FF 66 99 FF 99 99 FF CC 99 FF FF

CC 00 00 CC 00 33 CC 00 66 CC 00 99 CC 00 CC CC 00 FF
CC 33 00 CC 33 33 CC 33 66 CC 33 99 CC 33 CC CC 33 FF
CC 66 00 CC 66 33 CC 66 66 CC 66 99 CC 66 CC CC 66 FF
CC 99 00 CC 99 33 CC 99 66 CC 99 99 CC 99 CC CC 99 FF
CC CC 00 CC CC 33 CC CC 66 CC CC 99 CC CC CC CC CC FF
CC FF 00 CC FF 33 CC FF 66 CC FF 99 CC FF CC CC FF FF

FF 00 00 FF 00 33 FF 00 66 FF 00 99 FF 00 CC FF 00 FF
FF 33 00 FF 33 33 FF 33 66 FF 33 99 FF 33 CC FF 33 FF
FF 66 00 FF 66 33 FF 66 66 FF 66 99 FF 66 CC FF 66 FF
FF 99 00 FF 99 33 FF 99 66 FF 99 99 FF 99 CC FF 99 FF
FF CC 00 FF CC 33 FF CC 66 FF CC 99 FF CC CC FF CC FF
FF FF 00 FF FF 33 FF FF 66 FF FF 99 FF FF CC FF FF FF

The above table was generated by slicing along the red axis of the colour cube, that is, each table above is a sampled plane parallel to the green-blue plane. Slices could be generated along the other two axes as well.

Laid out as a PhotoShop table, the 256 colour palette for the Macintosh is shown on the left and the 216 palette for MS based machines is shown on the right.

All three slices can be viewed below

Red -- Green -- Blue

Note: The above refers to what colours should be used when creating simple colour diagrams, for example, backgrounds, logos, etc. For photographic or other images with more than 256 colours it is better to use an adaptive colour table when creating the gif rather than dithering to the browser palette.

C code to create colour table.




Converting between RGB and CMY, YIQ, YUV

Compiled by Paul Bourke
February 1994

RGB to/from CMY

Converting from RGB to CMY is simply the following

C =
M =
Y =
1 - R
1 - G
1 - B

The reverse mapping is similarly obvious

R =
G =
B =
1 - C
1 - M
1 - Y

The two system may also be known as additive for RGB and subtractive for CMY.

RGB to/from YIQ

The YIQ system is the colour primary system adopted by NTSC for colour television broadcasting. The YIQ Color solid is formed by a linear transformation of the RGB cube. Its purpose is to exploit certain characteristics of the human visual system to maximize the use of a fixed bandwidth. The transform matrix is as follows

Y
I
Q
 = 
0.2990.5870.114
0.596-0.274-0.322
0.212-0.5230.311
R
G
B

Note: First line Y = (0.299, 0.587, 0.144) (R,G,B) also gives pure B&W translation for RGB. The inverse transformation matrix that converts YIQ to RGB is

R
G
B
 = 
1.00.9560.621
1.0-0.272-0.647
1.0-1.1051.702
Y
I
Q

RGB to/from YUV

YUV is like YIQ, except that it is the PAL/European standard. Transformation matrix is

Y
U
V
 = 
0.2990.5870.114
-0.147-0.2890.437
0.615-0.515-0.100
R
G
B

The reverse transform is

R
G
B
 = 
1.00.01.140
1.0-0.394-0.581
1.02.0280.0
Y
U
V




HSL colour space

Hue - Saturation - Lightness

Written by Paul Bourke
June 2000

The HSL colour space has three coordinates: hue, saturation, and lightness (sometimes luminance) respectively, it is sometimes referred to as HLS. The hue is an angle from 0 to 360 degrees, typically 0 is red, 60 degrees yellow, 120 degrees green, 180 degrees cyan, 240 degrees blue, and 300 degrees magenta. Saturation typically ranges from 0 to 1 (sometimes 0 to 100%) and defines how grey the colour is, 0 indicates grey and 1 is the pure primary colour. Lightness is intuitively what it's name indicates, varying the lightness reduces the values of the primary colours while keeping them in the same ratio. If the colour space is represented by disks of varying lightness then the hue and saturation are the equivalent to polar coordinates (r,theta) of any point in the plane. These disks are shown on the right for various values of lightness.

C code to transform between RGB and HSL is given below

/*
   Calculate HSL from RGB
   Hue is in degrees
   Lightness is between 0 and 1
   Saturation is between 0 and 1
*/
HSL RGB2HSL(COLOUR c1)
{
   double themin,themax,delta;
   HSL c2;

   themin = MIN(c1.r,MIN(c1.g,c1.b));
   themax = MAX(c1.r,MAX(c1.g,c1.b));
   delta = themax - themin;
   c2.l = (themin + themax) / 2;
   c2.s = 0;
   if (c2.l > 0 && c2.l < 1)
      c2.s = delta / (c2.l < 0.5 ? (2*c2.l) : (2-2*c2.l));
   c2.h = 0;
   if (delta > 0) {
      if (themax == c1.r && themax != c1.g)
         c2.h += (c1.g - c1.b) / delta;
      if (themax == c1.g && themax != c1.b)
         c2.h += (2 + (c1.b - c1.r) / delta);
      if (themax == c1.b && themax != c1.r)
         c2.h += (4 + (c1.r - c1.g) / delta);
      c2.h *= 60;
   }
   return(c2);
}
/*
   Calculate RGB from HSL, reverse of RGB2HSL()
   Hue is in degrees
   Lightness is between 0 and 1
   Saturation is between 0 and 1
*/
COLOUR HSL2RGB(HSL c1)
{
   COLOUR c2,sat,ctmp;

   while (c1.h < 0)
      c1.h += 360;
   while (c1.h > 360)
      c1.h -= 360;

   if (c1.h < 120) {
      sat.r = (120 - c1.h) / 60.0;
      sat.g = c1.h / 60.0;
      sat.b = 0;
   } else if (c1.h < 240) {
      sat.r = 0;
      sat.g = (240 - c1.h) / 60.0;
      sat.b = (c1.h - 120) / 60.0;
   } else {
      sat.r = (c1.h - 240) / 60.0;
      sat.g = 0;
      sat.b = (360 - c1.h) / 60.0;
   }
   sat.r = MIN(sat.r,1);
   sat.g = MIN(sat.g,1);
   sat.b = MIN(sat.b,1);

   ctmp.r = 2 * c1.s * sat.r + (1 - c1.s);
   ctmp.g = 2 * c1.s * sat.g + (1 - c1.s);
   ctmp.b = 2 * c1.s * sat.b + (1 - c1.s);

   if (c1.l < 0.5) {
      c2.r = c1.l * ctmp.r;
      c2.g = c1.l * ctmp.g;
      c2.b = c1.l * ctmp.b;
   } else {
      c2.r = (1 - c1.l) * ctmp.r + 2 * c1.l - 1;
      c2.g = (1 - c1.l) * ctmp.g + 2 * c1.l - 1;
      c2.b = (1 - c1.l) * ctmp.b + 2 * c1.l - 1;
   }

   return(c2);
}
 
Lightness=0.0


Lightness=0.1


Lightness=0.2


Lightness=0.3


Lightness=0.4


Lightness=0.5


Lightness=0.6


Lightness=0.7


Lightness=0.8


Lightness=0.9


Lightness=1.0




HSV colour space

Hue - Saturation - Value

Written by Paul Bourke
June 2000

The HSV colour space has three coordinates: hue, saturation, and value (sometimes called brightness) respectively. This colour system is attributed to "Smith" around 1978 and used to be called the hexcone colour model. The hue is an angle from 0 to 360 degrees, typically 0 is red, 60 degrees yellow, 120 degrees green, 180 degrees cyan, 240 degrees blue, and 300 degrees magenta. Saturation typically ranges from 0 to 1 (sometimes 0 to 100%) and defines how grey the colour is, 0 indicates grey and 1 is the pure primary colour. Value is similar to luminance except it also varies the colour saturation. If the colour space is represented by disks of varying lightness then the hue and saturation are the equivalent to polar coordinates (r,theta) of any point in the plane. The disks on the right show this for various values.

C code to transform between RGB and HSV is given below

/*
   Calculate RGB from HSV, reverse of RGB2HSV()
   Hue is in degrees
   Lightness is between 0 and 1
   Saturation is between 0 and 1
*/
COLOUR HSV2RGB(HSV c1)
{
   COLOUR c2,sat;

   while (c1.h < 0)
      c1.h += 360;
   while (c1.h > 360)
      c1.h -= 360;

   if (c1.h < 120) {
      sat.r = (120 - c1.h) / 60.0;
      sat.g = c1.h / 60.0;
      sat.b = 0;
   } else if (c1.h < 240) {
      sat.r = 0;
      sat.g = (240 - c1.h) / 60.0;
      sat.b = (c1.h - 120) / 60.0;
   } else {
      sat.r = (c1.h - 240) / 60.0;
      sat.g = 0;
      sat.b = (360 - c1.h) / 60.0;
   }
   sat.r = MIN(sat.r,1);
   sat.g = MIN(sat.g,1);
   sat.b = MIN(sat.b,1);

   c2.r = (1 - c1.s + c1.s * sat.r) * c1.v;
   c2.g = (1 - c1.s + c1.s * sat.g) * c1.v;
   c2.b = (1 - c1.s + c1.s * sat.b) * c1.v;

   return(c2);
}
/*
   Calculate HSV from RGB
   Hue is in degrees
   Lightness is betweeen 0 and 1
   Saturation is between 0 and 1
*/
HSV RGB2HSV(COLOUR c1)
{
   double themin,themax,delta;
   HSV c2;

   themin = MIN(c1.r,MIN(c1.g,c1.b));
   themax = MAX(c1.r,MAX(c1.g,c1.b));
   delta = themax - themin;
   c2.v = themax;
   c2.s = 0;
   if (themax > 0)
      c2.s = delta / themax;
   c2.h = 0;
   if (delta > 0) {
      if (themax == c1.r && themax != c1.g)
         c2.h += (c1.g - c1.b) / delta;
      if (themax == c1.g && themax != c1.b)
         c2.h += (2 + (c1.b - c1.r) / delta);
      if (themax == c1.b && themax != c1.r)
         c2.h += (4 + (c1.r - c1.g) / delta);
      c2.h *= 60;
   }
   return(c2);
}
 
Value=0.0


Value=0.1


Value=0.2


Value=0.3


Value=0.4


Value=0.5


Value=0.6


Value=0.7


Value=0.8


Value=0.9


Value=1.0




Frequency to RGB

Compiled by Paul Bourke
February 1994

Reference: Rogers, David F., "Procedural Elements for Computer Graphics,"
McGraw-Hill Book Company, New York, 1985, pp390-398.

The best way of finding the RGB values of a given wavelength is to use the CIE chromaticity diagram. The spectral locus is on the edge of the wing-shaped curve. You just need to transform the xy coordinate from CIEXYZ space into RGB space.

Below I have spectral locus coordinates from 380nm to 780m in steps of 5 nm. It's written as a C array. We'll refer to any particular wavelength's coordinates as xc and yc. To transform into RGB, we need the xy coordinates of the RGB primaries and the alignment white. I'll use a set of typical values.

                  x       y
        Red     0.628   0.346           call these xr and yr
        Green   0.268   0.588            "     "   xg and yg
        Blue    0.150   0.070            "     "   xb and yb
        White   0.313   0.329            "     "   xw and yw

From these we can compute the linear transformation from CIEXYZ to RGB.It is:

        [R] = [  2.739 -1.145 -0.424 ] [X]
        [G] = [ -1.119  2.029  0.033 ] [Y]
        [B] = [  0.138 -0.333  1.105 ] [Z]

However, if the xy coordinate of the spectral Color isn't inside the triangle formed by the RGB xy coordinates given above (and none of them are), then we must first find a closest match that is inside the triangle. Otherwise our RGB values will go outside the range [0,1], i.e. some values will be less than zero or greater than one.

One common way of moving the colour inside the triangle is to make the colour whiter (i.e. desaturate it). Mathematically we do this by intersecting the line joining the colour and the alignment white with the edges of the triangle. First define the line that goes through white and your colour implicitly:

        Lc:     [-(yw - yc)] * P + [-(yw - yc)] * [xc]
                [  xw - wc ]       [  xw - xc ]   [yc]

For every pair of red, green, and blue, define an explicit (parametric line). For example, the line between the red and green primaries is:

        Lrg:    P = [xr] + t * [xg - xr]
                    [yr]       [yg - yr]

Substitute Lrg for P into Lc and solve for the parameter t. If 0 <= t <= 1, then you've got the answer and you don't need to try the other lines. Otherwise, substitute Lgb, and if necessary Lbr. If t is not in the range [0,1] then the intersection occurred outside the triangle.

Once you have t, plug it into the original equation for Lrg (or whatever line gave a good solution) and you get the xy coordinate. Plug this into the linear transformation above (using z = 1 - x - y) to get the RGB values. At least one of R, G, or B should be exactly one (ie fully saturated in the RGB colour space).

Since this is an involved procedure, I would suggest precomputing RGB's for all the wavelengths you'll need (or enough to interpolate) once for each display device you'll be using. If you know the chromaticity coordinates of your displays primaries and its white you can compute the linear transformation easily.

/* CIE 1931 chromaticity coordinates of spectral stimuli  (only xy,  z = 1 - x - y) */
float spectrum_xy[][2] = {
                {0.1741, 0.0050}, {0.1740, 0.0050}, {0.1738, 0.0049},
                {0.1736, 0.0049}, {0.1733, 0.0048}, {0.1730, 0.0048},
                {0.1726, 0.0048}, {0.1721, 0.0048}, {0.1714, 0.0051},
                {0.1703, 0.0058}, {0.1689, 0.0069}, {0.1669, 0.0086},
                {0.1644, 0.0109}, {0.1611, 0.0138}, {0.1566, 0.0177},
                {0.1510, 0.0227}, {0.1440, 0.0297}, {0.1355, 0.0399},
                {0.1241, 0.0578}, {0.1096, 0.0868}, {0.0913, 0.1327},
                {0.0687, 0.2007}, {0.0454, 0.2950}, {0.0235, 0.4127},
                {0.0082, 0.5384}, {0.0039, 0.6548}, {0.0139, 0.7502},
                {0.0389, 0.8120}, {0.0743, 0.8338}, {0.1142, 0.8262},
                {0.1547, 0.8059}, {0.1929, 0.7816}, {0.2296, 0.7543},
                {0.2658, 0.7243}, {0.3016, 0.6923}, {0.3373, 0.6589},
                {0.3731, 0.6245}, {0.4087, 0.5896}, {0.4441, 0.5547},
                {0.4788, 0.5202}, {0.5125, 0.4866}, {0.5448, 0.4544},
                {0.5752, 0.4242}, {0.6029, 0.3965}, {0.6270, 0.3725},
                {0.6482, 0.3514}, {0.6658, 0.3340}, {0.6801, 0.3197},
                {0.6915, 0.3083}, {0.7006, 0.2993}, {0.7079, 0.2920},
                {0.7140, 0.2859}, {0.7190, 0.2809}, {0.7230, 0.2770},
                {0.7260, 0.2740}, {0.7283, 0.2717}, {0.7300, 0.2700},
                {0.7311, 0.2689}, {0.7320, 0.2680}, {0.7327, 0.2673},
                {0.7334, 0.2666}, {0.7340, 0.2660}, {0.7344, 0.2656},
                {0.7346, 0.2654}, {0.7347, 0.2653}, {0.7347, 0.2653},
                {0.7347, 0.2653}, {0.7347, 0.2653}, {0.7347, 0.2653},
                {0.7347, 0.2653}, {0.7347, 0.2653}, {0.7347, 0.2653},
                {0.7347, 0.2653}, {0.7347, 0.2653}, {0.7347, 0.2653},
                {0.7347, 0.2653}, {0.7347, 0.2653}, {0.7347, 0.2653},
                {0.7347, 0.2653}, {0.7347, 0.2653}, {0.7347, 0.2653}};




YCC colour space and image compression

Written by Paul Bourke
April 2000
Introduction

There are an infinite number of possible colour spaces instead of the common RGB (Red, Green, Blue) system most commonly used in computer graphics. Many of these other colour spaces are derived by applying linear functions of r, g, b. So for example, a colour space based upon 3 coordinates v1, v2, and v3 can be written as:

   v1 = Ar1 R + Ag1 G + Ab1 B
   v2 = Ar2 R + Ag2 G + Ab2 B
   v3 = Ar3 R + Ag3 G + Ab3 B 

Where the Ar, Ag, Ab are constants for a particular colour space. Similarly for any such system there are linear functions to go back to RGB space. The coefficients Dr, Dg, Dc can be derived by solving the above equations for r,g,b. For example:

   R = Dr1 v1 + Dg1 v2 + Db1 v3
   G = Dr2 v1 + Dg2 v2 + Db2 v3
   B = Dr3 v1 + Dg3 v2 + Db3 v3

The human visual system has much less dynamic range (acuity) for spatial variation in colour than for brightness (luminance), in other words, we are acutely aware of changes in the brightness of details than of small changes in hue. So rather than saving as RGB it can be more efficient to encode luminance in one channel and colour information (that has had the luminance contribution removed) in two other channels. The two colour channels can be encoded with less bandwidth by using a number of techniques, predominantly by reducing the precision, or as will be discussed here, reducing the spatial resolution.

Since green dominates the luminance channel it makes sense to base the other two chrominance channels on luminance subtracted red and blue. Such luminance, red chrominance and blue chrominance systems are generally referred to as Y, Cr, and Cb. In what follows they will generally be referred this way or simply as YCC.

Example: Kodak PhotoCD YCC

The Kodak PhotoYCC colour space was designed for encoding images with the PhotoCD system. It is based on both CCIR Recommendations 709 and 601-1, having a colour gamut defined by the CCIR 709 primaries and a luminance - chrominance representation of colour like CCIR 601-1's YCbCr.

To convert from RGB to PhotoCD YCC, the following matrix operation is used (after appropriate gamma correction has been applied). Notice the major contribution to the Cr value is red and the main contribution to the Cb value is blue. The unbalanced scale difference between Chroma1 and Chroma2 is designed, according to Kodak, to follow the typical distribution of colours in real scenes.

  Y  =  0.299 R + 0.587 G + 0.114 B
  Cr =  0.701 R - 0.587 G - 0.114 B
  Cb = -0.299 R - 0.587 G + 0.886 B

The rationale behind these coefficients can be found by considering that the luminance subtracted blue (B-Y) signal reaches its extreme values at blue (R=0, G=0, B=1, Y=0.114, B-Y=0.886) and at yellow (R=1, G=1, B=0, Y=0.886, B-Y=-0.886). Similarly, the extrema of the luminance subtracted red (R-Y), +-0.701, occur at red and cyan.

In a similar way there is a matrix for creating RGB colour values from any colour space created as discussed above. Note that in many cases and this applies to YCC colour spaces, it is possible to represent colours that cannot be represented in RGB space. For the Kodak system the reverse mapping from YCC into RGB is:

Application to image compression

Given the human visual systems "preference" for luminance information, one can design an image compression system around YCC colour space. Consider an RGB image with Nx horizontal pixels and Ny vertical pixels. If each pixel is represented as one byte the the image size in bytes is Nx Ny 3. Consider storing the image in YCC space where the luminance channel Y is stored as one byte for each pixel but the two chrominance channels are only stored for each block of say 4x4 pixels. The resulting image would be Nx Ny + 2 (Nx/4 + Ny/4), this is an image 2 and 2/3rds smaller. Put another way, instead of using on average 3 bytes (24 bits) per pixel we would only be using 9 bits per pixel.

Note the chrominance is averaged over each 4x4 block. There is nothing particularly special about a 4x4 block instead of a 3x3 or 5x5 or other sizes. A 2x2 by two results in a factor 2 saving, a 3x3 grid results in a saving of 2.45. Larger grids give ever decreasing rates of return and soon impact on the image quality.

The YCC colour space used follows that used by TIFF and JPEG (Rec 601-1)
   Y  =  0.2989 R + 0.5866 G + 0.1145 B
   Cb = -0.1687 R - 0.3312 G + 0.5000 B
   Cr =  0.5000 R - 0.4183 G - 0.0816 B

RGB values are normally on the scale of 0 to 1, or since they are stored as unsigned single bytes, 0 to 255. The resulting luminance value is also on the scale of 0 to 255, the chrominance values need 127.5 added to them so they can saved in an unsigned byte.

Of course when the YCC values are turned back into RGB, then 127.5 must be first subtracted from the two chrominance values. The reverse transform is

   R = Y             + 1.4022 Cr
   G = Y - 0.3456 Cb - 0.7145 Cr
   B = Y + 1.7710 Cb

The full chrominance range of +-0.5 maps into a larger colour space than supported by RGB. The above equations can yield RGB values outside the 0 to 255 (or 0 to 1) range, these typically relate to very light or dark colours. The RGB values should be clipped so they lie within the allowed range after the transform from YCC to RGB is calculated.

An image compressed this way is obviously degraded but could one tell the difference? Below are two images, the one on the left uses 24 bits per pixel and the one on the right uses 9 bits per pixel as described above.

Before

The above is the original image that is used as an example in what follows. It was captured as a single from a digital movie camera.

 
After

The above is the image that results from a 1 byte Y channel and 4x4 subsampled Cr, Cc channels.


Y channel

First C channel (Cr)

Second C channel (Cb)

The compression scheme above is most noticeable when there are rapid changes in hue but little change in luminance, in this case the 4x4 subsampling of the Cr and Cb channels is most noticeable. For images of real world scenes this happens surprisingly rarely, in the before and after example above it is virtually impossible to zoom in and find examples of the 4x4 subsampling. Similarly one would not generally use this sort of compression for low colour images, in that case other compression techniques are available.

Appendix: Other standard colour space conversions

RGB -> CIE XYZitu (D65)
   X = 0.431 R + 0.342 G + 0.178 B
   Y = 0.222 R + 0.707 G + 0.071 B
   Z = 0.020 R + 0.130 G + 0.939 B
CIE XYZitu (D65) -> RGB
   R =  3.063 X - 1.393 Y - 0.476 Z
   G = -0.969 X + 1.876 Y + 0.042 Z
   B =  0.068 X - 0.229 Y + 1.069 Z

RGB -> CIE XYZrec601-1 (C illuminant)
   X = 0.607 R + 0.174 G + 0.200 B
   Y = 0.299 R + 0.587 G + 0.114 B
   Z = 0.000 R + 0.066 G + 1.116 B
CIE XYZrec601-1 (C illuminant) -> RGB
   R =  1.910 X - 0.532 Y - 0.288 Z
   G = -0.985 X + 1.999 Y - 0.028 Z
   B =  0.058 X - 0.118 Y + 0.898 Z

RGB -> CIE XYZccir709 (D65)
    X = 0.412 R + 0.358 G + 0.180 B
    Y = 0.213 R + 0.715 G + 0.072 B
    Z = 0.019 R + 0.119 G + 0.950 B
CIE XYZccir709 (D65) -> RGB
   R =  3.241 X - 1.537 Y - 0.499 Z
   G = -0.969 X + 1.876 Y + 0.042 Z
   B =  0.056 X - 0.204 Y + 1.057 Z

PAL television standard
RGB -> YUV
    Y =  0.299 R + 0.587 G + 0.114 B
    U = -0.147 R - 0.289 G + 0.436 B
    V =  0.615 R - 0.515 G - 0.100 B
YUV -> RGB
   R = Y + 0.000 U + 1.140 V
   G = Y - 0.396 U - 0.581 V
   B = Y + 2.029 U + 0.000 V

NTSC television standard
RGB -> YIQ
    Y =  0.299 R + 0.587 G + 0.114 B
    I =  0.596 R - 0.274 G - 0.322 B
    Q =  0.212 R - 0.523 G + 0.311 B
YIQ -> RGB
   R = Y + 0.956 I + 0.621 Q
   G = Y - 0.272 I - 0.647 Q
   B = Y - 1.105 I + 1.702 Q

References

Color Monitor Colorimetry. SMPTE Recommended Practice RP 145-1987

CIE Colorimetry. Official recommendations of the International Commission on Illumination, Publication 15.2 1986

Effective Color Displays. Theory and Practice. D. Travis, Academic Press, 1991. ISBN 0-12-697690-2

Measuring Colour (Second edition). R.W.G. Hunt, Ellis Horwood 1991, ISBN 0-13-567686-x

Precision requirements for digital colour reproduction. M. Stokes, M.D. Fairchild, R.S. Berns, ACM Transactions on graphics, v11 n4 1992

The Reproduction of Colour in Photography, Printing and Television. R.W.G. Hunt, Fountain Press, Tolworth, England, 1987




Colour Ramping for Data Visualisation

Written by Paul Bourke
July 1996

Contribution: Ramp.cs by Russell Plume in DotNet C#.

This note introduces the most commonly used colour ramps for mapping colours onto a range of scalar values as is often required in data visualisation. The colour space will be based upon the RGB system.

Grey Scale

The most basic mapping of some scalar value to a colour ramp is to use either an intensity ramp from black to a single colour or use the grey scale ramp from black to white. Black normally being the low end of the range of values, and white the high end. On the colour cube this is a diagonal line between the two opposite corners, the red, green, blue components are all the same.

The map from black to white is usually just a linear ramp, for a value (v) which varies from vmin to vmax, the colour is
red = green = blue = (v - vmin) / (vmax - vmin)

Colour

The most commonly used colour ramp is often refer to as the "hot- to-cold" colour ramp. Blue is chosen for the low values, green for middle values, and red for the high as these seem "intuitive" bounds. One could ramp between these points on the colour cube but this involves moving diagonally across the faces of the cube. Instead we add the colours cyan and yellow so that the colour ramp only moves along the edges of the colour cube from blue to red. This not only makes the mapping easier and faster but introduces more colour variation. The following illustrates the path on the colour cube.

The colour ramp is shown below along with the transition values.

Again there is a linear relationship of the scalar value with colour within each of the 4 colour bands. In some applications the variable being represented with the colour map is circular in nature in which case a cyclic colour map is desirable. The above can be simply modified to pass through magenta to yield one of many possible circular colour maps.

General map

A more general case ramps between N colours, each colour has a corresponding value within the range of values of the variable being mapped. The points within the colour cube can be linearly ramped between the points or the transition can be smoothed by some interpolation function such a bezier curve. For example a 3 colour ramp for terrain visualisation might choose 3 heights and three corresponding colours.

Displayed as a colour ramp

The resulting terrain map

C Source for the "standard" hot-to-cold colour ramp.

/*
   Return a RGB colour value given a scalar v in the range [vmin,vmax]
   In this case each colour component ranges from 0 (no contribution) to
   1 (fully saturated), modifications for other ranges is trivial.
   The colour is clipped at the end of the scales if v is outside
   the range [vmin,vmax]
   typedef struct {
      double r,g,b;
   } COLOUR;
*/
COLOUR GetColour(double v,double vmin,double vmax)
{
   COLOUR c = {1.0,1.0,1.0}; // white
   double dv;

   if (v < vmin)
      v = vmin;
   if (v > vmax)
      v = vmax;
   dv = vmax - vmin;

   if (v < (vmin + 0.25 * dv)) {
      c.r = 0;
      c.g = 4 * (v - vmin) / dv;
   } else if (v < (vmin + 0.5 * dv)) {
      c.r = 0;
      c.b = 1 + 4 * (vmin + 0.25 * dv - v) / dv;
   } else if (v < (vmin + 0.75 * dv)) {
      c.r = 4 * (v - vmin - 0.5 * dv) / dv;
      c.b = 0;
   } else {
      c.g = 1 + 4 * (vmin + 0.75 * dv - v) / dv;
      c.b = 0;
   }

   return(c);
}

Note about gamma

Please note that the above are not corrected for the gamma of the display device. As such the colours may indeed appear different on different displays. The gamma of displays can be adjusted and typically have a power relationship, that is, if (r,g,b) is the colour being displayed, it will appear as (rG,gG,bG). In order to achieve a display colour or (r,g,b) the inverse of the gamma function needs to be created, namely (r1/G,g1/G,b1/G). Note also that the gamma value G is not necessarily the same for each (r,g,b) component.




Simple method of storing colour ramps

Adapted from OGLE (By Dr. Michael J. Gourlay)

Written By Paul Bourke
June 2003

Many applications in computer graphics use colour and/or opacity ramps, in particular, visualisation using volume rendering. In this and other cases one often just requires colour and opacity index tables for 256 states. The following is a straightforward approach to storing such index colour maps, it has been adopted from the OGLE volume rendering software, at least this means there is one other piece of software that uses the format rather than dreaming up an entirely new format.

Example (ramp1.dat)

This is the standard blue to green to red colour map. The columns are the index value followed by the red, green, blue values ranging from 0 (no contribution) to 255 (full contribution).

   0   0   0 255     64   0 255 254    128   1 255   0    192 255 252   0   
   1   0   3 255     65   0 255 249    129   5 255   0    193 255 247   0   
   2   0   7 255     66   0 255 246    130   9 255   0    194 255 244   0   
   3   0  11 255     67   0 255 241    131  13 255   0    195 255 239   0   
   4   0  15 255     68   0 255 238    132  17 255   0    196 255 236   0   
   5   0  19 255     69   0 255 233    133  21 255   0    197 255 231   0   
   6   0  23 255     70   0 255 230    134  25 255   0    198 255 228   0   
   7   0  27 255     71   0 255 225    135  29 255   0    199 255 223   0   
   8   0  31 255     72   0 255 222    136  33 255   0    200 255 220   0   
   9   0  35 255     73   0 255 217    137  37 255   0    201 255 215   0   
  10   0  39 255     74   0 255 214    138  41 255   0    202 255 212   0   
  11   0  43 255     75   0 255 209    139  45 255   0    203 255 207   0   
  12   0  47 255     76   0 255 206    140  49 255   0    204 255 204   0   
  13   0  51 255     77   0 255 201    141  53 255   0    205 255 199   0   
  14   0  55 255     78   0 255 198    142  57 255   0    206 255 196   0   
  15   0  59 255     79   0 255 193    143  61 255   0    207 255 191   0   
  16   0  63 255     80   0 255 190    144  66 255   0    208 255 188   0   
  17   0  67 255     81   0 255 185    145  70 255   0    209 255 183   0   
  18   0  71 255     82   0 255 182    146  74 255   0    210 255 180   0   
  19   0  75 255     83   0 255 177    147  78 255   0    211 255 175   0   
  20   0  79 255     84   0 255 174    148  82 255   0    212 255 172   0   
  21   0  83 255     85   0 255 169    149  86 255   0    213 255 167   0   
  22   0  87 255     86   0 255 166    150  90 255   0    214 255 164   0   
  23   0  91 255     87   0 255 161    151  94 255   0    215 255 159   0   
  24   0  95 255     88   0 255 158    152  98 255   0    216 255 156   0   
  25   0  99 255     89   0 255 153    153 102 255   0    217 255 151   0   
  26   0 103 255     90   0 255 150    154 106 255   0    218 255 148   0   
  27   0 107 255     91   0 255 145    155 110 255   0    219 255 143   0   
  28   0 111 255     92   0 255 142    156 114 255   0    220 255 140   0   
  29   0 115 255     93   0 255 137    157 118 255   0    221 255 135   0   
  30   0 119 255     94   0 255 134    158 122 255   0    222 255 132   0   
  31   0 123 255     95   0 255 129    159 126 255   0    223 255 127   0   
  32   0 127 255     96   0 255 126    160 129 255   0    224 255 123   0   
  33   0 132 255     97   0 255 122    161 134 255   0    225 255 119   0   
  34   0 135 255     98   0 255 118    162 137 255   0    226 255 115   0   
  35   0 140 255     99   0 255 114    163 142 255   0    227 255 111   0   
  36   0 143 255    100   0 255 110    164 145 255   0    228 255 107   0   
  37   0 148 255    101   0 255 106    165 150 255   0    229 255 103   0   
  38   0 151 255    102   0 255 102    166 153 255   0    230 255  99   0   
  39   0 156 255    103   0 255  98    167 158 255   0    231 255  95   0   
  40   0 159 255    104   0 255  94    168 161 255   0    232 255  91   0   
  41   0 164 255    105   0 255  90    169 166 255   0    233 255  87   0   
  42   0 167 255    106   0 255  86    170 169 255   0    234 255  83   0   
  43   0 172 255    107   0 255  82    171 174 255   0    235 255  79   0   
  44   0 175 255    108   0 255  78    172 177 255   0    236 255  75   0   
  45   0 180 255    109   0 255  74    173 182 255   0    237 255  71   0   
  46   0 183 255    110   0 255  70    174 185 255   0    238 255  67   0   
  47   0 188 255    111   0 255  66    175 190 255   0    239 255  63   0   
  48   0 191 255    112   0 255  61    176 193 255   0    240 255  59   0   
  49   0 196 255    113   0 255  57    177 198 255   0    241 255  55   0   
  50   0 199 255    114   0 255  53    178 201 255   0    242 255  51   0   
  51   0 204 255    115   0 255  49    179 206 255   0    243 255  47   0   
  52   0 207 255    116   0 255  45    180 209 255   0    244 255  43   0   
  53   0 212 255    117   0 255  41    181 214 255   0    245 255  39   0   
  54   0 215 255    118   0 255  37    182 217 255   0    246 255  35   0   
  55   0 220 255    119   0 255  33    183 222 255   0    247 255  31   0   
  56   0 223 255    120   0 255  29    184 225 255   0    248 255  27   0   
  57   0 228 255    121   0 255  25    185 230 255   0    249 255  23   0   
  58   0 231 255    122   0 255  21    186 233 255   0    250 255  19   0   
  59   0 236 255    123   0 255  17    187 238 255   0    251 255  15   0   
  60   0 239 255    124   0 255  13    188 241 255   0    252 255  11   0   
  61   0 244 255    125   0 255   9    189 246 255   0    253 255   7   0   
  62   0 247 255    126   0 255   5    190 249 255   0    254 255   3   0   
  63   0 252 255    127   0 255   1    191 254 255   0    255 255   0   0   

Opacity maps are stored in the same way even though the RGB colour information is usually redundant (r == g == b). Having the opacity map separate from the colour map allows one to use them interchangeably, if the RGB values of a map used as an opacity map are not equal then the application can choose how to derive the single opacity value (eg: use the red channel, use the magnitude of the colour....).

Collection of colour ramps and associated data file

01.dat 02.dat
03.dat 04.dat
05.dat 06.dat
07.dat 08.dat
09.dat 10.dat
11.dat 12.dat
13.dat 14.dat
15.dat 16.dat
17.dat 18.dat
19.dat 20.dat
21.dat 22.dat




HSV colour ramp/selector

Written by Paul Bourke
July 2005

A useful colour picker interface is often based upon the HSV colour system, sometimes the HSL. The layout of the colour table is often as follows although there is some variation in the functions used for saturation and value. All the colours in RGB space lie on the surface of the colour cube.

Example

As a web based paint selection interface, note the extra bar for grey scale selection.




Index Colour

Macintosh palette -- PhotoShop colour tables

Written by Paul Bourke
February 1997

Index colour systems are still prevalent in many graphical computer systems. An index colour system is one in which colour is specified by an integer normally from 0 up to the number of colours in the colour table. Each entry in the colour table gives the red, green, blue components for that particular index.

In such colour systems the colour table is sometimes fixed. In these cases you can only use the preset colours. When a particular colour is required which isn't in the table the closest may be used or a number of colours that are in the table may be dithered in order to approximate the desired colour.

A more common situation is when the colour table is of a fixed size but the actual RGB values at each position in the table can be changed. Therefore, if you require a wide range of a particular hue you can load up the colour table accordingly. It also leads to situations where one might want to optimise the colour table to best represent a particular image. For example if an image only contains 100 different colours then irrespective of what these colours are, a colour table containing these colours will allow the image to be represented exactly. A much more common situation is when the colour table isn't large enough to hold all the different colours desired, in these cases one might engage in a process called "colour table optimisation" where one attempts to load the colour table with colours so that the image can be best represented.

In yet other systems the colour table can be made as large as desired. One often then wants to create a colour table for a particular task. The remained of this document will describe the most unbiased method of creating a colour table in cases where you don't know what colours will be required and thus want an equal sampling of the RGB colour space

There are two processes involved. The first is creating the entries in the colour table, the second is given an RGB value determining the index of its closest representation in the table. In what follows, an RGB triple will be floating point numbers in the inclusive range [0,1]. If your colour space uses different ranges then simple scaling converts one range to the [0,1] range used here.

To create a n sampling along each r,g,b axis will result in n3 colour values. Converting from (r,g,b) triples into the colour index is

index = b * (n-1) * n * n + g * (n-1) * n + r * (n-1)

Source Code

Some examples of colour tables are illustrated below. The index ranges from 0 top left, increasing left to right - top to bottom. The index in the bottom right cell is n3-1.

3x3x3 sampling

4x4x4 sampling

5x5x5 sampling

6x6x6 sampling

Finding the index of the colour closest to a particular (r,g,b) triple is accomplished as follows

index = nint(r * (n-1)) + nint(g * (n-1)) * n + nint(b * (n-1)) * n * n

where nint(x) is the nearest integer to x.

Note

  • If an odd sampling is used then the colour table will have a sampling of grey values (the diagonal of the colour cube where r = g = b)

  • Many colour tables are restricted to one byte for the index, that is, up to 256 entries. It is common in these cases to use a 6x6x6 sampling and fill in the remaining 40 colour table entries with red, blue, green, and grey 10 sample ramps.

  • Strictly speaking the samples generated as above are not all equally distributed, the 8 vertices of the colour cube are less likely than colours along the 12 edges which in turn are less likely than points lying near the 6 planes which are less likely that interior points.

The Macintosh 256 Colour Palette

The following shows the RGB value of each colour in the default 256 Macintosh palette. The first column is the index, column 2 to 4 are the RGB values in the range 0 to 65535, columns 5 to 7 are the RGB values in the range of 0 to 1.

The palette is arranged as follows: there are 256 colours to allocate, an even distribution of colours through the colour cube might be desirable but 256 is not the cube of an integer. 6x6x6 is 216 and so the first 216 colours are an equal 6x6x6 sampling of the colour cube. This leaves 40 colours to allocate, this has been done by choosing a ramp of 10 shades each for red, green, blue and grey.

The table

Index Red Green Blue Red Green Blue
0 65535 65535 65535 1 1 1
1 65535 65535 52428 1 1 0.8
2 65535 65535 39321 1 1 0.6
3 65535 65535 26214 1 1 0.4
4 65535 65535 13107 1 1 0.2
5 65535 65535 0 1 1 0
6 65535 52428 65535 1 0.8 1
7 65535 52428 52428 1 0.8 0.8
8 65535 52428 39321 1 0.8 0.6
9 65535 52428 26214 1 0.8 0.4
10 65535 52428 13107 1 0.8 0.2
11 65535 52428 0 1 0.8 0
12 65535 39321 65535 1 0.6 1
13 65535 39321 52428 1 0.6 0.8
14 65535 39321 39321 1 0.6 0.6
15 65535 39321 26214 1 0.6 0.4
16 65535 39321 13107 1 0.6 0.2
17 65535 39321 0 1 0.6 0
18 65535 26214 65535 1 0.4 1
19 65535 26214 52428 1 0.4 0.8
20 65535 26214 39321 1 0.4 0.6
21 65535 26214 26214 1 0.4 0.4
22 65535 26214 13107 1 0.4 0.2
23 65535 26214 0 1 0.4 0
24 65535 13107 65535 1 0.2 1
25 65535 13107 52428 1 0.2 0.8
26 65535 13107 39321 1 0.2 0.6
27 65535 13107 26214 1 0.2 0.4
28 65535 13107 13107 1 0.2 0.2
29 65535 13107 0 1 0.2 0
30 65535 0 65535 1 0 1
31 65535 0 52428 1 0 0.8
32 65535 0 39321 1 0 0.6
33 65535 0 26214 1 0 0.4
34 65535 0 13107 1 0 0.2
35 65535 0 0 1 0 0
36 52428 65535 65535 0.8 1 1
37 52428 65535 52428 0.8 1 0.8
38 52428 65535 39321 0.8 1 0.6
39 52428 65535 26214 0.8 1 0.4
40 52428 65535 13107 0.8 1 0.2
41 52428 65535 0 0.8 1 0
42 52428 52428 65535 0.8 0.8 1
43 52428 52428 52428 0.8 0.8 0.8
44 52428 52428 39321 0.8 0.8 0.6
45 52428 52428 26214 0.8 0.8 0.4
46 52428 52428 13107 0.8 0.8 0.2
47 52428 52428 0 0.8 0.8 0
48 52428 39321 65535 0.8 0.6 1
49 52428 39321 52428 0.8 0.6 0.8
50 52428 39321 39321 0.8 0.6 0.6
51 52428 39321 26214 0.8 0.6 0.4
52 52428 39321 13107 0.8 0.6 0.2
53 52428 39321 0 0.8 0.6 0
54 52428 26214 65535 0.8 0.4 1
55 52428 26214 52428 0.8 0.4 0.8
56 52428 26214 39321 0.8 0.4 0.6
57 52428 26214 26214 0.8 0.4 0.4
58 52428 26214 13107 0.8 0.4 0.2
59 52428 26214 0 0.8 0.4 0
60 52428 13107 65535 0.8 0.2 1
61 52428 13107 52428 0.8 0.2 0.8
62 52428 13107 39321 0.8 0.2 0.6
63 52428 13107 26214 0.8 0.2 0.4
64 52428 13107 13107 0.8 0.2 0.2
65 52428 13107 0 0.8 0.2 0
66 52428 0 65535 0.8 0 1
67 52428 0 52428 0.8 0 0.8
68 52428 0 39321 0.8 0 0.6
69 52428 0 26214 0.8 0 0.4
70 52428 0 13107 0.8 0 0.2
71 52428 0 0 0.8 0 0
72 39321 65535 65535 0.6 1 1
73 39321 65535 52428 0.6 1 0.8
74 39321 65535 39321 0.6 1 0.6
75 39321 65535 26214 0.6 1 0.4
76 39321 65535 13107 0.6 1 0.2
77 39321 65535 0 0.6 1 0
78 39321 52428 65535 0.6 0.8 1
79 39321 52428 52428 0.6 0.8 0.8
80 39321 52428 39321 0.6 0.8 0.6
81 39321 52428 26214 0.6 0.8 0.4
82 39321 52428 13107 0.6 0.8 0.2
83 39321 52428 0 0.6 0.8 0
84 39321 39321 65535 0.6 0.6 1
85 39321 39321 52428 0.6 0.6 0.8
86 39321 39321 39321 0.6 0.6 0.6
87 39321 39321 26214 0.6 0.6 0.4
88 39321 39321 13107 0.6 0.6 0.2
89 39321 39321 0 0.6 0.6 0
90 39321 26214 65535 0.6 0.4 1
91 39321 26214 52428 0.6 0.4 0.8
92 39321 26214 39321 0.6 0.4 0.6
93 39321 26214 26214 0.6 0.4 0.4
94 39321 26214 13107 0.6 0.4 0.2
95 39321 26214 0 0.6 0.4 0
96 39321 13107 65535 0.6 0.2 1
97 39321 13107 52428 0.6 0.2 0.8
98 39321 13107 39321 0.6 0.2 0.6
99 39321 13107 26214 0.6 0.2 0.4
100 39321 13107 13107 0.6 0.2 0.2
101 39321 13107 0 0.6 0.2 0
102 39321 0 65535 0.6 0 1
103 39321 0 52428 0.6 0 0.8
104 39321 0 39321 0.6 0 0.6
105 39321 0 26214 0.6 0 0.4
106 39321 0 13107 0.6 0 0.2
107 39321 0 0 0.6 0 0
108 26214 65535 65535 0.4 1 1
109 26214 65535 52428 0.4 1 0.8
110 26214 65535 39321 0.4 1 0.6
111 26214 65535 26214 0.4 1 0.4
112 26214 65535 13107 0.4 1 0.2
113 26214 65535 0 0.4 1 0
114 26214 52428 65535 0.4 0.8 1
115 26214 52428 52428 0.4 0.8 0.8
116 26214 52428 39321 0.4 0.8 0.6
117 26214 52428 26214 0.4 0.8 0.4
118 26214 52428 13107 0.4 0.8 0.2
119 26214 52428 0 0.4 0.8 0
120 26214 39321 65535 0.4 0.6 1
121 26214 39321 52428 0.4 0.6 0.8
122 26214 39321 39321 0.4 0.6 0.6
123 26214 39321 26214 0.4 0.6 0.4
124 26214 39321 13107 0.4 0.6 0.2
125 26214 39321 0 0.4 0.6 0
126 26214 26214 65535 0.4 0.4 1
127 26214 26214 52428 0.4 0.4 0.8
128 26214 26214 39321 0.4 0.4 0.6
129 26214 26214 26214 0.4 0.4 0.4
130 26214 26214 13107 0.4 0.4 0.2
131 26214 26214 0 0.4 0.4 0
132 26214 13107 65535 0.4 0.2 1
133 26214 13107 52428 0.4 0.2 0.8
134 26214 13107 39321 0.4 0.2 0.6
135 26214 13107 26214 0.4 0.2 0.4
136 26214 13107 13107 0.4 0.2 0.2
137 26214 13107 0 0.4 0.2 0
138 26214 0 65535 0.4 0 1
139 26214 0 52428 0.4 0 0.8
140 26214 0 39321 0.4 0 0.6
141 26214 0 26214 0.4 0 0.4
142 26214 0 13107 0.4 0 0.2
143 26300 4265 486 0.401312 0.0650797 0.00741588
144 13107 65535 65535 0.2 1 1
145 13107 65535 52428 0.2 1 0.8
146 13107 65535 39321 0.2 1 0.6
147 13107 65535 26214 0.2 1 0.4
148 13107 65535 13107 0.2 1 0.2
149 13107 65535 0 0.2 1 0
150 13107 52428 65535 0.2 0.8 1
151 13107 52428 52428 0.2 0.8 0.8
152 13107 52428 39321 0.2 0.8 0.6
153 13107 52428 26214 0.2 0.8 0.4
154 13107 52428 13107 0.2 0.8 0.2
155 13107 52428 0 0.2 0.8 0
156 13107 39321 65535 0.2 0.6 1
157 13107 39321 52428 0.2 0.6 0.8
158 13107 39321 39321 0.2 0.6 0.6
159 13107 39321 26214 0.2 0.6 0.4
160 13107 39321 13107 0.2 0.6 0.2
161 13107 39321 0 0.2 0.6 0
162 13107 26214 65535 0.2 0.4 1
163 13107 26214 52428 0.2 0.4 0.8
164 13107 26214 39321 0.2 0.4 0.6
165 13107 26214 26214 0.2 0.4 0.4
166 13107 26214 13107 0.2 0.4 0.2
167 13107 26214 0 0.2 0.4 0
168 13107 13107 65535 0.2 0.2 1
169 13107 13107 52428 0.2 0.2 0.8
170 13107 13107 39321 0.2 0.2 0.6
171 13107 13107 26214 0.2 0.2 0.4
172 13107 13107 13107 0.2 0.2 0.2
173 15976 14457 2622 0.243778 0.2206 0.0400092
174 13107 0 65535 0.2 0 1
175 13107 0 52428 0.2 0 0.8
176 13107 0 39321 0.2 0 0.6
177 13107 0 26214 0.2 0 0.4
178 13107 0 13107 0.2 0 0.2
179 13107 0 0 0.2 0 0
180 0 65535 65535 0 1 1
181 0 65535 52428 0 1 0.8
182 0 65535 39321 0 1 0.6
183 0 65535 26214 0 1 0.4
184 0 65535 13107 0 1 0.2
185 0 65535 0 0 1 0
186 0 52428 65535 0 0.8 1
187 0 52428 52428 0 0.8 0.8
188 0 52428 39321 0 0.8 0.6
189 0 52428 26214 0 0.8 0.4
190 0 52428 13107 0 0.8 0.2
191 0 52428 0 0 0.8 0
192 0 40000 65535 0 0.610361 1
193 0 39321 52428 0 0.6 0.8
194 0 39321 39321 0 0.6 0.6
195 0 39321 26214 0 0.6 0.4
196 0 39321 13107 0 0.6 0.2
197 0 39321 0 0 0.6 0
198 0 26214 65535 0 0.4 1
199 0 26214 52428 0 0.4 0.8
200 0 26214 39321 0 0.4 0.6
201 0 26214 26214 0 0.4 0.4
202 0 26214 13107 0 0.4 0.2
203 0 26214 0 0 0.4 0
204 0 13107 65535 0 0.2 1
205 0 13107 52428 0 0.2 0.8
206 0 13107 39321 0 0.2 0.6
207 0 13107 26214 0 0.2 0.4
208 0 13107 13107 0 0.2 0.2
209 0 13107 0 0 0.2 0
210 0 0 65535 0 0 1
211 0 0 52428 0 0 0.8
212 0 0 39321 0 0 0.6
213 0 0 26214 0 0 0.4
214 0 0 13107 0 0 0.2
215 61183 2079 4938 0.933593 0.0317235 0.0753491
216 56797 0 0 0.866667 0 0
217 48059 0 0 0.733333 0 0
218 43690 0 0 0.666667 0 0
219 34952 0 0 0.533333 0 0
220 30583 0 0 0.466667 0 0
221 21845 0 0 0.333333 0 0
222 17476 0 0 0.266667 0 0
223 8738 0 0 0.133333 0 0
224 4369 0 0 0.0666667 0 0
225 0 61166 0 0 0.933333 0
226 0 56797 0 0 0.866667 0
227 0 48059 0 0 0.733333 0
228 0 43690 0 0 0.666667 0
229 0 34952 0 0 0.533333 0
230 0 30583 0 0 0.466667 0
231 0 21845 0 0 0.333333 0
232 0 17476 0 0 0.266667 0
233 0 8738 0 0 0.133333 0
234 0 4369 0 0 0.0666667 0
235 0 0 61166 0 0 0.933333
236 0 0 56797 0 0 0.866667
237 0 0 48059 0 0 0.733333
238 0 0 43690 0 0 0.666667
239 0 0 34952 0 0 0.533333
240 0 0 30583 0 0 0.466667
241 0 0 21845 0 0 0.333333
242 0 0 17476 0 0 0.266667
243 0 0 8738 0 0 0.133333
244 0 0 4369 0 0 0.0666667
245 61166 61166 61166 0.933333 0.933333 0.933333
246 56797 56797 56797 0.866667 0.866667 0.866667
247 48059 48059 48059 0.733333 0.733333 0.733333
248 43690 43690 43690 0.666667 0.666667 0.666667
249 34952 34952 34952 0.533333 0.533333 0.533333
250 30583 30583 30583 0.466667 0.466667 0.466667
251 21845 21845 21845 0.333333 0.333333 0.333333
252 17476 17476 17476 0.266667 0.266667 0.266667
253 8738 8738 8738 0.133333 0.133333 0.133333
254 4369 4369 4369 0.0666667 0.0666667 0.0666667
255 0 0 0 0 0 0

The following is the C code that created the above colour table. Source code

PhotoShop Index Colour Table Files

Performing colour remapping with indexed colour images in PhotoShop is particularly straightforward mainly because the colour table files are so easy to generate. The colour table files (which can be loaded into PhotoShop) consist of 256 RGB colour entries, each RGB colour component is stored as 1 byte. Each colour component ranges from 0 to 255. As such they are straightforward to generate mathematically with a programming language.

Example 1

The following short piece of C will generate a blue to red ramp. Of course this can be created manually in PhotoShop itself but this example illustrates the principle by which more complicated colour tables could be automatically generated.

On the Macintosh the file creator will need to be set to 8BIM and the file type to 8BCT

int main(int argc,char **argv)
{
   int i;

   for (i=0;i<256;i++) {
      putchar(i);
      putchar(0);
      putchar(255-i);
   }
}

Example 2

A circular red -> yellow -> green -> cyan -> blue -> magenta -> red colour table file in hexadecimal and the map shown visually in PhotoShop are illustrated below

Address Hexadecimal data, 16 bytes per line
------- ---------------------------------------
0000000 ff00 00ff 0600 ff0c 00ff 1200 ff18 00ff
0000020 1e00 ff24 00ff 2a00 ff30 00ff 3600 ff3c
0000040 00ff 4200 ff48 00ff 4e00 ff54 00ff 5a00
0000060 ff60 00ff 6600 ff6c 00ff 7200 ff78 00ff
0000100 7e00 ff84 00ff 8a00 ff90 00ff 9600 ff9c
0000120 00ff a200 ffa8 00ff ae00 ffb4 00ff ba00
0000140 ffc0 00ff c600 ffcc 00ff d200 ffd8 00ff
0000160 de00 ffe4 00ff ea00 fff0 00ff f600 fffc
0000200 00fc ff00 f6ff 00f0 ff00 eaff 00e4 ff00
0000220 deff 00d8 ff00 d2ff 00cc ff00 c6ff 00c0
0000240 ff00 baff 00b3 ff00 aeff 00a8 ff00 a2ff
0000260 009c ff00 96ff 0090 ff00 8aff 0083 ff00
0000300 7eff 0078 ff00 71ff 006c ff00 66ff 0060
0000320 ff00 59ff 0054 ff00 4eff 0048 ff00 41ff
0000340 003c ff00 36ff 0030 ff00 29ff 0024 ff00
0000360 1eff 0018 ff00 11ff 000c ff00 06ff 0000
0000400 ff00 00ff 0600 ff0c 00ff 1200 ff18 00ff
0000420 1e00 ff24 00ff 2a00 ff30 00ff 3600 ff3c
0000440 00ff 4200 ff48 00ff 4e00 ff54 00ff 5a00
0000460 ff60 00ff 6600 ff6c 00ff 7200 ff78 00ff
0000500 7e00 ff84 00ff 8a00 ff90 00ff 9600 ff9c
0000520 00ff a200 ffa8 00ff ae00 ffb4 00ff ba00
0000540 ffc0 00ff c600 ffcc 00ff d200 ffd8 00ff
0000560 de00 ffe4 00ff ea00 fff0 00ff f600 fffc
0000600 00fc ff00 f6ff 00f0 ff00 eaff 00e4 ff00
0000620 deff 00d8 ff00 d2ff 00cc ff00 c6ff 00c0
0000640 ff00 baff 00b3 ff00 aeff 00a8 ff00 a2ff
0000660 009c ff00 96ff 0090 ff00 8aff 0083 ff00
0000700 7eff 0078 ff00 71ff 006c ff00 66ff 0060
0000720 ff00 59ff 0054 ff00 4eff 0048 ff00 41ff
0000740 003c ff00 36ff 0030 ff00 29ff 0024 ff00
0000760 1eff 0018 ff00 11ff 000c ff00 06ff 0000
0001000 ff06 00ff 0c00 ff12 00ff 1800 ff1e 00ff
0001020 2400 ff2a 00ff 3000 ff36 00ff 3c00 ff42
0001040 00ff 4800 ff4e 00ff 5400 ff5a 00ff 6000
0001060 ff66 00ff 6c00 ff72 00ff 7800 ff7e 00ff
0001100 8400 ff8a 00ff 9000 ff96 00ff 9c00 ffa2
0001120 00ff a800 ffae 00ff b400 ffba 00ff c000
0001140 ffc6 00ff cc00 ffd2 00ff d800 ffde 00ff
0001160 e400 ffea 00ff f000 fff6 00ff fc00 ffff
0001200 00fc ff00 f6ff 00f0 ff00 eaff 00e4 ff00
0001220 deff 00d8 ff00 d2ff 00cc ff00 c6ff 00c0
0001240 ff00 baff 00b3 ff00 aeff 00a8 ff00 a2ff
0001260 009c ff00 96ff 0090 ff00 8aff 0083 ff00
0001300 7eff 0078 ff00 71ff 006c ff00 66ff 0060
0001320 ff00 59ff 0054 ff00 4eff 0048 ff00 41ff
0001340 003c ff00 36ff 0030 ff00 29ff 0024 ff00
0001360 1eff 0018 ff00 11ff 000c ff00 06ff 0000

Example 3

Double (circular) grep ramp.




What is Gamma Correction?

or

Why do some pictures appear dark on some displays

Written by Paul Bourke
Original: February 1994. Updated Jan 1998.
Introduction

The phosphors on the face of the monitor tube luminesce (or some other computer projection device) when struck by the beam generated by the electron gun sweeping out the scanlines. By increasing the intensity of the beam, the phosphor dot luminesces more brightly, and by reducing the intensity of the beam, the phosphor glows less brightly. The output of the phosphor is not directly proportional to electron beam strength. The response of the phosphors usually looks something like this:

where the dotted line shows ideal linear response, and the solid line approximates the observed response of a normal phosphor. This response characteristic is due to physical phenomena and can be described via a function of the form

output luminance = beamstrength gamma

Typically monitors have a gamma from anything between 1.5 and 2.8

Subjectively, this causes the colors on the screen to appear darker than expected. Based on this behaviour, an inverse gamma correction function can be applied to compensate for this non-linear response:

Here, the solid line again represents the uncorrected phosphor response, the large dotted line is the inverse gamma function, and the fine dotted line is the resultant, linearized color response.

Determining the gamma value

Most computer systems now days have standard interfaces/utilities for determining and setting the gamma value. For example the Macintosh has a control panel with the OS, SGI machines come with a utility called "gamma" which both reports and sets the gamma value.

The following chart can be used to determine the combined gamma value for your system.

Setting the gamma value

To adjust the gamma value "roughly", set the contrast and brightness of your monitor to your preferred/comfortable level and use whatever gamma software comes with your hardware.

In the absence of other information it is usual to set the effective gamma to 2.2, this is the standard employed for PhotoCD's. On SGI machines this normally means setting a "gamma" value of around 1.2, this is because SGI's are normally configured with a natural gamma of 2.6, the combined gamma value is 2.6 divided by this number.

All well written image viewers should provide options for controlling the gamma or they should get the value from the system being used. For example on UNIX machine, the xv viewer can be called with the option -gamma 2.2. Unfortunately at the time of writing none of the two main WEB browsers support gamma correction for images in WWW pages, as a result the images tend to look dark on uncorrected monitors. This is a major problem when it comes to distributing images on a wide variety of systems. Fortunately the PNG format does support the specification of a colourmetric model and will go a long way to providing consistent image appearance across monitor hardware.




Converts to/from sRGB and LAB space

Presented as JavaScript, originally for Quartz Composer
Written by Paul Bourke
November 2018

The following present functions to convert to and from sRGB and LAB colour, in both cases going through XYZ as an intermediate stage. While presented as JavaScript, it does still uniquely define the transformations and should be easy to transcribe into other languages. Note the XYZ mappings require a reference illuminate white point, for example D65.

sRGB to XYZ
function (__structure xyzcolour) main(__number rgbcolour[4])
{
   var result = new Object();
   result.xyzcolour = new Array();
   var R = new Object();
   var g = new Object();
   var B = new Object();

   if (rgbcolour[0] > 0.04045 ) 
      R = Math.pow( ( (rgbcolour[0] + 0.055 ) / 1.055 ),2.4);
   else 
      R = rgbcolour[0] / 12.92;
   if (rgbcolour[1] > 0.04045 ) 
      G = Math.pow( ( (rgbcolour[1] + 0.055 ) / 1.055 ),2.4);   
   else                   
      G = rgbcolour[1] / 12.92;
   if (rgbcolour[2] > 0.04045 ) 
      B = Math.pow( ( (rgbcolour[2] + 0.055 ) / 1.055 ),2.4);
   else                   
      B = rgbcolour[2] / 12.92;
      
   R = R * 100;
   G = G * 100;
   B = B * 100;
   
   result.xyzcolour[0] = R * 0.4124 + G * 0.3576 + B * 0.1805;
   result.xyzcolour[1] = R * 0.2126 + G * 0.7152 + B * 0.0722;
   result.xyzcolour[2] = R * 0.0193 + G * 0.1192 + B * 0.9505;
   result.xyzcolour[3] = rgbcolour[3];
            
   return result;
}
XYZ to LAB
function (__structure labcolour) main(__number xyzcolour[4],__number xyzref[3])
{
   var result = new Object();
   result.labcolour = new Array();
   var X = new Object();
   var Y = new Object();
   var Z = new Object();
            
   X = xyzcolour[0] / xyzref[0];
   Y = xyzcolour[1] / xyzref[1];
   Z = xyzcolour[2] / xyzref[2];

   if ( X > 0.008856 ) 
      X = Math.pow( X , 1/3.0 );
   else                    
      X = ( 903.3 * X + 16.0 ) / 116.0;
   if ( Y > 0.008856 ) 
      Y = Math.pow( Y, 1/3.0 );
   else                    
      Y = ( 903.3 * Y + 16.0) / 116.0;
   if ( Z > 0.008856 ) 
      Z = Math.pow( Z, 1/3.0 );
   else                   
      Z = ( 903.3 * Z + 16.0) / 116.0;

   result.labcolour[0] = ( 116 * Y ) - 16;
   result.labcolour[1] = 500 * ( X - Y );
   result.labcolour[2] = 200 * ( Y - Z );
   result.labcolour[3] = xyzcolour[3];
            
   return result;
}
LAB to XYZ
function (__structure xyzcolour) main(__number labcolour[4],__number xyzref[3])
{
   var result = new Object();
   result.xyzcolour = new Array();
   var X = new Object();
   var Y = new Object();
   var Z = new Object();

   Y = (labcolour[0] + 16.0) / 116.0;
   X = labcolour[1] / 500.0 + Y;
   Z = Y - labcolour[2] / 200.0;
   
   Y = Adjust(Y);
   X = Adjust(X);
   Z = Adjust(Z);
               
   result.xyzcolour[0] = X * xyzref[0];
   result.xyzcolour[1] = Y * xyzref[1];
   result.xyzcolour[2] = Z * xyzref[2];
   result.xyzcolour[3] = labcolour[3];
            
   return result;
}

function Adjust(a) {
   if (Math.pow(a,3.0) > 0.008856)
      return(Math.pow(a,3.0));
   else
      return((a - 16.0/116.0) / 7.787);
}
XYZ to sRGB
function (__structure rgbcolour) main(__number xyzcolour[4])
{
   var result = new Object();
   result.rgbcolour = new Array();
   var R = new Object();
   var G = new Object();
   var B = new Object();
   var X = new Object();
   var Y = new Object();
   var Z = new Object();

   X = xyzcolour[0] / 100.0;
   Y = xyzcolour[1] / 100.0;
   Z = xyzcolour[2] / 100.0;
   
   R = X * 3.2406 + Y * (-1.5372) + Z * (-0.4986);
   G = X * (-0.9689) + Y *  1.8758 + Z * 0.0415;
   B = X * 0.0557 + Y * (-0.2040) + Z * 1.0570;

   R = Adjust(R);
   G = Adjust(G);
   B = Adjust(B);

   R = Clip(R);
   G = Clip(G);
   B = Clip(B);
         
   result.rgbcolour[0] = R * 255.0;
   result.rgbcolour[1] = G * 255.0;
   result.rgbcolour[2] = B * 255.0;
   result.rgbcolour[3] = xyzcolour[3];
            
   return result;
}

function Adjust(a) {
   if (a > 0.0031308)
      return(1.055*Math.pow(a,1.0/2.4)-0.055);
   else
      return(12.92*a);
}

function Clip(a) {
   if (a > 1)
      return(1);
   else if (a < 0)
      return(0);
   else
      return(a);
}