分页: 1 / 1

python首先去掉头像的白边,然后按比列裁切头像

发表于 : 2026-07-28 9:55
rungod

代码: 全选

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)