Image overlay certain pixels

Viewed 368

I have two functions.
The first one changes the opacity of all black pixels in the overlay image to 0.
The second image overlays onto another one with changing opacity. But only for all pixels that are not black.
Overall I want to overlay images with different opacities and prevent that the black background does not show up.
It works just fine, but not really fast (both avg > 2sec). Is there a way to speed up the process a little? Numpy for example?
Here is what I have so far:

def remove_background(image):
   im = image.convert('RGBA')
   w, h = im.size
   for i in range(w):
       for j in range(h):
           cordinate = i, j
           rgb_map = im.getpixel(cordinate)
           if rgb_map == (0, 0, 0, 255):
               im.putpixel(cordinate, (0, 0, 0, 0))
   return im


def overlay_image(background, overlay, opacity=0.5):
   background = background.convert('RGBA')
   overlay = overlay.convert('RGBA')
   opacity = int(opacity * 255)
   w, h = background.size
   for i in range(w):
       for j in range(h):
           cordinate = i, j
           rgb_map = overlay.getpixel(cordinate)
           r = rgb_map[0]
           g = rgb_map[1]
           b = rgb_map[2]
           if r == 64 and g == 128 and b == 19:
               overlay.putpixel(cordinate, (64, 128, 19, opacity))
   background.paste(overlay, (0,0), overlay)
   return background

I know there is something from PIL called Image.blend(), but this is just for the whole image and not for certain pixels.

Here is an example I want to overlay:

animal

enter image description here

mask

enter image description here

result

enter image description here

2 Answers

Updated Answer

I think this is what your updated question is looking for:

#!/usr/bin/env python3

from PIL import Image

# Open the mask, discarding any alpha channel
mask = Image.open('mask.png').convert('RGB')

# Generate a new alpha channel that is transparent wherever the mask is black
alpha = mask.copy()
alpha = alpha.convert('L')
alpha.point(lambda p: 128 if p > 0 else 0)

# Open the animal, discarding its pointless alpha channel
animal = Image.open('animal.png').convert('RGB')

# Paste the mask over the animal, respecting transparency
animal.paste(mask,mask=alpha)
animal.save('result.png')

enter image description here

Alternative

As an alternative, you could simply add the mask to the animal. Where the mask is black (i.e. zero) it will add nothing:

#!/usr/bin/env python3

from PIL import Image, ImageChops

# Open the mask, discarding any alpha channel
mask = Image.open('mask.png').convert('RGB')

# Open the animal, discarding any alpha channel
animal = Image.open('animal.png').convert('RGB')

# Add the mask to the animal
res = ImageChops.add(animal, mask)
res.save('result.png')

enter image description here

You could scale the mask first, say by a half, using Image.point() with a lambda like below, if the green is too much.

Original Answer

Mmmm, if you want to mask an input image with another input image and show the results, I think you'd need three images, not two.

So, I am guessing this is what you want and it should be nice and fast:

#!/usr/bin/env python3

from PIL import Image

# Open the mask, discarding its pointless alpha channel and colour information
mask = Image.open('mask.png').convert('L')

# Generate a new alpha channel that is transparent wherever the mask is black
mask.point(lambda p: 255 if p > 0 else 0)

# Open the animal, discarding its pointless alpha channel
animal = Image.open('animal.png').convert('RGB')

# Put our lovely new alpha channel into animal image and save
animal.putalpha(mask)
animal.save('result.png')

mask.png

enter image description here

animal.png

enter image description here

enter image description here

Just for fun, here's another way of doing it leveraging Numpy:

#!/usr/bin/env python3

from PIL import Image
import numpy as np

# Open the overlay and animal,  discarding any alpha channel
overlay = Image.open('mask.png').convert('RGB')
animal  = Image.open('animal.png').convert('RGB')
mask    = overlay.convert('L')

# Make into Numpy arrays
overlay= np.array(overlay)
animal = np.array(animal)
mask   = np.array(mask)

# Blend animal and overlay
blended = animal//2 + overlay//2

# Choose blended or original at each location depending on mask
res = np.where(mask[...,np.newaxis], blended, animal)

# Revert to PIL Image and save
Image.fromarray(res).save('result.png')

enter image description here

Related