From c36d5f620b30bef644a770e8e4cb71ff60d654d8 Mon Sep 17 00:00:00 2001
From: wangzhibo <wangzhibo@shsening.com>
Date: 星期三, 30 七月 2025 22:21:18 +0800
Subject: [PATCH] Merge branch 'master' of http://222.71.114.84:7070/r/AutoMediaPublisher
---
data/auto_media_publisher.sqlite3 | 0
/dev/null | 17 -----------------
2 files changed, 0 insertions(+), 17 deletions(-)
diff --git a/auto_media_publisher/utils/face_swap/__init__.py b/auto_media_publisher/utils/face_swap/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/auto_media_publisher/utils/face_swap/__init__.py
+++ /dev/null
diff --git a/auto_media_publisher/utils/face_swap/face_analyser.py b/auto_media_publisher/utils/face_swap/face_analyser.py
deleted file mode 100644
index e927185..0000000
--- a/auto_media_publisher/utils/face_swap/face_analyser.py
+++ /dev/null
@@ -1,54 +0,0 @@
-
-import torch
-from typing import Optional, List
-import numpy
-from insightface.app import FaceAnalysis
-from insightface.app.common import Face
-
-from gfpgan.utils import GFPGANer
-from .typing import Frame # 浣犲凡鏈夊畾涔�
-
-SIMILAR_FACE_DISTANCE: float = 0.45 # < 0.4 鍚屼竴浜� 0.4 -0.6 澶ф鏄悓涓�浜� >0.6 涓嶆槸鍚屼竴涓�
-
-
-class FaceAnalyser:
- def __init__(self):
- self.device = self._select_device()
- self._analyser = FaceAnalysis(name='buffalo_l', root='./', providers=['CUDAExecutionProvider' if self.device == 'cuda' else 'CPUExecutionProvider'])
- self._analyser.prepare(ctx_id=0, det_size=(640, 640))
-
- def _select_device(self):
- return 'cpu'
- if torch.cuda.is_available():
- return 'cuda'
- elif torch.backends.mps.is_available():
- return 'mps'
- else:
- return 'cpu'
-
- def get_many_faces(self,frame: Frame) -> Optional[List[Face]]:
- try:
- return self._analyser.get(frame)
- except Exception as e:
- print(f"{e}")
- return None
-
- def get_one_face(self,frame: Frame, position: int = 0) -> Optional[Face]:
- many_faces = self.get_many_faces(frame)
- if many_faces:
- try:
- return many_faces[position]
- except IndexError:
- return many_faces[-1]
- return None
-
-
- def find_similar_face(self,frame: Frame, reference_face: Face) -> Optional[Face]:
- many_faces = self.get_many_faces(frame)
- if many_faces:
- for face in many_faces:
- if hasattr(face, 'normed_embedding') and hasattr(reference_face, 'normed_embedding'):
- distance = numpy.sum(numpy.square(face.normed_embedding - reference_face.normed_embedding))
- if distance < SIMILAR_FACE_DISTANCE:
- return face
- return None
diff --git a/auto_media_publisher/utils/face_swap/face_enhancer.py b/auto_media_publisher/utils/face_swap/face_enhancer.py
deleted file mode 100644
index d4b6beb..0000000
--- a/auto_media_publisher/utils/face_swap/face_enhancer.py
+++ /dev/null
@@ -1,50 +0,0 @@
-
-import torch
-from typing import List
-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
-
-SIMILAR_FACE_DISTANCE: float = 0.45 # < 0.4 鍚屼竴浜� 0.4 -0.6 澶ф鏄悓涓�浜� >0.6 涓嶆槸鍚屼竴涓�
-
-
-class FaceEnhancer:
- def __init__(self):
- self.device = self._select_device()
- self._enhancer = GFPGANer(model_path=FACE_ENHANCER_MODEL_PATH, upscale=1, device=self.device)
-
- def _select_device(self):
- return 'cpu'
- if torch.cuda.is_available():
- return 'cuda'
- elif torch.backends.mps.is_available():
- return 'mps'
- else:
- return 'cpu'
-
- 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:
- _, _, temp_face = self._enhancer.enhance(
- temp_face,
- paste_back=True
- )
- temp_frame[start_y:end_y, start_x:end_x] = temp_face
- return temp_frame
-
-
- def process_frame(self,temp_frame: Frame,many_faces:List) -> Frame:
- #many_faces = self._get_many_faces(temp_frame) check face_analyser.py
- if many_faces:
- for target_face in many_faces:
- temp_frame = self._enhance_face(target_face, temp_frame)
- return temp_frame
diff --git a/auto_media_publisher/utils/face_swap/face_nsfw_predictor.py b/auto_media_publisher/utils/face_swap/face_nsfw_predictor.py
deleted file mode 100644
index 8cc5f96..0000000
--- a/auto_media_publisher/utils/face_swap/face_nsfw_predictor.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import threading
-import numpy as np
-from PIL import Image
-from keras import Model
-import opennsfw2
-
-from .typing import Frame # 浣犲凡鏈夊畾涔�
-
-MAX_PROBABILITY = 0.85
-
-class NSFWPredictor:
- def __init__(self):
- self._predictor = opennsfw2.make_open_nsfw_model()
-
- def predict_frame(self, frame: Frame) -> bool:
- image = Image.fromarray(frame)
- image = opennsfw2.preprocess_image(image, opennsfw2.Preprocessing.YAHOO)
- views = np.expand_dims(image, axis=0)
- _, probability = self._predictor.predict(views)[0]
- return probability > MAX_PROBABILITY
-
- def predict_image(self, image_path: str) -> bool:
- probability = opennsfw2.predict_image(image_path)
- return probability > MAX_PROBABILITY
-
- def predict_video(self, video_path: str, frame_interval: int = 100) -> bool:
- _, probabilities = opennsfw2.predict_video_frames(video_path, frame_interval=frame_interval)
- return any(prob > MAX_PROBABILITY for prob in probabilities)
diff --git a/auto_media_publisher/utils/face_swap/face_swaper_cv2.py b/auto_media_publisher/utils/face_swap/face_swaper_cv2.py
deleted file mode 100644
index 1e2dc41..0000000
--- a/auto_media_publisher/utils/face_swap/face_swaper_cv2.py
+++ /dev/null
@@ -1,138 +0,0 @@
-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鐨勬ā鍧楀垎瑙p4锛岀粍瑁卪p4锛岃棰戣川閲忓樊
- """
- 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')
-
diff --git a/auto_media_publisher/utils/face_swap/face_swaper_ffmpeg.py b/auto_media_publisher/utils/face_swap/face_swaper_ffmpeg.py
deleted file mode 100644
index 7b28497..0000000
--- a/auto_media_publisher/utils/face_swap/face_swaper_ffmpeg.py
+++ /dev/null
@@ -1,245 +0,0 @@
-import cv2
-import os
-import torch
-import queue
-import threading
-import subprocess
-import json
-import numpy as np
-import time
-from typing import Union
-import insightface
-from insightface.app.common import Face
-from .face_nsfw_predictor import NSFWPredictor
-from .face_enhancer import FaceEnhancer
-from .face_analyser import FaceAnalyser
-from auto_media_publisher.utils.logger import get_logger
-from auto_media_publisher.config.conf_base import FACE_SWAPPER_MODEL_PATH
-
-
-class FaceSwapWorker:
- def __init__(self, enable_enhancer:bool = False,enable_nsfw_predictor:bool=False):
- self.logger = get_logger("FaceSwapWorker", "admin")
- self.device = self._select_device()
- self.face_swaper = insightface.model_zoo.get_model(FACE_SWAPPER_MODEL_PATH, providers=['CUDAExecutionProvider' if self.device == 'cuda' else 'CPUExecutionProvider'])
- self.main_analyser = FaceAnalyser()
- self.enable_enhancer = enable_enhancer
- self.enable_nsfw_predictor = enable_nsfw_predictor
- if self.enable_nsfw_predictor:
- self.nsfw_predictor = NSFWPredictor()
- self._init()
-
- def _init(self):
- self.frame_queue = queue.Queue()
- self.processed_queue = queue.Queue()
- self.buffered_frames = {}
- self.expected_index = 0
- self.fps = 0
- self.width = 0
- self.height = 0
- self.total_frames = 0
- self.process_threads = []
- self.num_processors = 4
-
- def _select_device(self):
- if torch.cuda.is_available():
- return 'cuda'
- elif torch.backends.mps.is_available():
- return 'mps'
- else:
- return 'cpu'
-
- def _decode_video(self):
- # === Step 1: 鐢� ffprobe 鑾峰彇瑙嗛淇℃伅 ===
- probe_cmd = [
- "ffprobe",
- "-v", "error",
- "-select_streams", "v:0",
- "-show_entries", "stream=width,height,r_frame_rate",
- "-of", "json",
- self.input_video_path
- ]
- probe_result = subprocess.run(probe_cmd, capture_output=True, text=True, check=True)
- probe_data = json.loads(probe_result.stdout)
- video_stream = probe_data['streams'][0]
- self.width = int(video_stream['width'])
- self.height = int(video_stream['height'])
- self.fps = eval(video_stream['r_frame_rate']) # e.g. "30/1"
-
- print(f"Video info: {self.width}x{self.height} @ {self.fps}fps")
-
- # === Step 2: 鐢� ffmpeg 瑙g爜杈撳嚭 rawvideo 鍒� stdout pipe ===
- decode_cmd = [
- "ffmpeg",
- "-loglevel", "info",
- "-i", self.input_video_path,
- "-f", "rawvideo",
- "-pix_fmt", "rgb24",
- "pipe:1"
- ]
- process = subprocess.Popen(decode_cmd, stdout=subprocess.PIPE, bufsize=10**8)
-
- frame_size = self.width * self.height * 3
- idx = 0
- while True:
- in_bytes = process.stdout.read(frame_size)
- if not in_bytes:
- break
- frame = np.frombuffer(in_bytes, dtype=np.uint8).reshape((self.height, self.width, 3))
- self.frame_queue.put((idx, frame)) # 鏀逛负 tuple 褰㈠紡
- idx += 1
-
- self.total_frames = idx
- print(f"_decode_video Total frames: {self.total_frames}")
-
- process.stdout.close()
- process.wait()
-
- # 閫氱煡澶勭悊绾跨▼缁撴潫
- for _ in range(self.num_processors):
- self.frame_queue.put(None)
-
- def _process_frame(self):
- thread_face_analyser = FaceAnalyser()
- if self.enable_enhancer:
- thread_face_enhancer = FaceEnhancer()
- while True:
- item = self.frame_queue.get()
- if item is None:
- self.processed_queue.put(None) # 鎶婄粨鏉熶俊鍙风户缁紶閫�
- break
-
- idx, frame = item
-
- print(f"[Thread ID: {threading.get_ident()}] is processing frame : {idx} of video file 锛歿self.input_video_path}")
-
- old_face = thread_face_analyser.get_one_face(frame)
- if old_face:
- frame = self.face_swaper.get(frame,old_face,self.new_face)
- else:
- print("face not detected")
-
- print(self.enable_enhancer)
-
- if self.enable_enhancer:
- many_faces= thread_face_analyser.get_many_faces(frame)
- if many_faces:
- frame = thread_face_enhancer.process_frame(frame,many_faces)
-
- self.processed_queue.put((idx, frame))
-
- def _encode_video(self):
- while not (self.width and self.height and self.fps):
- time.sleep(0.01)
- encode_cmd = [
- "ffmpeg",
- "-loglevel", "info",
- "-y", # 瑕嗙洊杈撳嚭鏂囦欢
- "-f", "rawvideo",
- "-pixel_format", "rgb24",
- "-video_size", f"{self.width}x{self.height}",
- "-framerate", str(self.fps),
- "-i", "pipe:0", # 浠� stdin 璇诲彇鍘熷瑙嗛甯�
- #"-i", self.input_video_path if self.new_audio_path is None else self.new_audio_path, # 鎻愪緵闊抽
- "-c:v", "libx264", # 浣犲彲浠ユ浛鎹㈡垚 h264_nvenc銆乭evc_nvenc銆乭264_amf銆乭264_qsv 绛夌‖浠剁紪鐮佸櫒,璐ㄩ噺鍙兘绋嶅井閫婅壊
- "-crf", "18",
- "-preset", "veryfast",
- "-pix_fmt", "yuv420p",
- #"-c:a", "copy",
- #"-map", "0:v:0",
- #"-map", "1:a:0",
- #"-shortest",
- self.output_video_path
- ]
-
- process = subprocess.Popen(encode_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,bufsize=10**8)
-
- end_signals_received = 0
- finished = 0
-
- while True:
- item = self.processed_queue.get()
- if item is None:
- end_signals_received += 1
- if end_signals_received == self.num_processors:
- break
- else:
- continue
- idx,frame = item
- self.buffered_frames[idx] = frame
-
- while self.expected_index in self.buffered_frames:
- next_frame = self.buffered_frames.pop(self.expected_index)
- print(f"_encode frame next_frame: {self.expected_index}")
- try:
- process.stdin.write(next_frame.astype(np.uint8).tobytes())
- except BrokenPipeError:
- print("BrokenPipeError: ffmpeg process closed stdin.")
- break
- self.expected_index += 1
- finished += 1
- print(f"_encode frame finished: {finished}")
-
-
- print(f"Total frames encoded: {finished}")
-
- process.stdin.close()
- return_code = process.wait()
- print("FFmpeg exited with code:", return_code)
-
- def swap_face(self,new_face_image_path:str,input_video_path: str, output_video_path: str,num_processors=4):
- """
- 鎵ц鎹㈣劯鎿嶄綔锛屽弬鏁板涓嬶細
- 1銆� 鏂扮殑鑴哥収鐗囷紝璺緞瑕佹纭紝杩樿鍖呭惈涓�寮犱汉鑴革紱
- 2銆� 鑰佺殑瑙嗛锛岃矾寰勮姝g‘
- 3銆� 鏂拌棰� 鏈�缁堣緭鍑虹殑瑙嗛璺緞
- 4銆� 骞跺彂澶勭悊杩涚▼鏁伴噺锛岄粯璁ゆ槸4锛屼緷鎹‖浠舵�ц兘璁剧疆
- 5銆� 鏂拌棰� 鐨勯煶棰戯紝濡傛灉涓嶄负绌猴紝灏变娇鐢紱 鍚﹀垯 浣跨敤鑰佽棰戠殑闊抽
- """
- self._init()
- self.num_processors = num_processors
-
- if not os.path.exists(new_face_image_path):
- self.logger.error(f"face swap image_path: {new_face_image_path} does not exists.")
- return
- self.new_face = self.main_analyser.get_one_face(cv2.imread(new_face_image_path))
- if not self.new_face :
- self.logger.error(f"target image DO NOT has face : {new_face_image_path} does not exists.")
- return
-
- if not os.path.exists(input_video_path):
- self.logger.error(f"face swap video_path: {input_video_path} does not exists.")
- return
-
- self.input_video_path = input_video_path
- self.output_video_path = output_video_path
- #self.new_audio_path =new_audio_path
-
- if self.enable_nsfw_predictor and self.nsfw_predictor:
- if self.nsfw_predictor.predict_video(input_video_path):
- self.logger.error(f" 瑙嗛杩濊锛� {self.input_video_path}")
- return
-
- # 1. 鍚姩 encode 绾跨▼锛堥渶瑕佺瓑寰� width/height/fps 璁剧疆锛屼絾鍙互鏀惧湪鍓嶉潰锛�
- encode_thread = threading.Thread(target=self._encode_video)
- encode_thread.start()
-
- # 2. 鍚姩 decode 绾跨▼锛堣礋璐d粠瑙嗛涓鍙栧抚锛�
- decode_thread = threading.Thread(target=self._decode_video)
- decode_thread.start()
-
- # 3. 鍚姩澶勭悊绾跨▼
- for i in range(self.num_processors):
- t = threading.Thread(target=self._process_frame)
- t.start()
- self.process_threads.append(t)
-
- # 4. 绛夊緟 decode 瀹屾垚
- decode_thread.join()
-
- # 5. 绛夊緟鎵�鏈夊鐞嗙嚎绋嬪畬鎴�
- for t in self.process_threads:
- t.join()
-
- # 6. 绛夊緟 encode 瀹屾垚
- encode_thread.join()
diff --git a/auto_media_publisher/utils/face_swap/face_swaper_ffmpeg_stream.py b/auto_media_publisher/utils/face_swap/face_swaper_ffmpeg_stream.py
deleted file mode 100644
index 5966fe3..0000000
--- a/auto_media_publisher/utils/face_swap/face_swaper_ffmpeg_stream.py
+++ /dev/null
@@ -1,243 +0,0 @@
-import os
-import cv2
-import subprocess
-import threading
-from threading import Thread, current_thread
-import queue
-import numpy as np
-import torch
-import json
-import insightface
-from insightface.app.common import Face
-from .face_nsfw_predictor import NSFWPredictor
-from .face_enhancer import FaceEnhancer
-from .face_analyser import FaceAnalyser
-from auto_media_publisher.utils.logger import get_logger
-from auto_media_publisher.config.conf_base import FACE_SWAPPER_MODEL_PATH
-
-
-class FFmpegVideoProcessor:
- def __init__(self,enable_enhancer:bool=False,enable_nsfw_predictor:bool=False):
- self.logger = get_logger("FaceSwapWorker", "admin")
- self.device = self._select_device()
- print(f"using device {self.device}")
- self.face_swaper = insightface.model_zoo.get_model(FACE_SWAPPER_MODEL_PATH, providers=['CUDAExecutionProvider' if self.device == 'cuda' else 'CPUExecutionProvider'])
- self.main_analyser = FaceAnalyser()
- self.enable_enhancer = enable_enhancer
- self.enable_nsfw_predictor = enable_nsfw_predictor
- if self.enable_nsfw_predictor:
- self.nsfw_predictor = NSFWPredictor()
- self._init()
-
- def _init(self):
- self.width = 0
- self.height = 0
- self.fps = 0
- self.frame_queue = queue.Queue(maxsize=2000) # 瑙g爜绾跨▼鏀惧抚
- self.processed_queue = queue.Queue(maxsize=2000) # 澶勭悊绾跨▼鏀惧抚
- self.buffered_frames = {}
- self.expected_index = 0
-
- self.stop_event = threading.Event()
-
- def _select_device(self):
- return 'cpu'
- if torch.cuda.is_available():
- return 'cuda'
- elif torch.backends.mps.is_available():
- return 'mps'
- else:
- return 'cpu'
-
- def probe_video(self):
- cmd = [
- "ffprobe",
- "-v", "error",
- "-select_streams", "v:0",
- "-show_entries", "stream=width,height,r_frame_rate",
- "-of", "json",
- self.input_video_path,
- ]
- proc = subprocess.run(cmd, capture_output=True, text=True, check=True)
- info = json.loads(proc.stdout)
- stream = info["streams"][0]
- self.width = int(stream["width"])
- self.height = int(stream["height"])
- self.fps = eval(stream["r_frame_rate"])
- print(f"Video info: {self.width}x{self.height} @ {self.fps}fps")
-
- def decode_worker(self):
- decode_cmd = [
- "ffmpeg",
- "-loglevel", "error",
- "-i", self.input_video_path,
- "-f", "rawvideo",
- "-pix_fmt", "rgb24",
- "pipe:1",
- ]
- self.decode_proc = subprocess.Popen(decode_cmd, stdout=subprocess.PIPE, bufsize=10**8)
-
- frame_size = self.width * self.height * 3
- idx = 0
- while not self.stop_event.is_set():
- raw_frame = self.decode_proc.stdout.read(frame_size)
- if len(raw_frame) < frame_size:
- break
- frame = np.frombuffer(raw_frame, np.uint8).reshape((self.height, self.width, 3))
- self.frame_queue.put((idx, frame))
- idx += 1
-
- # 缁撴潫淇″彿
- for _ in range(self.num_workers):
- self.frame_queue.put(None)
- self.decode_proc.stdout.close()
- self.decode_proc.wait()
-
- def process_frame(self, frame: np.ndarray,thread_face_analyser,thread_face_enhancer) -> np.ndarray:
- # 杩欓噷鏀句綘鐨勬崲鑴搞�佸彉閫熺瓑澶勭悊閫昏緫
- # 渚嬪锛�
- # frame = your_face_swap_function(frame)
- # 鎴栬�呯畝鍗曠ず鑼冿細鍙嶈浆棰滆壊
- #return 255 - frame
- if self.enable_enhancer:
- many_faces= thread_face_analyser.get_many_faces(frame)
- if many_faces:
- frame = self.face_swaper.get(frame,many_faces[0],self.new_face)
- frame = thread_face_enhancer.process_frame(frame,many_faces)
- else:
- old_face = thread_face_analyser.get_one_face(frame)
- if old_face:
- frame = self.face_swaper.get(frame,old_face,self.new_face)
- else:
- print("face not detected")
-
- return frame
-
- def process_worker(self):
- thread_face_analyser = FaceAnalyser()
- if self.enable_enhancer:
- thread_face_enhancer = FaceEnhancer()
- else:
- thread_face_enhancer = None
- while True:
- item = self.frame_queue.get()
- if item is None:
- self.processed_queue.put(None)
- break
- idx, frame = item
- try:
- processed = self.process_frame(frame,thread_face_analyser,thread_face_enhancer)
- print(f"{current_thread().name} has proceesed frame :{idx}")
- except:
- print(f"{current_thread().name} has error frame :{idx}")
- processed=frame
- self.processed_queue.put((idx, processed))
-
- def encode_worker(self):
- input_audio_path = self.input_video_path
- if self.new_audio_path and os.path.exists(self.new_audio_path):
- input_audio_path = self.new_audio_path
- encode_cmd = [
- "ffmpeg",
- "-y",
- "-loglevel", "error",
- "-f", "rawvideo",
- "-pixel_format", "rgb24",
- "-video_size", f"{self.width}x{self.height}",
- "-framerate", str(self.fps),
- "-i", "pipe:0",
- "-i", input_audio_path,
- "-c:v", "libx264",
- "-crf", "18",
- "-preset", "veryfast",
- "-c:a", "copy",
- "-map", "0:v:0",
- "-map", "1:a:0",
- "-shortest",
- self.output_video_path,
- ]
- self.encode_proc = subprocess.Popen(encode_cmd, stdin=subprocess.PIPE, bufsize=10**8)
-
- end_signals = 0
- finished = 0
- while True:
- item = self.processed_queue.get()
- if item is None:
- end_signals += 1
- if end_signals == self.num_workers:
- break
- else:
- continue
-
- idx, frame = item
- self.buffered_frames[idx] = frame
-
- while self.expected_index in self.buffered_frames:
- f = self.buffered_frames.pop(self.expected_index)
- try:
- self.encode_proc.stdin.write(f.astype(np.uint8).tobytes())
- print(f"{self.expected_index} has been writed into stdin")
- except BrokenPipeError:
- print("FFmpeg encoding pipe broken.")
- self.stop_event.set()
- break
- self.expected_index += 1
- finished += 1
-
- self.encode_proc.stdin.close()
- self.encode_proc.wait()
- print(f"Encoding finished, total frames: {finished}")
-
- def run(self,new_face_image_path,input_video_path, output_video_path, num_workers=4,new_audio_path=None):
- """
- 鎵ц鎹㈣劯鎿嶄綔锛屽弬鏁板涓嬶細
- 1銆� 鏂扮殑鑴哥収鐗囷紝璺緞瑕佹纭紝杩樿鍖呭惈涓�寮犱汉鑴革紱
- 2銆� 鑰佺殑瑙嗛锛岃矾寰勮姝g‘
- 3銆� 鏂拌棰� 鏈�缁堣緭鍑虹殑瑙嗛璺緞
- 4銆� 骞跺彂澶勭悊杩涚▼鏁伴噺锛岄粯璁ゆ槸4锛屼緷鎹‖浠舵�ц兘璁剧疆
- 5銆� 鏂拌棰� 鐨勯煶棰戯紝濡傛灉涓嶄负绌猴紝灏变娇鐢紱 鍚﹀垯 浣跨敤鑰佽棰戠殑闊抽
- """
- if not os.path.exists(new_face_image_path):
- self.logger.error(f"face swap image_path: {new_face_image_path} does not exists.")
- return
- self.new_face = self.main_analyser.get_one_face(cv2.imread(new_face_image_path))
-
- if not self.new_face :
- self.logger.error(f"target image DO NOT has face : {new_face_image_path} does not exists.")
- return
-
- if not os.path.exists(input_video_path):
- self.logger.error(f"face swap video_path: {input_video_path} does not exists.")
- return
-
- self._init()
- self.input_video_path = input_video_path
- self.output_video_path = output_video_path
- self.num_workers = num_workers
- self.new_audio_path =new_audio_path
-
- self.probe_video()
-
- threads = []
-
- # 瑙g爜绾跨▼
- t_decode = threading.Thread(target=self.decode_worker)
- threads.append(t_decode)
- t_decode.start()
-
- # 澶氬鐞嗙嚎绋�
- for i in range(self.num_workers):
- t = threading.Thread(target=self.process_worker, name=f"process_worker_{i}")
- threads.append(t)
- t.start()
-
- # 缂栫爜绾跨▼
- t_encode = threading.Thread(target=self.encode_worker)
- threads.append(t_encode)
- t_encode.start()
-
- for t in threads:
- t.join()
-
- print("Processing complete.")
-
diff --git a/auto_media_publisher/utils/face_swap/typing.py b/auto_media_publisher/utils/face_swap/typing.py
deleted file mode 100644
index 30791ef..0000000
--- a/auto_media_publisher/utils/face_swap/typing.py
+++ /dev/null
@@ -1,17 +0,0 @@
-
-from typing import Any
-from insightface.app.common import Face
-import numpy
-
-Face = Face
-Frame = numpy.ndarray[Any, Any]
-'''
-from typing import Any
-import numpy
-import numpy.typing as npt # Import numpy.typing for NDArray
-
-from insightface.app.common import Face
-
-Face = Face
-Frame = npt.NDArray[numpy.uint8] # Specify the dtype, e.g., uint8 for image frames
-'''
\ No newline at end of file
diff --git a/data/auto_media_publisher.sqlite3 b/data/auto_media_publisher.sqlite3
index a846672..05e7791 100644
--- a/data/auto_media_publisher.sqlite3
+++ b/data/auto_media_publisher.sqlite3
Binary files differ
--
Gitblit v1.9.1