How to Generate Circular Thumbnails with Pil

How do I generate circular thumbnails with PIL?

The easiest way to do it is by using masks. Create a black and white mask with any shape you want. And use putalpha to put that shape as an alpha layer:

from PIL import Image, ImageOps

mask = Image.open('mask.png').convert('L')
im = Image.open('image.png')

output = ImageOps.fit(im, mask.size, centering=(0.5, 0.5))
output.putalpha(mask)

output.save('output.png')

Here is the mask I used:

alt text


If you want the thumbnail size to be variable you can use ImageDraw and draw the mask:

from PIL import Image, ImageOps, ImageDraw

size = (128, 128)
mask = Image.new('L', size, 0)
draw = ImageDraw.Draw(mask)
draw.ellipse((0, 0) + size, fill=255)

im = Image.open('image.jpg')

output = ImageOps.fit(im, mask.size, centering=(0.5, 0.5))
output.putalpha(mask)

output.save('output.png')

If you want the output in GIF then you need to use the paste function instead of putalpha:

from PIL import Image, ImageOps, ImageDraw

size = (128, 128)
mask = Image.new('L', size, 255)
draw = ImageDraw.Draw(mask)
draw.ellipse((0, 0) + size, fill=0)

im = Image.open('image.jpg')

output = ImageOps.fit(im, mask.size, centering=(0.5, 0.5))
output.paste(0, mask=mask)
output.convert('P', palette=Image.ADAPTIVE)

output.save('output.gif', transparency=0)

Note that I did the following changes:

  • The mask is now inverted. The white
    was replaced with black and vice versa.
  • I'm converting into 'P' with an 'adaptive' palette. Otherwise, PIL will only use web-safe colors and the result will look bad.
  • I'm adding transparency info to the image.

Please note: There is a big issue with this approach. If the GIF image contained black parts, all of them will become transparent as well. You can work around this by choosing another color for the transparency.
I would strongly advise you to use PNG format for this. But if you can't then that is the best you could do.

How to create a circular thumbnail using python pillow and overlap on background image

Here's an example using your images. Dimensions are hardcoded in the example, but you can easily replace them with calculations. avatar.jpg and background.jpg are images in your post saved as is.

Here's a link to github repo for this example : python_pillow_circular_thumbnail

from PIL import Image, ImageOps, ImageDraw

im = Image.open('avatar.jpg')
im = im.resize((120, 120));
bigsize = (im.size[0] * 3, im.size[1] * 3)
mask = Image.new('L', bigsize, 0)
draw = ImageDraw.Draw(mask)
draw.ellipse((0, 0) + bigsize, fill=255)
mask = mask.resize(im.size, Image.ANTIALIAS)
im.putalpha(mask)

output = ImageOps.fit(im, mask.size, centering=(0.5, 0.5))
output.putalpha(mask)
output.save('output.png')

background = Image.open('back.jpg')
background.paste(im, (150, 10), im)
background.save('overlap.png')

output.png:
Sample Image

overlap.png:
Sample Image

Crop part of this code is borrowed form this answer.

Hope it helps!

Creating Circular thumbnails in PIL on a certain position

The key here is the 'mask' argument of Image.paste - https://pillow.readthedocs.io/en/5.3.x/reference/Image.html#PIL.Image.Image.paste - 'If a mask is given, this method updates only the regions indicated by the mask.'

So that allows us to create an image of a circle to only paste the relevant part of the profile picture.

from PIL import Image, ImageDraw

background = Image.open('input.jpg')
im = Image.open('profilepicture.png')

# Scale the image to be the size of the circle
im = im.resize((1024, 1024), Image.ANTIALIAS)

# Create the circle mask
mask = Image.new('L', im.size)
draw = ImageDraw.Draw(mask)
draw.ellipse((0, 0) + im.size, fill=255)

background.paste(im, (410, 1104), mask)
background.save('output.png')

To improve image quality of made circular thumbnails

Try using a different downsampling filter, by adding, for example, method=Image.LANCZOS to the .fit call.

What's the most simple way to crop a circle thumbnail from an image?

first: HoughCircles is used to detect circles on image, not to crop it.


You can't have circle image. Image is always rectangle but some of pixels can be transparent (alpha channel in RGBA) and programs will not display them.

So you can first crop image to have square and later add alpha channel with information which pixels should be visible. And here you can use mask with white circle on black background. At the end you have to save it as png or tiff because jpg can't keep alpha channel.


I use module PIL/pillow for this.

I crop square region in center of image but you can use different coordinates for this.

Next I create grayscale image with the same size and black background and draw white circle/ellipse.

Finally I add this image as alpha channel to cropped image and save it as png.

from PIL import Image, ImageDraw

filename = 'dog.jpg'

# load image
img = Image.open(filename)

# crop image
width, height = img.size
x = (width - height)//2
img_cropped = img.crop((x, 0, x+height, height))

# create grayscale image with white circle (255) on black background (0)
mask = Image.new('L', img_cropped.size)
mask_draw = ImageDraw.Draw(mask)
width, height = img_cropped.size
mask_draw.ellipse((0, 0, width, height), fill=255)
#mask.show()

# add mask as alpha channel
img_cropped.putalpha(mask)

