A-A+

python 图片压缩脚本

2026年01月06日 19:37 学习笔记 暂无评论 共3529字 (阅读239 views次)

【注意:此文章为博主原创文章!转载需注意,请带原文链接,至少也要是txt格式!】

上一版的代码虽然会递归查找文件,但它把所有处理后的图片都“拍扁”放在了同一个输出文件夹里。

为了满足你保持原有层级结构(子文件夹)和文件名不变的需求,我们需要引入 os.path.relpath 来计算相对路径,从而在输出目录中重建一模一样的文件夹结构。

这是升级后的代码,完美支持递归查找并保持目录结构。

更新后的功能特点:

  1. 递归处理:会自动深入所有子文件夹。
  2. 保持结构:如果原图在 原图文件夹/2023/旅行/,处理后会在 输出文件夹/2023/旅行/
  3. 智能跳过:如果输出目录里已经存在同名文件,代码会跳过(防止重复劳动),或者你可以选择覆盖。
  4. 高质量压缩:依然保持 LANCZOS 算法和 subsampling=0 参数,确保 1024px 分辨率下的最顶级画质。

1. 核心算法回顾(LaTeX)

为了保证不变形,我们依然采用以下逻辑: 设原图宽 w,高 h,目标最大边长 Max=1024。 当 max⁡(w,h)>Max 时,缩放因子 k 为:

k=Maxmax⁡(w,h)

新的尺寸为:

wnew=⌊w×k⌋,hnew=⌊h×k⌋

2. 完整代码

请保存为 compress_structure.py

 

import os
from PIL import Image, ImageOps
from tqdm import tqdm

def compress_image(file_path, save_path, max_size=1024, quality=88):
    """
    读取 file_path,处理后保存到 save_path
    """
    try:
        # 打开图片
        img = Image.open(file_path)
        
        # 1. 修正图片方向 (Exif)
        img = ImageOps.exif_transpose(img)
        
        # 获取原始尺寸
        width, height = img.size
        
        # 2. 计算调整后的尺寸
        # 仅当图片尺寸超过 max_size 时才缩小
        if max(width, height) > max_size:
            scale_ratio = max_size / max(width, height)
            new_width = int(width * scale_ratio)
            new_height = int(height * scale_ratio)
            # 使用最优质的 LANCZOS 滤镜
            img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
        
        # 3. 格式处理 (PNG转JPG以获得更佳压缩率,也可以根据需求保留原格式)
        # 如果图片包含透明通道(RGBA),填充白色背景转为RGB
        if img.mode in ("RGBA", "P"):
            background = Image.new("RGB", img.size, (255, 255, 255))
            if img.mode == 'P':
                img = img.convert("RGBA")
            background.paste(img, mask=img.split()[3]) # 3 is the alpha channel
            img = background
        elif img.mode != "RGB":
            img = img.convert("RGB")

        # 4. 保存图片
        # optimize=True: 开启优化
        # subsampling=0: 4:4:4采样,防止颜色边缘模糊
        img.save(save_path, 'JPEG', quality=quality, optimize=True, subsampling=0)
        
        return True
        
    except Exception as e:
        print(f"\n[Error] {file_path}: {e}")
        return False

def batch_process_preserve_structure(input_root, output_root, max_size=1024):
    """
    递归遍历 input_root,保持结构输出到 output_root
    """
    valid_extensions = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.webp', '.heic'}
    
    # 收集任务列表 [(原图路径, 目标路径), ...]
    tasks = []
    
    print(f"正在扫描目录: {input_root} ...")
    
    for root, dirs, files in os.walk(input_root):
        for file in files:
            ext = os.path.splitext(file)[1].lower()
            if ext in valid_extensions:
                # 1. 构建原文件绝对路径
                src_path = os.path.join(root, file)
                
                # 2. 计算相对路径 (例如: subfolder/image.jpg)
                rel_path = os.path.relpath(root, input_root)
                
                # 3. 构建目标文件夹路径
                dest_folder = os.path.join(output_root, rel_path)
                
                # 4. 构建目标文件完整路径 (统一后缀为 .jpg)
                file_name_no_ext = os.path.splitext(file)[0]
                dest_path = os.path.join(dest_folder, file_name_no_ext + ".jpg")
                
                tasks.append((src_path, dest_folder, dest_path))

    total_files = len(tasks)
    if total_files == 0:
        print("未找到图片文件。")
        return

    print(f"找到 {total_files} 张图片,开始处理...")
    print(f"目标分辨率限制: {max_size}px")
    
    # 使用 tqdm 显示总进度
    with tqdm(total=total_files, unit="img") as pbar:
        for src_path, dest_folder, dest_path in tasks:
            
            # 如果目标文件夹不存在,创建它 (保持层级结构的关键)
            if not os.path.exists(dest_folder):
                os.makedirs(dest_folder)
            
            # 执行压缩
            compress_image(src_path, dest_path, max_size=max_size)
            
            pbar.update(1)

    print("-" * 30)
    print(f"全部完成!输出目录: {output_root}")

if __name__ == "__main__":
    # ================= 设置区域 =================
    
    # 你的原图根目录
    INPUT_DIR = r"C:\Users\YourName\Pictures\Originals" 
    
    # 你想输出的根目录 (会自动创建对应的子文件夹)
    OUTPUT_DIR = r"C:\Users\YourName\Pictures\Compressed_Structure"
    
    # ===========================================
    
    if not os.path.exists(INPUT_DIR):
        print(f"错误: 输入目录不存在 -> {INPUT_DIR}")
    else:
        batch_process_preserve_structure(INPUT_DIR, OUTPUT_DIR)



关键修改点解析

  1. os.path.relpath(root, input_root): 这是保持目录结构的核心。假设原图在 C:\Photos\2024\Japan\a.jpg,输入根目录是 C:\Photos。 这个函数会计算出相对路径 2024\Japan
  2. os.makedirs(dest_folder): 我们会将输出根目录 D:\Output 与上面的相对路径结合,得到 D:\Output\2024\Japan。如果这个文件夹不存在,程序会自动创建它。
  3. 透明背景处理优化: 在之前的代码中,如果遇到透明背景的 PNG,直接转 JPG 可能会变黑。这段新代码增加了一个逻辑:background = Image.new("RGB", img.size, (255, 255, 255))。它会先创建一个白色背景,然后把透明图片叠上去,最后保存为 JPG。这样处理出来的图片更加干净、美观。

如何使用

  1. 确保已安装库:pip install Pillow tqdm
  2. 修改代码底部的 INPUT_DIR 和 OUTPUT_DIR
  3. 运行即可。它会完美复刻你的文件夹层级,里面的图片都会被压缩到 1024px 以内

布施恩德可便相知重

微信扫一扫打赏

支付宝扫一扫打赏

×

给我留言