import gradio as gr from overlay import overlay_source from detect_face import predict, NUM_CLASSES from swapface import swap_face_now import os from pathlib import Path BASE_DIR = Path(__file__).parent # thư mục chứa app.py FOLDER = BASE_DIR / "example_wigs" # --- Hàm load ảnh từ folder --- def load_images_from_folder(folder_path: str) -> list[str]: """ Trả về list[str] chứa tất cả các hình (jpg, png, gif, bmp) trong folder_path. """ supported = {'.jpg', '.jpeg', '.png', '.gif', '.bmp'} if not os.path.isdir(folder_path): print(f"Cảnh báo: '{folder_path}' không phải folder hợp lệ.") return [] files = [ os.path.join(folder_path, fn) for fn in os.listdir(folder_path) if os.path.splitext(fn)[1].lower() in supported ] if not files: print(f"Không tìm thấy hình trong: {folder_path}") return files def on_gallery_select(evt: gr.SelectData): """ Khi click thumbnail: trả về 1) filepath để nạp vào Image Source 2) tên file (basename) để hiển thị trong Textbox """ val = evt.value # --- logic trích filepath y như cũ --- if isinstance(val, dict): img = val.get("image") if isinstance(img, str): filepath = img elif isinstance(img, dict): filepath = img.get("path") or img.get("url") else: filepath = next( (v for v in val.values() if isinstance(v, str) and os.path.isfile(v)), None ) elif isinstance(val, str): filepath = val else: raise ValueError(f"Kiểu không hỗ trợ: {type(val)}") filename = os.path.splitext(os.path.basename(filepath))[0] if filepath else "" return filepath, filename # --- Hàm xác định folder dựa trên phân lớp --- def infer_folder(image) -> str: cls = predict(image)["predicted_class"] folder = str(FOLDER / cls) return folder # --- Hàm gộp: phân loại + load ảnh --- def handle_bg_change(image): """ Khi thay đổi background: 1. Phân loại khuôn mặt 2. Load ảnh từ folder tương ứng """ if image is None: return "", [] try: folder = infer_folder(image) images = load_images_from_folder(folder) return folder, images except Exception as e: print(f"Lỗi xử lý ảnh: {e}") return "", [] # --- Hàm swap face --- def swap_face_wrapper(background_img, result_img): """ Wrapper function cho swap face giữa background và result image """ if background_img is None or result_img is None: return None try: # Swap face từ background vào result image swapped = swap_face_now(background_img, result_img, do_enhance=True) return swapped except Exception as e: print(f"Lỗi swap face: {e}") return result_img # Trả về ảnh gốc nếu có lỗi # --- Hàm gộp overlay + swap face --- def combined_hair_and_face(background_img, source_img): """ Hàm gộp: chạy overlay trước, sau đó swap face """ if background_img is None or source_img is None: return None try: # Bước 1: Chạy overlay (ghép tóc) hair_result = overlay_source(background_img, source_img) # Bước 2: Chạy swap face từ background lên kết quả overlay final_result = swap_face_wrapper(background_img, hair_result) return final_result except Exception as e: print(f"Lỗi trong quá trình gộp hair + face: {e}") return None # --- Xây dựng giao diện Gradio --- def build_demo(): with gr.Blocks(title="Hair Try-On & Face Swap", theme=gr.themes.Soft()) as demo: gr.Markdown(""" # 🎯 Hair Try-On & Face Swap Application """) with gr.Row(): bg = gr.Image(type="pil", label="Background", height=500) src = gr.Image(type="pil", label="Source", height=500, interactive=False) out = gr.Image(label="Result", height=500, interactive=False) folder_path_box = gr.Textbox(label="Folder path", visible=False) with gr.Row(): src_name_box = gr.Textbox( label="Wigs Name", interactive=False, show_copy_button=True , # tuỳ chọn – tiện copy đường dẫn scale = 1 ) gallery = gr.Gallery( label="Recommend For You", height=300, value=[], type="filepath", interactive=False, columns=5, object_fit="cover", allow_preview=True, scale = 8 ) combined_btn = gr.Button("✨ Try on now", variant="primary") # Chạy gộp hair + face swap combined_btn.click(fn=combined_hair_and_face, inputs=[bg, src], outputs=[out]) # Khi đổi ảnh background, tự động phân loại và load ảnh gợi ý bg.change( fn=handle_bg_change, inputs=[bg], outputs=[folder_path_box, gallery], show_progress=True ) # Khi chọn ảnh trong gallery, cập nhật vào khung Source gallery.select( fn=on_gallery_select, outputs=[src, src_name_box] ) return demo if __name__ == "__main__": build_demo().launch()