Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use PIL paste with mask?

I am trying to use PIL paste() function. I want to put in a mask as well but I keep getting this error:

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

canvases[0].paste(mnist_images[i],
                  box=tuple(map(lambda p: int(round(p)), positions[i])), mask=mask)

The code works without a mask. Mask is a numpy array. I have not seen an example with mask and the documentation is not clear.

https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.paste

If a mask is given, this method updates only the regions indicated by the mask. You can use either "1", "L", or "RGBA" images (in the latter case, the alpha band is used as mask). Where the mask is 255, the given image is copied as is. Where the mask is 0, the current value is preserved. Intermediate values will mix the two images together, including their alpha channels if they have them.

I dont have RGBA so how do I use "1" or "L"?

like image 798
Kong Avatar asked Dec 08 '17 22:12

Kong


1 Answers

The mask has to also be a PIL Image. This isn't explicitly mentioned in the docs, but it does state:

You can use either “1”, “L” or “RGBA” images (in the latter case, the alpha band is used as mask). Where the mask is 255, the given image is copied as is. Where the mask is 0, the current value is preserved. Intermediate values will mix the two images together, including their alpha channels if they have them.

So this hints that they need to be PIL Images. From the Pillow concepts page:

The mode of an image defines the type and depth of a pixel in the image. The current release supports the following standard modes:

1 (1-bit pixels, black and white, stored with one pixel per byte)
L (8-bit pixels, black and white)
...

The fix then is to simply turn your mask to a PIL Image with

mask = Image.fromarray(mask)

However, do note that for a binary mask, PIL expects the mask to have only 0 and 255 inside it as it states above (values between will blend). So if your mask is a numpy bool type, then you'd want to do something like:

mask = Image.fromarray(np.uint8(255*mask))

For e.g.:

>>> import numpy as np
>>> import cv2
>>> from PIL import Image
>>> img = Image.fromarray(np.uint8(255*np.random.rand(400, 400, 3)))
>>> sub_img = Image.fromarray(np.uint8(255*np.ones((200, 200, 3))))
>>> mask = Image.fromarray(np.uint8(255*(np.random.rand(200, 200) > 0.7)))
>>> img.paste(sub_img, (0, 0), mask)

Masked paste

Here I've pasted the white sub_img over img at the top left, and masked out ~70% of the pixels from the pasting operation, so only ~30% of the pixels in the region actually come out as white.

like image 178
alkasm Avatar answered Sep 19 '22 12:09

alkasm