Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Apply a color map / gradient map to an image using Python

Most image editing software has gradient map functions. Gradient maps take the brightness of a given pixel and apply a gradient of colors according to brightness. Photoshop and other software have ways to automate this, but they can't do exactly what I want, so I thought Python might do the trick. Unfortunately I'm having a very hard time understanding or applying any of the results that come up when I search for gradient maps or color maps with Python.

Gradient maps

All the potential solution threads I found used numpy or matplotlib which have lots of mathy lines that go right over my head... I would love some help with this. Initially I had something going with Processing, but I found the task of exporting tons of images with Processing to be weird and hacky. Plus I like Python, and want to learn how to edit and generate art with it.

This is what I'm working with right now.

from PIL import Image

myPalette = ['#1A1423', '#684756', '#AB8476']

def colorMap(pixel, palette): 
    # Calculate the brightness of pixel 
    R, G, B = pixel 
    brightness = sum([R, G, B])/3
 
    # Map gradient of colors to brightness
    # ???...
    
    return mappedColor

 
img = Image.open('image_input.png') 
pixels = img.load()
 
for x in range(img.size[0]): 
    for y in range(img.size[1]): 
        pixel = img.getpixel((x, y)) 
        pixels[x, y] = colorMap(pixel, myPalette)
 
img.save('image_output.png')

Loading, calculating brightness, and saving are easy. I just have no idea how to apply a gradient of my palette to the pixel.

like image 729
axe Avatar asked Sep 03 '25 03:09

axe


1 Answers

You can do that quite easily with ImageMagick or with PIL/Numpy/OpenCV.

The first thing is to get hold of the colormap - the vertical bar down the right side of your image. I don't have or know Clip Studio, so maybe it will let you export the colormap, or create a greyscale gradient and apply the colormap to it, then save the result as a PNG. In my case, I loaded your image into Photoshop, cut out the gradient and rotated it to make an image exactly 256-pixels wide by 1-pixel high. Enlarged, that looks like this:

colourmap.png

colormap going from navy to salmon, to yellow, to white

I also cropped your swirl thing off the left side of your image - please post images separately in future.

swirl.png

abstract grayscale image of a swirl


Now for applying it. First, just with ImageMagick in Terminal. I loaded your swirl image and separated it into its constituent RGB channels then averaged the channels and applied the colourmap, also known as CLUT or "Colour Lookup Table":

magick swirl.png -channel RGB -separate -evaluate-sequence mean colourmap.png -clut result.png

the previous swirl image with the previous colormap applied to it, giving it a navy-salmon-yellow color scheme


Next, same thing with PIL/Numpy:

#!/usr/bin/env python3

import numpy as np
from PIL import Image

# Load image, make into Numpy array and average RGB channels
im = Image.open('swirl.png').convert('RGB')
na = np.array(im)
grey = np.mean(na, axis=2).astype(np.uint8)
Image.fromarray(grey).save('DEBUG-grey.png')   # DEBUG only

# Load colourmap
cmap = Image.open('colourmap.png').convert('RGB')
# cmap must be resized to have a width of 256
# since grey's scaled from 0-255, so np.take will select from indices 0-255 only
cmap = cmap.resize((256, 1))

# Make output image same height and width as grey image, but 3-channel RGB
result = np.zeros((*grey.shape,3), dtype=np.uint8)

# Take entries from RGB colourmap according to greyscale values in image
np.take(cmap.getdata(), grey, axis=0, out=result)

# Save result
Image.fromarray(result).save('result.png')

You can also generate piece-wise linear colormaps like this:

 magick -size 160x1 gradient:navy-"rgb(220,110,110)"   \
        -size  60x1 gradient:"rgb(220,110,110)"-yellow \
        -size  35x1 gradient:yellow-white              \
        +append colourmap.png

colormap similar to first colormap, but with greater height and less width

That makes three segments each with a linear gradient:

  • 160x1 in navy to salmon,
  • 60x1 in salmon to yellow and
  • 35x1 in yellow to white

then appends them together.


If you make all the segments of the colour map the same length, you will get a different interpretation:

magick -size 85x1 \
   gradient:navy-"rgb(220,110,110)"   \
   gradient:"rgb(220,110,110)"-yellow \
   gradient:yellow-white +append -resize 256x1\! colourmap.png

colormap similar to previous colormap, but with longer segments of yellow and white, and less navy and salmon

That leads to this:

the provided swirl image with the previous colormap applied to it; it has more yellow and orange than the first colormapped swirl

like image 67
Mark Setchell Avatar answered Sep 04 '25 17:09

Mark Setchell