http://www.perlmonks.org?node_id=510659

BrowserUk has asked for the wisdom of the Perl Monks concerning the following question:

I want to represent a range of numerical values (approx: 0 -> 1000) using colors such that there is an visually obvious gradient between low and high values, with similar values being visually similar. Kind of like astronomers use to show differences and similarities on IR or UV photographs.

Should I use a lookup table (and is there one available)?

Or can I get away with a formula to map them (and does anyone know such a formula)?

Update: I finally found a reference for what astronomers use--it's call a Color Ramp, and there is a standard one.

The reference for anyone following along is Color Ramp and there is an implementation in C at C source.

I've translated this into Perl as:

sub colorRamp { my( $v, $vmin, $vmax ) = @_; my( $r, $g, $b ) = (1) x 3; $v = $vmax if $v > $vmax; $v = $vmin if $v < $vmin; ## Uncomment below to invert the color range ## so that small numbers are hot (red) ## and high numbers are cold (blue) ## $v = $vmax + $vmin - $v; my $dv = $vmax - $vmin; if( $v < ( $vmin + 0.25*$dv ) ) { $r = 0; $g = 4 * ($v - $vmin) / $dv; } elsif( $v < ( $vmin + 0.5 * $dv ) ) { $r = 0; $b = 1 + 4 * ($vmin + 0.25 * $dv - $v) / $dv; } elsif( $v < ( $vmin + 0.75 * $dv ) ) { $r = 4 * ($v - $vmin - 0.5 * $dv) / $dv; $b = 0; } else { $g = 1 + 4 * ($vmin + 0.75 * $dv - $v) / $dv; $b = 0; } ## Convert the 0->1 range rgb values to 0->255 ## and output as a web/tk style "#rrggbb" hex string return sprintf "#%02x%02x%02x", $r*255, $g*255, $b*255; }

Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.

Replies are listed 'Best First'.
Re: A hierarchy of color (intensity)?
by creamygoodness (Curate) on Nov 22, 2005 at 06:53 UTC

    It seems like Color::Scheme may be of some use here. The effect you want can be realized by varying the hue value in a hue-saturation-brightness color model -- the H in HSB. The only problem is to find an HSB implementation which meets the 1000-discrete-values criteria.

    --
    Marvin Humphrey
    Rectangular Research ― http://www.rectangular.com

      Color::Scheme produces sets of complimentary colors, in groups of 4 to 16. Not quite what I need.


      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.

        Mmm, you're right about Color::Scheme... not what I thought at first glance...

        ( ... time passes ... )

        Aha! Try Graphics::ColorObject.

        If you have access to any graphic app that has an HSB (aka HSV, HSL) color picker with sliders, you can see the desired effect by moving the H slider. That effect can be reproduced using Graphics::ColorObject to build a converter and holding the Saturation and Brightness values constant while varing the Hue.

        --
        Marvin Humphrey
        Rectangular Research ― http://www.rectangular.com
Re: A hierarchy of color (intensity)?
by Zaxo (Archbishop) on Nov 22, 2005 at 06:56 UTC

    By default, GD uses an indexed color table.

    One simple mapping is to think of the "color wheel" of red-yellow-green-cyan-blue-magenta-red and all the interpolated colors. Map an arc of, say, red through blue and insert those in order into the GD color table.

    With only eight-bit color channels, you may need to dither the intensity or the third color a bit if you want all thousand to be unique.

    After Compline,
    Zaxo

Re: A hierarchy of color (intensity)?
by Perl Mouse (Chaplain) on Nov 22, 2005 at 10:18 UTC
    A way of doing that would be by using the following table:
    NumberColour
    000.00.00
    25500.00.FF
    51000.FF.FF
    76500.FF.00
    1020FF.FF.00
    1275FF.FF.FF
    1530FF.00.FF
    1785FF.00.00
    204000.00.00
    Intermediate value are calculated by linear interpolation. This gives you 2040 different colours; nearby values are mapped to nearby numbers; and values far apart will have quite different values (with the caveat that the table above is cyclic). It shouldn't be too hard to turn this table into code.

    If you just have 1000 numbers, multiply the numbers by 2 before consulting the above table, or just use a subset of the colours.

    If you have a lot of numbers, much more numbers than there are different values, it probably pays to make a lookup table.

    Perl --((8:>*
Re: A hierarchy of color (intensity)?
by inman (Curate) on Nov 22, 2005 at 10:01 UTC
    The following code was actually the result of a Perl Monks discussion. The code simply creates an HTML page that contains all the websafe colours. The interesting bit is the luminosity calculation that is used to determine whether the text should be black or white. The colour table produces blocks of 256 colours but this could be extended by increasing the number of starting values.
Re: A hierarchy of color (intensity)?
by YuckFoo (Abbot) on Nov 22, 2005 at 20:55 UTC
    BrowserUK,

    A sine wave can be used to make a good color gradient, see Color Gradient Generator for the idea, sub make_rgb() for the formula. Turn of $WRAPPING for your purpose.

    YuckFoo

Re: A hierarchy of color (intensity)?
by renodino (Curate) on Nov 22, 2005 at 16:18 UTC
    I've used a fairly simplistic solution for DBD::Chart: a simple linear interpolation of RGB values between 2 endpoint colors. E.g., if "cold" is blue and "hot" is yellow, then simply subdivide the value range between the 2 colors into a fixed number of slots (number of available slots depends on your graphics library and/or the app's environment). Then just interpolate each of the R, G, and B values between the endpoint values of each slot. Works surprisingly well despite its simplicity (e.g., see the quadtree display at the DBIx::Chart homepage. I also used a similar technique to provide 3-D shading for the 3-D barcharts.)