Skip to content

Instantly share code, notes, and snippets.

@kwinkunks
Last active March 16, 2022 08:40
Show Gist options
  • Save kwinkunks/485190adcf3239341d8bebac94de3a2b to your computer and use it in GitHub Desktop.
Save kwinkunks/485190adcf3239341d8bebac94de3a2b to your computer and use it in GitHub Desktop.
Rip data from a pseudocolour image, given the colourmap

Ripping data from pseudocolour images

Because viridis, like all good colourmaps, is perceptually linear, it's easy to get the data from it: just use a greyscale version of the image. But you can rip the data from any pseudocolour image if you know (or can guess) the colourmap.

In the rip-data.py example, here's the approach:

  1. Read the image and transform the values to the range 0-1.
  2. Guess the colourmap, in this case it's viridis. Matplotlib conveniently gives us the RGB triples that make up a colourmap.
  3. Make the KD tree and look up colours. This gives us a way to find the nearest RGB triple to every pixel in the image.
  4. Make a plot with a different colourmap.

Related Twitter thread.


In the rip-data-2.py example, the colourmap is basically the hue wheel, so we can use the first channel from HSV to get at the data. The only snag is that there's hillshading, but fortunately the HS decomposition solves that too, putting the hillshade into the V channel.

Related Twitter thread.


In rip-data-3.py, the problem is that the colourmap does not match the hue wheel... however it is included in the image. So we can provide its location (x = 120, y = 60 to 1700) and grab it to use as a look-up table.

Related Twitter thread.

"""
If the colourmap matches all or part of the colour wheel or hue circle,
we can decompose the image to HSV and use H as a proxy for the data.
"""
from io import BytesIO
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
import requests
from skimage.color import rgb2hsv
# Read the image and transform to HSV.
uri = "https://pbs.twimg.com/media/ELX9zxIWoAId3FT.jpg"
data = requests.get(uri).content
img = Image.open(BytesIO(data))
rgb_im = np.asarray(img)[..., :3] / 255.
hsv_im = rgb2hsv(rgb_im)
hue = hsv_im[..., 0]
val = hsv_im[..., 2]
# Make hillshade from the value channel.
a = 1 - val
hillshade = a[..., None] * [0, 0, 0, 1]
# Make a new figure.
plt.figure(figsize=(20, 13))
plt.imshow(hue, cmap='YlGnBu')
plt.imshow(hillshade)
plt.axis('off')
plt.tight_layout()
plt.savefig('data/salt_fixed.png', dpi=100)
plt.show()
"""
In this example, the colourmap does not quite match the hue wheel, and there is
hillshade. Luckily, the image includes the colourmap, so we can use that as a LUT.
First, reduce to HSV to separate out the hillshade and get the colours on their own.
Then look up the colours in the LUT (whose position I had to figure out).
"""
from io import BytesIO
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
import requests
from skimage.color import rgb2hsv
from scipy.spatial import cKDTree
# Read the image and transform to HSV.
uri = "https://pbs.twimg.com/media/EQSALwMXkAcGm-9?format=jpg&name=4096x4096"
data = requests.get(uri).content
img = Image.open(BytesIO(data))
rgb_im = np.asarray(img)[..., :3] / 255.
hsv_im = rgb2hsv(rgb_im)
hue = hsv_im[..., 0]
val = hsv_im[..., 2]
# Make hillshade from the value channel.
a = 1 - val
hillshade = a[..., None] * [0, 0, 0, 1]
# Get the colormap.
colours = rgb_im[60:1700, 120:121].reshape(-1, 3)
# Make the KD tree and look up colours.
kdtree = cKDTree(colours)
dist, ix = kdtree.query(rgb_im)
ix = ix.astype(np.float)
# Make a new figure.
plt.figure(figsize=(15, 10))
plt.imshow(ix, cmap='YlGnBu')
plt.imshow(hillshade)
plt.axis('off')
plt.tight_layout()
plt.savefig('salty_fixed.png', dpi=200)
plt.show()
"""
If we know the colourmap is one of matplotlib's, then we can use it
as a look-up table, matching the colours to their position on the LUT.
In this example, the target image is already perceptually linear; I
wanted to make it horrible.
"""
from io import BytesIO
from scipy.spatial import cKDTree
from PIL import Image
import numpy as np
from matplotlib import cm
import matplotlib.pyplot as plt
import requests
# Read the image and transform to [0,1].
uri = "https://pbs.twimg.com/media/D4-CBRjXkAALioP.jpg:large"
data = requests.get(uri).content
img = Image.open(BytesIO(data))
im = np.asarray(img)[..., :3] / 255.
# Guess the colourmap, in this case it's viridis.
cmap = cm.get_cmap('viridis', 128)
c = cmap.colors[..., :3]
# Make the KD tree and look up colours.
kdtree = cKDTree(c)
_, ix = kdtree.query(im)
# Make a plot.
plt.figure(figsize=(15,10))
plt.imshow(ix, cmap='jet')
plt.axis('off')
plt.show()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment