分享自制 Python 图片压缩工具!自定义质量、尺寸,轻松优化图片体积~
平时发论坛、存图时总被 “图片太大不好传 / 占空间” 困扰,在线压缩要么限制多,要么担心隐私,
所以自己用 Python 写了个本地图片压缩工具 ,分享给有需要的朋友~工具长啥样?先看界面
核心功能:想怎么压,就怎么压!
自定义压缩质量:用滑块调节(数值 0-100,越小体积越小,但画质可能下降)。适合论坛贴图、日常分享等对画质要求不高的场景。
调节输出尺寸比例:滑块拉到0.8x,图片就会按原尺寸的 80% 缩小,体积也会跟着降低;想保持原尺寸,拉到1.0x即可。
目标文件大小限制:直接输入 “最大 KB 数”(比如论坛限制 200KB,就填200),工具会尽量把图片压到这个大小内。
傻瓜式操作:选图→调参数→点压缩,三步完成!压缩后的图片会自动以「原文件名_compressed」的格式,保存在原图片同目录下,不用手动选保存路径~
手把手教你用:
点击「选择图片」,选中要压缩的图片(支持 jpg、png 等常见格式)。
调节「压缩质量」滑块(默认60,亲测这个值画质和体积比较平衡)。
调节「输出尺寸比例」滑块(默认0.8x,想保留原图尺寸就拉到1.0x)。
(可选)在「目标文件大小 (KB)」输入框,填写你想要的最大体积(比如200)。
点击「压缩图片」,几秒后就能在原图片文件夹里,找到压缩好的文件~
本地处理,更安全:图片不用上传到第三方服务器,压缩个人照片、工作图也不用担心隐私泄露。
自定义选项多:质量、尺寸、目标大小都能精细调节,比很多在线工具更灵活。
免费无广告:自己写的工具,纯粹分享,没有弹窗和收费套路~
打包好的exe下载链接:https://bestfile.io/L4elW3zNKcXL22k/file
源码分享:
import tkinter as tk
from tkinter import filedialog, ttk, messagebox
from PIL import Image
import os
import math
class ImageCompressor:
def __init__(self, root):
self.root = root
self.root.title("图片压缩工具 - 体积优化版")
self.root.geometry("650x450")
self.root.resizable(True, True)
# 设置中文字体支持
self.style = ttk.Style()
self.style.configure("TLabel", font=("SimHei", 10))
self.style.configure("TButton", font=("SimHei", 10))
self.style.configure("TScale", font=("SimHei", 10))
# 源图片路径
self.source_path = tk.StringVar()
# 输出质量
self.quality = tk.IntVar(value=60) # 降低默认质量,确保体积减小
# 输出尺寸比例
self.scale = tk.DoubleVar(value=0.8) # 默认适当缩小
# 目标文件大小(KB)
self.target_size = tk.IntVar(value=200)
self.create_widgets()
def create_widgets(self):
# 创建主框架
main_frame = ttk.Frame(self.root, padding="20")
main_frame.pack(fill=tk.BOTH, expand=True)
# 源文件选择
ttk.Label(main_frame, text="源图片:").grid(row=0, column=0, sticky=tk.W, pady=5)
ttk.Entry(main_frame, textvariable=self.source_path, width=50).grid(row=0, column=1, pady=5)
ttk.Button(main_frame, text="选择图片", command=self.select_image).grid(row=0, column=2, padx=5, pady=5)
# 原始文件信息
self.original_info = ttk.Label(main_frame, text="原始图片信息: 未选择图片", foreground="gray")
self.original_info.grid(row=1, column=0, columnspan=3, sticky=tk.W, pady=5)
# 输出质量设置
ttk.Label(main_frame, text="压缩质量:").grid(row=2, column=0, sticky=tk.W, pady=5)
quality_scale = ttk.Scale(main_frame, from_=1, to=100, variable=self.quality, orient=tk.HORIZONTAL, length=300)
quality_scale.grid(row=2, column=1, pady=5)
ttk.Label(main_frame, textvariable=self.quality).grid(row=2, column=2, padx=5, pady=5)
ttk.Label(main_frame, text="(数值越小,文件越小,质量可能下降越多)").grid(row=3, column=1, sticky=tk.W, pady=2)
# 输出尺寸设置
ttk.Label(main_frame, text="输出尺寸比例:").grid(row=4, column=0, sticky=tk.W, pady=5)
scale_scale = ttk.Scale(main_frame, from_=0.1, to=1.0, variable=self.scale, orient=tk.HORIZONTAL, length=300)
scale_scale.grid(row=4, column=1, pady=5)
self.scale_label = ttk.Label(main_frame, text=f"{self.scale.get():.1f}x")
self.scale_label.grid(row=4, column=2, padx=5, pady=5)
scale_scale.bind("<Motion>", self.update_scale_label)
ttk.Label(main_frame, text="(小于1.0表示缩小图片尺寸)").grid(row=5, column=1, sticky=tk.W, pady=2)
# 目标文件大小设置
ttk.Label(main_frame, text="目标文件大小(KB):").grid(row=6, column=0, sticky=tk.W, pady=5)
ttk.Entry(main_frame, textvariable=self.target_size, width=10).grid(row=6, column=1, sticky=tk.W, pady=5)
ttk.Label(main_frame, text="(设置希望的最大文件大小)").grid(row=7, column=1, sticky=tk.W, pady=2)
# 压缩按钮
compress_btn = ttk.Button(main_frame, text="压缩图片", command=self.compress_image)
compress_btn.grid(row=8, column=0, columnspan=3, pady=20)
# 状态标签
self.status_label = ttk.Label(main_frame, text="请选择一张图片进行压缩", foreground="gray")
self.status_label.grid(row=9, column=0, columnspan=3, pady=10)
# 底部信息
ttk.Label(main_frame, text="压缩后的图片将保存为原文件名+_compressed", foreground="blue").grid(row=10, column=0, columnspan=3, pady=5)
def update_scale_label(self, event):
"""更新尺寸比例显示"""
self.scale_label.config(text=f"{self.scale.get():.1f}x")
def select_image(self):
"""选择图片文件并显示信息"""
file_path = filedialog.askopenfilename(
filetypes=[("图片文件", "*.jpg;*.jpeg;*.png;*.bmp;*.gif")]
)
if file_path:
self.source_path.set(file_path)
try:
# 获取图片信息
with Image.open(file_path) as img:
width, height = img.size
file_size = os.path.getsize(file_path) / 1024 # KB
info_text = f"原始图片信息: {width}x{height} 像素, {file_size:.2f}KB, 格式: {img.format}"
self.original_info.config(text=info_text, foreground="black")
self.status_label.config(text=f"已选择: {os.path.basename(file_path)}", foreground="green")
except Exception as e:
self.status_label.config(text=f"获取图片信息失败: {str(e)}", foreground="red")
def compress_image(self):
"""压缩图片,确保体积减小"""
input_path = self.source_path.get()
if not input_path or not os.path.exists(input_path):
messagebox.showerror("错误", "请先选择一张有效的图片")
return
try:
# 打开图片
with Image.open(input_path) as img:
# 转换为RGB模式(对于PNG等有透明通道的格式,避免压缩问题)
if img.mode in ('RGBA', 'LA') or (img.mode == 'P' and 'transparency' in img.info):
background = Image.new(img.mode[:-1], img.size, (255, 255, 255))
background.paste(img, img.split()[-1])
img = background
# 计算新尺寸
width, height = img.size
new_width = int(width * self.scale.get())
new_height = int(height * self.scale.get())
# 确保新尺寸不会大于原尺寸
new_width = min(new_width, width)
new_height = min(new_height, height)
# 调整尺寸 - 使用更高效的压缩算法
resized_img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
# 生成输出路径
dir_name, file_name = os.path.split(input_path)
base_name, ext = os.path.splitext(file_name)
# 强制转换为JPG格式,通常比PNG更节省空间
output_ext = '.jpg' if ext.lower() not in ['.jpg', '.jpeg'] else ext
output_path = os.path.join(dir_name, f"{base_name}_compressed{output_ext}")
# 检查是否需要多次压缩以达到目标大小
quality = self.quality.get()
target_size_kb = self.target_size.get()
# 第一次压缩
resized_img.save(output_path, format='JPEG' if output_ext.lower() in ['.jpg', '.jpeg'] else 'PNG',
quality=quality, optimize=True, progressive=True)
# 检查文件大小,如果超过目标大小则降低质量重新压缩
current_size_kb = os.path.getsize(output_path) / 1024
max_attempts = 5
attempts = 0
while current_size_kb > target_size_kb and quality > 10 and attempts < max_attempts:
# 计算需要降低的质量比例
size_ratio = target_size_kb / current_size_kb
quality = max(10, int(quality * math.sqrt(size_ratio))) # 使用平方根比例降低质量
# 重新压缩
resized_img.save(output_path, format='JPEG',
quality=quality, optimize=True, progressive=True)
current_size_kb = os.path.getsize(output_path) / 1024
attempts += 1
# 获取最终文件大小信息
original_size = os.path.getsize(input_path) / 1024
compressed_size = os.path.getsize(output_path) / 1024
size_reduction = ((original_size - compressed_size) / original_size) * 100
self.status_label.config(
text=f"压缩完成! 已保存至: {output_path}\n"
f"原始大小: {original_size:.2f}KB, 压缩后: {compressed_size:.2f}KB\n"
f"体积减少: {size_reduction:.2f}%",
foreground="green"
)
messagebox.showinfo("成功",
f"图片已成功压缩并保存至:\n{output_path}\n"
f"原始大小: {original_size:.2f}KB → 压缩后: {compressed_size:.2f}KB\n"
f"体积减少: {size_reduction:.2f}%")
except Exception as e:
self.status_label.config(text=f"压缩失败: {str(e)}", foreground="red")
messagebox.showerror("错误", f"压缩过程中发生错误:\n{str(e)}")
if __name__ == "__main__":
root = tk.Tk()
# 确保中文显示正常
root.option_add("*Font", "SimHei 10")
app = ImageCompressor(root)
root.mainloop()
#电脑软件(12)#小技巧(16)
评论