Skip to content

Smiley Face, STAC and Cloud Optimised Geotiffs

  • blog

This is a light hearted post which hopefully you find fun. Let’s take a png of a (hand drawn!) smiley face and hide it in Satellite images that I have streamed as a Cloud Optimized Geotiff and found via a SpatioTemporal Asset Catalog (STAC).

We are going to be using this 2kb png and hiding it in a Satellite image.


Images are just (Numpy) arrays

Let’s use OpenCV to read this image and plot it in Python:

%matplotlib inline # only need this if you are using a Jupyter notebook
import cv2
import numpy as np
import matplotlib.pyplot as plt
image_file = 'smiley.png'
img = cv2.imread(image_file)
not quite right…

By default OpenCV displays images in blue, green, red (BGR), rather than the more familiar RGB. Luckily we can call a simple BGR to RGB method.

RGBimage = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

And sure enough we get the more familiar face:

Add Smiley to a (Satellite image)

I’ve downloaded a thumbnail from the AWS S3 bucket containing Landsat 8 images. It’s here if you wish to download it. This is the last image I am downloading for this guide. Its 221kb in size and I’ve called it ‘Landsat_thumb_large.jpg’. I’m going to reuse this bit of code from stackoverflow to overlay Smiley on my Landsat 8 thumbnail.

l_img = cv2.imread("Landsat_thumb_large.jpg")
l_img[y_offset:y_offset+RGBimage.shape[0], x_offset:x_offset+RGBimage.shape[1]] = RGBimage

Giving me:

Can you find him?

Ok great, but pretty easy to spot right? I need to make smiley smaller. Again OpenCV comes to the rescue with its resize method:

small = cv2.resize(RGBimage, (0,0), fx=0.2, fy=0.2)

And reusing the code from before replacing ‘RGBimage’ with ‘small’ I get:

hard to find now?

Using Cloud Based Geotiff’s

There is of course no need to download the image. We can just stream it and open it with Rasterio. I can open the image_url with rasterio and the only real change to the previous code is that I need to transpose the array to RGB again (array.transpose(1,2,0). The other change is that I am using a random integer value for the x_offset and the y_offset to move my smiley to a more random place:

import random
import rasterio
image_url = ""src =
array =
rearranged_arr = array.transpose(1, 2, 0)
x_offset=random.randint(300, 800)
y_offset=random.randint(300, 800)
rearranged_arr[y_offset:y_offset+small.shape[0], x_offset:x_offset+small.shape[1]] = small

Which gives me:

Smiley has moved!

Use STAC and COG to hide smiley in a Sentinel 2 image

So that is the basics covered. A bit of image manipulation, a bit of streaming satellite imagery. A nice Landsat 8 thumnail. Let’s use a true colour image from Sentinel 2 using a pre defined area interest within a search from a STAC and stream a RGB image. I’m not going to repeat my previous blog, but If you want more details on this process please revisit it here.

from satsearch import Search
import geopandas as gpd
gdf = gpd.read_file('map.geojson')
bounds = gdf.bounds
boundary = bounds.values.tolist()search = Search(bbox=boundary[0], datetime='2020-05-01/2020-07-30', collections=['sentinel-s2-l2a-cogs'],
url='')items = search.items()
file_url = items[52].asset('visual')['href']

and integrating this with the code from before:

src =
array =
rearranged_arr = array.transpose(1, 2, 0)
x_offset=random.randint(300, 9000)
y_offset=random.randint(300, 9000)
rearranged_arr[y_offset:y_offset+RGBimage.shape[0], x_offset:x_offset+RGBimage.shape[1]] = RGBimage

Can you spot smiley now?

Can you spot smiley now?

All the code is avaliable here.

I am a freelancer able to help you with your projects. I offer consultancy, training and writing. Iā€™d be delighted to hear from you.

Feel free to connect or follow me; I am always keen to talk about Earth Observation.

I am @map_andrew on twitter