How to use PIL to make all white pixels transparent?

I'm trying to make all white pixels transparent using the Python Image Library. (I'm a C hacker trying to learn python so be gentle) I've got the conversion working (at least the pixel values look correct) but I can't figure out how to convert the list into a buffer to re-create the image. Here's the code

img = Image.open('img.png')
imga = img.convert("RGBA")
datas = imga.getdata()

newData = list()
for item in datas:
    if item[0] == 255 and item[1] == 255 and item[2] == 255:
        newData.append([255, 255, 255, 0])
    else:
        newData.append(item)

imgb = Image.frombuffer("RGBA", imga.size, newData, "raw", "RGBA", 0, 1)
imgb.save("img2.png", "PNG")


Asked by: Marcus594 | Posted: 30-11-2021






Answer 1

You need to make the following changes:

  • append a tuple (255, 255, 255, 0) and not a list [255, 255, 255, 0]
  • use img.putdata(newData)

This is the working code:

from PIL import Image

img = Image.open('img.png')
img = img.convert("RGBA")
datas = img.getdata()

newData = []
for item in datas:
    if item[0] == 255 and item[1] == 255 and item[2] == 255:
        newData.append((255, 255, 255, 0))
    else:
        newData.append(item)

img.putdata(newData)
img.save("img2.png", "PNG")

Answered by: Marcus818 | Posted: 01-01-2022



Answer 2

You can also use pixel access mode to modify the image in-place:

from PIL import Image

img = Image.open('img.png')
img = img.convert("RGBA")

pixdata = img.load()

width, height = img.size
for y in range(height):
    for x in range(width):
        if pixdata[x, y] == (255, 255, 255, 255):
            pixdata[x, y] = (255, 255, 255, 0)

img.save("img2.png", "PNG")

You can probably also wrap the above into a script if you use it often.

Answered by: Alissa222 | Posted: 01-01-2022



Answer 3

Since this is currently the first Google result while looking for "Pillow white to transparent", I'd like to add that the same can be achieved with numpy, and in my benchmark (a single 8MP image with lots of white background) is about 10 times faster (about 300ms vs 3.28s for the proposed solution). The code is also a bit shorter:

import numpy as np

def white_to_transparency(img):
    x = np.asarray(img.convert('RGBA')).copy()

    x[:, :, 3] = (255 * (x[:, :, :3] != 255).any(axis=2)).astype(np.uint8)

    return Image.fromarray(x)

It is also easily exchanble to a version where the "almost white" (e.g. one channel is 254 instead of 255) is "almost transparent". Of course this will make the entire picture partly transparent, except for the pure black:

def white_to_transparency_gradient(img):
    x = np.asarray(img.convert('RGBA')).copy()

    x[:, :, 3] = (255 - x[:, :, :3].mean(axis=2)).astype(np.uint8)

    return Image.fromarray(x)

Remark: the .copy() is needed because by default Pillow images are converted to read-only arrays.

Answered by: Melanie834 | Posted: 01-01-2022



Answer 4

import Image
import ImageMath

def distance2(a, b):
    return (a[0] - b[0]) * (a[0] - b[0]) + (a[1] - b[1]) * (a[1] - b[1]) + (a[2] - b[2]) * (a[2] - b[2])

def makeColorTransparent(image, color, thresh2=0):
    image = image.convert("RGBA")
    red, green, blue, alpha = image.split()
    image.putalpha(ImageMath.eval("""convert(((((t - d(c, (r, g, b))) >> 31) + 1) ^ 1) * a, 'L')""",
        t=thresh2, d=distance2, c=color, r=red, g=green, b=blue, a=alpha))
    return image

if __name__ == '__main__':
    import sys
    makeColorTransparent(Image.open(sys.argv[1]), (255, 255, 255)).save(sys.argv[2]);

Answered by: Eric937 | Posted: 01-01-2022



Answer 5

A more pythonic way since looping take a very long time for a big image

from PIL import Image

img = Image.open('img.png')
img = img.convert("RGBA")

imgnp = np.array(img)

white = np.sum(imgnp[:,:,:3], axis=2)
white_mask = np.where(white == 255*3, 1, 0)

alpha = np.where(white_mask, 0, imgnp[:,:,-1])

imgnp[:,:,-1] = alpha 

img = Image.fromarray(np.uint8(imgnp))
img.save("img2.png", "PNG")

Answered by: Daniel492 | Posted: 01-01-2022



Answer 6

Python 3 version with all the files in a dir

