开发环境准备
python 3.6+
pillow库 (pil的fork版本)
安装pillow库:
pip install pillow
核心功能
支持多种常见图片格式之间的转换(jpg, png, bmp, gif, tiff等)
批量处理指定文件夹中的所有图片
可选择保留原始图片或仅保留转换后的图片
支持调整图片质量和大小
简单的命令行界面和图形用户界面(gui)两种使用方式
技术要点
1. pillow库基础
pillow是python图像处理库(pil)的一个分支,提供了丰富的图像处理功能。主要用到的模块:
- image: 图像对象的创建、读取和保存
- imageops: 图像操作,如调整大小、翻转等
- imageenhance: 图像增强,如亮度、对比度调整
2. 文件系统操作
使用os和pathlib模块处理文件路径
递归遍历目录
文件类型判断
3. 用户界面
命令行参数解析
简单gui界面构建(使用tkinter)
代码实现与解析
1. 导入必要的库
from pil import image import os import sys import argparse from pathlib import path import tkinter as tk from tkinter import filedialog, messagebox, ttk
知识点:
pil.image: pillow的核心模块,用于图像处理
os和pathlib: 文件系统操作
argparse: 命令行参数解析
tkinter: python标准gui库
2. 图片格式转换核心函数
def convert_image(input_path, output_path, format, quality=95, resize=none): """ 转换单个图片的格式 参数: input_path - 输入图片路径 output_path - 输出图片路径 format - 目标格式 (如 'jpeg', 'png') quality - 图片质量 (1-100, 仅对jpeg格式有效) resize - 调整大小的元组 (width, height) 或 none 返回: bool - 转换是否成功 """ try: # 打开图片 img = image.open(input_path) # 如果是rgba模式且转换为jpeg,需要转换为rgb模式 if img.mode == 'rgba' and format.upper() == 'jpeg': img = img.convert('rgb') # 调整大小 if resize: img = img.resize(resize, image.lanczos) # 保存转换后的图片 if format.upper() == 'jpeg': img.save(output_path, format=format, quality=quality) else: img.save(output_path, format=format) return true except exception as e: print(f"转换图片 {input_path} 时出错: {e}") return false
知识点:
image.open(): 打开图片文件
图像模式转换: rgba转rgb(jpeg不支持透明通道)
image.lanczos: 高质量的图像缩放算法
异常处理: 捕获并处理可能的错误
3. 批量转换功能
def batch_convert(input_dir, output_dir, target_format, quality=95, resize=none, recursive=false, keep_original=true): """ 批量转换指定目录中的图片 参数: input_dir - 输入目录 output_dir - 输出目录 target_format - 目标格式 quality - 图片质量 resize - 调整大小的元组 recursive - 是否递归处理子目录 keep_original - 是否保留原始图片 返回: tuple - (成功数量, 失败数量) """ # 支持的图片格式 supported_formats = ['.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tiff'] # 创建输出目录 os.makedirs(output_dir, exist_ok=true) success_count = 0 failed_count = 0 # 遍历目录 for root, dirs, files in os.walk(input_dir): # 如果不递归处理子目录且不是根目录,则跳过 if not recursive and root != input_dir: continue # 创建对应的输出子目录 rel_path = os.path.relpath(root, input_dir) if rel_path != '.': current_output_dir = os.path.join(output_dir, rel_path) os.makedirs(current_output_dir, exist_ok=true) else: current_output_dir = output_dir # 处理文件 for file in files: # 检查文件扩展名 ext = os.path.splitext(file)[1].lower() if ext not in supported_formats: continue # 构建输入和输出路径 input_path = os.path.join(root, file) output_filename = os.path.splitext(file)[0] + '.' + target_format.lower() output_path = os.path.join(current_output_dir, output_filename) # 转换图片 if convert_image(input_path, output_path, target_format, quality, resize): success_count += 1 # 如果不保留原始图片且不是在原目录操作,则删除原始图片 if not keep_original and input_dir != output_dir: try: os.remove(input_path) except: pass else: failed_count += 1 return (success_count, failed_count)
知识点:
os.walk(): 递归遍历目录
os.path.relpath(): 获取相对路径
os.makedirs(): 创建多级目录
文件扩展名处理: 使用os.path.splitext()分离文件名和扩展名
4. 命令行界面
def setup_cli(): """设置命令行参数解析器""" parser = argparse.argumentparser(description='批量转换图片格式') parser.add_argument('input', help='输入目录或文件') parser.add_argument('output', help='输出目录') parser.add_argument('format', help='目标格式 (如: jpg, png, bmp)') parser.add_argument('-q', '--quality', type=int, default=95, help='图片质量 (1-100, 默认: 95)') parser.add_argument('-r', '--recursive', action='store_true', help='递归处理子目录') parser.add_argument('-k', '--keep', action='store_true', help='保留原始图片') parser.add_argument('--resize', nargs=2, type=int, metavar=('width', 'height'), help='调整图片大小') return parser.parse_args()
知识点:
argparse: 命令行参数解析
参数类型: 位置参数、可选参数、标志参数
参数类型转换: 使用type=int将字符串转换为整数
5. 图形用户界面
class imageconvertergui: def __init__(self, root): self.root = root self.root.title("图片格式批量转换器") self.root.geometry("600x450") self.root.resizable(true, true) # 创建主框架 main_frame = ttk.frame(root, padding="10") main_frame.pack(fill=tk.both, expand=true) # 输入目录 ttk.label(main_frame, text="输入目录:").grid(column=0, row=0, sticky=tk.w, pady=5) self.input_dir = tk.stringvar() ttk.entry(main_frame, width=50, textvariable=self.input_dir).grid(column=1, row=0, pady=5) ttk.button(main_frame, text="浏览...", command=self.browse_input).grid(column=2, row=0, padx=5, pady=5) # 输出目录 ttk.label(main_frame, text="输出目录:").grid(column=0, row=1, sticky=tk.w, pady=5) self.output_dir = tk.stringvar() ttk.entry(main_frame, width=50, textvariable=self.output_dir).grid(column=1, row=1, pady=5) ttk.button(main_frame, text="浏览...", command=self.browse_output).grid(column=2, row=1, padx=5, pady=5) # 目标格式 ttk.label(main_frame, text="目标格式:").grid(column=0, row=2, sticky=tk.w, pady=5) self.format_var = tk.stringvar(value="jpeg") formats = ["jpeg", "png", "bmp", "gif", "tiff"] ttk.combobox(main_frame, textvariable=self.format_var, values=formats, width=10).grid(column=1, row=2, sticky=tk.w, pady=5) # 图片质量 ttk.label(main_frame, text="图片质量:").grid(column=0, row=3, sticky=tk.w, pady=5) self.quality_var = tk.intvar(value=95) quality_frame = ttk.frame(main_frame) quality_frame.grid(column=1, row=3, sticky=tk.w, pady=5) ttk.scale(quality_frame, from_=1, to=100, variable=self.quality_var, orient=tk.horizontal, length=200).pack(side=tk.left) ttk.label(quality_frame, textvariable=self.quality_var).pack(side=tk.left, padx=5) # 调整大小 ttk.label(main_frame, text="调整大小:").grid(column=0, row=4, sticky=tk.w, pady=5) resize_frame = ttk.frame(main_frame) resize_frame.grid(column=1, row=4, sticky=tk.w, pady=5) self.resize_enabled = tk.booleanvar(value=false) ttk.checkbutton(resize_frame, text="启用", variable=self.resize_enabled).pack(side=tk.left) ttk.label(resize_frame, text="宽:").pack(side=tk.left, padx=(10, 0)) self.width_var = tk.intvar(value=800) ttk.entry(resize_frame, width=5, textvariable=self.width_var).pack(side=tk.left, padx=(0, 5)) ttk.label(resize_frame, text="高:").pack(side=tk.left) self.height_var = tk.intvar(value=600) ttk.entry(resize_frame, width=5, textvariable=self.height_var).pack(side=tk.left) # 递归处理 self.recursive_var = tk.booleanvar(value=false) ttk.checkbutton(main_frame, text="递归处理子目录", variable=self.recursive_var).grid(column=1, row=5, sticky=tk.w, pady=5) # 保留原始图片 self.keep_original_var = tk.booleanvar(value=true) ttk.checkbutton(main_frame, text="保留原始图片", variable=self.keep_original_var).grid(column=1, row=6, sticky=tk.w, pady=5) # 转换按钮 ttk.button(main_frame, text="开始转换", command=self.start_conversion).grid(column=1, row=7, pady=10) # 进度条 self.progress_var = tk.doublevar() ttk.progressbar(main_frame, variable=self.progress_var, maximum=100).grid(column=0, row=8, columnspan=3, sticky=(tk.w, tk.e), pady=5) # 状态标签 self.status_var = tk.stringvar(value="就绪") ttk.label(main_frame, textvariable=self.status_var).grid(column=0, row=9, columnspan=3, sticky=tk.w, pady=5) def browse_input(self): directory = filedialog.askdirectory() if directory: self.input_dir.set(directory) def browse_output(self): directory = filedialog.askdirectory() if directory: self.output_dir.set(directory) def start_conversion(self): input_dir = self.input_dir.get() output_dir = self.output_dir.get() target_format = self.format_var.get() quality = self.quality_var.get() recursive = self.recursive_var.get() keep_original = self.keep_original_var.get() # 检查输入 if not input_dir or not output_dir: messagebox.showerror("错误", "请指定输入和输出目录") return # 检查调整大小参数 resize = none if self.resize_enabled.get(): try: width = self.width_var.get() height = self.height_var.get() if width <= 0 or height <= 0: raise valueerror("宽度和高度必须大于0") resize = (width, height) except: messagebox.showerror("错误", "调整大小参数无效") return # 开始转换 self.status_var.set("转换中...") self.root.update() try: success, failed = batch_convert( input_dir, output_dir, target_format, quality, resize, recursive, keep_original ) self.status_var.set(f"转换完成。成功: {success}, 失败: {failed}") messagebox.showinfo("完成", f"转换完成\n成功: {success}\n失败: {failed}") except exception as e: self.status_var.set(f"转换出错: {str(e)}") messagebox.showerror("错误", f"转换过程中出错:\n{str(e)}")
知识点:
tkinter: python标准gui库
布局管理: 使用grid布局
控件使用: 标签、输入框、按钮、复选框、组合框、进度条等
事件处理: 按钮点击事件
文件对话框: 使用filedialog选择目录
6. 主函数
def main(): # 检查是否有命令行参数 if len(sys.argv) > 1: # 命令行模式 args = setup_cli() # 检查输入和输出目录 if not os.path.exists(args.input): print(f"错误: 输入路径 '{args.input}' 不存在") return # 调整大小参数 resize = tuple(args.resize) if args.resize else none # 开始转换 print(f"开始转换图片从 {args.input} 到 {args.output},格式: {args.format}") success, failed = batch_convert( args.input, args.output, args.format.upper(), args.quality, resize, args.recursive, args.keep ) print(f"转换完成。成功: {success}, 失败: {failed}") else: # gui模式 root = tk.tk() app = imageconvertergui(root) root.mainloop() if __name__ == "__main__": main()
完整代码
#!/usr/bin/env python # -*- coding: utf-8 -*- """ 图片格式批量转换器 功能: 批量将图片从一种格式转换为另一种格式,支持调整大小和质量 作者: python开发者 """ from pil import image import os import sys import argparse from pathlib import path import tkinter as tk from tkinter import filedialog, messagebox, ttk def convert_image(input_path, output_path, format, quality=95, resize=none): """ 转换单个图片的格式 参数: input_path - 输入图片路径 output_path - 输出图片路径 format - 目标格式 (如 'jpeg', 'png') quality - 图片质量 (1-100, 仅对jpeg格式有效) resize - 调整大小的元组 (width, height) 或 none 返回: bool - 转换是否成功 """ try: # 打开图片 img = image.open(input_path) # 如果是rgba模式且转换为jpeg,需要转换为rgb模式 if img.mode == 'rgba' and format.upper() == 'jpeg': img = img.convert('rgb') # 调整大小 if resize: img = img.resize(resize, image.lanczos) # 保存转换后的图片 if format.upper() == 'jpeg': img.save(output_path, format=format, quality=quality) else: img.save(output_path, format=format) return true except exception as e: print(f"转换图片 {input_path} 时出错: {e}") return false def batch_convert(input_dir, output_dir, target_format, quality=95, resize=none, recursive=false, keep_original=true): """ 批量转换指定目录中的图片 参数: input_dir - 输入目录 output_dir - 输出目录 target_format - 目标格式 quality - 图片质量 resize - 调整大小的元组 recursive - 是否递归处理子目录 keep_original - 是否保留原始图片 返回: tuple - (成功数量, 失败数量) """ # 支持的图片格式 supported_formats = ['.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tiff'] # 创建输出目录 os.makedirs(output_dir, exist_ok=true) success_count = 0 failed_count = 0 # 遍历目录 for root, dirs, files in os.walk(input_dir): # 如果不递归处理子目录且不是根目录,则跳过 if not recursive and root != input_dir: continue # 创建对应的输出子目录 rel_path = os.path.relpath(root, input_dir) if rel_path != '.': current_output_dir = os.path.join(output_dir, rel_path) os.makedirs(current_output_dir, exist_ok=true) else: current_output_dir = output_dir # 处理文件 for file in files: # 检查文件扩展名 ext = os.path.splitext(file)[1].lower() if ext not in supported_formats: continue # 构建输入和输出路径 input_path = os.path.join(root, file) output_filename = os.path.splitext(file)[0] + '.' + target_format.lower() output_path = os.path.join(current_output_dir, output_filename) # 转换图片 if convert_image(input_path, output_path, target_format, quality, resize): success_count += 1 # 如果不保留原始图片且不是在原目录操作,则删除原始图片 if not keep_original and input_dir != output_dir: try: os.remove(input_path) except: pass else: failed_count += 1 return (success_count, failed_count) def setup_cli(): """设置命令行参数解析器""" parser = argparse.argumentparser(description='批量转换图片格式') parser.add_argument('input', help='输入目录或文件') parser.add_argument('output', help='输出目录') parser.add_argument('format', help='目标格式 (如: jpg, png, bmp)') parser.add_argument('-q', '--quality', type=int, default=95, help='图片质量 (1-100, 默认: 95)') parser.add_argument('-r', '--recursive', action='store_true', help='递归处理子目录') parser.add_argument('-k', '--keep', action='store_true', help='保留原始图片') parser.add_argument('--resize', nargs=2, type=int, metavar=('width', 'height'), help='调整图片大小') return parser.parse_args() class imageconvertergui: def __init__(self, root): self.root = root self.root.title("图片格式批量转换器") self.root.geometry("600x450") self.root.resizable(true, true) # 创建主框架 main_frame = ttk.frame(root, padding="10") main_frame.pack(fill=tk.both, expand=true) # 输入目录 ttk.label(main_frame, text="输入目录:").grid(column=0, row=0, sticky=tk.w, pady=5) self.input_dir = tk.stringvar() ttk.entry(main_frame, width=50, textvariable=self.input_dir).grid(column=1, row=0, pady=5) ttk.button(main_frame, text="浏览...", command=self.browse_input).grid(column=2, row=0, padx=5, pady=5) # 输出目录 ttk.label(main_frame, text="输出目录:").grid(column=0, row=1, sticky=tk.w, pady=5) self.output_dir = tk.stringvar() ttk.entry(main_frame, width=50, textvariable=self.output_dir).grid(column=1, row=1, pady=5) ttk.button(main_frame, text="浏览...", command=self.browse_output).grid(column=2, row=1, padx=5, pady=5) # 目标格式 ttk.label(main_frame, text="目标格式:").grid(column=0, row=2, sticky=tk.w, pady=5) self.format_var = tk.stringvar(value="jpeg") formats = ["jpeg", "png", "bmp", "gif", "tiff"] ttk.combobox(main_frame, textvariable=self.format_var, values=formats, width=10).grid(column=1, row=2, sticky=tk.w, pady=5) # 图片质量 ttk.label(main_frame, text="图片质量:").grid(column=0, row=3, sticky=tk.w, pady=5) self.quality_var = tk.intvar(value=95) quality_frame = ttk.frame(main_frame) quality_frame.grid(column=1, row=3, sticky=tk.w, pady=5) ttk.scale(quality_frame, from_=1, to=100, variable=self.quality_var, orient=tk.horizontal, length=200).pack( side=tk.left) ttk.label(quality_frame, textvariable=self.quality_var).pack(side=tk.left, padx=5) # 调整大小 ttk.label(main_frame, text="调整大小:").grid(column=0, row=4, sticky=tk.w, pady=5) resize_frame = ttk.frame(main_frame) resize_frame.grid(column=1, row=4, sticky=tk.w, pady=5) self.resize_enabled = tk.booleanvar(value=false) ttk.checkbutton(resize_frame, text="启用", variable=self.resize_enabled).pack(side=tk.left) ttk.label(resize_frame, text="宽:").pack(side=tk.left, padx=(10, 0)) self.width_var = tk.intvar(value=800) ttk.entry(resize_frame, width=5, textvariable=self.width_var).pack(side=tk.left, padx=(0, 5)) ttk.label(resize_frame, text="高:").pack(side=tk.left) self.height_var = tk.intvar(value=600) ttk.entry(resize_frame, width=5, textvariable=self.height_var).pack(side=tk.left) # 递归处理 self.recursive_var = tk.booleanvar(value=false) ttk.checkbutton(main_frame, text="递归处理子目录", variable=self.recursive_var).grid(column=1, row=5, sticky=tk.w, pady=5) # 保留原始图片 self.keep_original_var = tk.booleanvar(value=true) ttk.checkbutton(main_frame, text="保留原始图片", variable=self.keep_original_var).grid(column=1, row=6, sticky=tk.w, pady=5) # 转换按钮 ttk.button(main_frame, text="开始转换", command=self.start_conversion).grid(column=1, row=7, pady=10) # 进度条 self.progress_var = tk.doublevar() ttk.progressbar(main_frame, variable=self.progress_var, maximum=100).grid(column=0, row=8, columnspan=3, sticky=(tk.w, tk.e), pady=5) # 状态标签 self.status_var = tk.stringvar(value="就绪") ttk.label(main_frame, textvariable=self.status_var).grid(column=0, row=9, columnspan=3, sticky=tk.w, pady=5) def browse_input(self): directory = filedialog.askdirectory() if directory: self.input_dir.set(directory) def browse_output(self): directory = filedialog.askdirectory() if directory: self.output_dir.set(directory) def start_conversion(self): input_dir = self.input_dir.get() output_dir = self.output_dir.get() target_format = self.format_var.get() quality = self.quality_var.get() recursive = self.recursive_var.get() keep_original = self.keep_original_var.get() # 检查输入 if not input_dir or not output_dir: messagebox.showerror("错误", "请指定输入和输出目录") return # 检查调整大小参数 resize = none if self.resize_enabled.get(): try: width = self.width_var.get() height = self.height_var.get() if width <= 0 or height <= 0: raise valueerror("宽度和高度必须大于0") resize = (width, height) except: messagebox.showerror("错误", "调整大小参数无效") return # 开始转换 self.status_var.set("转换中...") self.root.update() try: success, failed = batch_convert( input_dir, output_dir, target_format, quality, resize, recursive, keep_original ) self.status_var.set(f"转换完成。成功: {success}, 失败: {failed}") messagebox.showinfo("完成", f"转换完成\n成功: {success}\n失败: {failed}") except exception as e: self.status_var.set(f"转换出错: {str(e)}") messagebox.showerror("错误", f"转换过程中出错:\n{str(e)}") def main(): # 检查是否有命令行参数 if len(sys.argv) > 1: # 命令行模式 args = setup_cli() # 检查输入和输出目录 if not os.path.exists(args.input): print(f"错误: 输入路径 '{args.input}' 不存在") return # 调整大小参数 resize = tuple(args.resize) if args.resize else none # 开始转换 print(f"开始转换图片从 {args.input} 到 {args.output},格式: {args.format}") success, failed = batch_convert( args.input, args.output, args.format.upper(), args.quality, resize, args.recursive, args.keep ) print(f"转换完成。成功: {success}, 失败: {failed}") else: # gui模式 root = tk.tk() app = imageconvertergui(root) root.mainloop() if __name__ == "__main__": main()
知识点:
命令行模式和gui模式的切换
sys.argv: 获取命令行参数
tkinter主循环: root.mainloop()
使用方法
命令行方式
# 基本用法 python image_converter.py 输入目录 输出目录 目标格式 # 示例: 将input_folder中的图片转换为png格式并保存到output_folder python image_converter.py input_folder output_folder png # 高级用法 python image_converter.py input_folder output_folder jpg -q 85 -r --resize 800 600
gui方式
直接运行程序,不带任何参数:
python image_converter.py
然后在图形界面中:
- 选择输入目录
- 选择输出目录
- 设置目标格式和其他选项
- 点击"开始转换"按钮
进阶知识点
1. 图像处理基础
像素: 图像的基本单位
颜色模式: rgb, rgba, cmyk, 灰度等
图像格式特点:
- jpeg: 有损压缩,不支持透明度,适合照片
- png: 无损压缩,支持透明度,适合图标和截图
- gif: 支持动画,有限的颜色数量
- bmp: 无压缩,文件较大
- tiff: 高质量,支持多页,常用于专业印刷
2. pillow高级特性
- 图像增强: 亮度、对比度、锐化等调整
- 滤镜效果: 模糊、锐化、边缘检测等
- 图像合成: 图层混合、水印等
- 批处理: 多进程处理提高效率
3. 性能优化
- 使用生成器减少内存占用
- 多线程/多进程处理提高转换速度
- 缩略图生成优化
可扩展功能
批量水印添加:为图片添加文字或图片水印
图片批量裁剪:自动裁剪图片到指定比例或尺寸
批量图片优化:自动调整亮度、对比度和图片锐化
批量重命名:根据规则批量重命名图片文件
元数据处理:保留或清除exif信息
到此这篇关于基于python实现图片格式批量转换器的文章就介绍到这了,更多相关python图片格式转换内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论