Okay I want the speed and stuff of this but want the end result of this here is 1 import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import os
import subprocess
import threading
import concurrent.futures
import queue
import sys

class VideoImageConverterApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Video to Image Converter")
        self.root.geometry("1000x700")
        self.root.minsize(800, 600)
        self.root.configure(bg="#0b0c10")  # Dark background

        # Initialize data structures
        self.input_items = []  # List of tuples: (path, type)
        self.output_folder = tk.StringVar()
        self.selected_format = tk.StringVar(value="JPG")

        # Configure styles
        self.configure_styles()

        # Setup UI
        self.setup_ui()

    def configure_styles(self):
        style = ttk.Style(self.root)
        style.theme_use('clam')  # Modern theme

        # Define color palette
        self.BACKGROUND_COLOR = "#0b0c10"
        self.FRAME_COLOR = "#1f2833"
        self.BUTTON_COLOR = "#c5c6c7"
        self.TEXT_COLOR = "#66fcf1"
        self.PROGRESS_COLOR = "#45a29e"

        # Style configurations
        style.configure("TButton",
                        foreground=self.BACKGROUND_COLOR,
                        background=self.BUTTON_COLOR,
                        font=("Segoe UI", 10, "bold"),
                        borderwidth=1)
        style.map("TButton",
                  background=[('active', self.PROGRESS_COLOR)])

        style.configure("TLabel",
                        foreground=self.TEXT_COLOR,
                        background=self.BACKGROUND_COLOR,
                        font=("Segoe UI", 10))

        style.configure("Horizontal.TProgressbar",
                        troughcolor=self.FRAME_COLOR,
                        background=self.PROGRESS_COLOR)

        style.configure("Readonly.TEntry",
                        fieldbackground=self.FRAME_COLOR,
                        foreground=self.TEXT_COLOR,
                        font=("Segoe UI", 10))

        # Style for Treeview
        style.configure("Custom.Treeview",
                        background=self.FRAME_COLOR,
                        foreground=self.TEXT_COLOR,
                        fieldbackground=self.FRAME_COLOR,
                        font=("Segoe UI", 10),
                        bordercolor=self.FRAME_COLOR,
                        lightcolor=self.FRAME_COLOR,
                        darkcolor=self.FRAME_COLOR)
        style.map("Custom.Treeview",
                  background=[('selected', self.PROGRESS_COLOR)],
                  foreground=[('selected', self.BACKGROUND_COLOR)])

    def setup_ui(self):
        # Configure grid layout
        self.root.columnconfigure(0, weight=1)
        self.root.rowconfigure(0, weight=1)

        main_frame = ttk.Frame(self.root)
        main_frame.grid(row=0, column=0, sticky="NSEW", padx=10, pady=10)
        main_frame.columnconfigure(0, weight=1)
        main_frame.rowconfigure(1, weight=1)

        # Input Items Section using Frame and Label instead of LabelFrame
        input_frame = tk.Frame(main_frame, bg=self.FRAME_COLOR, bd=0, relief='groove')
        input_frame.grid(row=0, column=0, sticky="EW", padx=5, pady=5)
        input_frame.columnconfigure(0, weight=1)

        input_label = tk.Label(input_frame, text="Input Items", bg=self.FRAME_COLOR, fg=self.TEXT_COLOR, font=("Segoe UI", 12, "bold"))
        input_label.pack(anchor="nw", padx=5, pady=5)

        # Treeview for input items
        columns = ("Type", "Path")
        self.tree = ttk.Treeview(input_frame, columns=columns, show='headings', style="Custom.Treeview")
        self.tree.heading("Type", text="Type")
        self.tree.heading("Path", text="Path")
        self.tree.column("Type", width=100, anchor='center')
        self.tree.column("Path", anchor='w')

        self.tree.pack(fill="both", expand=True, padx=5, pady=(0,5))

        # Scrollbar for Treeview
        tree_scroll = ttk.Scrollbar(input_frame, orient="vertical", command=self.tree.yview)
        self.tree.configure(yscrollcommand=tree_scroll.set)
        tree_scroll.pack(side="right", fill="y")

        # Buttons for adding/removing items
        button_frame = ttk.Frame(main_frame)
        button_frame.grid(row=1, column=0, sticky="EW", padx=5, pady=5)
        button_frame.columnconfigure((0,1,2,3), weight=1)

        add_folder_btn = ttk.Button(button_frame, text="Add Folder", command=self.select_input_folder)
        add_folder_btn.grid(row=0, column=0, padx=5, pady=5, sticky="EW")

        add_video_btn = ttk.Button(button_frame, text="Add Videos", command=self.select_input_videos)
        add_video_btn.grid(row=0, column=1, padx=5, pady=5, sticky="EW")

        remove_selected_btn = ttk.Button(button_frame, text="Remove Selected", command=self.remove_selected_items)
        remove_selected_btn.grid(row=0, column=2, padx=5, pady=5, sticky="EW")

        clear_all_btn = ttk.Button(button_frame, text="Clear All", command=self.clear_all_items)
        clear_all_btn.grid(row=0, column=3, padx=5, pady=5, sticky="EW")

        # Output Folder Section using Frame and Label instead of LabelFrame
        output_frame = tk.Frame(main_frame, bg=self.FRAME_COLOR, bd=0, relief='groove')
        output_frame.grid(row=2, column=0, sticky="EW", padx=5, pady=5)
        output_frame.columnconfigure(0, weight=1)
        output_frame.columnconfigure(1, weight=0)

        output_label = tk.Label(output_frame, text="Output Folder", bg=self.FRAME_COLOR, fg=self.TEXT_COLOR, font=("Segoe UI", 12, "bold"))
        output_label.pack(anchor="nw", padx=5, pady=5)

        output_entry = ttk.Entry(output_frame, textvariable=self.output_folder, state='readonly', style="Readonly.TEntry")
        output_entry.pack(fill="x", padx=5, pady=(0,5))

        select_output_btn = ttk.Button(output_frame, text="Select Output Folder", command=self.select_output_folder)
        select_output_btn.pack(anchor="e", padx=5, pady=5)

        # Format Selection Section using Frame and Label instead of LabelFrame
        format_frame = tk.Frame(main_frame, bg=self.FRAME_COLOR, bd=0, relief='groove')
        format_frame.grid(row=3, column=0, sticky="W", padx=5, pady=5)

        format_label = tk.Label(format_frame, text="Select Format", bg=self.FRAME_COLOR, fg=self.TEXT_COLOR, font=("Segoe UI", 12, "bold"))
        format_label.grid(row=0, column=0, columnspan=2, padx=5, pady=5, sticky="W")

        image_format_label = ttk.Label(format_frame, text="Image Format:")
        image_format_label.grid(row=1, column=0, padx=5, pady=5, sticky="W")

        format_dropdown = ttk.Combobox(format_frame, textvariable=self.selected_format, values=["JPG", "PNG"], state="readonly", width=10)
        format_dropdown.grid(row=1, column=1, padx=5, pady=5, sticky="W")
        format_dropdown.current(0)  # Default to JPG

        # Progress Bar Section
        progress_frame = ttk.Frame(main_frame)
        progress_frame.grid(row=4, column=0, sticky="EW", padx=5, pady=5)
        progress_frame.columnconfigure(0, weight=1)

        progress_label = ttk.Label(progress_frame, text="Processing Progress:", foreground=self.TEXT_COLOR, background=self.FRAME_COLOR, font=("Segoe UI", 10))
        progress_label.grid(row=0, column=0, sticky="W", padx=5, pady=(0,5))

        self.progress_bar = ttk.Progressbar(progress_frame, orient='horizontal', mode='determinate', style="Horizontal.TProgressbar")
        self.progress_bar.grid(row=1, column=0, sticky="EW", padx=5, pady=5)

        # Status Label
        self.status_label = ttk.Label(main_frame, text="Ready.", anchor='w', relief='sunken', foreground=self.TEXT_COLOR, background=self.FRAME_COLOR, font=("Segoe UI", 10))
        self.status_label.grid(row=5, column=0, sticky="EW", padx=5, pady=(0,5))

        # Process Button
        process_button = ttk.Button(main_frame, text="Process", command=self.process_videos_thread)
        process_button.grid(row=6, column=0, pady=10, sticky="E", padx=5, ipadx=10, ipady=5)

    def select_input_folder(self):
        folder = filedialog.askdirectory(title="Select Input Folder", mustexist=True)
        if folder:
            if not any(item[0] == folder for item in self.input_items):
                self.input_items.append((folder, "Folder"))
                self.tree.insert("", "end", values=("Folder", folder))
                self.update_status(f"Added folder: {folder}")
            else:
                messagebox.showwarning("Duplicate Folder", f"The folder '{folder}' is already selected.")

    def select_input_videos(self):
        videos = filedialog.askopenfilenames(title="Select Video Files",
                                             filetypes=[("Video Files", "*.mp4 *.avi *.mov *.mkv *.flv *.wmv")])
        if videos:
            for video in videos:
                if not any(item[0] == video for item in self.input_items):
                    self.input_items.append((video, "Video"))
                    self.tree.insert("", "end", values=("Video", video))
                    self.update_status(f"Added video: {video}")
                else:
                    messagebox.showwarning("Duplicate Video", f"The video '{video}' is already selected.")

    def remove_selected_items(self):
        selected = self.tree.selection()
        if not selected:
            messagebox.showinfo("No Selection", "Please select items to remove.")
            return
        for sel in selected:
            item = self.tree.item(sel)
            path = item['values'][1]
            self.input_items = [i for i in self.input_items if i[0] != path]
            self.tree.delete(sel)
        self.update_status("Selected items removed.")

    def clear_all_items(self):
        if messagebox.askyesno("Confirm", "Are you sure you want to clear all input items?"):
            self.input_items.clear()
            for item in self.tree.get_children():
                self.tree.delete(item)
            self.update_status("All items cleared.")

    def select_output_folder(self):
        folder = filedialog.askdirectory(title="Select Output Folder")
        if folder:
            self.output_folder.set(folder)
            self.update_status(f"Output folder set to: {folder}")

    def sanitize_name(self, name):
        """Sanitize the folder or file name to remove or replace problematic characters."""
        return "".join(c if c.isalnum() or c in (' ', '_', '-') else "_" for c in name).replace(" ", "_")

    def process_videos_thread(self):
        """Run the video processing in a separate thread."""
        threading.Thread(target=self.process_videos, daemon=True).start()

    def process_videos(self):
        if not self.input_items:
            messagebox.showerror("Error", "Please select at least one input folder or video.")
            return
        if not self.output_folder.get():
            messagebox.showerror("Error", "Please select an output folder.")
            return

        selected_format = self.selected_format.get().lower()
        if selected_format not in ['jpg', 'png']:
            messagebox.showerror("Error", "Please select a valid format (JPG or PNG).")
            return

        # Ensure the output directory exists
        os.makedirs(self.output_folder.get(), exist_ok=True)

        # Disable buttons to prevent multiple clicks
        self.set_buttons_state(tk.DISABLED)

        # Initialize progress
        total_tasks = len(self.input_items)
        if total_tasks == 0:
            messagebox.showerror("Error", "No tasks to process.")
            self.set_buttons_state(tk.NORMAL)
            return

        self.progress_bar.config(maximum=total_tasks)
        self.progress_bar['value'] = 0
        self.update_status("Starting processing...")

        progress_queue = queue.Queue()
        errors = []

        def worker(item_path, item_type):
            base_name = os.path.basename(os.path.normpath(item_path))
            sanitized_base = self.sanitize_name(base_name)
            unique_output_dir = os.path.join(self.output_folder.get(), f"{sanitized_base}_{base_name}")
            os.makedirs(unique_output_dir, exist_ok=True)
            try:
                if item_type == "Folder":
                    # Iterate through all video files in the folder
                    for root_dir, _, files in os.walk(item_path):
                        for file in files:
                            if file.lower().endswith(('.mp4', '.avi', '.mov', '.mkv', '.flv', '.wmv')):
                                video_path = os.path.join(root_dir, file)
                                video_base = self.sanitize_name(os.path.splitext(file)[0])
                                video_output_dir = os.path.join(unique_output_dir, video_base)
                                os.makedirs(video_output_dir, exist_ok=True)
                                cmd = ['video2image', '-i', video_path, '-o', video_output_dir, '-f', selected_format]
                                subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                else:
                    # Single video file
                    cmd = ['video2image', '-i', item_path, '-o', unique_output_dir, '-f', selected_format]
                    subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                progress_queue.put(("success", f"{item_type}: {item_path}"))
            except subprocess.CalledProcessError as e:
                error_msg = f"Failed to process {item_type.lower()}: {item_path}\nError: {e.stderr.decode().strip()}"
                progress_queue.put(("error", error_msg))
            except Exception as ex:
                error_msg = f"An unexpected error occurred while processing {item_type.lower()}: {item_path}\nError: {ex}"
                progress_queue.put(("error", error_msg))

        # Start thread pool
        executor = concurrent.futures.ThreadPoolExecutor(max_workers=4)
        for path, type_ in self.input_items:
            executor.submit(worker, path, type_)

        self.executor = executor  # Keep a reference

        # Start checking the queue
        self.process_queue(progress_queue, total_tasks, errors)

    def process_queue(self, progress_queue, total_tasks, errors, processed=0):
        try:
            while True:
                status, info = progress_queue.get_nowait()
                if status == "success":
                    self.progress_bar['value'] += 1
                    processed += 1
                    self.update_status(f"Processed: {info}")
                elif status == "error":
                    errors.append(info)
                    self.progress_bar['value'] += 1
                    processed += 1
                    self.update_status(f"Error: {info}")

                if processed >= total_tasks:
                    self.executor.shutdown(wait=False)
                    self.finalize_processing(errors)
        except queue.Empty:
            pass
        if processed < total_tasks:
            self.root.after(100, self.process_queue, progress_queue, total_tasks, errors, processed)

    def finalize_processing(self, errors):
        self.set_buttons_state(tk.NORMAL)
        if errors:
            error_message = "\n\n".join(errors)
            messagebox.showerror("Processing Completed with Errors", error_message)
        else:
            messagebox.showinfo("Success", "All videos and folders have been successfully processed.")
        self.progress_bar['value'] = 0
        self.update_status("Processing completed.")

    def set_buttons_state(self, state):
        # Disable/Enable all relevant buttons
        for child in self.root.winfo_children():
            self.recursive_set_state(child, state)

    def recursive_set_state(self, widget, state):
        for child in widget.winfo_children():
            if isinstance(child, ttk.Button):
                child.config(state=state)
            self.recursive_set_state(child, state)

    def update_status(self, message):
        self.status_label.config(text=message)

