colourmaps

01:54 09/06/2024 1319 words
10:16 18/06/2024 (modified)
contents

colour maps are important[1]. and i’ve mentioned them before. specifically, this paper by thyng et al., (2016) and this library of colormaps by fabio crameri. of course, there are many other resources that i’ve stumbled across and leaned on.

anyway, this isn’t a treatise on colour. i read this earlier, and watched a few of the videos linked within, which provided me with the motivation required to do a thing i’ve been meaning to do for a while…make a colourmap.

mean colours

relatedly, but not quite. during the #30DayMapChallenge, whilst playing with tanaka contours, i tried to colour in each elevation level using the “average colour” seen within that elevation range from a satellite image. the goal was to create a local version of gist_earth (fig 1)

colormap from dark blues, to greens, tan and then white
Fig 1: gist_earth from matplotlib

but my naive attempts (just taking the mean from the rgb channels) ended up looking a bit brown (fig 2).

on the left, a true colour satellite image of stromboli, on the right a filled contour map of stromboli, where the fills are all a bit brown
Fig 2: (left) stromboli, sentinel-2 rgb from 2022-08-15. (right) tanaka contours with average rgb colour at each level.

someone on the internet suggested that clustering would prove a more effective means of averaging colours. so let’s do that.
colour clusters

photo of an iceberg in a frozen lake. both lake and iceberg have snow cover. the sky is clear blue. to the right are 5 colour colormaps using with 4 - 8 representative colours from the image
Fig 3: iceberg in a frozen lake

colour clusters

because sklearn.clusters.KMeans is a quick and easy[2] method of identifying groups (clusters) within n-dimensional datasets, that’s what i used. before tackling the (slightly) harder task of finding average clusters within different regions of an image (as above), i first identified clusters from a single image (fig 3). and by choosing an image that isn’t too colourful, the cluster centroids end up making a reasonably nice sequential[3] colour scheme. after that, it was straightforward enough to drop in any other image i wanted (fig 4 & fig 5)

view from a steep sided mountain looking down towards a valley. there is a small town in the valley. to the right are 5 colour colormaps using with 4 - 8 representative colours from the image
Fig 4: the view from Cima Lancia
looking across a river, in the foreground are grey pebbles, then the bluey-green water, then trees in leaf on the a cliff on the opposite bank
Fig 5: La Valserine

not wanting to give too much time to this deviation, i thought the colours needed ordering somehow. once the cluster centroids had been found i did a hasty conversion from rgb to hsv then sorted the centroids by the s (for saturation, i think) in hsv. this works most some of the time[4].

averaging isn’t too brown

four images of a norwegian fjord, each with slightly different colour maps applied to the filled contours
Fig 6: nordfjorden marine verneområde. clockwise from top left: true colour image from sentinel-2 (2023-08-18); mean average colour in rgb space for each elevation band; elevation bands coloured with the centroid of the largest of 2 clusters identified in rgb space; ditto, but from largest of 3 clusters. tanaka contours generated from copernicus global dem.

between when i first started working on this post a week or so ago and now[5], i have mucked around with the code used to make fig 2 to such an extent that i now cannot replicate it. worse still, the new code that averages the rgb channels is actually doing an okay job – in fact – it might be better than clustering (fig 6). i also dabbled with the median (fig 7), and that seemed to work quite nicely.

a satellite image of red rock canyon; and two images with filled contours of the same area, with colours similar to those in the satellite.
Fig 7: redrock canyon. left to right: rgb image from sentinel-2 (2023-08-28); elevation bands coloured with the centroid of the largest of 3 clusters identified in rgb space; elevation bands coloured with median rgb value. tanaka contours generated from copernicus global dem.

to do

here, the contour intervals aren’t equal. i used natural breaks. which, as it happens, doesn’t matter. getting the elevation range of each band is trivial. and so...

diversions in, and around, colour space

despite what i said a moment ago, i might have just lost a few hours to reading about colour spaces. and, despite not having a clue what this figure[8] is about. i love it. just look at the multitude of hatching. and that font. i’m sorry my figures aren’t labelled as such.

anyway, the above clustering and averaging all happened in rgb colour space. this wasn’t a terrible idea. after all, the multi-spectral instrument on board sentinel-2 has a red, green and blue spectral band. but…well. maybe cielab space is the one that i ought to be working in.
a thought

perhaps the geometric mean is more appropriate. these colour spaces aren’t linear.

the future

skimage.color has a wealth of colour space conversion functions. i will pursue this avenue further. just not right now[9].

footnotes


  1. they are ↩︎

  2. it’s all relative ↩︎

  3. here, i’ve just picked images that i thought would make good sequential palettes. a qualitative palette might require be hard to derive from a landscape (as in, of a landscape, not the orientation) image ↩︎

  4. fig 5: n=4; and fig 4: n=7; n=8, for cases where it did not work ↩︎

  5. as in 8th June 2024 23:56 BST ↩︎

  6. that was the whole point of this ↩︎

  7. is it just the mean? or something fancier? setting n_clusters=1 yields something that looks suspiciously similar to the mean average. but i eyeballed it. rather than checking properly. which wouldn’t have taken very long ↩︎

  8. which is discussed in the article on hsl and hsv, but originally comes from this patent ↩︎

  9. as in 8th June 2024 15:44 BST ↩︎


#python #dataviz #remotesensing #maps