wangzhibo
2025-07-30 c706a69abfe9bae1322adbd344dfdaae37cdf564
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
import av
import torch
import threading, queue
from threading import current_thread
import numpy as np
import insightface
import cv2
import time
from insightface.app.common import Face
from gfpgan.utils import GFPGANer
from .typing import Frame # 你已有定义
from auto_media_publisher.config.conf_base import FACE_ENHANCER_MODEL_PATH,FACE_SWAPPER_MODEL_PATH
from insightface.app import FaceAnalysis
 
SIMILAR_FACE_DISTANCE: float = 0.45     # < 0.4 同一人 0.4 -0.6 大概是同一人 >0.6 不是同一个
 
def _select_device():
    if torch.cuda.is_available():
        return 'cuda:0'
    elif torch.backends.mps.is_available():
        return 'mps'
    else:
        return 'cpu'
    
NUM_WORKERS = 1
 
#face_swapper = insightface.model_zoo.get_model(FACE_SWAPPER_MODEL_PATH, providers=['CUDAExecutionProvider'])
#face_enhancer = GFPGANer(model_path=FACE_ENHANCER_MODEL_PATH, upscale=1, device=_select_device())
 
frame_q = queue.Queue(maxsize=10)
processed_q = queue.Queue(maxsize=10)
 
THREAD_SEMAPHORE =threading.Semaphore(4)  # 不限制了。
 
def enhance_face(thread_face_enhancer,target_face, temp_frame:np.ndarray) -> np.ndarray:
    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]
    #print(temp_face.size)
    if temp_face.size:
        _, _, temp_face = thread_face_enhancer.enhance(
                temp_face,
                paste_back=True
        )
        temp_frame[start_y:end_y, start_x:end_x] = temp_face
    return temp_frame
    
 
def decode_video(video_path):
    container = av.open(video_path)
    for idx, frame in enumerate(container.decode(video=0)):
        img = frame.to_ndarray(format='rgb24')
        frame_q.put((idx, img))
    for _ in range(NUM_WORKERS):
        frame_q.put((None, None))
 
def process_frame_loop(new_face):
    start_time = time.time()
    thread_face_analyser = FaceAnalysis(name='buffalo_l', root='./', providers=['CUDAExecutionProvider'])
    thread_face_analyser.prepare(ctx_id=3, det_size=(640, 640))
    #thread_face_enhancer = GFPGANer(model_path=FACE_ENHANCER_MODEL_PATH, upscale=1, device=_select_device())
    thread_face_enhancer = GFPGANer(model_path=FACE_ENHANCER_MODEL_PATH, upscale=1, device=_select_device())
    thread_face_swapper = insightface.model_zoo.get_model(FACE_SWAPPER_MODEL_PATH, providers=['CUDAExecutionProvider'])
 
    while True:
        idx, frame = frame_q.get()
        if idx is None:
            processed_q.put((None, None))
            break
        try:
            many_faces = thread_face_analyser.get(frame)
            if many_faces:
                old_face = many_faces[0]
                frame = thread_face_swapper.get(frame, old_face, new_face, paste_back=True)
 
                for sinlge_face in many_faces:
                    frame_ = enhance_face(thread_face_enhancer,sinlge_face, frame)                
 
            processed_q.put((idx, frame_))
            print(f"{current_thread().name} has proceesed frame :{idx}")           
 
        except Exception as e:
            print(f"[ERROR] Frame {idx} failed: {e}")
    
    print(f"totol time is : {time.time()- start_time}")
 
def encode_video(output_path, fps, width, height):
    out = av.open(output_path, mode='w')
 
    #stream = out.add_stream('h264_nvenc', rate=fps)
    stream = out.add_stream('libx264', rate=fps)
    stream.width = width
    stream.height = height
    stream.pix_fmt = 'yuv420p'
 
    stream.options = {'crf': '18', 'preset': 'veryfast'}
    #stream.options = {'rc': 'constqp', 'qp': '18','preset': 'p7','profile': 'high'}
 
    next_index = 0
    buffer = {}
    end_count = 0
 
    while True:
        idx, frame = processed_q.get()
        if idx is None:
            end_count += 1
            if end_count >= NUM_WORKERS:
                break
            continue
        buffer[idx] = frame
 
        # 只要有当前要写入的帧,就输出
        while next_index in buffer:
            frame = buffer.pop(next_index)
            video_frame = av.VideoFrame.from_ndarray(frame, format='rgb24')
            for packet in stream.encode(video_frame):
                out.mux(packet)
            next_index += 1
 
    # 清空编码缓存
    for packet in stream.encode():
        out.mux(packet)
    out.close()
# 启动任务
video_path = './temp/buhui.mp4'
output_path = './temp/buhui-wangrong-enhancer.mp4'
new_face_image_path = './temp/wangrong.jpg'
face_analyser = FaceAnalysis(name='buffalo_l', root='./', providers=['CUDAExecutionProvider'])
face_analyser.prepare(ctx_id=0, det_size=(640, 640))
 
faces = face_analyser.get(cv2.imread(new_face_image_path))
if faces:
    new_face = sorted(faces, key=lambda x: x.bbox[0])[0]
        
    #threading.Thread(target=decode_video, args=(video_path,)).start()
    #threading.Thread(target=process_frame_loop, args=(new_face,)).start()
    #threading.Thread(target=encode_video, args=(output_path, 25,576,1080)).start()
    threads = []
        
    # 解码线程
    t_decode = threading.Thread(target=decode_video,args=(video_path,))
    threads.append(t_decode)
    t_decode.start()
    
    # 多处理线程
    for i in range(NUM_WORKERS):
        t = threading.Thread(target=process_frame_loop, args=(new_face,),name=f"process_worker_{i}")
        threads.append(t)
        t.start()
    
    # 编码线程
    t_encode = threading.Thread(target=encode_video,args=(output_path, 25,576,1080))
    threads.append(t_encode)
    t_encode.start()
    
    for t in threads:
        t.join()
    
    print("Processing complete.")