Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to generate a mask using Pillow's Image.load() function

I want to create a mask based on certain pixel values. For example: every pixel where B > 200

The Image.load() method seems to be exactly what I need for identifying the pixels with these values, but I can't seem to figure out how to take all these pixels and create a mask image out of them.

            R, G, B = 0, 1, 2

            pixels = self.input_image.get_value().load()
            width, height = self.input_image.get_value().size

            for y in range(0, height):
                for x in range(0, width):
                    if pixels[x, y][B] > 200:
                        print("%s - %s's blue is more than 200" % (x, y))
``
like image 304
applepie Avatar asked Dec 23 '22 22:12

applepie


2 Answers

I meant for you to avoid for loops and just use Numpy. So, starting with this image:

enter image description here

from PIL import Image
import numpy as np

# Open image
im = Image.open('colorwheel.png')

# Make Numpy array
ni = np.array(im)

# Mask pixels where Blue > 200
blues = ni[:,:,2]>200

# Save logical mask as PNG
Image.fromarray((blues*255).astype(np.uint8)).save('result.png')

enter image description here

If you want to make the masked pixels black, use:

ni[blues] = 0
Image.fromarray(ni).save('result.png')

enter image description here


You can make more complex, compound tests against ranges like this:

#!/usr/bin/env python3

from PIL import Image
import numpy as np

# Open image
im = Image.open('colorwheel.png')

# Make Numpy array
ni = np.array(im)

# Mask pixels where 100 < Blue < 200
blues = ( ni[:,:,2]>100 ) & (ni[:,:,2]<200)

# Save logical mask as PNG
Image.fromarray((blues*255).astype(np.uint8)).save('result.png')

enter image description here

You can also make a condition on Reds, Greens and Blues and then use Numpy's np.logical_and() and np.logical_or() to make compound conditions, e.g.:

bluesHi = ni[:,:,2] > 200 
redsLo  = ni[:,:,0] < 50

mask = np.logical_and(bluesHi,redsLo)
like image 156
Mark Setchell Avatar answered Dec 25 '22 12:12

Mark Setchell


Thanks to the reply from Mark Setchell, I solved by making a numpy array the same size as my image filled with zeroes. Then for every pixel where B > 200, I set the corresponding value in the array to 255. Finally I converted the numpy array to a PIL image in the same mode as my input image was.

            R, G, B = 0, 1, 2

            pixels = self.input_image.get_value().load()
            width, height = self.input_image.get_value().size
            mode = self.input_image.get_value().mode

            mask = np.zeros((height, width))

            for y in range(0, height):
                for x in range(0, width):
                    if pixels[x, y][2] > 200:
                        mask[y][x] = 255

            mask_image = Image.fromarray(mask).convert(mode)
like image 29
applepie Avatar answered Dec 25 '22 11:12

applepie