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

查看 8|回复 0
作者:LIUHJV5   
大家好!平时发论坛、存图时总被 **“图片太大不好传 / 占空间”[color=rgb(0, 0, 0) !important]困扰,在线压缩要么限制多,要么担心隐私,所以自己用 Python 写了个本地图片压缩工具 **,分享给有需要的朋友~工具长啥样?先看界面



核心功能:想怎么压,就怎么压!
  • [color=rgb(0, 0, 0) !important]自定义压缩质量:用滑块调节(数值 0-100,越小体积越小,但画质可能下降)。适合论坛贴图、日常分享等对画质要求不高的场景。
  • [color=rgb(0, 0, 0) !important]调节输出尺寸比例:滑块拉到0.8x,图片就会按原尺寸的 80% 缩小,体积也会跟着降低;想保持原尺寸,拉到1.0x即可。
  • [color=rgb(0, 0, 0) !important]目标文件大小限制:直接输入 “最大 KB 数”(比如论坛限制 200KB,就填200),工具会尽量把图片压到这个大小内。
  • [color=rgb(0, 0, 0) !important]傻瓜式操作:选图→调参数→点压缩,三步完成!压缩后的图片会自动以「原文件名_compressed」的格式,保存在原图片同目录下,不用手动选保存路径~
    手把手教你用:
  • 点击「选择图片」,选中要压缩的图片(支持 jpg、png 等常见格式)。
  • 调节「压缩质量」滑块(默认60,亲测这个值画质和体积比较平衡)。
  • 调节「输出尺寸比例」滑块(默认0.8x,想保留原图尺寸就拉到1.0x)。
  • (可选)在「目标文件大小 (KB)」输入框,填写你想要的最大体积(比如200)。
  • 点击「压缩图片」,几秒后就能在原图片文件夹里,找到压缩好的文件~

  • [color=rgb(0, 0, 0) !important]本地处理,更安全:图片不用上传到第三方服务器,压缩个人照片、工作图也不用担心隐私泄露。
  • [color=rgb(0, 0, 0) !important]自定义选项多:质量、尺寸、目标大小都能精细调节,比很多在线工具更灵活。
  • [color=rgb(0, 0, 0) !important]免费无广告:自己写的工具,纯粹分享,没有弹窗和收费套路~
  • [Python] 纯文本查看 复制代码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("", 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

    图片, 尺寸

  • 您需要登录后才可以回帖 登录 | 立即注册

    返回顶部