How can I quickly change pixels in a image from a color dictionary?

I am providing two answers to this question. This answer is more based in PIL/Pillow and the other is more based in OpenCV. Read this answer in conjunction with my other answer and potentially mix and match.

You can do it using the palette. In case you are unfamiliar with palettised images, rather than having an RGB value at each pixel location, you have a simple 8-bit index into a palette of up to 256 colours.

So, what we can do, is load your image as a PIL Image, and quantise it to the set of input colours you have. Then each pixel will have the index of the colour in your map. Then just replace the palette with the colours you want to map to.

#!/usr/bin/env python3

import numpy as np
from PIL import Image

def QuantizeToGivenPalette(im, palette):
    """Quantize image to a given palette.

    The input image is expected to be a PIL Image.
    The palette is expected to be a list of no more than 256 R,G,B values."""

    e = len(palette)
    assert e>0,    "Palette unexpectedly short"
    assert e<=768, "Palette unexpectedly long"
    assert e%3==0, "Palette not multiple of 3, so not RGB"

    # Make tiny, 1x1 new palette image
    p = Image.new("P", (1,1))

    # Zero-pad the palette to 256 RGB colours, i.e. 768 values and apply to image
    palette += (768-e)*[0]
    p.putpalette(palette)

    # Now quantize input image to the same palette as our little image
    return im.convert("RGB").quantize(palette=p)

# Open input image and palettise to "inPalette" so each pixel is replaced by palette index
# ... so all black pixels become 0, all red pixels become 1, all green pixels become 2...
im = Image.open('image.png').convert('RGB')

inPalette = [
    0,0,0,    # black
    255,0,0,  # red
    0,255,0,  # green
    0,0,255,  # blue
    255,255,255 # white
    ]
r = QuantizeToGivenPalette(im,inPalette)

# Now simply replace the palette leaving the indices unchanged
newPalette = [
    255,255,255,  # white
    0,255,255,    # cyan
    255,0,255,    # magenta
    255,255,0,    # yellow
    0,0,0         # black
    ]

# Zero-pad the palette to 256 RGB colours, i.e. 768 values
newPalette += (768-len(newPalette))*[0]

# And finally replace the palette with the new one
r.putpalette(newPalette)

# Save result
r.save('result.png')

Input Image

enter image description here

Output Image

enter image description here

So, to do specifically what you asked with a dictionary that maps old colour values to new ones, you will want to initialise oldPalette to the keys of your dictionary and newPalette to the values of your dictionary.

Keywords: Python, PIL, Pillow, image, image processing, quantise, quantize, specific palette, given palette, specified palette, known palette, remap, re-map, colormap, map.

There are some hopefully useful words about palettised images here, and here.

Leave a Comment