SuperResolution

This is a simple tkinter programm base on the Open Source Project: GitHub - xinntao/Real-ESRGAN: Real-ESRGAN aims at developing Practical Algorithms for General Image/Video Restoration.

When you enter the link, you need to download the "Portable executable files (NCNN)" one and then make dir "Load_picture", and put a picture name "Default.jpg" into it. You can choose whatever picuture.

Then, you can put my python project in the sibling folders which contains a executable procedure called "realesrgan-ncnn-vulkan.exe"

import tkinter as tk
from threading import Semaphore, Thread
from tkinter import filedialog, messagebox, Scrollbar, ttk
from subprocess import Popen, PIPE, STDOUT
from re import search
from PIL import Image, ImageTk
from os import path, remove, scandir


class GUI(object):
    def __init__(self):
        self.root = tk.Tk()
        self.img_app = ImageApp()
        self.infile_path = None
        self.outfile_path = None
        self.infile_path_entry = None
        self.output_folder_entry = None
        self.img_label1 = None
        self.img_label2 = None
        self.open_button1 = None
        self.open_button2 = None
        self.original_frame = None
        self.dealt_frame = None
        self.command_frame = None
        self.output_info_frame = None
        self.dealt_mode_combobox = None
        self.module_combobox = None
        self.scale_combobox = None
        self.extension_combobox = None
        self.output_text = None
        self.set_all_element()

    def set_all_element(self):
        # 布置所有控件
        self.set_root_win()
        self.set_frame()
        self.set_info_label()
        self.set_open_button()
        self.set_entry_and_output_text()
        self.show_default_img()
        self.set_combobox()
        self.root.mainloop()

    def set_root_win(self):
        self.root.resizable(False, False)
        self.root.title("超分辨率处理GUI")
        self.root.geometry('1080x650+300+100')

    def set_frame(self):
        self.original_frame = tk.Frame(self.root, borderwidth=2, relief='solid',
                                       highlightbackground='gray', highlightthickness=2)
        self.dealt_frame = tk.Frame(self.root, borderwidth=2, relief='solid',
                                    highlightbackground='gray', highlightthickness=2)
        self.command_frame = tk.Frame(self.root, borderwidth=2, relief='solid')
        self.output_info_frame = tk.Frame(self.root, borderwidth=2, relief='solid')
        self.command_frame.place(x=0, y=322, height=328, width=540)
        self.output_info_frame.place(x=540, y=322, height=328, width=540)

    def set_info_label(self):
        label1 = tk.Label(self.root, text='原图', font=('宋体', 17))
        label2 = tk.Label(self.root, text='处理后图片', font=('宋体', 17))
        label3 = tk.Label(self.root, text='图片处理模式:', font=('宋体', 16))
        label4 = tk.Label(self.root, text='图片输入路径:', font=('宋体', 16))
        label5 = tk.Label(self.root, text='图片输出路径:', font=('宋体', 16))
        label6 = tk.Label(self.root, text='参数模型路径:', font=('宋体', 16))
        label7 = tk.Label(self.root, text='图片放大尺寸:', font=('宋体', 16))
        label8 = tk.Label(self.root, text='输出文件格式:', font=('宋体', 16))
        label1.place(x=210, y=293)
        label2.place(x=780, y=293)
        label3.place(x=25, y=340)
        label4.place(x=25, y=390)
        label5.place(x=25, y=440)
        label6.place(x=25, y=490)
        label7.place(x=25, y=540)
        label8.place(x=240, y=540)

    def set_open_button(self):
        self.open_button1 = tk.Button(self.root, text="预览图片", bg="wheat", font=('宋体', 13),
                                      command=lambda: self.img_app.open_img(self.infile_path))
        self.open_button2 = tk.Button(self.root, text="预览图片", bg="wheat", font=('宋体', 13),
                                      command=lambda: self.img_app.open_img(self.outfile_path))
        open_folder1 = tk.Button(self.root, text="打开文件夹", bg="wheat", font=('宋体', 13),
                                 command=self.img_app.open_folder)
        open_folder2 = tk.Button(self.root, text='选择文件夹', bg="wheat",
                                 command=lambda: self.choose_folder(1))
        open_file = tk.Button(self.root, text='打开文件', bg='wheat', font=('宋体', 12),
                              command=self.check_open)
        execute_btn = tk.Button(self.root, text='开始图片处理', font=('宋体', 16), bg='peachpuff',
                                command=self.execute_entry)
        self.open_button1.place(x=378, y=293, width=80)
        self.open_button2.place(x=570, y=293)
        open_folder1.place(x=665, y=293)
        open_folder2.place(x=378, y=440, width=75)
        open_file.place(x=378, y=390, height=30)
        execute_btn.place(x=200, y=590)

    def set_entry_and_output_text(self):
        self.infile_path_entry = tk.Entry(self.root, width=30)
        self.infile_path_entry.place(x=165, y=390, height=30)
        self.output_folder_entry = tk.Entry(self.root, width=30)
        self.output_folder_entry.place(x=165, y=440, height=30)
        output_scrollbar = Scrollbar(self.root)
        output_scrollbar.place(x=1060, y=325, height=320)
        self.output_text = tk.Text(self.root, font=('宋体', 18))
        self.output_text.place(x=542, y=323, width=518, height=323)  # width=535
        self.output_text.config(yscrollcommand=output_scrollbar.set)
        output_scrollbar.config(command=self.output_text.yview)

    def set_combobox(self):
        self.dealt_mode_combobox = ttk.Combobox(self.root, values=['单张图片处理', '批量图片处理'], font=('宋体', 14))
        self.dealt_mode_combobox.insert(tk.END, '单张图片处理')
        self.dealt_mode_combobox.config(state='readonly')
        self.module_combobox = ttk.Combobox(self.root, values=[
               r"models/realesr-animevideov3-x2.bin",
               r"models/realesr-animevideov3-x3.bin",
               r"models/realesr-animevideov3-x4.bin",
               r"models/realesrgan-x4plus.bin",
               r"models/realesrgan-x4plus-anime.bin"
        ])
        self.module_combobox.insert(tk.END, r"models/realesrgan-x4plus-anime.bin")
        self.module_combobox.config(state='readonly')
        self.scale_combobox = ttk.Combobox(self.root, values=['2', '3', '4'])
        self.scale_combobox.insert(tk.END, '4')
        self.scale_combobox.config(state='readonly')
        self.extension_combobox = ttk.Combobox(self.root, values=['.jpg', '.png', '.webp'])
        self.extension_combobox.insert(tk.END, '.png')
        self.extension_combobox.config(state='readonly')
        self.dealt_mode_combobox.place(x=165, y=340, width=160, height=30)
        self.module_combobox.place(x=165, y=490, width=285, height=30)
        self.scale_combobox.place(x=165, y=540, width=60, height=30)
        self.extension_combobox.place(x=380, y=540, width=70, height=30)

    def choose_img(self):
        choose_img = filedialog.askopenfilename(title='选择图片', initialdir=self.infile_path)
        if not choose_img:
            return
        elif not search(r'\.(jpg|png|jpeg|webp)$', choose_img, 2):
            messagebox.showwarning('警告', '文件加载失败!\n只允许选择图片文件\n(png/jpg/jpeg/webp)!')
            return
        elif path.getsize(choose_img) > 15728640:
            messagebox.showerror('错误', '图片过大(>15MB),为避免最终\n生成的图片过大,拒绝加载!!')
            return
        self.infile_path = choose_img
        self.infile_path_entry.insert(tk.END, choose_img)
        self.img_app.change_img(self.img_label1, choose_img)

    def choose_folder(self, flag):
        folder_selected = filedialog.askdirectory(initialdir=self.infile_path)
        if not folder_selected:
            return
        if flag == 1:
            self.img_app.folder_path = folder_selected
            self.output_folder_entry.delete(0, 'end')
            self.output_folder_entry.insert(0, folder_selected)
            if self.dealt_mode_combobox.get() == '单张图片处理':
                original_file_name = path.basename(self.infile_path)
                self.outfile_path = path.join(folder_selected, f'output-{original_file_name}')
            else:
                self.outfile_path = folder_selected
        elif flag == 2:
            self.infile_path_entry.delete(0, 'end')
            self.infile_path_entry.insert(0, folder_selected)
            self.infile_path = folder_selected

    def check_open(self):
        if self.dealt_mode_combobox.get() == '单张图片处理':
            self.choose_img()
        else:
            self.choose_folder(2)

    def show_default_img(self):
        original_img = Image.open("Load_picture/Default.jpg").copy()
        dealt_img = Image.open("Load_picture/Default.jpg").copy()
        original_img.thumbnail((540, 292), Image.LANCZOS)
        dealt_img.thumbnail((540, 292), Image.LANCZOS)
        photo1 = ImageTk.PhotoImage(original_img)
        photo2 = ImageTk.PhotoImage(dealt_img)
        self.img_label1 = tk.Label(self.original_frame, image=photo1)
        self.img_label2 = tk.Label(self.dealt_frame, image=photo2)
        self.img_label1.pack(fill=tk.BOTH, expand=True)
        self.img_label2.pack(fill=tk.BOTH, expand=True)
        self.img_label1.image = photo1
        self.img_label2.image = photo2
        self.original_frame.place(x=0, y=0, height=291.6, width=540)
        self.dealt_frame.place(x=540, y=0, height=291.6, width=540)

    def execute_entry(self):
        # 传入基本参数
        if self.infile_path is None or self.outfile_path is None:
            messagebox.showinfo('提示', '文件输入或输出\n路径不能为空!')
            return
        scale = self.scale_combobox.get()
        extension = self.extension_combobox.get().replace('.', '')
        module = self.module_combobox.get()
        # 传入大量必要参数
        exe = ExecuteCommand(self.infile_path, self.outfile_path, scale, extension,
                             module, self.output_text, self.img_app, self.img_label1,
                             self.img_label2, self.open_button1, self.open_button2)
        if exe.catch_exception(self.infile_path, True):
            messagebox.showerror('提示', '图片解压缩后体积\n超过15MB,无法处理!')
            return
        exe.enter_mode(self.dealt_mode_combobox.get())


