English 简体中文 繁體中文 한국 사람 日本語 Deutsch русский بالعربية TÜRKÇE português คนไทย french
查看: 17|回复: 0

[python]png转webp的命令行工具

[复制链接]
查看: 17|回复: 0

[python]png转webp的命令行工具

[复制链接]
查看: 17|回复: 0

360

主题

0

回帖

1090

积分

金牌会员

积分
1090
wowoqu

360

主题

0

回帖

1090

积分

金牌会员

积分
1090
2025-2-4 22:43:25 | 显示全部楼层 |阅读模式
前言

网页上使用webp格式的图片更加省网络流量和存储空间,但本地图片一般是png格式的,所以考虑用python的pillow库将png格式的图片转换为webp格式。
需求:

  • 可以在系统任意地方调用。这需要编译成二进制程序或写成脚本放到PATH环境变量下
  • 支持指定图片文件输入目录。默认为当前目录。
  • 支持指定图片文件输出目录。默认为输入文件的同级目录。
  • 支持指定图片压缩质量。默认为80。需要校验传参。
  • 支持并发同时压缩多个图片文件。默认为串行。传参的并发数最大为CPU核心数。
代码

from PIL import Imageimport argparsefrom pathlib import Pathfrom concurrent.futures import ThreadPoolExecutorimport osfrom time import timedef parse_args():    """解析命令行参数"""    parser = argparse.ArgumentParser(description="Convert PNG to WEBP",         usage="""        # 直接执行, 默认转换当前目录下的所有png文件到同级目录        python main.py        # 将转换后的webp文件保存到output目录下        python main.py -o output        # 转换单个png文件, 单独转换时不支持指定输出目录        python main.py -f 1.png        # 同时转换, -t 指定最大并发数, 默认为1, 最大不得超过CPU核心数        python main.py -t 2        # 指定图片压缩质量, 默认为80, 取值区间为[0, 100], 值越高, 质量越好, 生成图片体积越大        python main.py -q 75        """)    parser.add_argument(        "-i", type=str, default=os.getcwd(), help="Path to the input PNG image"    )    parser.add_argument(        "-o", type=str, default=os.getcwd(), help="Path to the output WEBP image"    )    parser.add_argument("-f", type=str, default="", help="specific file name")    parser.add_argument("-t", type=int, default=1, help="Number of threads to use")    parser.add_argument(        "-q", type=int, default=80, help="Quality of the output WEBP image"    )    return parser.parse_args()def convert_png_to_webp(input_path: Path, output_path: Path, quality=80) -> None:    """    转换PNG为WEBP    Args:        input_path (Path): 输入文件路径        output_path (Path): 输出文件路径, 可以是一个目录, 也可以是一个webp文件的路径        quality (int, optional): 图片压缩质量. 默认为 80.    """    # 如果quality不在0到100之间, 则设置为80    if quality > 100 or quality < 0:        print("quality must be between 0 and 100, now set to 80")    real_q = quality if quality <= 100 and quality > 0 else 80    # 如果输入文件不存在, 则打印错误信息并返回    if not input_path.exists():        print(f"input file {input_path} not found")        return    # 如果指定了输出目录, 则尝试创建输出目录    if not output_path.exists() and output_path.suffix.lower() != ".webp":        try:            output_path.mkdir(parents=True)        except Exception as e:            print(e)            print("Failed to create output directory")            return    # 如果指定了输出目录, 则修改输出文件名为为输入文件名, 并修改扩展名为.webp    if output_path.suffix.lower() != ".webp":        output_path = output_path / input_path.with_suffix(".webp").name    start = time()    try:        with Image.open(input_path) as img:            print(                f"Converting {input_path}, quality={real_q}, size: {input_path.stat().st_size / 1024:.2f}KB"            )            img.save(output_path, "WEBP", quality=real_q)            print(                f"Convert png2webp successfully, output file: {output_path.name}, size: {int(output_path.stat().st_size) / 1024:.2f}KB, elapsed time: {time() - start:.2f}s"            )    except Exception as e:        print(f"Convert png2webp failed: {e}")def multi_thread_convert(max_workers: int, input_path, output_path, quality) -> None:    """并发转换png为webp"""    print(f"convert png to webp with multi threads, max_workers: {max_workers}")    p = Path(input_path)    op = Path(output_path) if output_path != os.getcwd() else None    max_workers = max_workers if max_workers < os.cpu_count() else os.cpu_count()    with ThreadPoolExecutor(max_workers=max_workers) as executor:        for f in p.glob("**/*.png"):            executor.submit(                convert_png_to_webp, f, op or f.with_suffix(".webp"), quality            )def main():    start = time()    args = parse_args()    if not args.f:        if args.t > 1:            multi_thread_convert(args.t, args.i, args.o, args.q)        else:            p = Path(args.i)            op = Path(args.o) if args.o != os.getcwd() else None            for f in p.glob("**/*.png"):                convert_png_to_webp(f, op or f.with_suffix(".webp"), args.q)    else:        p = Path(args.f)        convert_png_to_webp(p, p.with_suffix(".webp"), args.q)    print(f"Finished! Total elapsed time: {time() - start:.2f}s")if __name__ == "__main__":    main()编译

因为是在python虚拟环境中安装的pillow,如果要在其它位置调用这个脚本,个人想了两种方式:

  • 另外编写一个shell脚本,如果是windows,则编写powershell脚本,在这个脚本内编写调用逻辑,并把这个脚本放到PATH环境变量的路径下。
  • 编译成二进制文件,将编译好的二进制文件放到PATH环境变量下。这比较方便发送给别人,这样别人就不需要在电脑上安装python环境。
这里用pyinstaller将程序编译成二进制文件,尽量在python虚拟环境下编译,以减小二进制文件的体积

  • 创建虚拟环境
python -m venv png2webp

  • 激活虚拟环境
# linuxcd png2webpsource ./bin/activate# windows powershellcd png2webp.\Scripts\activate

  • 安装依赖
python -m pip install pillow pyinstaller

  • 编译。注意修改实际的python文件路径。
pyinstaller -F --clean .\main.py

  • 生成的二进制文件在当前目录下的dist目录,将其放置到PATH环境变量下,如有需要可重命名。
  • 测试在其他目录下调用
png2webp --help使用

# 直接执行, 默认转换当前目录下的所有png文件到同级目录png2webp# 将转换后的webp文件保存到output目录下png2webp -o output# 转换单个png文件, 单独转换时不支持指定输出目录png2webp -f 1.png# 同时转换, -t 指定最大并发数, 默认为1, 最大不得超过CPU核心数png2webp -t 2# 指定图片压缩质量, 默认为80, 取值区间为[0, 100], 值越高, 质量越好, 生成图片体积越大png2webp -q 75
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

360

主题

0

回帖

1090

积分

金牌会员

积分
1090

QQ|智能设备 | 粤ICP备2024353841号-1

GMT+8, 2025-3-10 15:03 , Processed in 2.669541 second(s), 27 queries .

Powered by 智能设备

©2025

|网站地图