Home > other >  Apply a color map / gradient map to an image using Python
Apply a color map / gradient map to an image using Python

Time:03-23

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.

CodePudding user response:

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

enter image description here

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

swirl.png

enter image description here


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 enter image description here


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')

# 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')

CodePudding user response:

The trick is to define a mapping table that associates a color to every gray value in the range [0, 255] (in your case, you can even map [0, 255 x 3] by not taking the average).

Now to get a smooth effect, you need to change the color components regularly. The options are unlimited. Here is a typical example:

enter image description here

enter image description here

CodePudding user response:

I know you said you didn't want matplotlib, but I don't think you need mathy lines to make it work. I tested it with a PNG of my own, so hopefully it works for yours as well. Here's an example that draws heavily on this documentation.

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap

# Read PNG and take average:
img = plt.imread('image_input.png')
Z = img.mean(-1)

#   Kestrel
color_list = [np.array([207,117,80])/255,
              np.array([219,179,127])/255,
              np.array([248,249,251])/255,
              np.array([185,191,205])/255,
              np.array([88,104,127])/255]

cmap_name = 'Kestrel'
N_bin=100
# Create the colormap
cmap = LinearSegmentedColormap.from_list(cmap_name, color_list, N=N_bin)
# Fewer bins will result in "coarser" colomap interpolation
fig, ax = plt.subplots()
im = ax.imshow(Z, cmap=cmap)
fig.colorbar(im, ax=ax)

plt.show()
  • Related