0

I'm trying to paste a picture with transparent background into a picture using PIL. The problem is that I'm unable to remove the background and paste it without background onto the base picture

The picture I want to paste has a red background. I'm trying to remove the backround and paste the picture like this:

rm_image = Image.open("formations/testrm.png")

    rm_copy = rm_image.copy().convert("RGBA")

    background_color = (255, 0, 0, 255)

    for x in range(rm_copy.width):
        for y in range(rm_copy.height):
            r, g, b, a = rm_copy.getpixel((x, y))
            if (r, g, b, a) == background_color:
                rm_copy.putpixel((x, y), (0, 0, 0, 0))

    formation.paste(rm_copy, (1039, 396))

I also copied the base image and converted it to rgba like this:

    formation = Image.open(BytesIO(requests.get("LINK TO DISCORD ATTACHMENT").content))
    formation = formation.copy().convert("RGBA")

before I converted the base picture to RGBA the pasted Image had a completely black background. After converting the base picture to RGBA the background of the pasted Image is dark gray.

It would be really nice if someone could point out what is wrong and suggest possible ways to fix it.

Full code for recreation:

from io import BytesIO

import requests
from PIL import Image

formation = Image.open(BytesIO(requests.get("https://media.discordapp.net/attachments/1101171452377571428/1114864498424160316/FUSSBALL_4_3_3.png?width=1430&height=804").content))
formation = formation.copy().convert("RGBA")

rm_image = Image.open(BytesIO(requests.get("https://cdn.discordapp.com/attachments/1101171452377571428/1117737449716715570/testrm.png").content))

rm_copy = rm_image.copy().convert("RGBA")

background_color = (255, 0, 0, 255)

for x in range(rm_copy.width):
    for y in range(rm_copy.height):
        r, g, b, a = rm_copy.getpixel((x, y))
        if (r, g, b, a) == background_color:
            rm_copy.putpixel((x, y), (0, 0, 0, 0))

formation.paste(rm_copy, (1039, 396))

formation.show()
JustLutz
  • 3
  • 3
  • A proper [mcve] for a question about image processing needs input image(s), and current and expected output image(s). You should also include a runnable script, which means `import` statements too. – Mark Setchell Jun 12 '23 at 08:42
  • Try [this](https://stackoverflow.com/questions/5324647/how-to-merge-a-transparent-png-image-with-another-image-using-pil/5324782#5324782). – ken Jun 12 '23 at 09:32

2 Answers2

2

To enable transparency the trick is to pass to the paste method a mask as parameter (that is the same as the image to copy).
Hoping is what you're looking for!

from io import BytesIO
import requests
from PIL import Image

#Load and convert as before (but convert directly the image without creating a copy)
formation = Image.open(BytesIO(requests.get("https://media.discordapp.net/attachments/1101171452377571428/1114864498424160316/FUSSBALL_4_3_3.png?width=1430&height=804").content))
formation = formation.convert("RGBA")
rm_image = Image.open(BytesIO(requests.get("https://cdn.discordapp.com/attachments/1101171452377571428/1117737449716715570/testrm.png").content))
rm_image = rm_image.convert("RGBA")

#Create the copy with transparent background
rm_copy = Image.new('RGBA', rm_image.size, (0, 0, 0, 0))

for x in range(rm_image.width):
    for y in range(rm_image.height):
        r, g, b, a = rm_image.getpixel((x, y))
        if r != 255 or g != 0 or b != 0: #copy if it's not red
            rm_copy.putpixel((x, y), (r, g, b, a))

formation.paste(rm_copy, (1045, 400), rm_copy) #slightly change the coordinates to overlap better
formation.show()

enter image description here

Myron_Ben4
  • 432
  • 4
  • 13
1

Iterating over images with for loops in Python is very inadvisable - it is slow and error-prone. Try to use built-in PIL functions written in C or vectorised Numpy operations.

import Numpy as np
from PIL import Image

# Open 2 input images
formation = Image.open('formation.png')
rm = Image.open('rm.png')

# Make "rm" into Numpy array and find all opaque red pixels
na   = np.array(rm)
reds = np.all(na==[255,0,0,255], axis=-1)

# Convert reds back into PIL Image to use as alpha mask
alpha = Image.fromarray(~reds)

# Paste "rm" over "formation" with alpha as transparency mask
formation.paste(rm, (1045, 400), alpha)

enter image description here

This takes 250μs as opposed to 11.5ms using for loops, so it is 44 times faster.

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432