I'd been feeling a bit lost, so decided to throw myself into a small, contained project. It took a while to find the perfect idea, but I decided to create [Earth Palettes](https://earthpalettes.tumblr.com/), taking slices of satellite imagery and then pulling a colour palette from it. For me it was an exercise in trying to explore the intersection between nature, art, and technology. ![Sentinel-2 image and associate colour palette](/img/2021-09-13-sentinel-2-and-colour-palette.jpg) ## Sourcing Data My first thought was finding an open access API feed and downloading 3- or 4-band true colour georectified images and then snipping out small segments.After faffing about trying to get easy [Landsat-8](https://www.usgs.gov/core-science-systems/nli/landsat/landsat-8?qt-science_support_page_related_con=0#) data, I ended up looking at [Sentinel-2](https://sentinel.esa.int/web/sentinel/missions/sentinel-2) options and managed to find an open API that would get me the data I wanted through the [Copernicus Open Access Hub](https://scihub.copernicus.eu/). Unfortunately, download speeds and sizes were an issue here, and a 500MB scene at 200kB/s was not really compatible with my goal. At this point, I also realised that downloading massive scenes just to chop out tiny areas, or have to mosaic a few together, wasn't really an optimal solution. Thankfully, [EOX](https://eox.at/) provides the [Sentinel-2 cloudless](https://s2maps.eu/) mosaic, which includes free WMTS and WMS feed. I also have some experience working with [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) thanks to my time at [Landgate SRSS](https://srss.landgate.wa.gov.au/), so I knew it would be possible to select a particular [bounding box](https://wiki.openstreetmap.org/wiki/Bounding_Box) by feeding coordinates in a URL, along with a resolution, and then get just the snippet I want to use. ## Development My original idea of slicing segments out of GeoTIFF imagery was going to be done in Python because of [GDAL](https://gdal.org/index.html), so I stuck with that even though I was just using `urllib` for WMS requests, but that turned out to be a boon which I will get to later. There's a few options for generating colour palettes, it appears that [colour quantisation](https://en.wikipedia.org/wiki/Color_quantization) is a pretty known field with a bevy of algorithms and pacakges. I settled on [colorgram.py](https://github.com/obskyr/colorgram.py) in this case, although I also experimented with [Pillow's](https://pillow.readthedocs.io/en/stable/) capabilities too. The library I chose also extracts the colours directly and lets me work on them as named tuples for the red, green, and blue bands, and so I used that to generate an SVG of containing the colour palette as a nice rectangle, which I then converted to a PNG file. If I was better at image handling in Python I could have done it directly as a canvas to PNG and not had to add SVG handling libraries, but I was more focused on results than performance. ### Landlubber Having arrived at a working prototype with set coordinates, I started developing some stuff to randomly pick locations over the globe. Unfortunately, seas and oceans mostly reflect the suns lights with little scatter, so unless the sun was right behind the satellite (it's not), you're going to get much darker colours rather than the false-colour imagery you usually see of the water since there's little light bouncing up from it. And since the Earth's surface is 70% water I wanted to exclude scenes that were majority sea/ocean. To do this I needed to tap into some GIS learning, so I grabbed the World Seas shapefiles from the [Marineregions.org](https://www.marineregions.org/downloads.php) site and did some trickery. It's not very optimum, but I grab a random segment of the Earth, then do the following: * Open the seas shapefile. * Grab only the bounding-box. * Rasterise it at the pixel dimensions I'm using. * Convert the raster to a Numpy array and count all values with data as a proprotion of overall values. * If it's more than 50% water, I repeat the process. ### Kinda Dull I noticed a lot of the palettes generated are extremely dull, which could be related to the imagery used, so I run all the images through a 1.25 times factor contrast enhancement before palette extraction. I still post the original images, but the palette is slightly higher contrast which seems to work well. ## Conclusion It's up now on the [Earth Palettes Tumblr](https://earthpalettes.tumblr.com/), and it should post every 6 hours thanks to [GitLab CI scheduled pipelines](https://docs.gitlab.com/ee/ci/pipelines/schedules.html). Let me know if you have any thoughts or questions or comments on Twitter at [@judges119](https://twitter.com/judges119). One thing of interest is how the palettes represent common colours, not highlights! The human eye and brain perceive things differently from machines, and we'd likely pick different colours from palettes for things like rich veins of something that are high contrast but not actually super frequent. There is some [interesting work](http://colormind.io/blog/generating-color-palettes-with-deep-learning/) done in that space with AI though.