it编程 > 前端脚本 > Python

基于Python实现图片格式批量转换器

8人参与 2025-06-11 Python

开发环境准备

python 3.6+

pillow库 (pil的fork版本)

安装pillow库:

pip install pillow

核心功能

支持多种常见图片格式之间的转换(jpg, png, bmp, gif, tiff等)

批量处理指定文件夹中的所有图片

可选择保留原始图片或仅保留转换后的图片

支持调整图片质量和大小

简单的命令行界面和图形用户界面(gui)两种使用方式

技术要点

1. pillow库基础

pillow是python图像处理库(pil)的一个分支,提供了丰富的图像处理功能。主要用到的模块:

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, 灰度等

图像格式特点:

2. pillow高级特性

3. 性能优化

可扩展功能

批量水印添加:为图片添加文字或图片水印

图片批量裁剪:自动裁剪图片到指定比例或尺寸

批量图片优化:自动调整亮度、对比度和图片锐化

批量重命名:根据规则批量重命名图片文件

元数据处理:保留或清除exif信息

到此这篇关于基于python实现图片格式批量转换器的文章就介绍到这了,更多相关python图片格式转换内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

您想发表意见!!点此发布评论

推荐阅读

Python编写邮件自动发送工具的完整指南

06-11

现代 Python 包管理器 uv的使用详解

06-11

Python使用sort()方法对数组进行排序的操作指南

06-11

Python获取CMD环境变量值的完整实现指南

06-11

Python标准库中email模块的使用方法与内部机制详解

06-11

Python使用glob库批量匹配文件路径

06-11

猜你喜欢

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论