12

I am pasting an image on another image, and after looking at this question, I saw that in order to paste a transparent image, you need to do

background = Image.open("test1.png")
foreground = Image.open("test2.png")

background.paste(foreground, (0, 0), foreground)

with a normal image, you should do

background = Image.open("test1.png")
foreground = Image.open("test2.png")

background.paste(foreground, (0, 0)) // difference here

my question is, how can I check if an image it transparent so I can determine how to use the paste method (with or without the last parameter).

Community
  • 1
  • 1
Ofek Agmon
  • 5,040
  • 14
  • 57
  • 101

5 Answers5

26

I've come up with the following function as a no-dependencies solution (besides PIL of course), which takes a PIL Image object as an argument:

def has_transparency(img):
    if img.info.get("transparency", None) is not None:
        return True
    if img.mode == "P":
        transparent = img.info.get("transparency", -1)
        for _, index in img.getcolors():
            if index == transparent:
                return True
    elif img.mode == "RGBA":
        extrema = img.getextrema()
        if extrema[3][0] < 255:
            return True

    return False

This function works by first checking to see if a "transparency" property is defined in the image's info -- if so, we return "True". Then, if the image is using indexed colors (such as in GIFs), it gets the index of the transparent color in the palette (img.info.get("transparency", -1)) and checks if it's used anywhere in the canvas (img.getcolors()). If the image is in RGBA mode, then presumably it has transparency in it, but it double-checks by getting the minimum and maximum values of every color channel (img.getextrema()), and checks if the alpha channel's smallest value falls below 255.

José Manuel Sánchez
  • 5,215
  • 2
  • 31
  • 24
  • This doesn't seem to detect images that trigger PIL's "Palette images with Transparency expressed in bytes should be converted to RGBA images" error. – Brad Root Dec 07 '20 at 23:04
  • 1
    I added an additional conditional which seemed to catch these files: `if image.info.get("transparency", None):` – Brad Root Dec 07 '20 at 23:18
  • 1
    A year later: `if image.info.get("transparency", None):` resolves to false if the image file has white defined as the transparency color (`0`), so `if image.info.get("transparency", None) is not None:` is better, and obviously more properly explicit... – Brad Root Nov 08 '21 at 21:39
  • @BradRoot so your extra conditional would return True or False? – moyo Feb 11 '22 at 15:35
  • 2
    @moyo it should resolve to True. I've submitted an edit to this answer to include it in the code snipper. `if image.info.get("transparency", None) is not None: return True` – Brad Root Feb 14 '22 at 20:36
3

I use numpy to check for alpha channel:

def im_has_alpha(img_arr):
    '''
    returns True for Image with alpha channel
    '''
    h,w,c = img_arr.shape
    return True if c ==4 else False

to use it, assuming pil_im is your Pillow Image:

import numpy as np
has_transparency = im_has_alpha(np.array(pil_im))
jho
  • 41
  • 3
1

Check the mode of the image for an alpha transparency layer. For example, RGB has no transparency, but RGBA does.

See https://pillow.readthedocs.io/en/latest/handbook/concepts.html for more.

Hugo
  • 27,885
  • 8
  • 82
  • 98
0

How this function works:

  • if there is an alpha channel, check the minimum alpha value
  • if there is a palette, find colors containing alpha that are used in the image (they might not be)
def image_is_transparent(image: Image, opaque: int = 255) -> bool:
    if 'A' in image.mode:
        # see if minimum alpha channel is below opaque threshold
        return image.getextrema()[image.mode.index('A')][0] < opaque
    if image.mode != 'P' or 'transparency' not in image.info:
        # format doesn't support transparency
        return False
    transparency = image.info['transparency']
    colors = image.getcolors()
    # check each color in the image
    if isinstance(transparency, bytes):
        # transparency is one byte per palette entry
        for _, index in colors:
            if transparency[index] < opaque:
                return True
    else:
        # transparency is palette index of fully transparent
        for _, index in colors:
            if transparency == index:
                return True
    return False
spiderlama
  • 1,539
  • 15
  • 10
0

There is no need for there to be a difference. If your image doesn't have an alpha channel, converting it to a mode that includes one will set it to 255 for every pixel. If it does have an alpha channel already the conversion won't change anything.

background = Image.open("test1.png")
foreground = Image.open("test2.png")

background.paste(foreground, (0, 0), foreground.convert('RGBA'))
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622