File size: 3,258 Bytes
6cddcb9
1129cff
 
6cddcb9
 
8d7e167
6cddcb9
1129cff
5bfcc41
8d7e167
 
 
 
5bfcc41
 
8d7e167
 
 
6cddcb9
5bfcc41
 
 
d81049c
e92cd88
5bfcc41
e92cd88
5bfcc41
1129cff
5bfcc41
1129cff
 
5bfcc41
1129cff
 
5bfcc41
e92cd88
5bfcc41
 
 
e92cd88
 
 
5bfcc41
 
 
 
8d7e167
e92cd88
 
5bfcc41
 
 
e92cd88
 
 
 
5bfcc41
 
e92cd88
5bfcc41
e92cd88
 
 
5bfcc41
 
 
 
 
 
e92cd88
 
1129cff
5bfcc41
 
 
1129cff
5bfcc41
 
 
1129cff
5bfcc41
6cddcb9
 
5bfcc41
 
 
6cddcb9
5bfcc41
 
6cddcb9
5bfcc41
e92cd88
5bfcc41
 
6cddcb9
1129cff
5bfcc41
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
import os
import cv2
import numpy as np
import gradio as gr
import insightface
import urllib.request
from insightface.app import FaceAnalysis

# --- 0. モデルの自動ダウンロード ---
def download_model_if_missing():
    model_filename = 'inswapper_128.onnx'
    url = "https://huggingface.co/ezioruan/inswapper_128.onnx/resolve/main/inswapper_128.onnx"
    if not os.path.exists(model_filename):
        print("Downloading model...")
        urllib.request.urlretrieve(url, model_filename)

download_model_if_missing()

# --- 1. モデルの初期化 ---
app = FaceAnalysis(name='buffalo_l', providers=['CPUExecutionProvider'])
app.prepare(ctx_id=0, det_size=(640, 640))
swapper = insightface.model_zoo.get_model('inswapper_128.onnx', download=False, providers=['CPUExecutionProvider'])

# --- 2. 動画処理ロジック ---
def process_video(source_img, target_video, progress=gr.Progress()):
    if source_img is None or target_video is None:
        return None, "画像と動画を選択してください。"

    # ソース顔の抽出
    source_faces = app.get(source_img)
    if not source_faces:
        return None, "画像から顔が見つかりません。"
    source_face = source_faces[0]

    # 動画の読み込み
    cap = cv2.VideoCapture(target_video)
    if not cap.isOpened():
        return None, "動画ファイルを開けませんでした。"

    fps = cap.get(cv2.CAP_PROP_FPS)
    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))

    # 出力ファイル設定 (より汎用的な mp4v を使用)
    output_path = "output_result.mp4"
    fourcc = cv2.VideoWriter_fourcc(*'mp4v')
    out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))

    # フレーム処理ループ
    frame_idx = 0
    while True:
        ret, frame = cap.read()
        if not ret:
            break
        
        # 顔の検出と入れ替え
        faces = app.get(frame)
        res_frame = frame.copy()
        for face in faces:
            res_frame = swapper.get(res_frame, face, source_face, paste_back=True)
        
        out.write(res_frame)
        frame_idx += 1
        
        # 進捗をUIに反映
        if total_frames > 0:
            progress(frame_idx / total_frames, desc=f"処理中... {frame_idx}/{total_frames}")

    cap.release()
    out.release()
    
    # 最終的なファイルの存在確認
    if not os.path.exists(output_path):
        return None, "動画の生成に失敗しました。"

    return output_path, "完了しました!"

# --- 3. UI ---
with gr.Blocks() as demo:
    gr.Markdown("### 🎬 Stable Face Swapper")
    with gr.Row():
        with gr.Column():
            img_input = gr.Image(label="入れ替える顔画像", type="numpy")
            vid_input = gr.Video(label="ターゲット動画")
            submit_btn = gr.Button("変換開始")
        with gr.Column():
            vid_output = gr.Video(label="出力結果")
            status_text = gr.Textbox(label="ステータス")

    submit_btn.click(
        fn=process_video,
        inputs=[img_input, vid_input],
        outputs=[vid_output, status_text]
    )

demo.launch()