Search code examples
pythonimage-processingpython-imaging-librarycolormap

Turn color image into a false color image aka simple (1D) LUT in Python


For a small camera program I need to convert a JPG taken by the RPi camera (RPi3) into a false color image. I want to map every grey value of a color picture to a specific new color value and save the picture as JPEG.

I have the LUT exported in all formats from Photoshop, but a 256x1 png file is supposed to make it easier. I tried to use that (256x1, 8 Bit, PNG, horizontal with the leftmost pixel for the darkest values). My source pictures are 1296x972px, JPEG, 8 Bit. I tried using pillow and matplotlib.

The closest I've come to get anything working is the following code.

from PIL import Image
import numpy as np

image = Image.open('testbild.jpg')
image = image.convert("L")

# Ensure the image has 3 channels.
image = image.convert('RGB')

# Open the LUT image and convert it to a NumPy array.
lut_image = Image.open('LUT.png')
lut_array = np.array(lut_image)

# Extract the pixel values from the array and flatten them.
lut = lut_array.flatten()

# Apply the LUT to the image.
image = image.point(lut)

image.save('output.jpg')

This gives me a blue-ish output, when my LUT is more purple.

This is the LUT.

[LUT]

The process should be as simple as possible, so I get the result as fast as possible. The rest of the program is working; I'm just pulling my hair over this part. I tried all methods that are reported to work, including matplotlibs color maps. I can get that to work with its own cmaps. It looks good, but it's slow and I can't get my own cmap in.

This is my first program and I'm normally a photographer, so this might be the main problem.

Any help is welcome.


Solution

  • Your problem is that you flatten your LUT in the wrong way, PIL want it like this

    [r0, r1, ..., r255, g0, g1, ..., g255, b0, b1, ..., b255]
    

    while you obtained

    [r0, g0, b0, r1, g1, b1, ..., r255, g255, b255]
    

    because when you read and "array" the LUT, the array has shape (1, 256, 3), that is 1 row, 256 columns, and 3 bytes per pixel, and flattening collapse the last dimension first.

    We want to remove the first dimension, this can be done using .reshape(-1, 3) where -1 means collapse all the dimensions that are not explicitly mentioned, so that the array has shape (256,3).

    Next we want to transpose the array, using .T, and now it has shape `(3, 256).

    Finally we can flatten the array and have the LUT in the correct format.

    Note that these operations (image to array, reshaping, transposition and flattening) have been chained in my code.

    Here it is the code

    from PIL import Image
    img = Image.open('Figure_4.png')
    lut = np.array(Image.open('LUT.png')).reshape(-1,3).T.flatten()
    img = img.convert('L') ; img = img.convert('RGB')
    img.save('Gray.png')
    img = img.point(lut)
    img.save('Converted.png')
    

    and here the original image, the gray scale image and the converted image

    enter image description here

    enter image description here

    enter image description here