import glob
from PIL import Image

def transparent(myimage):
    img = Image.open(myimage)
    img = img.convert("RGBA")

    pixdata = img.load()

    width, height = img.size
    for y in range(height):
        for x in range(width):
            if pixdata[x, y] == (255, 255, 255, 255):
                pixdata[x, y] = (255, 255, 255, 0)

    img.save(myimage, "PNG")

for image in glob.glob("*.png"):
    transparent(image)

Answered by: First Name427 | Posted: 01-01-2022



Answer 7

This function combines all the advantages of the previous solutions: it allows any background and uses numpy (that is faster than the classical lists).

import numpy as np
from PIL import Image

def convert_png_transparent(src_file, dst_file, bg_color=(255,255,255)):
    image = Image.open(src_file).convert("RGBA")
    array = np.array(image, dtype=np.ubyte)
    mask = (array[:,:,:3] == bg_color).all(axis=2)
    alpha = np.where(mask, 0, 255)
    array[:,:,-1] = alpha
    Image.fromarray(np.ubyte(array)).save(dst_file, "PNG")

Answered by: Arthur508 | Posted: 01-01-2022



Answer 8

I'm surprised no one has seen the need to not just change a specific color, but rather the blends of that color with others as well. This would be what Gimp does with the functionality "color to alpha". Extending cr333's code with https://stackoverflow.com/a/62334218/5189462 we get something that resembles this functionality:

from PIL import Image

target_color = (255, 255, 255)

img   = Image.open('img.png')
imga  = img.convert("RGBA")
datas = imga.getdata()

newData = list()
for item in datas:
    newData.append((
        item[0], item[1], item[2],
        max( 
            abs(item[0] - target_color[0]), 
            abs(item[1] - target_color[1]), 
            abs(item[2] - target_color[2]), 
        )  
    ))

imgb = Image.frombuffer("RGBA", imga.size, newData, "raw", "RGBA", 0, 1)
imgb.save("img2.png", "PNG")

Answered by: Tess532 | Posted: 01-01-2022



Answer 9

@egeres method of using using the distance to a target color to create an alpha value is really neat and creates a much nicer result. Here it is using numpy:

import numpy as np
import matplotlib.pyplot as plt

def color_to_alpha(im, target_color):
    alpha = np.max(
        [
            np.abs(im[..., 0] - target_color[0]),
            np.abs(im[..., 1] - target_color[1]),
            np.abs(im[..., 2] - target_color[2]),
        ],
        axis=0,
    )
    ny, nx, _ = im.shape
    im_rgba = np.zeros((ny, nx, 4), dtype=im.dtype)
    for i in range(3):
        im_rgba[..., i] = im[..., i]
    im_rgba[..., 3] = alpha
    return im_rgba

target_color = (0.0, 0.0, 0.0)
im = plt.imread("img.png")
im_rgba = color_to_alpha(im, target_color)

For completeness I've included a comparison with the mask-based version applied to the matplotlib logo below:

from pathlib import Path
import matplotlib.pyplot as pl
import numpy as np


def color_to_alpha(im, alpha_color):
    alpha = np.max(
        [
            np.abs(im[..., 0] - alpha_color[0]),
            np.abs(im[..., 1] - alpha_color[1]),
            np.abs(im[..., 2] - alpha_color[2]),
        ],
        axis=0,
    )
    ny, nx, _ = im.shape
    im_rgba = np.zeros((ny, nx, 4), dtype=im.dtype)
    for i in range(3):
        im_rgba[..., i] = im[..., i]
    im_rgba[..., 3] = alpha
    return im_rgba


def color_to_alpha_mask(im, alpha_color):
    mask = (im[..., :3] == alpha_color).all(axis=2)
    alpha = np.where(mask, 0, 255)
    ny, nx, _ = im.shape
    im_rgba = np.zeros((ny, nx, 4), dtype=im.dtype)
    im_rgba[..., :3] = im
    im_rgba[..., -1] = alpha
    return im_rgba


# load example from images included with matplotlib
fn_img = Path(plt.__file__).parent / "mpl-data" / "images" / "matplotlib_large.png"
im = plt.imread(fn_img)[..., :3]  # get rid of alpha channel already in image

target_color = [1.0, 1.0, 1.0]
im_rgba = color_to_alpha(im, target_color)
im_rgba_masked = color_to_alpha_mask(im, target_color)