# save as png which keeps alpha channel
img_cropped.save('dog_circle.png')

img_cropped.show()

Result

Sample Image


BTW:

In mask you can use values from 0 to 255 and different pixels may have different transparency - some of them can be half-transparent to make smooth border.

If you want to use it in HTML on own page then you don't have to create circle image because web browser can round corners of image and display it as circle. You have to use CSS for this.


EDIT: Example with more circles on mask.

Sample Image

from PIL import Image, ImageDraw

filename = 'dog.jpg'

# load image
img = Image.open(filename)

# crop image
width, height = img.size
x = (width - height)//2
img_cropped = img.crop((x, 0, x+height, height))

# create grayscale image with white circle (255) on black background (0)
mask = Image.new('L', img_cropped.size)
mask_draw = ImageDraw.Draw(mask)
width, height = img_cropped.size
mask_draw.ellipse((50, 50, width-50, height-50), fill=255)
mask_draw.ellipse((0, 0, 250, 250), fill=255)
mask_draw.ellipse((width-250, 0, width, 250), fill=255)

# add mask as alpha channel
img_cropped.putalpha(mask)

# save as png which keeps alpha channel
img_cropped.save('dog_2.png')

img_cropped.show()

able to create thumbnails with PIL but my multiprocessing code does not create PIL thumbnails

the code "pool.map(create_thumbnail, _path)" needs to be pool.map(create_thumbnail(_path), _path) to create the thumbnails

Create circular image PIL Tkinter

Adapting from this answer (you need ImageOps and ImageDraw imported from PIL), you can create a circular mask for your zoomed image using:

def create_mask(self):
self.mask = Image.new('L', (200,200), 0)
draw = ImageDraw.Draw(self.mask)
draw.ellipse((0, 0) + self.mask.size, fill=255)

Then, you have to apply the mask in your crop function:

output = ImageOps.fit(tmp, self.mask.size, centering=(0.5, 0.5))
output.putalpha(self.mask)
self.zimg = ImageTk.PhotoImage(output)

For reference, a complete working example would be something like this:

import Tkinter as tk
from PIL import Image, ImageTk, ImageOps, ImageDraw

class App():
def __init__(self, master, image_path):
self.orig_img = Image.open(image_path)
self.tk_img = ImageTk.PhotoImage(self.orig_img)

w, h = self.orig_img.size
self.canvas = tk.Canvas(master, width=w, height=h)
self.canvas.pack()

self.canvas.create_image(0, 0, image=self.tk_img, anchor='nw')
self.canvas.bind_all("<MouseWheel>", self.zoom)
self.canvas.bind_all("<Motion>", self.crop)

self.create_mask()
self.zoomValue = 0
self.zimg_id = None

def create_mask(self):
self.mask = Image.new('L', (200,200), 0)
draw = ImageDraw.Draw(self.mask)
draw.ellipse((0, 0) + self.mask.size, fill=255)

def zoom(self, event):
if(event.delta > 0):
if self.zoomValue != 4 : self.zoomValue += 1
elif(event.delta < 0):
if self.zoomValue != 0 : self.zoomValue -= 1
self.crop(event)

def crop(self, event):
if self.zimg_id: self.canvas.delete(self.zimg_id)

if (self.zoomValue) != 0:
x, y = event.x, event.y
if self.zoomValue == 1:
tmp = self.orig_img.crop((x-45, y-30, x+45, y+30))
elif self.zoomValue == 2:
tmp = self.orig_img.crop((x-30, y-20, x+30, y+20))
elif self.zoomValue == 3:
tmp = self.orig_img.crop((x-15, y-10, x+15, y+10))
elif self.zoomValue == 4:
tmp = self.orig_img.crop((x-6, y-4, x+6, y+4))

output = ImageOps.fit(tmp, self.mask.size, centering=(0.5, 0.5))
output.putalpha(self.mask)
self.zimg = ImageTk.PhotoImage(output)
self.zimg_id = self.canvas.create_image(event.x, event.y, image=self.zimg)

root = tk.Tk()
App(root, r'C:\Users\user\Desktop\bg.gif')
root.mainloop()

how to fill a part of a circle using PIL?

It's been a while since I used PIL, but in various other graphics libraries, there's often an aliasing problem when drawing arcs.

Have you tried enabling anti-aliasing or drawing with thicker lines?

[Edit] Having a quick look over the PIL library, I think you're right about line width etc.

Sounds like the easiest thing to do here is to build up a polygon which covers each area. So a pair of points at each end and then a load round the middle to stop the edges looking jagged. Does that make sense?

Draw circle with PIL (Old ones doesn't works)

You can use either ImageDraw.arc() or ImageDraw.ellipse.

from PIL import Image, ImageDraw

# Image size
W, H = 100, 100

# Bounding box points
X0 = int(W / 4)
X1 = int(X0 * 3)
Y0 = int(H / 4)
Y1 = int(X0 * 3)

# Bounding box
bbox = [X0, Y0, X1, Y1]

# Set up
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)

# Draw a circle
draw.arc(bbox, 0, 360)

# Show the image
im.show()

a white circle on a black background

Or:

# Draw a circle
draw.ellipse(bbox)

another white circle on a black background



Related Topics



Leave a reply



Submit