Godrebh
Godrebh

Reputation: 564

Convert a large grayscale PIL image to black and transparent

I am trying to use a large 2d array to create an image mask with black and transparent parts. Originally, the input 2d array was a PIL.Image that was loaded in grayscale ('L') mode. So it contains values between 0 and 255. And now I want to replace all the 0s with [0,0,0,255] (black stays black) and all values >0 should be [0,0,0,0] (transparent). I can do this simply like this:

import numpy as np

# generate some random test data - normally I just read the input image, which is fast
input_data = np.array([np.array([random.choice([0,10]) for x in range(22000)]) for y in range(9000)])

# create a new img containing black and transparent pixels (r,g,b,alpha) and this takes ages
overlay_img = [[[0, 0, 0, 255] if input_data[y][x] == 0 else [0, 0, 0, 0] for x in range(len(input_data[0]))] for y in range(len(input_data))]
overlay_img = np.array(overlay_img)

This takes quite some time because the input data is so large (~22000x9000). I am curious if it is somehow possible to do this faster. I also tried np.where, but I could not get it to work. Maybe there is even a way to directly change the PIL image?

fyi: In the end, I just want to plot this image on top of my matplotlib plot with imshow, so that only the relevant regions are visible (where the image is transparent) and the rest is hidden/black.

Here just a very quick and small example of what I want to do: enter image description here

Upvotes: 1

Views: 401

Answers (1)

Mark Setchell
Mark Setchell

Reputation: 207465

I think you want this, but you haven't shown your code for imshow():

#!/usr/bin/env python3

import random
import numpy as np

# Set up dimensions and random input image
h, w = 9000, 22000
im = np.random.randint(0, 11, (h,w), dtype=np.uint8)

# Create 4-channel mask image
mask = np.zeros((h,w,4), dtype=np.uint8)
mask[...,3] = (im==0) * 255

The last line takes 800ms on my MacBook Pro.


If you need a bit more performance, you can use numexpr as follows and the time required is 300ms instead of 800ms:

import random
import numexpr as ne
import numpy as np

# Set up dimensions and random input image
h, w = 9000, 22000
im = np.random.randint(0, 11, (h,w), dtype=np.uint8)

# Create 4-channel mask image
mask = np.zeros((h,w,4), dtype=np.uint8)

# Same but with "numexpr"
mask[...,3] = ne.evaluate("(im==0)*255")

Upvotes: 1

Related Questions