未来路 发表于 2025-6-5 18:45:59

图片无损放大,无需AI模型一个脚本搞定

直接用这个脚本就行
本地运行,无需网络,上万张图片无损放大几分钟就搞定
这个脚本一键搞定一个目录下所有图片三倍无损放大(递归处理)并覆盖原图
import os
import threading
from tkinter import (
    Tk, Label, Entry, Button, StringVar, IntVar, Checkbutton, Text,
    filedialog, Frame, ttk, DISABLED, NORMAL
)
from PIL import Image


class ImageResizerApp:
    def __init__(self, root):
      self.root = root
      self.root.title("图片批量无损放大工具")
      self.center_window(858, 600)# 居中窗口

      self.stop_flag = False
      self.supported_exts = ['.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tiff']
      self.selected_exts = []
      self.processed_count = 0
      self.skipped_count = 0
      self.failed_count = 0

      self.build_gui()

    def center_window(self, width, height):
      screen_width = self.root.winfo_screenwidth()
      screen_height = self.root.winfo_screenheight()
      x = int((screen_width - width) / 2)
      y = int((screen_height - height) / 2)
      self.root.geometry(f"{width}x{height}+{x}+{y}")

    def build_gui(self):
      # 输入路径
      Label(self.root, text="输入文件夹:").grid(row=0, column=0, sticky='e')
      self.input_entry = Entry(self.root, width=60)
      self.input_entry.grid(row=0, column=1, padx=5)
      Button(self.root, text="浏览", command=self.select_input).grid(row=0, column=2)

      # 覆盖原图(独立一行)
      self.overwrite_var = IntVar()
      Checkbutton(self.root, text="覆盖原图", variable=self.overwrite_var, command=self.toggle_output).grid(
            row=1, column=0, columnspan=3, sticky='w', padx=20, pady=2)

      # 输出路径(带标签)
      Label(self.root, text="输出文件夹:").grid(row=2, column=0, sticky='e')
      self.output_entry = Entry(self.root, width=60)
      self.output_entry.grid(row=2, column=1, padx=5)
      Button(self.root, text="浏览", command=self.select_output).grid(row=2, column=2)

      # 放大倍数
      Label(self.root, text="放大倍数:").grid(row=3, column=0, sticky='e')
      self.scale_var = StringVar(value="3")
      Entry(self.root, textvariable=self.scale_var, width=10).grid(row=3, column=1, sticky='w')

      # 文件大小限制
      Label(self.root, text="图片体积大于 KB自动跳过:").grid(row=3, column=1, sticky='e')
      self.size_limit_var = StringVar(value="50")
      Entry(self.root, textvariable=self.size_limit_var, width=10).grid(row=3, column=2, sticky='w')

      # 图片格式选择
      Label(self.root, text="图片格式:").grid(row=4, column=0, sticky='ne')
      self.ext_vars = {}
      format_frame = Frame(self.root)
      format_frame.grid(row=4, column=1, sticky='w')
      for ext in self.supported_exts:
            var = IntVar(value=1)
            Checkbutton(format_frame, text=ext, variable=var).pack(side='left')
            self.ext_vars = var

      # 自定义扩展名
      Label(self.root, text="追加自定义\n图片类型扩展名 (逗号分隔):").grid(row=5, column=0, sticky='e')
      self.custom_ext_entry = Entry(self.root, width=60)
      self.custom_ext_entry.grid(row=5, column=1, padx=5, pady=2)

      # 按钮 + 进度条
      button_frame = Frame(self.root)
      button_frame.grid(row=6, column=0, columnspan=3, pady=10)
      self.start_btn = Button(button_frame, text="开始处理", command=self.start_processing)
      self.start_btn.pack(side='left', padx=5)
      self.stop_btn = Button(button_frame, text="停止", command=self.stop_processing, state=DISABLED)
      self.stop_btn.pack(side='left', padx=5)

      self.progress = ttk.Progressbar(self.root, length=500)
      self.progress.grid(row=7, column=0, columnspan=3, pady=10)

      # 日志区
      Label(self.root, text="日志:").grid(row=8, column=0, sticky='ne')
      self.log_text = Text(self.root, width=95, height=20)
      self.log_text.grid(row=8, column=1, columnspan=2)

    def select_input(self):
      folder = filedialog.askdirectory()
      if folder:
            self.input_entry.delete(0, 'end')
            self.input_entry.insert(0, folder)

    def select_output(self):
      folder = filedialog.askdirectory()
      if folder:
            self.output_entry.delete(0, 'end')
            self.output_entry.insert(0, folder)

    def toggle_output(self):
      state = DISABLED if self.overwrite_var.get() else NORMAL
      self.output_entry.config(state=state)

    def log(self, message):
      self.log_text.insert('end', message + '\n')
      self.log_text.see('end')

    def start_processing(self):
      self.stop_flag = False
      self.start_btn.config(state=DISABLED)
      self.stop_btn.config(state=NORMAL)
      self.log_text.delete('1.0', 'end')
      threading.Thread(target=self.process_images).start()

    def stop_processing(self):
      self.stop_flag = True
      self.log("正在停止...")

    def process_images(self):
      input_dir = self.input_entry.get().strip()
      output_dir = self.output_entry.get().strip()
      scale = int(self.scale_var.get())
      size_limit_kb = int(self.size_limit_var.get())
      self.selected_exts =
      custom_exts = [e.strip().lower() if e.startswith('.') else f".{e.strip().lower()}"
                     for e in self.custom_ext_entry.get().replace(',', ',').split(',') if e.strip()]
      self.selected_exts.extend(custom_exts)

      if not os.path.isdir(input_dir):
            self.log("❌ 输入文件夹无效!")
            return

      all_files = []
      for foldername, _, filenames in os.walk(input_dir):
            for filename in filenames:
                ext = os.path.splitext(filename).lower()
                if ext in self.selected_exts:
                  all_files.append(os.path.join(foldername, filename))

      self.progress["maximum"] = len(all_files)
      self.progress["value"] = 0
      self.processed_count = self.skipped_count = self.failed_count = 0

      for i, file_path in enumerate(all_files):
            if self.stop_flag:
                self.log("⏹ 已停止处理。")
                break

            try:
                if os.path.getsize(file_path) >= size_limit_kb * 1024:
                  self.log(f"跳过(文件过大): {file_path}")
                  self.skipped_count += 1
                  continue

                with Image.open(file_path) as img:
                  new_size = (img.width * scale, img.height * scale)
                  resized_img = img.resize(new_size, Image.Resampling.LANCZOS)

                  if self.overwrite_var.get():
                        save_path = file_path
                  else:
                        rel_path = os.path.relpath(file_path, input_dir)
                        save_path = os.path.join(output_dir, rel_path)
                        os.makedirs(os.path.dirname(save_path), exist_ok=True)

                  file_ext = os.path.splitext(save_path).lower()
                  temp_path = save_path + ".tmp"
                  save_args = {'format': img.format, 'quality': 95} if file_ext in ('.jpg', '.jpeg') \
                        else {'format': img.format, 'optimize': True} if file_ext == '.png' \
                        else {'format': img.format}

                  resized_img.save(temp_path, **save_args)
                  os.replace(temp_path, save_path)
                  self.log(f"✅ 已处理: {file_path}")
                  self.processed_count += 1

            except Exception as e:
                self.log(f"❌ 失败: {file_path} - 错误: {str(e)}")
                self.failed_count += 1

            self.progress["value"] = i + 1

      self.log(f"🎉 完成!成功处理: {self.processed_count} 张,跳过: {self.skipped_count} 张,失败: {self.failed_count} 张")
      self.start_btn.config(state=NORMAL)
      self.stop_btn.config(state=DISABLED)


if __name__ == "__main__":
    root = Tk()
    app = ImageResizerApp(root)
    root.mainloop()

btpan 发表于 2025-6-5 20:09:34

感谢大佬

jm1234 发表于 2025-6-6 00:40:48

看不懂

sso 发表于 2025-6-8 01:51:13

不是py吗?~/Desktop$ python3 ai.py
Traceback (most recent call last):
File "/home/m/Desktop/ai.py", line 3, in <module>
    from tkinter import (
ModuleNotFoundError: No module named 'tkinter'

trouvailleme 发表于 2025-6-11 09:59:53

感谢大佬
页: [1]
查看完整版本: 图片无损放大,无需AI模型一个脚本搞定