此前的代码使用了Pillow库集成的库,这次使用WebP官方库,对GIF、PNG的处理也比较友好。需要添加WebP的库到系统环境变量后使用。
功能实现:
代码使用cwebp、gif2webp两种方式转换不同的格式图片,使用库本身的压缩分辨率方法,压缩图片到2560最长、宽。
对于gif,该代码可以实现对原图动态的保持,此前使用pillow库则可以设定gif的持续或者最后一帧静帧。
遗憾:
没有能够传递exif,win平台中cwebp压根没法有效传递exif信息,显示——Warning: only ICC profile extraction is currently supported on this platform!元数据只有ICC才能支持传递,所以只依靠cwebp是没法很好在win中进行转换的。
这个问题采用exiftool进行了解决,下篇文章可以看到。
import tkinter as tk
from tkinter import filedialog, messagebox
import os
import subprocess
from PIL import Image
def validate_file(input_path):
input_path = os.path.abspath(input_path)
if not os.path.exists(input_path):
raise FileNotFoundError(f"文件 {input_path} 不存在,请检查路径。")
return input_path
def get_resized_dimensions(width, height, max_size):
if width > height:
new_width = max_size
new_height = int((new_width / width) * height)
else:
new_height = max_size
new_width = int((new_height / height) * width)
return new_width, new_height
# 使用 cwebp 或 gif2webp 进行转换
def convert_image(input_path, output_path, new_width=None, new_height=None, cwebp_metadata="none", gif2webp_metadata="none"):
try:
file_extension = os.path.splitext(input_path)[1].lower()
if file_extension == ".gif":
command = ["gif2webp", "-metadata", gif2webp_metadata, "-mt", input_path, "-o", output_path]
else:
if new_width and new_height:
command = ["cwebp", "-q", "85", "-resize", str(new_width), str(new_height), "-metadata", cwebp_metadata, "-mt", input_path, "-o", output_path]
else:
command = ["cwebp", "-q", "85", "-metadata", cwebp_metadata, "-mt", input_path, "-o", output_path]
subprocess.run(command, check=True)
except subprocess.CalledProcessError as e:
raise RuntimeError(f"转换工具运行出错: {e}")
def convert_to_webp(input_path, max_size=2560, cwebp_metadata="none", gif2webp_metadata="none"):
try:
# 验证文件路径
input_path = validate_file(input_path)
output_path = os.path.splitext(input_path)[0] + ".webp"
# 使用 PIL 获取图像分辨率
with Image.open(input_path) as img:
width, height = img.size
if width <= max_size and height <= max_size:
convert_image(input_path, output_path, cwebp_metadata=cwebp_metadata, gif2webp_metadata=gif2webp_metadata)
else:
new_width, new_height = get_resized_dimensions(width, height, max_size)
convert_image(input_path, output_path, new_width, new_height, cwebp_metadata=cwebp_metadata, gif2webp_metadata=gif2webp_metadata)
return f"图片已转换并保存为 {output_path}"
except (subprocess.CalledProcessError, FileNotFoundError) as e:
return str(e)
except Exception as e:
return f"处理文件时发生错误: {e}"
def select_files():
file_paths = filedialog.askopenfilenames(
title="选择图片文件",
filetypes=[("*所有图片格式", "*.jpg;*.jpeg;*.png;*.gif"),
("JPEG 图片", "*.jpg;*.jpeg"),
("PNG 图片", "*.png"),
("GIF 图片", "*.gif")]
)
if file_paths:
for path in file_paths:
file_listbox.insert(tk.END, path)
def convert_and_save_batch():
files = file_listbox.get(0, tk.END)
if not files:
messagebox.showerror("错误", "请选择至少一个图片文件!")
return
cwebp_metadata = "exif" # 设置cwebp要复制的元数据类型为exif
gif2webp_metadata = "xmp" # 设置gif2webp要复制的元数据类型为xmp
results = [convert_to_webp(file_path, cwebp_metadata=cwebp_metadata, gif2webp_metadata=gif2webp_metadata) for file_path in files]
messagebox.showinfo("完成", "\n".join(results))
def clear_list():
file_listbox.delete(0, tk.END)
# 创建主窗口
root = tk.Tk()
root.title("批量图片转换为 WebP 工具")
root.geometry("600x400")
frame = tk.Frame(root)
frame.pack(pady=10, padx=10, fill=tk.BOTH, expand=True)
scrollbar = tk.Scrollbar(frame, orient=tk.VERTICAL)
file_listbox = tk.Listbox(frame, selectmode=tk.EXTENDED, yscrollcommand=scrollbar.set)
scrollbar.config(command=file_listbox.yview)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
file_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
button_frame = tk.Frame(root)
button_frame.pack(pady=10)
select_button = tk.Button(button_frame, text="选择文件", command=select_files, width=15)
select_button.grid(row=0, column=0, padx=5)
clear_button = tk.Button(button_frame, text="清空列表", command=clear_list, width=15)
clear_button.grid(row=0, column=1, padx=5)
convert_button = tk.Button(button_frame, text="批量转换", command=convert_and_save_batch, width=15)
convert_button.grid(row=0, column=2, padx=5)
root.mainloop()