pythonpython-imaging-libraryimage-editingimage-generation

Python PIL circle crop image from corner


I have an existing PNG image that I want to crop to a circle, but I want the circle centered at the top left corner of the image. Then, the cropped image needs to be posted onto another image I've generated. I've tried doing this with putalpha, using this as a guide: https://note.nkmk.me/en/python-pillow-putalpha/. The error I am currently getting is that the image is in an illegal mode, even though they should in both mode and size. The code that gives me this error:

width = 853
fullWidth = 1280
fullHeight = 720
height = 853

img = Image.open(f"path/to/image")
img = img.convert("RGB")
img.resize((width ,width ))

# Generate ellipse
ellipseMask = Image.new(mode="RGB", size=(width ,width ), color="#000000")
drawMask = ImageDraw.Draw(ellipseMask)
drawMask.ellipse((-width , -width , width , width), fill=255)
img.putalpha(ellipseMask) # error occurs here

# Generate full image
fullimage = Image.new(mode="RGBA", size=(fullWidth,fullHeight), color="#424040")
drawImage = ImageDraw.Draw(fullimage)
ellipseRadius = width+50
drawImage.ellipse((-ellipseRadius, -ellipseRadius, ellipseRadius, ellipseRadius), fill=(255,255,255))
fullimage.paste(img, (0, 0))

fullimage.save(f"path/to/save")

I've also tried a different approach with this page https://www.geeksforgeeks.org/cropping-an-image-in-a-circular-way-using-python/ as a guide, which also got me nowhere. This code didn't raise any errors, but rather just gave me a result with the image that should be cropped circularly not being cropped

from PIL import Image, ImageDraw
import numpy as np

width = 853
fullWidth = 1280
fullHeight = 720
height = 853

img = Image.open(f"path/to/image")
img.resize((width,width))

height,width = img.size
lum_img = Image.new('L', [height,width] , 0)
        
draw = ImageDraw.Draw(lum_img)
draw.pieslice([(0,0), (height,width)], 0, 360, fill = 255, outline = "white")
img_arr =np.array(img)
lum_img_arr =np.array(lum_img)
final_img_arr = np.dstack((img_arr,lum_img_arr))

circularCrop = Image.fromarray(final_img_arr)

fullThumbnail = Image.new(mode="RGBA", size=(1280,720), color="#424040")
drawThumbnail = ImageDraw.Draw(fullThumbnail)
ellipseRadius = width+50
drawThumbnail.ellipse((-ellipseRadius, -ellipseRadius, ellipseRadius, ellipseRadius), fill=(255,255,255))
fullThumbnail.paste(circularCrop, (0, 0))

fullThumbnail.save(f"path/to/save")

I'd rather use PIL for this, as that is the library that I use everywhere else, but I can't figure out if this is even possible, as I cant find an exact example of this anywhere.


Solution

  • The Image.resize method doesn't modify the image, it returns a new image that has been resized. If you keep this instead your code mostly basically works, you just need the alpha channel to be a single channel (i.e. a mode of L).

    Putting this together I get:

    from PIL import Image, ImageDraw
    
    size = 500
    
    img = Image.open("input.png")
    img = img.resize((size, size))
    
    mask = Image.new(mode="L", size=(size, size), color=0)
    draw = ImageDraw.Draw(mask)
    draw.ellipse((-size, -size, size, size), fill=255)
    
    img.putalpha(mask)
    
    img.save('output.png')