You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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")

错误原因拆解

第一段代码报错的问题

  1. putalpha只接受**单通道灰度图(模式"L")**当遮罩,但你创建的ellipseMask是RGB模式,直接触发非法模式错误。
  2. img.resize()不会修改原图片,必须重新赋值:img = img.resize((width, width)),不然尺寸根本没变化。
  3. 椭圆坐标写错了:(-width, -width, width, width)会画到画布外面去,以左上角为圆心的话,应该用(0, 0, width, width),刚好覆盖整个正方形画布。

第二段代码无效果的问题

  1. 同样犯了resize未赋值的错误,原图片尺寸没改。
  2. height, width = img.size搞反了,PIL的size是(宽, 高),应该写成width, height = img.size,导致后续遮罩尺寸不匹配。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 08:17:53