حلل هذا الكود:

import os
import face_recognition
import tkinter as tk
from tkinter import filedialog, messagebox
from PIL import Image, ImageTk
from tkinter import ttk
import threading
import pickle
import time
import datetime

# Global variables
clients_dir = ''
search_image_path = ''
matched_image_paths = []
cache = {}
cache_file_path = ''

def generate_cache_filename():
    """Generate a unique filename for the cache based on the current date and time."""
    timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
    return os.path.join(os.path.expanduser('~'), 'Documents', f'face_encodings_cache_{timestamp}.pkl')

def load_cache():
    """Load face encodings cache from the cache file."""
    global cache
    if os.path.exists(cache_file_path):
        with open(cache_file_path, 'rb') as cache_file:
            cache = pickle.load(cache_file)

def save_cache():
    """Save face encodings cache to the cache file."""
    with open(cache_file_path, 'wb') as cache_file:
        pickle.dump(cache, cache_file)

def load_face_encoding(image_path):
    """Load and encode faces from an image file, using cache if available."""
    if image_path in cache:
        return cache[image_path]

    image = face_recognition.load_image_file(image_path)
    encodings = face_recognition.face_encodings(image)
    if encodings:
        encoding = encodings[0]
        cache[image_path] = encoding  # Cache the encoding
        save_cache()  # Save the updated cache
        return encoding
    return None

def face_matches(search_image_path, client_image_path):
    """Compare two images and return True if the face matches."""
    search_encoding = load_face_encoding(search_image_path)
    client_encoding = load_face_encoding(client_image_path)

    if search_encoding is None or client_encoding is None:
        return False

    # Compare faces
    matches = face_recognition.compare_faces([client_encoding], search_encoding)
    return matches[0]

def search_client_by_picture(search_picture_path):
    """Search for all clients by comparing the search picture to client pictures."""
    global matched_image_paths
    matched_image_paths = []

    if not clients_dir:
        return "Client folder directory not set."

    image_paths = []
    for client_folder in os.listdir(clients_dir):
        client_path = os.path.join(clients_dir, client_folder)
        if os.path.isdir(client_path):
            for file in os.listdir(client_path):
                if file.endswith(('.jpg', '.jpeg', '.png')):
                    image_paths.append(os.path.join(client_path, file))

    total_images = len(image_paths)
    start_time = time.time()

    for idx, client_picture in enumerate(image_paths):
        if face_matches(search_picture_path, client_picture):
            matched_image_paths.append(client_picture)

        # Update progress and estimated time remaining
        elapsed_time = time.time() - start_time
        average_time_per_image = elapsed_time / (idx + 1)
        remaining_time = (total_images - (idx + 1)) * average_time_per_image
        update_progress_bar(idx + 1, total_images, remaining_time)

    return matched_image_paths

def update_progress_bar(current, total, remaining_time):
    """Update the progress bar and show estimated remaining time."""
    progress_percent = int((current / total) * 100)
    progress_bar['value'] = progress_percent
    progress_label.config(text=f"Progress: {progress_percent}% - Estimated time left: {int(remaining_time)} seconds")

def show_results():
    """Show matched images and their folders in a new window."""
    result_window = tk.Toplevel(app)
    result_window.title("Search Results")
    result_window.geometry("600x400")

    scrollbar = tk.Scrollbar(result_window)
    scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

    canvas = tk.Canvas(result_window, yscrollcommand=scrollbar.set)
    canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

    scrollbar.config(command=canvas.yview)

    result_frame = tk.Frame(canvas)
    canvas.create_window((0, 0), window=result_frame, anchor='nw')

    if matched_image_paths:
        for image_path in matched_image_paths:
            img = Image.open(image_path)
            img.thumbnail((100, 100))
            img_tk = ImageTk.PhotoImage(img)

            label = tk.Label(result_frame, image=img_tk)
            label.image = img_tk
            label.pack(side=tk.TOP, padx=5, pady=5)

            folder_label = tk.Label(result_frame, text=os.path.dirname(image_path))
            folder_label.pack(side=tk.TOP, padx=5, pady=5)
    else:
        no_results_label = tk.Label(result_frame, text="No matching images found.")
        no_results_label.pack(pady=10)

    result_frame.update_idletasks()
    canvas.config(scrollregion=canvas.bbox("all"))

def start_search():
    """Start the search process in a separate thread."""
    def search_thread():
        global cache_file_path
        cache_file_path = generate_cache_filename()  # Generate a unique cache filename for each search
        load_cache()  # Load the cache for the current search
        progress_bar.start()
        search_client_by_picture(search_image_path)
        progress_bar.stop()
        show_results()
        save_cache()  # Save the cache after the search is completed

    threading.Thread(target=search_thread).start()

def select_folder():
    """Open a folder dialog to select the directory containing client folders."""
    global clients_dir
    clients_dir = filedialog.askdirectory()
    if clients_dir:
        messagebox.showinfo("Directory Selected", f"Clients directory set to:\n{clients_dir}")

def select_image_file():
    """Open a file dialog to select an image file and start search."""
    global search_image_path
    file_path = filedialog.askopenfilename(filetypes=[("Image Files", "*.jpg;*.jpeg;*.png")])
    if file_path:
        search_image_path = file_path
        show_thumbnails()  # Optionally show thumbnails of the selected image
        start_search()  # Start search

def show_thumbnails():
    """Show thumbnail of the search image."""
    if search_image_path:
        search_img = Image.open(search_image_path)
        search_img.thumbnail((100, 100))
        search_img_tk = ImageTk.PhotoImage(search_img)

        search_label.config(image=search_img_tk)
        search_label.image = search_img_tk

# Create the GUI application
app = tk.Tk()
app.title("Client Image Search")

# Add a button to select the clients folder
select_folder_button = tk.Button(app, text="Select Clients Folder", command=select_folder)
select_folder_button.pack(pady=10)

# Add a button to select image file
select_picture_button = tk.Button(app, text="Select Picture", command=select_image_file)
select_picture_button.pack(pady=10)

# Labels for displaying thumbnails
search_label = tk.Label(app)
search_label.pack(side=tk.LEFT, padx=10, pady=10)

# Progress bar for search indication
progress_bar = ttk.Progressbar(app, orient="horizontal", length=200, mode="determinate")
progress_bar.pack(pady=10)

# Label for displaying progress and remaining time
progress_label = tk.Label(app, text="Progress: 0%")
progress_label.pack(pady=10)

# Run the application
app.mainloop()