wangzhibo
2025-07-30 1dd99f552485fe006eb0e9a0ebeacfe559d1e765
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
import cv2
import numpy as np
import insightface
from insightface.app import FaceAnalysis
from insightface.app.common import Face
import threading
from gfpgan.utils import GFPGANer
from typing import Any
 
Frame = np.ndarray
 
 
class FaceProcessor:
    """
    调用cv的模块分解mp4,组装mp4,视频质量差 
    """
    def __init__(self, face_enhancer_model: str = 'GFPGANv1.4.pth', face_swapper_model: str = 'inswapper_128.onnx', device: str = 'cuda'):
        self.FACE_ENHANCER = None
        self.FACE_SWAPPER = None
        self.THREAD_SEMAPHORE = threading.Semaphore()
        self.THREAD_LOCK = threading.Lock()
 
        # Set device
        self.device = device  # 'cuda' for GPU, 'cpu' for CPU
 
        # Load models
        self.face_enhancer_model = face_enhancer_model
        self.face_swapper_model = face_swapper_model
 
        # Initialize face analysis app
        self.app = FaceAnalysis(providers=['CUDAExecutionProvider' if device == 'cuda' else 'CPUExecutionProvider'])
        self.app.prepare(ctx_id=0, det_size=(640, 640))
 
    def get_face_enhancer(self) -> GFPGANer:
        with self.THREAD_LOCK:
            if self.FACE_ENHANCER is None:
                self.FACE_ENHANCER = GFPGANer(
                    model_path=self.face_enhancer_model, upscale=1, device=self.device)
        return self.FACE_ENHANCER
 
    def get_face_swapper(self) -> Any:
        with self.THREAD_LOCK:
            if self.FACE_SWAPPER is None:
                self.FACE_SWAPPER = insightface.model_zoo.get_model(
                    self.face_swapper_model, providers=['CUDAExecutionProvider' if self.device == 'cuda' else 'CPUExecutionProvider'])
        return self.FACE_SWAPPER
 
    def enhance_face(self, target_face: Face, temp_frame: Frame) -> Frame:
        start_x, start_y, end_x, end_y = map(int, target_face['bbox'])
        padding_x = int((end_x - start_x) * 0.5)
        padding_y = int((end_y - start_y) * 0.5)
        start_x = max(0, start_x - padding_x)
        start_y = max(0, start_y - padding_y)
        end_x = max(0, end_x + padding_x)
        end_y = max(0, end_y + padding_y)
        temp_face = temp_frame[start_y:end_y, start_x:end_x]
        if temp_face.size:
            with self.THREAD_SEMAPHORE:
                _, _, temp_face = self.get_face_enhancer().enhance(
                    temp_face,
                    paste_back=True
                )
            temp_frame[start_y:end_y, start_x:end_x] = temp_face
        return temp_frame
 
    def swap_face(self, source_face: Face, target_face: Face, temp_frame: Frame) -> Frame:
        return self.get_face_swapper().get(temp_frame, target_face, source_face, paste_back=True)
 
    def process_video(self, src_mp4: str, image_path: str, dst_mp4: str) -> None:
        # Read the target image (source face)
        target_image = cv2.imread(image_path)
        target_faces = self.app.get(target_image)
        if len(target_faces) == 0:
            print("No face detected in the target image.")
            return
        target_face = target_faces[0]
 
        # Open the source video
        cap = cv2.VideoCapture(src_mp4)
        if not cap.isOpened():
            print("Error opening video stream or file")
            return
 
        # Get video properties
        fourcc = cv2.VideoWriter_fourcc(*'mp4v')
        fps = cap.get(cv2.CAP_PROP_FPS)
        frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
        frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
 
        # Create VideoWriter object to save the output video
        out = cv2.VideoWriter(dst_mp4, fourcc, fps, (frame_width, frame_height))
 
        # Get the total frame count
        total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
 
        # Process each frame of the video
        frame_count = 0
        while cap.isOpened():
            ret, frame = cap.read()
            if not ret:
                break
 
            # Detect faces in the current frame
            faces = self.app.get(frame)
            if len(faces) > 0:
                # Swap face from the target image to the current frame
                swapped_face_frame = self.swap_face(target_face, faces[0], frame)
                self.enhance_face(faces[0], swapped_face_frame)
                out.write(swapped_face_frame)
            else:
                # If no face detected, just write the original frame
                out.write(frame)
 
            # Increment frame count
            frame_count += 1
 
            # Calculate and print progress every 5%
            progress = (frame_count / total_frames) * 100
            if progress >= 5 * (frame_count // (total_frames // 20)):
                print(f"Processing: {int(progress)}% completed")
 
        cap.release()
        out.release()
        print(f"Video processing complete. Output saved to {dst_mp4}")
 
 
# Usage example:
 
# Initialize the FaceProcessor (use 'cuda' for GPU)
face_processor = FaceProcessor(device='cpu')
 
# Process the video by swapping faces from the target image
# face_processor.process_video('g1.mp4', 'from-face/girl-1.png', './g1-demo_output.mp4')
# face_processor.process_video('g1.mp4', 'from-face/girl-2.png', './g2-demo_output.mp4')
face_processor.process_video('g3.mp4', 'lilizhen.jpg', './lilizhen_output.mp4')
face_processor.process_video('g3.mp4', 'from-face/girl-1.png', './g1-demo_output.mp4')
face_processor.process_video('g3.mp4', 'from-face/girl-2.png', './g2-demo_output.mp4')