如何用PIL实现以图片左上角为圆心的圆形裁剪及合成?
使用PIL实现以左上角为圆心的圆形裁剪并合成图片
问题描述
我需要将一张现有PNG图片以左上角为圆心裁剪为圆形,再把裁剪后的图片贴到另一张已生成的图片上。尝试用putalpha方法时触发了非法模式错误;另一种方法没报错,但完全没实现圆形裁剪效果,希望用PIL完成这个需求。
尝试的报错代码
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")
尝试的无效果代码
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")
错误原因拆解
第一段代码报错的问题
putalpha只接受**单通道灰度图(模式"L")**当遮罩,但你创建的ellipseMask是RGB模式,直接触发非法模式错误。img.resize()不会修改原图片,必须重新赋值:img = img.resize((width, width)),不然尺寸根本没变化。- 椭圆坐标写错了:
(-width, -width, width, width)会画到画布外面去,以左上角为圆心的话,应该用(0, 0, width, width),刚好覆盖整个正方形画布。
第二段代码无效果的问题
- 同样犯了
resize未赋值的错误,原图片尺寸没改。 height, width = img.size搞反了,PIL的size是(宽, 高),应该写成width, height = img.size,导致后续遮罩尺寸不匹配。paste的时候没传遮罩参数,圆形区域的透明部分被背景色覆盖,看起来就像没裁剪一样。
正确实现代码
from PIL import Image, ImageDraw # 配置参数 crop_size = 853 # 圆形的直径,对应左上角为基准的正方形边长 full_width = 1280 full_height = 720 # 1. 打开并预处理图片 img = Image.open("path/to/image.png") # 调整为正方形(以左上角为起点的区域) img = img.resize((crop_size, crop_size)) # 转为RGBA模式,支持透明通道 img = img.convert("RGBA") # 2. 创建圆形遮罩(必须是单通道灰度图) mask = Image.new("L", (crop_size, crop_size), 0) draw = ImageDraw.Draw(mask) # 绘制以左上角(0,0)为圆心的圆形,坐标范围覆盖整个正方形 draw.ellipse((0, 0, crop_size, crop_size), fill=255) # 3. 给图片应用遮罩,实现圆形裁剪 img.putalpha(mask) # 4. 创建背景图并合成 full_image = Image.new("RGBA", (full_width, full_height), "#424040") draw_full = ImageDraw.Draw(full_image) # 绘制外层白色椭圆(可选) ellipse_radius = crop_size + 50 draw_full.ellipse((0, 0, ellipse_radius, ellipse_radius), fill=(255, 255, 255)) # 粘贴圆形图片,传入遮罩保留透明效果 full_image.paste(img, (0, 0), mask=img) # 保存最终结果 full_image.save("path/to/save.png")
关键要点
- 遮罩必须是模式"L"的单通道灰度图:白色(255)区域显示原图内容,黑色(0)区域变为透明。
- PIL的图片是不可变对象,
resize、crop这类方法必须重新赋值给变量才会生效。 - 使用
paste合成时,一定要传入第三个mask参数,才能保留粘贴图片的透明通道效果。 - 以左上角为圆心画圆时,椭圆的坐标范围要从(0,0)到(crop_size, crop_size),这样画出的圆刚好覆盖左上角的正方形区域。
内容的提问来源于stack exchange,提问作者Thomas Smeman
相关产品推荐
相关产品推荐

