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.
Related
I want to create frames with a gradient colour change from black to white.For instance first frame should be black(0,0,0), then (50,0,0), then (50,50,0).....so on all the way to (255,255,255). But im only able to produce a single frame (0,0,0) with this code. Also is it possible to create a sequentially numbered jpg file for each image?
import cv2
import numpy as np
def create_blank(width, height, rgb_color=(0, 0, 0)):
"""Create new image(numpy array) filled with certain color in RGB"""
# Create black blank image
image = np.zeros((height, width, 3), np.uint8)
# Since OpenCV uses BGR, convert the color first
color = tuple(reversed(rgb_color))
# Fill image with color
image[:] = color
return image
for i in range(0,50,255):
for j in range(0,50,255):
for k in range(0,50,255):
image=create_blank(width, height, rgb_color=(i,j,k))
cv2.imwrite('img.jpg',image)
import cv2
import numpy as np
def create_gradient(width, height):
image = np.zeros((1,width,3), dtype='uint8') # starting image with one row black pixels
for row in range(height):
pixel_value = int(row*255/height)
new_row = np.ones((1,width,3), dtype='uint8')*pixel_value # creating new row
image = np.concatenate((image, new_row)) # concatenate images
return image
img = create_gradient(200,500)
cv2.imshow('gradient', img)
cv2.waitKey(0)
If you want switch from vertical to horizontal just swap heigh with width.
To save images sequentialy you should add cv2.imwrite in for loop.
Return from code above looks like this:
I have two images.
Image 1:
Image 2:
Both the images have the same resolution. I want to overlay the second image on the first image.
This is what I tried:
from PIL import Image
img1 = Image.open("D:\\obj1__0.png")
img1 = img1.convert("RGBA")
img1 = img1.resize((640,360))
img2 = Image.open('D:\\Renderforest_Watermark.png')
img2 = img2.convert("RGBA")
img2 = img2.resize((640,360))
new_img = Image.blend(img1,img2,0.7)
new_img.save("D:\\Final.png")
Output:
My Question: How do I overlay image 2 on image 1 such that only the watermark is overlaid?
P.S: I already searched for this on Stack Overflow, but couldn't find any answers.
Your overlay image is incorrectly formed. It should be transparent where it is white. It has no transparency, so it isn't see-through. I can only suggest to make a synthetic alpha channel by guessing that it should be transparent where the overlay image is white:
#!/usr/bin/env python3
from PIL import Image
# Set a common size
size = (640, 360)
# Load background and overlay, removing the pointless alpha channel and resizing to a common size
bg = Image.open('background.png').convert('RGB').resize(size)
overlay = Image.open('overlay.png').convert('RGB').resize(size)
# Try and invent a mask by making white pixels transparent
mask = overlay.convert('L')
mask = mask.point(lambda p: 255 if p < 225 else 0)
# Paste overlay onto background only where the mask is, then save
bg.paste(overlay, None, mask)
bg.save('result.png')
If your image did have an alpha channel, you would avoid deleting the original alpha channel and open it like this instead:
overlay = Image.open('overlay.png').resize(size)
then delete these lines:
# Try and invent a mask by making white pixels transparent
mask = overlay.convert('L')
mask = mask.point(lambda p: 255 if p < 225 else 0)
then change the line after the above to:
# Paste overlay onto background only where the mask is, then save
bg.paste(overlay, None, overlay)
Keywords: Image processing, PIL, Pillow, overlay, watermark, transparent, alpha.
from PIL import Image
from PIL import ImageDraw
from io import BytesIO
from urllib.request import urlopen
url = "https://i.ytimg.com/vi/W4qijIdAPZA/maxresdefault.jpg"
file = BytesIO(urlopen(url).read())
img = Image.open(file)
img = img.convert("RGBA")
draw = ImageDraw.Draw(img, "RGBA")
draw.rectangle(((0, 00), (img.size[0], img.size[1])), fill=(0,0,0,127))
img.save('dark-cat.jpg')
This is giving me a giant black square. I want it to be a semi transparent black square with a cat. Any Ideas?
Sorry, the comment I made about it being a bug was incorrect, so...
You can do it by creating a temporary image and using Image.alpha_composite() as shown in the code below. Note that it supports semi-transparent squares other than black.
from PIL import Image, ImageDraw
from io import BytesIO
from urllib.request import urlopen
TINT_COLOR = (0, 0, 0) # Black
TRANSPARENCY = .25 # Degree of transparency, 0-100%
OPACITY = int(255 * TRANSPARENCY)
url = "https://i.ytimg.com/vi/W4qijIdAPZA/maxresdefault.jpg"
with BytesIO(urlopen(url).read()) as file:
img = Image.open(file)
img = img.convert("RGBA")
# Determine extent of the largest possible square centered on the image.
# and the image's shorter dimension.
if img.size[0] > img.size[1]:
shorter = img.size[1]
llx, lly = (img.size[0]-img.size[1]) // 2 , 0
else:
shorter = img.size[0]
llx, lly = 0, (img.size[1]-img.size[0]) // 2
# Calculate upper point + 1 because second point needs to be just outside the
# drawn rectangle when drawing rectangles.
urx, ury = llx+shorter+1, lly+shorter+1
# Make a blank image the same size as the image for the rectangle, initialized
# to a fully transparent (0% opaque) version of the tint color, then draw a
# semi-transparent version of the square on it.
overlay = Image.new('RGBA', img.size, TINT_COLOR+(0,))
draw = ImageDraw.Draw(overlay) # Create a context for drawing things on it.
draw.rectangle(((llx, lly), (urx, ury)), fill=TINT_COLOR+(OPACITY,))
# Alpha composite these two images together to obtain the desired result.
img = Image.alpha_composite(img, overlay)
img = img.convert("RGB") # Remove alpha for saving in jpg format.
img.save('dark-cat.jpg')
img.show()
Here's the result of applying it to your test image:
Given that I keep coming back to this issue whenever I want to draw a transparent rectangle with PIL, I decided to give an update.
Your code is pretty much working for me if I just change one thing: Save the image in the PNG format instead of JPEG.
So when I'm running
from io import BytesIO
from urllib.request import urlopen
from PIL import Image
from PIL import ImageDraw
url = "https://i.ytimg.com/vi/W4qijIdAPZA/maxresdefault.jpg"
file = BytesIO(urlopen(url).read())
img = Image.open(file)
draw = ImageDraw.Draw(img, "RGBA")
draw.rectangle(((280, 10), (1010, 706)), fill=(200, 100, 0, 127))
draw.rectangle(((280, 10), (1010, 706)), outline=(0, 0, 0, 127), width=3)
img.save('orange-cat.png')
I get this wonderful image:
If you just want to dim the entire image, there's a simpler way:
img = Image.eval(img, lambda x: x/2)
How do I create a new image with a black background and paste another image on top of it?
What I am looking to do is turn some 128x128 transparent icons into 75x75 black background icons.
Doesnt work ...
import Image
theFile = "/home/xxxxxx/Pictures/xxxxxx_128.png"
img = Image.open(theFile)
newImage = Image.new(img.mode, img.size, "black")
newImage.paste(img)
newImage.resize((75,75))
newImage.save("out.png")
print "Done"
Thanks!
The resize method returns a new image object, rather than changing the existing one. Also, you should resize the image before pasting it. The following works for me:
import Image
theFile = "foo.png"
img = Image.open(theFile)
resized = img.resize((75,75))
r, g, b, alpha = resized.split()
newImage = Image.new(resized.mode, resized.size, "black")
newImage.paste(resized, mask=alpha)
newImage.save("out.png")
print "Done"
I found an example of this split + mask technique from this blog post.
Example input:
Output:
I need to convert series of images drawn as white on black background letters to images where white and black are inverted (as negative). How can I achieve this using PIL?
Try the following from the docs: https://pillow.readthedocs.io/en/stable/reference/ImageOps.html
from PIL import Image
import PIL.ImageOps
image = Image.open('your_image.png')
inverted_image = PIL.ImageOps.invert(image)
inverted_image.save('new_name.png')
Note: "The ImageOps module contains a number of 'ready-made' image processing operations. This module is somewhat experimental, and most operators only work on L and RGB images."
If the image is RGBA transparent this will fail... This should work though:
from PIL import Image
import PIL.ImageOps
image = Image.open('your_image.png')
if image.mode == 'RGBA':
r,g,b,a = image.split()
rgb_image = Image.merge('RGB', (r,g,b))
inverted_image = PIL.ImageOps.invert(rgb_image)
r2,g2,b2 = inverted_image.split()
final_transparent_image = Image.merge('RGBA', (r2,g2,b2,a))
final_transparent_image.save('new_file.png')
else:
inverted_image = PIL.ImageOps.invert(image)
inverted_image.save('new_name.png')
For anyone working with an image in "1" mode (i.e., 1-bit pixels, black and white, stored with one pixel per byte -- see docs), you need to convert it into "L" mode before calling PIL.ImageOps.invert.
Thus:
im = im.convert('L')
im = ImageOps.invert(im)
im = im.convert('1')
now ImageOps must be:
PIL.ImageChops.invert(PIL.Image.open(imagepath))
note that this works for me in python 3.8.5
In case someone is inverting a CMYK image, the current implementations of PIL and Pillow don't seem to support this and throw an error. You can, however, easily circumvent this problem by inverting your image's individual bands using this handy function (essentially an extension of Greg Sadetsky's post above):
def CMYKInvert(img) :
return Image.merge(img.mode, [ImageOps.invert(b.convert('L')) for b in img.split()])
Of course ImageOps does its job well, but unfortunately it can't work with some modes like 'RGBA'. This code will solve this problem.
def invert(image: Image.Image) -> Image.Image:
drawer = ImageDraw.Draw(image)
pixels = image.load()
for x in range(image.size[0]):
for y in range(image.size[1]):
data = pixels[x, y]
if data != (0, 0, 0, 0) and isinstance(data, tuple):
drawer.point((x, y), (255 - data[0], 255 - data[1], 255 - data[2], data[3]))
return image
from PIL import Image
img = Image.open("archive.extension")
pixels = img.load()
for i in range(img.size[0]):
for j in range(img.size[1]):
x,y,z = pixels[i,j][0],pixels[i,j][1],pixels[i,j][2]
x,y,z = abs(x-255), abs(y-255), abs(z-255)
pixels[i,j] = (x,y,z)
img.show()
`