Ian Shulman
Ian Shulman

Reputation: 45

Image from array with dictionary for colors

I have an 2D-array test_df which is filled with zeroes and numbers:

   0     1     2     3     4     5     6     7     ...   1272  1273  1274  1275  1276  1277  1278  1279
0     0     0     0     0     0     0     0     0  ...      0     0     0     0     0     0     0     0
1     0     0     0     0     0     0     0     0  ...      0     0     0     0     0     0     0     0
2     0     0     0     0     0     0     0     0  ...      0     0     0     0     0     0     0     0
3     0     0     0     0     0     0     0     0  ...      0     0     0     0     0     0     0     0
4     0     0     0     0     0     0     0     0  ...      0     0     0     0     0     0     0     0

I would like to create an image based on the array, which has the same dimensions as the array, and where each number would be represented by a particular color, and each pixel is colored in accordance to the number occupying the corresponding position of the array. So, if array's row 1 / column 2 is 0, and 0 = black, the corresponding pixel of the image (row 1 / column 2) is also black. I see it as a dictionary, where each number is mapped to a particular color (e.g. 0 : black), so that I can apply it to any other array and know exactly which color corresponds to which number. So far I was using this code to visualize the array, but it doesn't preserve the number - color mapping, and I'm wondering if there's a better way to create an image.

  from matplotlib import pyplot as plt

  im = Image.fromarray(np.uint8(cm.prism(test_df)*255))
  im.show()
  im.save(save_path)

Additionally, I'd like to use the same dictionary to decode an image back to the array which was used to build it.

Upvotes: 1

Views: 1215

Answers (2)

HansHirse
HansHirse

Reputation: 18925

What you're looking for, are indexed colors – or: images with color palettes. Since you're already using Pillow's Image module, there's support for images with color palettes, see the (color) mode P.

I will provide a short example, how to create an image with a color palette, incorporating your idea of a dictionary as the color lookup table:

from matplotlib import pyplot as plt
import numpy as np
from PIL import Image


# Create color palette from color dictionary
def palette_from_dict(c_dict):
    palette = []
    for i in np.arange(256):
        if i in c_dict:
            palette.extend(c_dict[i])
        else:
            palette.extend([0, 0, 0])
    return palette


# Set up sample image as NumPy array
img_np = np.sort(np.random.randint(0, 256, (512, 512), np.uint8), axis=1)
img_np[20:60, 20:60] = 1
img_np[120:160, 120:160] = 8
img_np[220:260, 220:260] = 9

# Create Pillow image, use 'P' mode for using a color palette
img_pil = Image.fromarray(img_np, 'P')

# Set up color dictionary #1 (some simple colors)
colors_dict = {0: [0, 0, 0], 1: [255, 0, 0], 8: [0, 255, 0], 9: [0, 0, 255]}

# Apply color palette #1 to Pillow image, and show
img_pil.putpalette(palette_from_dict(colors_dict))
plt.figure(1, figsize=(16, 8.5))
plt.subplot(1, 2, 1), plt.imshow(img_pil)

# Set up color dictionary #2 (grayscale color map)
colors_dict = {}
for i in np.arange(256):
    colors_dict[i] = [i, i, i]

# Apply color palette #2 to Pillow image, and show
img_pil.putpalette(palette_from_dict(colors_dict))
plt.subplot(1, 2, 2), plt.imshow(img_pil)
plt.tight_layout()
plt.show()

Output:

Output

When creating a color palette, pay attention to the documentation on Image.putpalette (emphasis by me):

Attaches a palette to this image. The image must be a “P”, “PA”, “L” or “LA” image, and the palette sequence must contain 768 integer values, where each group of three values represent the red, green, and blue values for the corresponding pixel index. Instead of an integer sequence, you can use an 8-bit string.

You can provide a shorter palette, then the rest is padded with zeros.

If you have a P mode image, you can use Image.getpalette, and do a reverse lookup in your dictionary to restore the original array.

Hope that helps!


@MarkSetchell: Palettised images, he? :-)

Upvotes: 3

rponthieu dev
rponthieu dev

Reputation: 126

Many weird things can append when you manipulate arrays and convert them to images. One of them is the fact that your image will likely be encoded in uint8 at some point. And in uint8 weird things happends, such as 255+2 = 1. Most imaging library convert whatever array you give them to uint8 before saving, but you never know what happend exactly. For exemple matplotlib will often encode your array with the max value of it as 255 and the min as 0, thus rescaling it and a color does not always correspond to the same value ! What I would advice is make sure you stick close to the image format when sending the array to be recorded to a file (uint8). Imageio is a really nice library for that. As for your idea of using a dictionary for colours, it is good if you dont have too much different values.

Let me know if you want exemples of imageio code with a dictionary color encoding I can make that when I reach my computer.

Best, R

Upvotes: 0

Related Questions