一、项目背景
在日常工作尤其是素材处理过程中,我常常遇到需要批量调整图片分辨率的场景。虽然可以使用 PhotoShop 等专业工具,但每次手动调整多张图片、统一输出尺寸时,步骤繁琐且容易出错,尤其是需要保持宽高比时,PS中的操作往往无法做到快速精准的批量匹配。
为了提升效率,我用 Python 开发了一个带图形界面的 “图片分辨率批量修改工具”,支持一键导入多张图片、自定义输出尺寸、保持原始宽高比,并能实时预览图片信息。代码完全开源,无任何依赖限制,适合日常办公与学习使用。
![图片[1]-修改图片分辨率的工具Python源码-QQ沐编程](https://www.qqmu.com/wp-content/uploads/2026/01/fenbianlv.webp)
二、主要功能特点
- 📁 批量导入:支持一次性导入多张图片,自动识别常见格式(JPG、PNG、BMP、GIF、WebP 等)
- 🖼 尺寸灵活设置:可自由设定目标宽度与高度,并可选“保持原始宽高比”防止图片变形
- 📊 图片信息预览:列表显示每张图片的文件名、原始尺寸与格式
- 📂 输出路径自定义:可指定保存目录,自动避免文件覆盖
- 🚀 一键处理与重置:提供开始、重置、退出等操作按钮,界面清晰易用
- 🖥 纯 Python 实现:基于
tkinter构建图形界面,使用PIL(Pillow)进行图像处理,跨平台运行
三、代码结构与技术实现
工具采用面向对象的方式设计,主要分为界面构建与图像处理两大模块。
1. 界面布局(tkinter)
- 标题与主框架:设定窗口标题、尺寸与背景色
- 控制面板:包含分辨率设置、导入/导出区域、操作按钮
- 图片列表:使用
ttk.Treeview表格展示已导入图片的详细信息
2. 核心功能方法
import_images():通过文件对话框选择图片,支持多选与格式过滤refresh_image_list():使用PIL读取图片尺寸与格式,刷新列表显示start_processing():- 根据是否勾选“保持宽高比”,按比例或强制拉伸调整尺寸
- 使用
LANCZOS重采样算法,保证缩放质量 - 自动为重复文件名添加后缀,避免覆盖
reset():清空所有设置与列表,恢复初始状态
3. 依赖库
tkinter:Python 标准 GUI 库,无需安装PIL(Pillow):图像处理库,需通过pip install Pillow安装
四、使用说明
1. 环境准备
pip install Pillow
2. 运行程序
将下方完整代码保存为 image_resizer.py,双击运行或通过命令行启动:
python image_resizer.py
3. 操作步骤
- 点击 “导入图片”,选择一张或多张图片
- 设置目标 宽度 与 高度(单位:像素)
- 如需保持图片原比例,请勾选 “保持原始宽高比”
- 点击 “选择输出路径” 指定保存位置(可选,默认为弹窗选择)
- 点击 “开始处理”,程序将批量调整尺寸并保存至目标文件夹
- 可使用 “重置” 清空当前任务,或 “退出” 关闭程序
五、完整源代码
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
from PIL import Image
import os
class ImageResizerApp:
def __init__(self, root):
self.root = root
self.root.title("图片分辨率修改工具")
self.root.geometry("800x650")
self.root.configure(bg="#f0f0f0")
self.image_paths = []
self.target_width = tk.IntVar(value=800)
self.target_height = tk.IntVar(value=600)
self.keep_aspect_ratio = tk.BooleanVar(value=True)
self.output_path = ""
self.setup_ui()
def setup_ui(self):
# 标题
title_label = tk.Label(
self.root,
text="图片分辨率修改工具",
font=("Arial", 18, "bold"),
bg="#f0f0f0",
fg="#333"
)
title_label.pack(pady=15)
# 主内容区域
main_frame = tk.Frame(self.root, bg="#f0f0f0")
main_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=10)
# 左侧控制面板
control_frame = tk.LabelFrame(
main_frame,
text="控制面板",
font=("Arial", 12),
bg="#f0f0f0",
fg="#333",
padx=10,
pady=10
)
control_frame.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 10))
# 分辨率设置区域
resolution_frame = tk.LabelFrame(
control_frame,
text="分辨率设置",
font=("Arial", 10),
bg="#f0f0f0",
fg="#333"
)
resolution_frame.pack(fill=tk.X, pady=(0, 15))
# 宽度设置
width_frame = tk.Frame(resolution_frame, bg="#f0f0f0")
width_frame.pack(fill=tk.X, pady=5)
tk.Label(width_frame, text="宽度:", font=("Arial", 10), bg="#f0f0f0").pack(side=tk.LEFT)
width_entry = tk.Entry(
width_frame,
textvariable=self.target_width,
font=("Arial", 10),
width=10
)
width_entry.pack(side=tk.RIGHT)
# 高度设置
height_frame = tk.Frame(resolution_frame, bg="#f0f0f0")
height_frame.pack(fill=tk.X, pady=5)
tk.Label(height_frame, text="高度:", font=("Arial", 10), bg="#f0f0f0").pack(side=tk.LEFT)
height_entry = tk.Entry(
height_frame,
textvariable=self.target_height,
font=("Arial", 10),
width=10
)
height_entry.pack(side=tk.RIGHT)
# 宽高比选项
aspect_check = tk.Checkbutton(
resolution_frame,
text="保持原始宽高比",
variable=self.keep_aspect_ratio,
font=("Arial", 10),
bg="#f0f0f0"
)
aspect_check.pack(pady=5)
# 导入导出设置
io_frame = tk.LabelFrame(
control_frame,
text="导入导出设置",
font=("Arial", 10),
bg="#f0f0f0",
fg="#333"
)
io_frame.pack(fill=tk.X, pady=10)
tk.Label(io_frame, text="导入图片:", font=("Arial", 10), bg="#f0f0f0").pack(anchor=tk.W, pady=(0, 5))
self.import_path_var = tk.StringVar(value="未选择")
import_path_label = tk.Label(
io_frame,
textvariable=self.import_path_var,
font=("Arial", 9),
bg="#ffffff",
relief=tk.SUNKEN,
anchor=tk.W
)
import_path_label.pack(fill=tk.X, pady=(0, 5))
import_btn = tk.Button(
io_frame,
text="导入图片",
command=self.import_images,
bg="#4CAF50",
fg="white",
font=("Arial", 10, "bold"),
relief=tk.FLAT,
padx=10,
pady=5
)
import_btn.pack(fill=tk.X, pady=5)
tk.Label(io_frame, text="输出路径:", font=("Arial", 10), bg="#f0f0f0").pack(anchor=tk.W, pady=(0, 5))
self.output_path_var = tk.StringVar(value="未选择")
output_path_label = tk.Label(
io_frame,
textvariable=self.output_path_var,
font=("Arial", 9),
bg="#ffffff",
relief=tk.SUNKEN,
anchor=tk.W
)
output_path_label.pack(fill=tk.X, pady=(0, 5))
select_output_btn = tk.Button(
io_frame,
text="选择输出路径",
command=self.select_output_path,
bg="#9C27B0",
fg="white",
font=("Arial", 10, "bold"),
relief=tk.FLAT,
padx=10,
pady=3
)
select_output_btn.pack(fill=tk.X)
# 操作按钮区域
button_frame = tk.Frame(control_frame, bg="#f0f0f0")
button_frame.pack(fill=tk.X, pady=10)
start_btn = tk.Button(
button_frame,
text="开始处理",
command=self.start_processing,
bg="#2196F3",
fg="white",
font=("Arial", 10, "bold"),
relief=tk.FLAT,
padx=10,
pady=5
)
start_btn.pack(fill=tk.X, pady=5)
reset_btn = tk.Button(
button_frame,
text="重置",
command=self.reset,
bg="#FF9800",
fg="white",
font=("Arial", 10, "bold"),
relief=tk.FLAT,
padx=10,
pady=5
)
reset_btn.pack(fill=tk.X, pady=5)
exit_btn = tk.Button(
button_frame,
text="退出",
command=self.root.quit,
bg="#f44336",
fg="white",
font=("Arial", 10, "bold"),
relief=tk.FLAT,
padx=10,
pady=5
)
exit_btn.pack(fill=tk.X, pady=5)
# 右侧图片列表
list_frame = tk.LabelFrame(
main_frame,
text="图片列表",
font=("Arial", 12),
bg="#f0f0f0",
fg="#333",
padx=10,
pady=10
)
list_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
columns = ('filename', 'original_size', 'format')
self.tree = ttk.Treeview(list_frame, columns=columns, show='headings', height=5)
self.tree.heading('filename', text='文件名')
self.tree.heading('original_size', text='原始尺寸')
self.tree.heading('format', text='格式')
self.tree.column('filename', width=200)
self.tree.column('original_size', width=100)
self.tree.column('format', width=80)
scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self.tree.yview)
self.tree.configure(yscrollcommand=scrollbar.set)
self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
def import_images(self):
supported_formats = [
("All Supported Images", "*.jpg;*.jpeg;*.png;*.bmp;*.gif;*.tiff;*.webp;*.ico"),
("JPEG", "*.jpg;*.jpeg"),
("PNG", "*.png"),
("BMP", "*.bmp"),
("GIF", "*.gif"),
("TIFF", "*.tiff"),
("WEBP", "*.webp"),
("All Files", "*.*")
]
filenames = filedialog.askopenfilenames(
title="选择图片文件",
filetypes=supported_formats
)
if filenames:
for filename in filenames:
if filename not in self.image_paths:
self.image_paths.append(filename)
if len(filenames) > 1:
directories = set(os.path.dirname(path) for path in filenames)
if len(directories) == 1:
common_dir = list(directories)[0]
display_text = f"来自同目录: {os.path.basename(common_dir)} ({len(filenames)}张)"
else:
display_text = f"多目录导入 ({len(filenames)}张)"
self.import_path_var.set(display_text)
elif len(filenames) == 1:
self.import_path_var.set(os.path.dirname(filenames[0]))
else:
self.import_path_var.set("未选择")
self.refresh_image_list()
def refresh_image_list(self):
for item in self.tree.get_children():
self.tree.delete(item)
for path in self.image_paths:
try:
img = Image.open(path)
size_str = f"{img.width}x{img.height}"
format_str = img.format or "Unknown"
filename = os.path.basename(path)
self.tree.insert('', tk.END, values=(filename, size_str, format_str))
except Exception as e:
print(f"无法读取图片 {path}: {e}")
def start_processing(self):
if not self.image_paths:
messagebox.showwarning("警告", "请先导入图片!")
return
if not self.output_path:
self.output_path = filedialog.askdirectory(title="选择输出目录")
if not self.output_path:
return
self.output_path_var.set(self.output_path)
target_w = self.target_width.get()
target_h = self.target_height.get()
if target_w <= 0 or target_h <= 0:
messagebox.showerror("错误", "请输入有效的分辨率值!")
return
success_count = 0
error_count = 0
for img_path in self.image_paths:
try:
with Image.open(img_path) as img:
original_size = img.size
if self.keep_aspect_ratio.get():
original_ratio = original_size[0] / original_size[1]
target_ratio = target_w / target_h
if original_ratio > target_ratio:
new_width = target_w
new_height = int(target_w / original_ratio)
else:
new_height = target_h
new_width = int(target_h * original_ratio)
resized_img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
else:
resized_img = img.resize((target_w, target_h), Image.Resampling.LANCZOS)
base_name = os.path.splitext(os.path.basename(img_path))[0]
extension = os.path.splitext(img_path)[1]
output_path = os.path.join(self.output_path, f"{base_name}{extension}")
counter = 1
original_output_path = output_path
while os.path.exists(output_path):
name_part = os.path.splitext(original_output_path)[0]
ext_part = os.path.splitext(original_output_path)[1]
output_path = f"{name_part}_resized_{counter}{ext_part}"
counter += 1
resized_img.save(output_path, optimize=True, quality=95)
success_count += 1
except Exception as e:
print(f"处理图片失败 {img_path}: {e}")
error_count += 1
messagebox.showinfo(
"处理完成",
f"成功处理 {success_count} 张图片\n{error_count} 张图片处理失败"
)
def reset(self):
self.image_paths.clear()
self.target_width.set(800)
self.target_height.set(600)
self.keep_aspect_ratio.set(True)
self.output_path = ""
self.output_path_var.set("未选择")
self.refresh_image_list()
def select_output_path(self):
selected_path = filedialog.askdirectory(title="选择输出目录")
if selected_path:
self.output_path = selected_path
self.output_path_var.set(selected_path)
if __name__ == "__main__":
root = tk.Tk()
app = ImageResizerApp(root)
root.mainloop()
六、总结
这个工具虽然界面简洁,但切实解决了图片批量处理中的高频痛点。它避免了在专业软件中反复操作的繁琐,通过几步简单设置即可完成大量图片的尺寸统一,特别适合新媒体运营、电商美工、素材整理等场景。
所有代码均已开源,你可以根据需求进一步扩展功能,例如:
- 添加图片格式转换(如 PNG 转 JPG)
- 支持更多分辨率预设选项
- 增加图片旋转、裁剪等基础编辑功能
- 添加水印功能
如果你也经常需要处理图片尺寸,不妨尝试使用或改进这个工具,让重复性工作变得更高效。
提示:本工具为个人开发,完全免费开源。如需用于商业场景或批量处理重要图片,建议先在小批量图片上测试效果,确保符合预期后再正式使用。
© 版权声明
本站资源来自互联网收集,仅供用于学习和交流,请勿用于商业用途。如有侵权、不妥之处,请联系站长并出示版权证明以便删除。敬请谅解!
THE END












