I would like to know if its possible with python pil to cut text out and make the picture transparent. Here is an example to explain it better. Picture 1.png is a circle.
Picture 2.png is text.png that just says thanks. What I would like to do is put picture2 into picture1 and cut that out so it becomes transparent. so it should look like this picture3.png
This is what I have so far but it only pasting the image. I am new to PIl. I am not sure how to tell python I want this cut out like that.
img = Image.open('picture1.png').convert("RGBA")
bg_w, bg_h = img.size
layer = Image.open('picture2.png').convert("RGBA") # this file is the transparent one
img_w, img_h = layer.size
offset = ((bg_w - img_w)/2, (bg_h - img_h) / 3)
img.paste(layer, offset, mask=layer)
img.save('Picture3.png')
I figured it out. Here is the answer:
from PIL import Image, ImageDraw
img = Image.open('circle.png').convert("RGBA")
layer = Image.open('Thanks.png').convert("RGBA") # this file is the transparent one
img.paste(layer, mask=layer)
img.save('Picture3White.png')
img = Image.open('Picture3White.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("Transparent.png", "PNG")
Related
Actually I am doing some experiments with python but I came to the point where I want to add an image on a transparent GIF with dimensions of the image.
I am getting an error of bad transparency mask.
Code -
from PIL import Image, ImageSequence
background = Image.open(...)
animated_gif = Image.open(...)
frames = []
for frame in ImageSequence.Iterator(animated_gif):
frame = frame.copy()
frame.paste(background, mask=bg)
frames.append(frame)
frames[0].save('output.gif', save_all=True, append_images=frames[1:])
Here is the answer of my question...
from PIL import Image, ImageSequence
background = Image.open("img.jpg")
animated_gif = Image.open("GIFF.gif")
frames = []
for frame in ImageSequence.Iterator(animated_gif):
output = background.copy()
frame_px = frame.load()
output_px = output.load()
transparent_foreground = frame.convert('RGBA')
transparent_foreground_px = transparent_foreground.load()
for x in range(frame.width):
for y in range(frame.height):
if frame_px[x, y] in (frame.info["background"], frame.info["transparency"]):
continue
output_px[x, y] = transparent_foreground_px[x, y]
frames.append(output)
frames[0].save('output.gif', save_all=True, append_images=frames[1:-1])
import Image
background = Image.open("test1.png")
foreground = Image.open("test2.png")
background.paste(foreground, (0, 0), foreground)
background.show()
I will explain the parameters for .paste() function.
first - the image to paste
second - coordinates
third - This indicates a mask that will be used to paste the image. If you pass a image with transparency, then the alpha channel is used as mask.
If this is not what you want to do, please add a comment for your need.
Hello I am trying to give a black and white photo a colored background, first I convert pixels to either white or black then I would like to replace the white with a specific color (148,105,39) then save it.
here is the image I am working on
here is my function so far it doesn't change the color (also doesn't give error)
def binary_image(img):
gray = img.convert('L')
bw = gray.point(lambda x: 0 if x<128 else 255, '1')
img = bw.convert('RGBA')
pixdata = img.load()
for y in range(img.size[1]):
for x in range(img.size[0]):
if pixdata[x, y] == (255, 255, 255, 255): #white color
pixdata[x, y] = (148,105,39,255)
return full_4
img=Image.open('logo.jpg')
bg_fps = binary_image(img)
bg_fps.show()
You really, really want to avoid for loops with PIL images, try to use Numpy and it will be easier to write and faster to run.
I would do it like this:
import numpy as np
from PIL import image
# Load image and ensure greyscale
im = Image.open('U6IhW.jpg').convert('L')
# Make Numpy version for fast, efficient access
npim = np.array(im)
# Make solid black RGB output image, same width and height but 3 channels (RGB)
res = np.zeros((npim.shape[0],npim.shape[1],3), dtype=np.uint8)
# Anywhere the grey image is >127, make result image new colour
res[npim>127] = [148,105,39]
# Convert back to PIL Image and save to disk
Image.fromarray(res).save('result.png')
You could equally use PIL's ImageOps.colorize() like this:
from PIL import Image, ImageOps
im = Image.open('U6IhW.jpg').convert('L')
# Threshold to pure black and white
bw = im.point(lambda x: 0 if x<128 else 255)
# Colorize
result = ImageOps.colorize(bw, (0,0,0), (148,105,39))
I have no background in image-processing. I am interested in getting the difference between these two images.
After writing the following code :
from PIL import Image
from PIL import ImageChops
im1 = Image.open("1.png")
im2 = Image.open("2.png")
diff = ImageChops.difference(im2, im1)
diff.save("diff.png")
I get this output :-
I am looking for some customisations here :
1) I want to label the differences in output in different colours. Things from the 1.png and 2.png should have a different colours.
2) background should be white.
3) I want my output to have axises and axis labels. Would it be possible somehow ?
You probably can't do this with the high-level difference method, but it's quite easy if you compare the images pixel by pixel yourself. Quick attempt:
Code:
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
im1 = Image.open("im1.jpeg").convert('1') # binary image for pixel evaluation
rgb1 = Image.open("im1.jpeg").convert('RGB') # RGB image for border copy
p1 = im1.load()
prgb1 = rgb1.load()
im2 = Image.open("im2.jpeg").convert('1') # binary image for pixel evaluation
p2 = im2.load()
width = im1.size[0]
height = im1.size[1]
imd = Image.new("RGB", im1.size)
draw = ImageDraw.Draw(imd)
dest = imd.load()
fnt = ImageFont.truetype('/System/Library/Fonts/OpenSans-Regular.ttf', 20)
for i in range(0, width):
for j in range(0, height):
# border region: just copy pixels from RGB image 1
if j < 30 or j > 538 or i < 170 or i > 650:
dest[i,j] = prgb1[i,j]
# pixel is only set in im1, make red
elif p1[i,j] == 255 and p2[i,j] == 0:
dest[i,j] = (255,0,0)
# pixel is only set in im2, make blue
elif p1[i,j] == 0 and p2[i,j] == 255:
dest[i,j] = (0,0,255)
# unchanged pixel/background: make white
else:
dest[i,j] = (255,255,255)
draw.text((700, 50),"blue", "blue", font=fnt)
draw.text((700, 20),"red", "red", font=fnt)
imd.show()
imd.save("diff.png")
This assumes that the images are the same size and have identical axes.
I used this following code from another stackoverflow post
from PIL import Image as image
img = image.open('output.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")
to transform my png's background to transparent. However, when I tried to add some shapes in powerpoint underneath the transparent image, it still has some residual white pixels left. Anyone know how to solve this?
Those pixels are not exactly "white". The color you are testing against and removing from the image is, with its value of #FFFFFF. But those slanted lines are heavily antialiased, "fading" from the pure white of the background to the pure color of the center of the lines.
This can be seen when zooming in just a teeny bit:
You can lower the threshold of when to make a pixel entirely transparent:
if item[0] > 240 and item[1] > 240 and item[2] > 240:
newData.append((255, 255, 255, 0))
else:
newData.append(item)
but no matter how much you do this, you will always end up with either visibly lighter pixels around the lines, or – when only matching the center "line" color exactly – with disconnected pixels, not resembling the original lines anymore.
But there is no reason to use a Yes/No mask with PNG images! PNG supports full 8-bit transparency, and so you can make the 'solid' center lines fully opaque, the solid white fully transparent, and have the gradually darkening pixels fade between these values.
This works best if you know the exact original color that was used to draw the lines with. Measuring it with Adobe PhotoShop, I get something like #818695. Plugging in these values into your program and adjusting the 'tint' (towards white) to transparency, flattened towards the full possible range, I suggest this code:
from PIL import Image as image
img = image.open('input.png')
img = img.convert("RGBA")
datas = img.getdata()
retain = (0x81,0x86,0x95)
retain_gray = (39*retain[0] + 50*retain[1] + 11*retain[2])
newData = []
for item in datas:
if item[0] > retain[0] and item[1] > retain[1] and item[2] > retain[2]:
# convert to grayscale
val = 39*item[0] + 50*item[1] + 11*item[2]
# invert
val = 25500 - val;
# difference with 'retain'
val = retain_gray - val
# scale down
val = 255*val/retain_gray
# invert to act as transparency
transp = 255-val
# apply transparency to original 'full' color value
newData.append((retain[0], retain[1], retain[2], transp ))
else:
newData.append(item)
img.putdata(newData)
img.save("output.png", "PNG")
print "done"
What it essentially does is converting the input image to grayscale, scaling it (because the scale from darkest to lightest should be in the full transparency range of 0..255), then using this as the 'transparent' byte. The result is way better than your on/off approach:
I am using the PIL to take an image with a black background and make a mask out of it. What I want the program to do is iterate through all the pixels in the image and if the pixel is black make it white and if it is any other color make it black, but I am not sure how to appropriately compare pixel values to determine what to do with the pixel.
Here is my code so far which creates an all black image.
import os, sys
import Image
filename = "C:\Users\pdiffley\Dropbox\C++2\Code\Test\BallSpriteImage.bmp"
height = 50
width = 50
im = Image.open(filename)
im = im.load()
i = 0
j = 0
while i<height:
while j<width:
if im[j,i] == (0,0,0):
im[j,i] = (255,255,255)
else:
im[j,i] = (0,0,0)
j = j+1
i = i+1
mask = Image.new('RGB', (width, height))
newfile = filename.partition('.')
newfile = newfile[0] + "Mask.bmp"
mask.save(newfile)
I believe the problem is in the if statement comparing the im[j,i] to the RGB value (0,0,0) which always evaluates to false. What is the correct way to compare the pixel?
The pixel data comparison is correct. But there are two problems with the logic:
When you are finished with a row, you should reset j to 0.
You are modifying the object "im", but writing "mask".
This should work (as long as you have no alpha channel - as andrewdski pointed out):
img = Image.open(filename)
im = img.load()
i = 0
while i<height:
j = 0
while j<width:
if im[j,i] == (0,0,0):
im[j,i] = (255,255,255)
else:
im[j,i] = (0,0,0)
j = j+1
i = i+1
newfile = filename.partition('.')
newfile = newfile[0] + "Mask.png"
img.save(newfile)
Here's how I'd rewrite it, which avoids a pixel index reset problem by using for loops, writes the data to a separate mask image rather than back onto the source, and removes the hardcoded image size. I also added an r prefix to the filename string to handle the backslashes in it.
import os, sys
import Image
BLACK = (0,0,0)
WHITE = (255, 255, 255)
filename = r"C:\Users\pdiffley\Dropbox\C++2\Code\Test\BallSpriteImage.bmp"
img = Image.open(filename)
width, height = img.size
im = img.load()
mask = Image.new('RGB', (width, height))
msk = mask.load()
for y in xrange(height):
for x in xrange(width):
if im[x,y] == BLACK:
msk[x,y] = WHITE
else: # not really needed since mask's initial color is black
msk[x,y] = BLACK
newfilename = filename.partition('.')
newfilename = newfilename[0] + "Mask.bmp"
mask.save(newfilename)
The following function uses the .point method and works on separately on each band of the image:
CVT_TABLE= (255,) + 255 * (0,)
def do_convert(img):
return img.point(CVT_TABLE * len(img.getbands()))
Working separately on each band means that a picture like this:
will be converted into this:
However, you can get almost what you want if you convert the image to mode "L" first:
CVT_TABLE= (255,) + 255 * (0,)
def do_convert(img):
return img.convert("L").point(CVT_TABLE)
producing the following result:
The only drawback is that a few darkest colors (e.g. #000001, the darkest blue possible) will probably be converted to black by the mode conversion.