class ImageApp(object):
    def __init__(self):
        self.folder_path = 'Load_picture'

    def open_folder(self):
        # 这一步替换异常关键,我发现用'/'没法跳转到正确路径,估计和系统有关
        self.folder_path = self.folder_path.replace('/', '\\')
        Popen(['explorer', self.folder_path], shell=True)

    @staticmethod
    def open_img(img_path):
        if img_path is None:
            img_path = "Load_picture/Default.jpg"
        Popen(['start', img_path], shell=True)

    @staticmethod
    def resize_image(image_path):
        # 先给权限!
        Image.MAX_IMAGE_PIXELS = 314572800
        try:
            img = Image.open(image_path)
        except Image.DecompressionBombError:
            messagebox.showerror('错误', '图片过大(>200MB),压缩失败!\n生成图片已销毁!')
            remove(image_path)
            return
        w, h = img.size
        img.resize((w // 2, h // 2), Image.LANCZOS)
        img.save(image_path)
        # 恢复原权限
        Image.MAX_IMAGE_PIXELS = 89478485

    @staticmethod
    def change_img(img_label: tk.Label, img_path):
        image = Image.open(img_path).copy()
        image.thumbnail((540, 292), Image.LANCZOS)
        photo = ImageTk.PhotoImage(image)
        img_label.config(image=photo)
        # 保持对新图片的引用
        img_label.image = photo


class ExecuteCommand:
    def __init__(self, infile_path, outfile_path, scale, extension, module, output_text,
                 img_app, input_label, output_label, open_btn1, open_btn2):
        self.infile_path = infile_path
        self.outfile_path = outfile_path
        self.scale = scale
        self.extension = extension
        self.module = module
        self.output_text = output_text
        self.img_app = img_app
        self.input_label = input_label
        self.output_label = output_label
        self.open_btn1 = open_btn1
        self.open_btn2 = open_btn2
        # 至多开启5个线程同步操作
        self.semaphore = Semaphore(5)

    def catch_exception(self, infile_path, twice_check=False) -> bool:
        # 因为这里的异常捕捉是要反复用的,因此要更细致的判断
        if path.isdir(infile_path):
            return False
        try:
            Image.MAX_IMAGE_PIXELS = 15728640
            img = Image.open(infile_path)
        except Image.DecompressionBombError:
            self.output_text.insert(tk.END, f'\n图片:{infile_path}解压缩后体积超过15MB, \n无法继续处理!\n')
            return True
        finally:
            Image.MAX_IMAGE_PIXELS = 89478485
        if not twice_check:
            # 如果不是二次检查的话,那么就当它通过检查了
            return False

        w, h = img.size
        img.close()
        if w * h > 10485760:
            return not messagebox.askokcancel('警告', '当前图片体积超过10MB,继续处理\n最终产出的照片'
                                                    '大概率效果不佳\n且体积极大!!\n确定继续处理?')

    def enter_mode(self, mode: str):
        if mode == '单张图片处理':
            if path.isdir(self.infile_path):
                messagebox.showerror('错误', '单张图片处理要求\n输入路径为图片!')
                return
            messagebox.showinfo('提示', '开始处理!')
            # 如果是单张图片处理,处理完一张照片就会显示处理完成
            self.single_img_mode(self.outfile_path, True)
        elif mode == '批量图片处理':
            if path.isfile(self.infile_path):
                messagebox.showerror('错误', '批量图片处理要求\n输入路径为文件夹!')
                return
            messagebox.showinfo('提示', '开始处理!')
            output_folder = self.outfile_path
            self.multiple_img_mode(output_folder)

    def single_img_mode(self, outfile_path, flag):
        command = f'''realesrgan-ncnn-vulkan -i {self.infile_path} -o {outfile_path}
                          -m {self.module} -s {self.scale} -v {self.extension}'''
        self.output_text.config(state='normal')
        self.output_text.delete('1.0', tk.END)
        Thread(target=self.stream_command_output,
               args=(command, outfile_path, flag)).start()

    def multiple_img_mode(self, output_folder):
        file_count = sum(1 for entry in scandir(self.infile_path) if entry.is_file())
        if file_count > 200:
            response = messagebox.askokcancel('警告', '您要处理的照片超过200张,\n确定继续处理吗?')
            if not response:
                return
        files = scandir(self.infile_path)
        # outfile_path不能共享!!不然前面的函数还没执行完,后面的函数就着急忙慌的把路径给改了!!
        flag = False
        for index, file in enumerate(files, 1):
            if path.isdir(file.path):
                self.output_text.insert(tk.END, f'{file.name}为文件夹,已跳过。\n')
                continue
            elif self.catch_exception(file.path):
                continue
            elif index == file_count:
                flag = True
            self.img_app.change_img(self.input_label, file.path)
            self.open_btn1.config(command=lambda: self.img_app.open_img(file.path))
            outfile_path = path.join(output_folder, f'output-{file.name}')
            self.open_btn2.config(command=lambda: self.img_app.open_img(outfile_path))
            self.infile_path = file.path
            self.single_img_mode(outfile_path, flag)

    def stream_command_output(self, command, outfile_path, flag=False):
        self.semaphore.acquire()
        self.output_text.insert(tk.END, f'执行命令>>\n{command}\n')
        process = Popen(command, stdout=PIPE, stderr=STDOUT,
                        shell=True, text=True)
        for line in iter(process.stdout.readline, ''):
            self.output_text.insert(tk.END, line)
            self.output_text.see(tk.END)
        if path.getsize(outfile_path) > 10485760:
            self.output_text.insert(tk.END, '\n正在压缩图片......\n')
            self.img_app.resize_image(outfile_path)
            self.output_text.insert(tk.END, '\n图片压缩完成!\n')
        self.output_text.insert(tk.END, '100.00%\n')
        self.output_text.see(tk.END)
        self.semaphore.release()
        # 如果能赶得上加载图片的话就加载
        self.img_app.change_img(self.output_label, outfile_path)
        process.stdout.close()
        process.wait()
        if flag:
            messagebox.showinfo('提示', '处理完成!')


if __name__ == '__main__':
    Gui = GUI()

As you see, it's a broken program but it can still run, hhh

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值