fig, axes = plt.subplots(ncols=3, figsize=(12, 4))
[ax.set_facecolor("lightblue") for ax in axes]
axes[0].imshow(im)
axes[0].set_title("original")
axes[1].imshow(im_rgba)
axes[1].set_title("using distance to color")
axes[2].imshow(im_rgba_masked)
axes[2].set_title("mask on color")

comparison of different color-to-alpha techniques

Answered by: Paul468 | Posted: 01-01-2022



Answer 10

I like Jonathan's answer a lot. An alternative way of how this could be achieved using NumPy and without the use of np.where:

import numpy as np
from PIL import Image

img = Image.open('img.png') # n x m x 3
imga = img.convert("RGBA")  # n x m x 4

imga = np.asarray(imga) 
r, g, b, a = np.rollaxis(imga, axis=-1) # split into 4 n x m arrays 
r_m = r != 255 # binary mask for red channel, True for all non white values
g_m = g != 255 # binary mask for green channel, True for all non white values
b_m = b != 255 # binary mask for blue channel, True for all non white values

# combine the three masks using the binary "or" operation 
# multiply the combined binary mask with the alpha channel
a = a * ((r_m == 1) | (g_m == 1) | (b_m == 1))

# stack the img back together 
imga =  Image.fromarray(np.dstack([r, g, b, a]), 'RGBA')

I benchmarked my method against keithb's (highest rated answer), and mine is 18 faster (averaged over 102 images of size 124*124).

Answered by: Dainton228 | Posted: 01-01-2022



Answer 11

Use RGBA in the mode if you open it img = img.convert("RGBA") or IMG = Image.new(mode="RGB", size=(width,high)) so you can add alpha into image IMG.putpixel((Y_Axis, X_Axis),(R, G, B))

Answered by: Ned371 | Posted: 01-01-2022



Similar questions

python - How can I make certain pixels transparent in opencv?

I checked the brightness of each pixel with the for statement and tried to make it transparent if the brightness was over 100. But invisibility does not work. look at the code, the fourth of image_al [x, y] is alpha. What should I do? (If you I the value of bgr rather than transparency, it changed.) image = cv2.imread('cat.jpg', cv2.IMREAD_COLOR) image_al=cv2.cvtColor(image,cv2.COLOR_BGR2BGRA) im...


python - PIL: How to make area transparent in PNG?

I've been using PIL to crop Images, now I also want to make certain rectangular areas transparent, say from PIL import Image im = Image.open("sample.png") transparent_area = (50,80,100,200) ...


python - Transparent 3D bar graphs

I would like to generate 3D bar graphs with transparent surfaces so that I can see what is going on behind tall bars. The mplot3d API docs say that keywords are allowed for the bar3d function. I pass all the required parameters but can only output graphs with solid surfaces.


python - How to merge a transparent png image with another image using PIL

I have a transparent png image "foo.png" and I've opened another image with im = Image.open("foo2.png"); now what i need is to merge foo.png with foo2.png. ( foo.png contains some text and I want to print that text on foo2.png )


python - In Pygame, why am I unable to make part of an image transparent?

I have an image of a red ball on a blue background saved as a BMP file. There are only two colors used in the image: red (255, 0, 0) and blue (0, 0, 255). I wrote the following Pygame test program to isolate the problem. It simply loads an image, sets the upper-left pixel's color as the transparent color, and blits it to the screen. It's about as simple as it gets, but I can't get it to work. import pygame ...


python - How to draw a transparent Line in pygame

I made a little game in which I need a background pattern with lines. Because of a better performance, I would like to draw the pattern in Python instead of taking an image. The problem is that I can't find a way to draw the lines with transparency. There are solutions for surfaces, but not for lines. Here is the pattern code: import pygame from math import pi pygame.init() size = [600, 600] ...


python - Fully transparent mask -> fully opaque widget

I ran into a strange corner case when attempting to render a fully transparent widget in PySide. If I call setMask with a QBitmap that is filled with Qt.color0, it makes the widget completely opaque (rather than completely transparent, as I would expect). Putting in a single pixel of Qt.color leads to the expected behavior: the widget becomes ...


Python . Select transparent colour or no colour for lines / points etc

How do I plot a line of transparent colour or no colour ? For example : plot(x,y,color='transparent') or plot(x,y,color='None') BTW , color='white' is not what I'm looking for . Thanks in advance invisible computer people .


Python PIL: Blend transparent image onto another

I need to blend an image over another image using Pythons PIL Library. As you can see in the image below, my two source images are A and B. When I do: imageA.paste(imageB, (0, 0), imageB) I get C as a result, but the part at the top of the gray background is now transparent. Image D is what I get when I put B over A in Photoshop and is what I need to achieve with PIL. What ...


