代码: 全选
from PIL import Image, ImageChops
import os
def auto_trim_whitespace(img, border=0):
"""
自动切除图片四周的纯白边
:param img: PIL Image 对象
:param border: 切除后保留的额外边距(防止切得太紧),默认为0
:return: 裁切后的 PIL Image 对象
"""
# 创建一个与原图一样大的纯白图片
bg = Image.new(img.mode, img.size, "white")
# 计算两张图的差异,找出非白色的区域
diff = ImageChops.difference(img, bg)
# 获取差异图的边界框 (left, top, right, bottom)
bbox = diff.getbbox()
if bbox:
# 如果设置了 border,稍微向外扩展一点边界,防止切到主体
if border > 0:
left = max(0, bbox[0] - border)
top = max(0, bbox[1] - border)
right = min(img.width, bbox[2] + border)
bottom = min(img.height, bbox[3] + border)
bbox = (left, top, right, bottom)
return img.crop(bbox)
return img # 如果没找到边界框,说明全白或全透明,返回原图
def batch_crop_portrait(input_dir, output_dir, target_ratio):
"""
先切除白边,再按指定比例最大裁切(人像模式:从顶部向下裁切)
"""
os.makedirs(output_dir, exist_ok=True)
for filename in os.listdir(input_dir):
if not filename.lower().endswith(('.jpg', '.jpeg', '.png', '.bmp', '.webp')):
continue
img_path = os.path.join(input_dir, filename)
output_path = os.path.join(output_dir, filename)
try:
img = Image.open(img_path).convert("RGB") # 强制转为RGB,避免RGBA模式导致白边检测失效
# 1. 第一步:自动切除白边
trimmed_img = auto_trim_whitespace(img, border=2)
orig_w, orig_h = trimmed_img.size
orig_ratio = orig_w / orig_h
# 2. 第二步:计算比例裁切坐标
if orig_ratio > target_ratio:
# 原图较宽,裁剪左右两侧(依然居中)
new_w = int(orig_h * target_ratio)
new_h = orig_h
left = (orig_w - new_w) // 2
top = 0
else:
# 原图较高,从顶部向下裁切(保头)
new_w = orig_w
new_h = int(orig_w / target_ratio)
left = 0
top = 0
right = left + new_w
bottom = top + new_h
# 3. 第三步:执行裁剪并保存
cropped_img = trimmed_img.crop((left, top, right, bottom))
cropped_img.save(output_path)
print(f"✅ 成功处理: {filename}")
except Exception as e:
print(f"❌ 处理 {filename} 时出错: {e}")
# ========== 使用示例 ==========
if __name__ == "__main__":
input_folder = "./media" # 原图文件夹路径
output_folder = "./media_o" # 输出文件夹路径
ratio = 22.5/30 # 目标比例,如 3/4, 9/16, 1/1
batch_crop_portrait(input_folder, output_folder, ratio)