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')
|