import gradio as gr import cv2 import numpy as np from PIL import Image from pathlib import Path # 導入臉部處理器 from core.face_processor import FaceProcessor # --- 初始化模型 --- face_processor = None try: print("🚀 正在初始化 AI 模型,請稍候...") face_processor = FaceProcessor() print("✅ AI 模型載入成功!") except Exception as e: print(f"❌ 模型載入失敗: {e}") print(" 如果看到下載錯誤,請參考 README.md 中的手動下載指南。") face_processor = None # --- Gradio 介面 --- def create_examples(): """檢查範例圖片是否存在,如果存在則建立範例列表""" example_list = [] template_dir = Path(__file__).parent / 'models' / 'templates' # 檢查範例圖片 step01.jpg 和 step02.jpg 是否存在 source_example_path = template_dir / 'step01.jpg' target_example_path = template_dir / 'step02.jpg' if source_example_path.exists() and target_example_path.exists(): print("✅ 找到範例圖片,正在建立 Gradio 範例...") example_list.append([ str(source_example_path), str(target_example_path), "0", # 臉部索引 None # 模板 ]) else: print("⚠️ 未找到範例圖片(例如 models/templates/step01.jpg),Gradio 將不會顯示範例。") print(f" - 檢查路徑: {source_example_path}") print(f" - 檢查路徑: {target_example_path}") return example_list # 建立範例 examples = create_examples() # 取得模板圖片列表 def get_template_files(): template_dir = Path(__file__).parent / "models" / "templates" try: templates = [str(p) for p in template_dir.glob("*.jpg")] print(f"✅ 成功載入 {len(templates)} 個模板。") return templates except FileNotFoundError: print("⚠️ 找不到模板資料夾 'models/templates',模板選擇功能將無法使用。") return [] except Exception as e: print(f"❌ 載入模板時發生錯誤: {e}") return [] def swap_face_gradio(source_image, target_image, face_index_str, template_choice): """Gradio 的主處理函數""" if face_processor is None: raise gr.Error("AI 模型未能成功載入,無法處理請求。請檢查後台日誌。") print(f"ℹ️ 開始處理... 來源臉孔索引: {face_index_str}") try: # --- 1. 參數驗證和準備 --- if source_image is None: raise gr.Error("請務必上傳來源圖片。") # 如果選擇了模板,目標圖片變成模板 if template_choice and template_choice != "無": print(f" -> 使用模板: {template_choice}") target_image = np.array(Image.open(template_choice)) elif target_image is None: raise gr.Error("請上傳目標圖片,或選擇一個模板。") try: face_index = int(face_index_str) if face_index < 0: raise ValueError except (ValueError, TypeError): raise gr.Error("臉部索引必須是一個大於等於 0 的整數。") # --- 2. 圖片格式轉換 --- # Gradio 提供 RGB 格式,insightface 需要 BGR 格式 source_bgr = cv2.cvtColor(source_image, cv2.COLOR_RGB2BGR) target_bgr = cv2.cvtColor(target_image, cv2.COLOR_RGB2BGR) # --- 3. 執行換臉 --- result_bgr = face_processor.swap_face( source_bgr, target_bgr, face_index ) # --- 4. 處理結果 --- if result_bgr is None: raise gr.Error("換臉失敗,可能是因為無法在圖片中偵測到足夠的臉部。") # 轉回 RGB 格式供 Gradio 顯示 result_rgb = cv2.cvtColor(result_bgr, cv2.COLOR_BGR2RGB) print("✅ 處理完成!") return result_rgb except ValueError as ve: print(f"❌ 處理時發生錯誤: {ve}") raise gr.Error(str(ve)) except Exception as e: import traceback error_details = traceback.format_exc() print(f"❌ 發生未預期的錯誤: {e}") print(f"詳細錯誤堆疊:\n{error_details}") raise gr.Error(f"錯誤: {str(e)}") # --- 建立 Gradio 介面 --- with gr.Blocks( title="換臉 by DAVID888", theme=gr.themes.Soft(), css=".gradio-container {max-width: 1000px !important}" ) as demo: gr.HTML("""
上傳一張包含臉部的來源圖片和一張目標圖片,或從下方選擇一個模板作為目標,即可進行換臉。
如果來源圖片中有多張臉,請在「來源臉部索引」中指定要替換的臉部(從 0 開始)。