分享自制 Python 图片压缩工具!自定义质量、尺寸,轻松优化图片体积~

九月 25, 2025 / Ming / 2阅读 / 0评论/ 分类: 默认分类

平时发论坛、存图时总被 “图片太大不好传 / 占空间” 困扰,在线压缩要么限制多,要么担心隐私,

所以自己用 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)

评论