File size: 7,376 Bytes
8af4a0a |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
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
)
|