python - How do you make an image transparent over a period of time?

I am currently trying to display a death screen when the player dies in my game, but rather than it just popping up I want it to gradually becoming less and less transparent until the opacity is 256. I currently have this line of code below to display the image, but I was wondering how I would go about gradually increasing the opacity over a period of few seconds. screen.blit(pygame.image.load("Images/dead....


python - How to draw a transparent image in pygame?

Consider a chess board, i have a transparent image of queen(queen.png) of size 70x70 and i want to display it over a black rectangle. Code: BLACK=(0,0,0) queen = pygame.image.load('queen.png') pygame.draw.rect(DISPLAYSURF, BLACK, (10, 10, 70, 70)) DISPLAYSURF.blit(queen, (10, 10)) Error: i am not getting transparent image ie black rectangle is not visible at all, only queen with white bac...


python - How to make a surface with a transparent background in pygame

Can someone give me some example code that creates a surface with a transparent background in pygame?


pygame - Updating part of a surface in python, or transparent surfaces

I have an application written in python that's basically an etch-a-sketch, you move pixels around with WASD and arrow keys and it leaves a trail. However, I want to add a counter for the amount of pixels on the screen. How do I have the counter update without updating the entire surface and pwning the pixel drawings? Alternatively, can I make a surface that's completely transparent except for the text so you can se...


django - python PIL - background displayed opaque instead of transparent

I want to generate 32x32 sized thumbnails from uploaded images (actually avatars). To prevent a thumbnail from being smaller than that size, I want to create a transparent 32x32 background and paste the thumbnail on it. The code below tries to do so. However, the avatar is displayed on a black and opaque background; I lose transparency information somewhere through the process. Where am I doing wrong?...


python - Transparent FrameBuffer background in OpenGL

I want to use glClear and glClearColor to fill a frame buffer with a colour including alpha transparency. However the framebuffer always renders as opaque when binded to a texture which is rendered to the screen. I want everything which is rendered to the framebuffer to kept their transparency. I just want to change the background. See the following code: def create_texture(surface): surface...


How do I create a Status Icon / System Tray Icon with custom text and transparent background using Python and GTK?

Here is the code that I have so far to define the icon: icon_bg = gtk.gdk.pixbuf_new_from_file('gmail.png') w, h = icon_bg.get_width(), icon_bg.get_height() cmap = gtk.gdk.Colormap(gtk.gdk.visual_get_system(), False) drawable = gtk.gdk.Pixmap(None, w, h, 24) drawable.set_colormap = cmap gc = drawable.new_gc() drawable.draw_pixbuf(gc, icon_bg, 0, 0, 0, 0, w, h) drawn_icon = gtk.gdk.Pixbuf(gtk.gdk.COLORSPAC...


python - How to make cStringIO transparent to another function that expects a real local file

I came up with the following problem: CODE A works right now.. I am saving a png file called chart.png locally, and then I am loading it into the proprietary function (which I do not have access). However, in CODE B, am trying to use cStringIO.StringIO() so that I do not have to write the file "chart.png" to the disk. But I cannot find a way to pass it to the pproprietaryfunction because it is expecting a real fil...


python - PIL: How to make area transparent in PNG?

I've been using PIL to crop Images, now I also want to make certain rectangular areas transparent, say from PIL import Image im = Image.open("sample.png") transparent_area = (50,80,100,200) ...


python - Matplotlib transparent line plots

I am plotting two similar trajectories in matplotlib and I'd like to plot each of the lines with partial transparency so that the red (plotted second) doesn't obscure the blue. EDIT: Here's the image with transparent lines.


python - Transparent 3D bar graphs

I would like to generate 3D bar graphs with transparent surfaces so that I can see what is going on behind tall bars. The mplot3d API docs say that keywords are allowed for the bar3d function. I pass all the required parameters but can only output graphs with solid surfaces.


python - Partially transparent scatter plot, but with a solid color bar

In Python, with Matplotlib, how to simply do a scatter plot with transparency (alpha < 1), but with a color bar that represents their color value, but has alpha = 1? Here is what one gets, with from pylab import *; scatter(range(10), arange(0, 100, 10), c=range(10), alpha=0.2); color_bar = colorbar(): How can the color ba...






Still can't find your answer? Check out these communities...



PySlackers | Full Stack Python | NHS Python | Pythonist Cafe | Hacker Earth | Discord Python



top