Swap-Face / app.py
tbdavid2019's picture
1
8af4a0a
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("""
<div style="text-align: center; font-family: 'Arial', sans-serif; color: #333;">
<h1 style="color: #2c3e50;">換臉工具</h1>
<p style="font-size: 1.1em;">上傳一張包含臉部的<b>來源圖片</b>和一張<b>目標圖片</b>,或從下方選擇一個<b>模板</b>作為目標,即可進行換臉。</p>
<p style="font-size: 0.9em; color: #7f8c8d;">如果來源圖片中有多張臉,請在「來源臉部索引」中指定要替換的臉部(從 0 開始)。</p>
</div>
""")
with gr.Row():
with gr.Column(scale=1):
source_image = gr.Image(
label="來源圖片 (您的臉部)",
type="numpy",
height=300
)
face_index_input = gr.Textbox(
label="來源臉部索引",
value="0",
placeholder="如果有多張臉,指定要使用的臉部(從 0 開始)",
info="0=第一張臉, 1=第二張臉, 以此類推"
)
with gr.Column(scale=1):
target_image = gr.Image(
label="目標圖片 (要換到的目標)",
type="numpy",
height=300
)
# 模板選擇 (如果有可用模板)
template_files = get_template_files()
if template_files:
template_choice = gr.Dropdown(
choices=["無"] + template_files,
value="無",
label="或選擇一個模板",
info="選擇模板會覆蓋上方的目標圖片"
)
else:
template_choice = gr.Dropdown(
choices=["無"],
value="無",
label="模板 (無可用模板)",
interactive=False
)
# 處理按鈕
swap_button = gr.Button(
"🚀 開始換臉",
variant="primary",
size="lg"
)
# 結果顯示
result_image = gr.Image(
label="換臉結果",
type="numpy",
height=400
)
# 範例 (如果有的話)
if examples:
gr.Examples(
examples=examples,
inputs=[source_image, target_image, face_index_input, template_choice],
label="點擊範例快速開始",
examples_per_page=3
)
# 綁定處理函數
swap_button.click(
fn=swap_face_gradio,
inputs=[
source_image,
target_image,
face_index_input,
template_choice
],
outputs=result_image,
api_name="face_swap"
)
# --- 啟動應用 ---
if __name__ == "__main__":
demo.launch(
server_name="0.0.0.0",
server_port=7860,
share=True
)