如何使用Python批量裁剪图片?3种场景,代码直接拿去用
为什么要批量裁剪图片?
做电商要统一主图尺寸、社交媒体头像要裁成正方形、证件照需要固定比例……手动一张张裁,几十张就够你喝两杯咖啡了。
用Python + Pillow,几行代码就能批量搞定。这篇博客覆盖最常用的3个场景,每个都给可运行代码。
场景一:固定尺寸裁剪(左上角裁剪)
最简单粗暴的方式:把所有图片裁成统一的宽×高。
fromPILimportImageimportosdefcrop_fixed_size(input_dir,output_dir,width,height):os.makedirs(output_dir,exist_ok=True)forfilenameinos.listdir(input_dir):ifnotfilename.lower().endswith(('.jpg','.jpeg','.png','.bmp','.webp')):continueimg_path=os.path.join(input_dir,filename)img=Image.open(img_path)# 左上角裁剪cropped=img.crop((0,0,width,height))cropped.save(os.path.join(output_dir,filename))print(f"{filename}→{width}x{height}")# 使用:裁成 800x600crop_fixed_size(r'D:\原图',r'D:\裁剪后',800,600)⚠️ 如果原图比目标尺寸小,会报错。下面的"居中裁剪"可以解决这个问题。
场景二:居中裁剪成正方形(最常用)
社交媒体头像、商品主图,基本都要正方形。这个方法会自动居中裁剪,不会拉伸变形。
fromPILimportImageimportosdefcrop_center_square(input_dir,output_dir,size):os.makedirs(output_dir,exist_ok=True)forfilenameinos.listdir(input_dir):ifnotfilename.lower().endswith(('.jpg','.jpeg','.png','.bmp','.webp')):continueimg_path=os.path.join(input_dir,filename)img=Image.open(img_path)# 取最小边作为裁剪尺寸,保证不会切掉内容min_side=min(img.width,img.height)left=(img.width-min_side)//2top=(img.height-min_side)//2right=left+min_side bottom=top+min_side cropped=img.crop((left,top,right,bottom))# 再缩放到目标尺寸(可选)cropped=cropped.resize((size,size),Image.LANCZOS)cropped.save(os.path.join(output_dir,filename))print(f"{filename}→{size}x{size}")# 使用:裁成 500x500 正方形crop_center_square(r'D:\照片',r'D:\头像',500)效果对比:
| 原图 | 居中裁剪 | 左上角裁剪 |
|---|---|---|
| 1200×800 横图 | ✅ 保留中间主体 | ❌ 只保留左上角 |
| 800×1200 竖图 | ✅ 保留中间主体 | ❌ 只保留左上角 |
场景三:按坐标批量裁剪(精确控制)
有些场景需要精确裁剪某个区域,比如证件照只取头部、截图只取特定区域。
fromPILimportImageimportosdefcrop_by_coordinates(input_dir,output_dir,coords):""" coords: (left, top, right, bottom) 例如:(100, 50, 400, 400) 表示从(100,50)到(400,400)的矩形区域 """os.makedirs(output_dir,exist_ok=True)left,top,right,bottom=coords crop_width=right-left crop_height=bottom-topforfilenameinos.listdir(input_dir):ifnotfilename.lower().endswith(('.jpg','.jpeg','.png','.bmp','.webp')):continueimg_path=os.path.join(input_dir,filename)img=Image.open(img_path)# 判断原图是否够大ifimg.width<rightorimg.height<bottom:print(f"⚠️{filename}尺寸不足,跳过")continuecropped=img.crop((left,top,right,bottom))cropped.save(os.path.join(output_dir,filename))print(f"{filename}→ 裁剪区域{coords}")# 使用:裁剪左上角 (100, 50) 到 (600, 550) 的区域crop_by_coordinates(r'D:\截图',r'D:\裁剪后',(100,50,600,550))进阶:批量裁剪 + 加水印(一气呵成)
裁剪完直接加上水印,电商场景特别实用。
fromPILimportImage,ImageDraw,ImageFontimportosdefcrop_and_watermark(input_dir,output_dir,size,watermark_text):os.makedirs(output_dir,exist_ok=True)forfilenameinos.listdir(input_dir):ifnotfilename.lower().endswith(('.jpg','.jpeg','.png')):continueimg_path=os.path.join(input_dir,filename)img=Image.open(img_path)# 居中裁剪min_side=min(img.width,img.height)left=(img.width-min_side)//2top=(img.height-min_side)//2cropped=img.crop((left,top,left+min_side,top+min_side))cropped=cropped.resize((size,size),Image.LANCZOS)# 加水印draw=ImageDraw.Draw(cropped)font=ImageFont.truetype("simhei.ttf",int(size*0.08))# Windows自带黑体text_bbox=draw.textbbox((0,0),watermark_text,font=font)text_width=text_bbox[2]-text_bbox[0]x=(size-text_width)//2y=size-int(size*0.1)draw.text((x,y),watermark_text,fill=(255,255,255,128),font=font)cropped.save(os.path.join(output_dir,filename),quality=95)print(f"{filename}→ 裁剪+水印完成")# 使用crop_and_watermark(r'D:\商品图',r'D:\成品',800,'店铺名称')💡 如果是Mac/Linux,字体路径改成
/System/Library/Fonts/Supplemental/Arial Unicode.ttf或安装中文字体。
常见问题速查
| 问题 | 解决方案 |
|---|---|
| 图片有透明通道,保存后变黑底 | 用cropped.convert('RGB')转成RGB再保存 |
| 裁剪后图片模糊 | resize()用Image.LANCZOS而不是默认的Image.BILINEAR |
| 批量处理速度慢 | 考虑用concurrent.futures.ThreadPoolExecutor多线程处理 |
| 需要保留EXIF信息 | 用piexif库,或pillow-heif处理HEIC格式 |
总结
批量裁剪图片的核心就三步:
- 打开图片→
Image.open() - 裁剪区域→
img.crop((左, 上, 右, 下)) - 保存结果→
img.save()
上面的代码覆盖了固定尺寸、居中正方形、精确坐标三种最常见需求,复制过去改一下路径就能跑。
如果你需要更复杂的操作(比如按人脸自动裁剪、批量加圆角),评论区说,我接着写。