def main():
    root = tk.Tk()
    app = VideoImageConverterApp(root)
    root.mainloop()

if __name__ == "__main__":
    main()    here is 2 import sys, os, cv2
from pathlib import Path

sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import traceback
from PyQt5.QtWidgets import QApplication, QFileDialog, QMessageBox
from Ui_MainWindow import Ui_MainWindow
from facelib.LandmarksProcessor import get_transform_mat
from DFLIMG import DFLIMG
from DFLIMG import DFLJPG

import facelib, tqdm
from multiprocessing import Process
from app import FaceAnalysis
from data import get_image as ins_get_image

class detect_UI(Ui_MainWindow):
    def __init__(self):
        super(detect_UI, self).__init__()
        self.detect_face_dir = ""
        self.detected_face_dir = ""

        self.faceAnalysis = FaceAnalysis(allowed_modules=['detection', 'landmark_2d_106'])  # landmark_2d_106
        self.faceAnalysis.prepare(ctx_id=0, det_size=(640, 640))

        self.setupUi(self)

        self.choose_detect_btn.clicked.connect(self.choose_detect_click)
        self.choose_detected_btn.clicked.connect(self.choose_detected_click)

        self.execute.clicked.connect(self.extract_face_proc)

    def cv2_imwrite(self, filename, img, *args):
        ret, buf = cv2.imencode(Path(filename).suffix, img, *args)
        if ret == True:
            try:
                with open(filename, "wb") as stream:
                    stream.write(buf)
            except:
                pass

    def landmark106to68(self, pt106):
        landmark106to68 = [1, 10, 12, 14, 16, 3, 5, 7, 0, 23, 21, 19, 32, 30, 28, 26, 17,  # 脸颊17点
                           43, 48, 49, 51, 50,  # 左眉毛5点
                           102, 103, 104, 105, 101,  # 右眉毛5点
                           72, 73, 74, 86, 78, 79, 80, 85, 84,  # 鼻子9点
                           35, 41, 42, 39, 37, 36,  # 左眼睛6点
                           89, 95, 96, 93, 91, 90,  # 右眼睛6点
                           52, 64, 63, 71, 67, 68, 61, 58, 59, 53, 56, 55, 65, 66, 62, 70, 69, 57, 60, 54  # 嘴巴20点
                           ]

        pt68 = []
        if len(pt106) != 106:
            return None
        for i in range(68):
            index = landmark106to68[i]
            pt68.append(pt106[index])

        return pt68


    def extract_to_dfl_img(self,
                           img_path,
                           img_name,
                           aligned_path):

        #image_file = "D:\DeepFaceLab\DeepFaceLab_DirectX12\workspace\data_dst/08687.png"
        img_mat = cv2.imread(img_path)
        faces = self.faceAnalysis.get(img_mat)
        if faces is None or len(faces) == 0:
            return None


        img_name = img_name.replace("png", "jpg").replace("PNG", "jpg")

        for face in faces:

            image_size = 512

            landmark = self.landmark106to68(face.landmark_2d_106)

            image_to_face_mat = get_transform_mat(landmark, image_size, facelib.FaceType.WHOLE_FACE)

            face_image = cv2.warpAffine(img_mat, image_to_face_mat, (image_size, image_size), cv2.INTER_LANCZOS4)
            output_filepath = aligned_path + "/" + img_name
            #print(output_filepath)
            self.cv2_imwrite(output_filepath, face_image)

            dflimg = DFLJPG.load(output_filepath)
            dflimg.set_face_type(facelib.FaceType.toString(facelib.FaceType.WHOLE_FACE))

            face_image_landmarks = facelib.LandmarksProcessor.transform_points(landmark, image_to_face_mat)
            ###相似变换之后的人脸关键点
            dflimg.set_landmarks(face_image_landmarks.tolist())
            dflimg.set_source_filename(img_name)
            dflimg.set_source_rect(face.bbox)

            ###源人脸关键点
            dflimg.set_source_landmarks(landmark)

            dflimg.set_image_to_face_mat(image_to_face_mat)
            dflimg.save()

    def choose_detect_click(self):
        outdir = QFileDialog.getExistingDirectory(self, caption='Select Image Directory',
                                                  directory=os.getcwd(),
                                                  options=QFileDialog.DontUseNativeDialog)

        self.need_detect_dir_text.setText(outdir)
        self.detect_face_dir = outdir
        print(outdir)

    def choose_detected_click(self):
        outdir = QFileDialog.getExistingDirectory(self, caption='Select Image Directory',
                                                  directory=os.getcwd(),
                                                  options=QFileDialog.DontUseNativeDialog)
        self.detected_face_dir_text.setText(outdir)

        self.detected_face_dir = outdir
        print(outdir)

    def face_det_lmk_dir(self, dir_path, aligned_path, max_workers = 2):

        import concurrent.futures

        # max_workers > 1报错
        with concurrent.futures.ThreadPoolExecutor(max_workers) as executor:
            to_do = []

            for root, dirs, files in os.walk(dir_path):
                '''
                for img_name in files:
                    img_path = dir_path + "/" + img_name
                    self.extract_to_dfl_img(img_path, img_name, aligned_path, centerface, faceAlignment)
                '''

                for img_name in files:
                    try:
                        img_path = dir_path + "/" + img_name
                        # extract_to_dfl_img(img_path, img_name, aligned_path, centerface, faceAlignment)

                        future = executor.submit(self.extract_to_dfl_img, img_path, img_name, aligned_path)
                        #print(img_path)
                        to_do.append(future)
                    except Exception as e:
                        traceback.print_exc()


                for future in tqdm.tqdm(concurrent.futures.as_completed(to_do), desc="人脸提取进度", leave=True, ascii=True, initial=0):  # 并发执行 , desc="progress", leave=True, ascii=True, initial=0
                    try:
                        future.result()
                    except Exception as e:
                        traceback.print_exc()
            print("执行完成")
            QMessageBox.information(None, "消息", "人脸提取执行完成", QMessageBox.Ok)
            sys.exit(-1)

    def extract_face_proc(self):

        max_worker = self.max_worker_text.text()

        if not max_worker.isdigit():
            QMessageBox.information(None, "消息", "线程数只能输入数字，建议1-8之间", QMessageBox.Ok)
            return


        import threading
        extract_face_thread = threading.Thread(target=self.extract_face, args=(int(max_worker), ))
        extract_face_thread.start()

    def extract_face(self, max_workers):
        if len(self.detect_face_dir) == 0:
            QMessageBox.information(None, "消息", "待提取人脸的目录不能为空", QMessageBox.Ok)
            return
        if len(self.detected_face_dir) == 0:
            QMessageBox.information(None, "消息", "存储人脸的目录不能为空", QMessageBox.Ok)
            return

        QMessageBox.information(None, "消息",
                                "人脸提取任务已经提交，请观察：" + self.detected_face_dir + " 目录是否有新文件写入",
                                QMessageBox.Ok)
        self.face_det_lmk_dir(self.detect_face_dir, self.detected_face_dir, max_workers)
        print("extract_face")


if __name__ == '__main__':
    app = QApplication(sys.argv)

    frame = detect_UI()
    frame.show()

    sys.exit(app.exec())