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.")
|