Colormaps and Colorscales
Color interpolation
Generating a range of colors
The range()
function has a method that accepts colors:
Base.range(start::T; stop::T, length=100) where T<:Colorant
This generates N (=length
) colors in a linearly interpolated ramp from start
to stop
, inclusive, returning an Array
of colors.
julia> using Colors
julia> c1 = colorant"red"
RGB{N0f8}(1.0, 0.0, 0.0)
julia> c2 = colorant"green"
RGB{N0f8}(0.0, 0.502, 0.0)
julia> range(c1, stop=c2, length=15)
15-element Vector{RGB{FixedPointNumbers.N0f8}}:
RGB(1.0, 0.0, 0.0)
RGB(0.929, 0.035, 0.0)
RGB(0.859, 0.071, 0.0)
RGB(0.784, 0.106, 0.0)
RGB(0.714, 0.145, 0.0)
RGB(0.643, 0.18, 0.0)
RGB(0.573, 0.216, 0.0)
RGB(0.502, 0.251, 0.0)
RGB(0.427, 0.286, 0.0)
RGB(0.357, 0.322, 0.0)
RGB(0.286, 0.357, 0.0)
RGB(0.216, 0.396, 0.0)
RGB(0.141, 0.431, 0.0)
RGB(0.071, 0.467, 0.0)
RGB(0.0, 0.502, 0.0)
If you use Julia through VSCode or IJulia, you can get the following color swatches.
range(colorant"red", stop=colorant"green", length=15)
The intermediate colors depend on their colorspace. For example:
range(HSL(colorant"red"), stop=HSL(colorant"green"), length=15)
The range
and weighted_color_mean
described below support colors with hues which are out of the range [0, 360]. The hues of generated colors are normalized into [0, 360].
range(HSV(0,1,1), stop=HSV(-360,1,1), length=90) # inverse rotation
range(LCHab(70,70,0), stop=LCHab(70,70,720), length=90) # multiple rotations
While sometimes useful in particular circumstances, typically it is recommended that the hue be within [0, 360]. See normalize_hue
.
Base.range
— Functionrange(start::T; stop::T, length=100) where T<:Colorant
range(start::T, stop::T; length=100) where T<:Colorant
Generates N (=length
) >2 colors in a linearly interpolated ramp from start
to stop
, inclusive, returning an Array
of colors.
stop
as a positional argument requires at least Julia 1.1.
Weighted color means
The weighted_color_mean()
function returns a color that is the weighted mean of c1
and c2
, where c1
has a weight 0 ≤ w1
≤ 1.
For example:
julia> weighted_color_mean(0.8, colorant"red", colorant"green")
RGB{N0f8}(0.8, 0.102, 0.0)
You can also get the weighted mean of three or more colors by passing the collections of weights and colors. The following is an example of bilinear interpolation.
[weighted_color_mean([(1-s)*(1-t), s*(1-t), (1-s)*t, s*t], # collection of weights
Colors.JULIA_LOGO_COLORS) # collection of colors
for s = 0:0.2:1, t = 0:0.05:1]
Colors.weighted_color_mean
— Functionweighted_color_mean(w1, c1, c2)
Returns the color w1*c1 + (1-w1)*c2
that is the weighted mean of c1
and c2
, where c1
has a weight 0 ≤ w1
≤ 1.
weighted_color_mean(weights, colors)
Returns the weighted mean of the given collection colors
with weights
. This is semantically equivalent to the calculation of sum(weights .* colors)
.
Examples
julia> rgbs = (RGB(1, 0, 0), RGB(0, 1, 0), RGB(0, 0, 1));
julia> weighted_color_mean([0.2, 0.2, 0.6], rgbs)
RGB{N0f8}(0.2, 0.2, 0.6)
julia> weighted_color_mean(0.5:-0.25:0.0, RGB{Float64}.(rgbs))
RGB{Float64}(0.5, 0.25, 0.0)
wighted_color_mean
with collection or iterator inputs requires Colors v0.13 or later.
In a cylindrical color space such as HSV, a weighted mean of more than three colors is generally not meaningful.
Colormaps
This package provides some pre-defined colormaps (described below). There are also several other packages which provide colormaps:
Predefined sequential and diverging colormaps
The colormap()
function returns a predefined sequential or diverging colormap computed using the algorithm by Wijffelaars, M., et al. (2008).
colormap(cname::String [, N::Int=100; mid=0.5, logscale=false, <keyword arguments>])
The cname
specifies the name of colormap. The currently supported names are:
Sequential
- "Blues"
- "Greens"
- "Grays"
- "Oranges"
- "Purples"
- "Reds"
Diverging
- "RdBu" (from red to blue)
The optional arguments of colormap()
are:
- the number of colors
N
- position of the middle point
mid
for diverging colormaps - the use of logarithmic scaling with the
logscale
keyword
Colormaps computed by this algorithm are guaranteed to have an increasing perceived depth or saturation making them ideal for data visualization. This also means that they are (in most cases) color-blind friendly and suitable for black-and-white printing.
Colors.colormap
— Functioncolormap(cname, N=100; logscale=false, <keyword arguments>)
Returns a predefined sequential or diverging colormap computed using the algorithm by Wijffelaars, M., et al. (2008).
Sequential colormaps cname
choices are:
"Blues"
"Grays"
"Greens"
"Oranges"
"Purples"
"Reds"
Diverging colormap choices are "RdBu"
.
Optionally, you can specify the number of colors N
(default 100).
Extra control is provided by keyword arguments.
mid
sets the position of the midpoint for diverging colormaps.logscale=true
uses logarithmically-spaced steps in the colormap.
You can also use keyword argument names that match the argument names in sequential_palette
or diverging_palette
.
Sequential and diverging color palettes
You can create your own color palettes by using sequential_palette()
:
sequential_palette(h, [N::Int=100; c=0.88, s=0.6, b=0.75, w=0.15, d=0.0,
wcolor=RGB(1,1,0), dcolor=RGB(0,0,1), logscale=false])
which creates a sequential map for a hue h
(defined in LCHuv space).
Other possible parameters that you can fine tune are:
N
- number of colorsc
- the overall lightness contrast [0,1]s
- saturation [0,1]b
- brightness [0,1]w
- cold/warm parameter, i.e. the strength of the starting color [0,1]d
- depth of the ending color [0,1]wcolor
- starting color (usually defined to be yellow)dcolor
- ending color (depth)logscale
-true
/false
for toggling logspacing
Two sequential maps can also be combined into a diverging colormap by using diverging_palette()
:
diverging_palette(h1, h2 [, N::Int=100; mid=0.5, c=0.88, s=0.6, b=0.75, w=0.15, d1=0.0, d2=0.0,
wcolor=RGB(1,1,0), dcolor1=RGB(1,0,0), dcolor2=RGB(0,0,1), logscale=false])
where the arguments are:
h1
,h2
- the main hue of the first/latter part [0,360]
and the optional arguments are:
N
- number of colorsmid
- the position of the midpoint (0,1)c
,s
,b
- contrast, saturation, brightness [0,1]w
- cold/warm parameter, i.e. the strength of the middle color [0,1]d1
,d2
- depth of the ending color in the first/latter part [0,1]wcolor
- starting color i.e. the middle colordcolor1
,dcolor2
- ending color of the first/latter part (depth)logscale
-true
/false
for toggling logspacing
For examples:
diverging_palette(0, 200, 32)
diverging_palette(0, 200, 32, mid=0.3)
diverging_palette(0, 200, 32, mid=0.3, logscale=true)
Colors.sequential_palette
— Functionsequential_palette(h, N::Int=100; <keyword arguments>)
Implements the color palette creation technique by Wijffelaars, M., et al. (2008).
Colormaps are formed using Bézier curves in LCHuv colorspace with some constant hue. In addition, start and end points can be given that are then blended to the original hue smoothly.
Arguments
N
- number of colorsh
- the main hue [0,360]c
- the overall lightness contrast [0,1]s
- saturation [0,1]b
- brightness [0,1]w
- cold/warm parameter, i.e. the strength of the starting color [0,1]d
- depth of the ending color [0,1]wcolor
- starting color (warmness)dcolor
- ending color (depth)logscale
-true
/false
for toggling logspacing
Colors.diverging_palette
— Functiondiverging_palette(h1, h2, N::Int=100; <keyword arguments>)
Create diverging palettes by combining 2 sequential palettes.
Arguments
N
- number of colorsmid
- the position of the midpoint (0,1)h1
- the main hue of the first part [0,360]h2
- the main hue of the latter part [0,360]c
- the overall lightness contrast [0,1]s
- saturation [0,1]b
- brightness [0,1]w
- cold/warm parameter, i.e. the strength of the starting color [0,1]d1
- depth of the ending color in the first part [0,1]d2
- depth of the ending color in the latter part [0,1]wcolor
- starting color, i.e. the middle color (warmness)dcolor1
- ending color of the first part (depth)dcolor2
- ending color of the latter part (depth)logscale
-true
/false
for toggling logspacing
Generating distinguishable colors
distinguishable_colors()
generates n
maximally distinguishable colors in LCHab space. A seed color or array of seed colors can be provided, and the remaining colors will be chosen to be maximally distinguishable from the seed colors and each other.
distinguishable_colors(n::Integer, seed::Color)
distinguishable_colors(n::Integer, seed::AbstractVector{<:Color})
By default, distinguishable_colors
chooses maximally distinguishable colors from the outer product of lightness, chroma, and hue values specified by lchoices
, cchoices
, and hchoices
. The set of colors that distinguishable_colors
chooses from can be specified by passing different choices as keyword arguments.
distinguishable_colors(n::Integer, seed::AbstractVector{<:Color};
dropseed = false,
transform::Function = identity,
lchoices::AbstractVector = range(0, stop=100, length=15),
cchoices::AbstractVector = range(0, stop=100, length=15),
hchoices::AbstractVector = range(0, stop=342, length=20)
)
Distinguishability is maximized with respect to the CIEDE2000 color difference formula (see colordiff
in Color Differences). If a transform
function is specified, color difference is instead maximized between colors a
and b
according to colordiff(transform(a), transform(b))
.
Color arrays generated by distinguishable_colors
are particularly useful for improving the readability of multiple trace plots. Here’s an example using PyPlot
:
using PyPlot, Colors
vars = 1:10
cols = distinguishable_colors(length(vars), [RGB(1,1,1), RGB(0,0,0)], dropseed=true)
pcols = map(col -> (red(col), green(col), blue(col)), cols)
xs = 0:12
for i in vars
plot(xs, map(x -> rand() + 0.1x - 0.5i, xs), c = pcols[i])
end
legend(vars, loc="upper right", bbox_to_anchor=[1.1, 1.])
To ensure that the generated colors stand out against the default white background and black texts, white
and black
([RGB(1,1,1), RGB(0,0,0)]
) were used as seed colors to distinguishable_colors()
, then dropped from the resulting array with dropseed=true
.
The distinguishable_colors
returns a vector of length n
regardless of the dropseed
option. If dropseed
is true
, the leading seed colors will be dropped, and the succeeding values of length(seed)
will be appended to the end.
permutedims(hcat(
distinguishable_colors(10, [RGB(1,1,1), RGB(0,0,0)], dropseed=false),
distinguishable_colors(10, [RGB(1,1,1), RGB(0,0,0)], dropseed=true),
distinguishable_colors(12, [RGB(1,1,1), RGB(0,0,0)])[3:end] # manually drop the seed colors
))
dropseed
requires at least Colors v0.10. If you use an older version, drop the seed manually.
Colors.distinguishable_colors
— Functioncolors = distinguishable_colors(n, seed=RGB{N0f8}[];
dropseed=false,
transform=identity,
lchoices=range(0, stop=100, length=15),
cchoices=range(0, stop=100, length=15),
hchoices=range(0, stop=342, length=20))
Generate n
maximally distinguishable colors.
This uses a greedy brute-force approach to choose n
colors that are maximally distinguishable. Given seed color(s), and a set of possible hue, chroma, and lightness values (in LCHab space), it repeatedly chooses the next color as the one that maximizes the minimum pairwise distance to any of the colors already in the palette.
Arguments
n
: Number of colors to generate.seed
: Initial color(s) included in the palette.
Keyword arguments
dropseed
: if true, theseed
values will be dropped. This provides an easy mechanism to ensure that the chosen colors are distinguishable from the seed value(s). When true,n
does not include the seed color(s).transform
: Transform applied to colors before measuring distance. Default isidentity
; other choices includedeuteranopic
to simulate color-blindness.lchoices
: Possible lightness valuescchoices
: Possible chroma valueshchoices
: Possible hue values
Returns a Vector
of colors of length n
, of the type specified in seed
.