Merge pull request #886 from facefusion/feat/video_manager

feat/video manager
This commit is contained in:
Henry Ruhs
2025-05-22 14:38:22 +02:00
committed by GitHub
15 changed files with 78 additions and 31 deletions

View File

@@ -6,7 +6,7 @@ from time import time
import numpy import numpy
from facefusion import cli_helper, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, logger, process_manager, state_manager, voice_extractor, wording from facefusion import cli_helper, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, logger, process_manager, state_manager, video_manager, voice_extractor, wording
from facefusion.args import apply_args, collect_job_args, reduce_job_args, reduce_step_args from facefusion.args import apply_args, collect_job_args, reduce_job_args, reduce_step_args
from facefusion.common_helper import get_first from facefusion.common_helper import get_first
from facefusion.content_analyser import analyse_image, analyse_video from facefusion.content_analyser import analyse_image, analyse_video
@@ -468,8 +468,10 @@ def process_video(start_time : float) -> ErrorCode:
source_audio_path = get_first(filter_audio_paths(state_manager.get_item('source_paths'))) source_audio_path = get_first(filter_audio_paths(state_manager.get_item('source_paths')))
if source_audio_path: if source_audio_path:
if replace_audio(state_manager.get_item('target_path'), source_audio_path, state_manager.get_item('output_path')): if replace_audio(state_manager.get_item('target_path'), source_audio_path, state_manager.get_item('output_path')):
video_manager.clear_video_pool()
logger.debug(wording.get('replacing_audio_succeed'), __name__) logger.debug(wording.get('replacing_audio_succeed'), __name__)
else: else:
video_manager.clear_video_pool()
if is_process_stopping(): if is_process_stopping():
process_manager.end() process_manager.end()
return 4 return 4
@@ -477,8 +479,10 @@ def process_video(start_time : float) -> ErrorCode:
move_temp_file(state_manager.get_item('target_path'), state_manager.get_item('output_path')) move_temp_file(state_manager.get_item('target_path'), state_manager.get_item('output_path'))
else: else:
if restore_audio(state_manager.get_item('target_path'), state_manager.get_item('output_path'), trim_frame_start, trim_frame_end): if restore_audio(state_manager.get_item('target_path'), state_manager.get_item('output_path'), trim_frame_start, trim_frame_end):
video_manager.clear_video_pool()
logger.debug(wording.get('restoring_audio_succeed'), __name__) logger.debug(wording.get('restoring_audio_succeed'), __name__)
else: else:
video_manager.clear_video_pool()
if is_process_stopping(): if is_process_stopping():
process_manager.end() process_manager.end()
return 4 return 4

View File

@@ -30,7 +30,7 @@ def set_static_faces(vision_frame : VisionFrame, faces : List[Face]) -> None:
def clear_static_faces() -> None: def clear_static_faces() -> None:
FACE_STORE['static_faces'] = {} FACE_STORE['static_faces'].clear()
def create_frame_hash(vision_frame : VisionFrame) -> Optional[str]: def create_frame_hash(vision_frame : VisionFrame) -> Optional[str]:
@@ -53,4 +53,4 @@ def append_reference_face(name : str, face : Face) -> None:
def clear_reference_faces() -> None: def clear_reference_faces() -> None:
FACE_STORE['reference_faces'] = {} FACE_STORE['reference_faces'].clear()

View File

@@ -9,7 +9,7 @@ import facefusion.choices
import facefusion.jobs.job_manager import facefusion.jobs.job_manager
import facefusion.jobs.job_store import facefusion.jobs.job_store
import facefusion.processors.core as processors import facefusion.processors.core as processors
from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, inference_manager, logger, process_manager, state_manager, wording from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, inference_manager, logger, process_manager, state_manager, video_manager, wording
from facefusion.common_helper import create_int_metavar from facefusion.common_helper import create_int_metavar
from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url
from facefusion.execution import has_execution_provider from facefusion.execution import has_execution_provider
@@ -115,6 +115,7 @@ def pre_process(mode : ProcessMode) -> bool:
def post_process() -> None: def post_process() -> None:
read_static_image.cache_clear() read_static_image.cache_clear()
video_manager.clear_video_pool()
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]: if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
clear_inference_pool() clear_inference_pool()
if state_manager.get_item('video_memory_strategy') == 'strict': if state_manager.get_item('video_memory_strategy') == 'strict':

View File

@@ -9,7 +9,7 @@ from cv2.typing import Size
import facefusion.jobs.job_manager import facefusion.jobs.job_manager
import facefusion.jobs.job_store import facefusion.jobs.job_store
import facefusion.processors.core as processors import facefusion.processors.core as processors
from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, inference_manager, logger, process_manager, state_manager, wording from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, inference_manager, logger, process_manager, state_manager, video_manager, wording
from facefusion.common_helper import create_int_metavar from facefusion.common_helper import create_int_metavar
from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url_by_provider from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url_by_provider
from facefusion.face_analyser import get_many_faces, get_one_face from facefusion.face_analyser import get_many_faces, get_one_face
@@ -311,6 +311,7 @@ def pre_process(mode : ProcessMode) -> bool:
def post_process() -> None: def post_process() -> None:
read_static_image.cache_clear() read_static_image.cache_clear()
video_manager.clear_video_pool()
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]: if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
clear_inference_pool() clear_inference_pool()
if state_manager.get_item('video_memory_strategy') == 'strict': if state_manager.get_item('video_memory_strategy') == 'strict':

View File

@@ -8,7 +8,7 @@ import numpy
import facefusion.jobs.job_manager import facefusion.jobs.job_manager
import facefusion.jobs.job_store import facefusion.jobs.job_store
import facefusion.processors.core as processors import facefusion.processors.core as processors
from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, inference_manager, logger, process_manager, state_manager, wording from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, inference_manager, logger, process_manager, state_manager, video_manager, wording
from facefusion.common_helper import create_int_metavar from facefusion.common_helper import create_int_metavar
from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url
from facefusion.face_analyser import get_many_faces, get_one_face from facefusion.face_analyser import get_many_faces, get_one_face
@@ -129,6 +129,7 @@ def pre_process(mode : ProcessMode) -> bool:
def post_process() -> None: def post_process() -> None:
read_static_image.cache_clear() read_static_image.cache_clear()
video_manager.clear_video_pool()
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]: if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
clear_inference_pool() clear_inference_pool()
if state_manager.get_item('video_memory_strategy') == 'strict': if state_manager.get_item('video_memory_strategy') == 'strict':

View File

@@ -7,7 +7,7 @@ import numpy
import facefusion.jobs.job_manager import facefusion.jobs.job_manager
import facefusion.jobs.job_store import facefusion.jobs.job_store
import facefusion.processors.core as processors import facefusion.processors.core as processors
from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, logger, process_manager, state_manager, wording from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, logger, process_manager, state_manager, video_manager, wording
from facefusion.face_analyser import get_many_faces, get_one_face from facefusion.face_analyser import get_many_faces, get_one_face
from facefusion.face_helper import warp_face_by_face_landmark_5 from facefusion.face_helper import warp_face_by_face_landmark_5
from facefusion.face_masker import create_occlusion_mask, create_region_mask, create_static_box_mask from facefusion.face_masker import create_occlusion_mask, create_region_mask, create_static_box_mask
@@ -56,6 +56,7 @@ def pre_process(mode : ProcessMode) -> bool:
def post_process() -> None: def post_process() -> None:
read_static_image.cache_clear() read_static_image.cache_clear()
video_manager.clear_video_pool()
if state_manager.get_item('video_memory_strategy') == 'strict': if state_manager.get_item('video_memory_strategy') == 'strict':
content_analyser.clear_inference_pool() content_analyser.clear_inference_pool()
face_classifier.clear_inference_pool() face_classifier.clear_inference_pool()

View File

@@ -8,7 +8,7 @@ import numpy
import facefusion.jobs.job_manager import facefusion.jobs.job_manager
import facefusion.jobs.job_store import facefusion.jobs.job_store
import facefusion.processors.core as processors import facefusion.processors.core as processors
from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, inference_manager, logger, process_manager, state_manager, wording from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, inference_manager, logger, process_manager, state_manager, video_manager, wording
from facefusion.common_helper import create_float_metavar from facefusion.common_helper import create_float_metavar
from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url
from facefusion.face_analyser import get_many_faces, get_one_face from facefusion.face_analyser import get_many_faces, get_one_face
@@ -182,6 +182,7 @@ def pre_process(mode : ProcessMode) -> bool:
def post_process() -> None: def post_process() -> None:
read_static_image.cache_clear() read_static_image.cache_clear()
video_manager.clear_video_pool()
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]: if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
clear_inference_pool() clear_inference_pool()
if state_manager.get_item('video_memory_strategy') == 'strict': if state_manager.get_item('video_memory_strategy') == 'strict':

View File

@@ -8,7 +8,7 @@ import numpy
import facefusion.jobs.job_manager import facefusion.jobs.job_manager
import facefusion.jobs.job_store import facefusion.jobs.job_store
import facefusion.processors.core as processors import facefusion.processors.core as processors
from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, inference_manager, logger, process_manager, state_manager, wording from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, inference_manager, logger, process_manager, state_manager, video_manager, wording
from facefusion.common_helper import create_float_metavar, create_int_metavar from facefusion.common_helper import create_float_metavar, create_int_metavar
from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url
from facefusion.face_analyser import get_many_faces, get_one_face from facefusion.face_analyser import get_many_faces, get_one_face
@@ -275,6 +275,7 @@ def pre_process(mode : ProcessMode) -> bool:
def post_process() -> None: def post_process() -> None:
read_static_image.cache_clear() read_static_image.cache_clear()
video_manager.clear_video_pool()
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]: if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
clear_inference_pool() clear_inference_pool()
if state_manager.get_item('video_memory_strategy') == 'strict': if state_manager.get_item('video_memory_strategy') == 'strict':

View File

@@ -8,7 +8,7 @@ import facefusion.choices
import facefusion.jobs.job_manager import facefusion.jobs.job_manager
import facefusion.jobs.job_store import facefusion.jobs.job_store
import facefusion.processors.core as processors import facefusion.processors.core as processors
from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, inference_manager, logger, process_manager, state_manager, wording from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, inference_manager, logger, process_manager, state_manager, video_manager, wording
from facefusion.common_helper import get_first from facefusion.common_helper import get_first
from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url
from facefusion.execution import has_execution_provider from facefusion.execution import has_execution_provider
@@ -406,9 +406,10 @@ def pre_process(mode : ProcessMode) -> bool:
def post_process() -> None: def post_process() -> None:
read_static_image.cache_clear() read_static_image.cache_clear()
video_manager.clear_video_pool()
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]: if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
clear_inference_pool()
get_static_model_initializer.cache_clear() get_static_model_initializer.cache_clear()
clear_inference_pool()
if state_manager.get_item('video_memory_strategy') == 'strict': if state_manager.get_item('video_memory_strategy') == 'strict':
content_analyser.clear_inference_pool() content_analyser.clear_inference_pool()
face_classifier.clear_inference_pool() face_classifier.clear_inference_pool()

View File

@@ -8,7 +8,7 @@ import numpy
import facefusion.jobs.job_manager import facefusion.jobs.job_manager
import facefusion.jobs.job_store import facefusion.jobs.job_store
import facefusion.processors.core as processors import facefusion.processors.core as processors
from facefusion import config, content_analyser, inference_manager, logger, process_manager, state_manager, wording from facefusion import config, content_analyser, inference_manager, logger, process_manager, state_manager, video_manager, wording
from facefusion.common_helper import create_int_metavar from facefusion.common_helper import create_int_metavar
from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url
from facefusion.execution import has_execution_provider from facefusion.execution import has_execution_provider
@@ -188,6 +188,7 @@ def pre_process(mode : ProcessMode) -> bool:
def post_process() -> None: def post_process() -> None:
read_static_image.cache_clear() read_static_image.cache_clear()
video_manager.clear_video_pool()
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]: if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
clear_inference_pool() clear_inference_pool()
if state_manager.get_item('video_memory_strategy') == 'strict': if state_manager.get_item('video_memory_strategy') == 'strict':

View File

@@ -8,7 +8,7 @@ import numpy
import facefusion.jobs.job_manager import facefusion.jobs.job_manager
import facefusion.jobs.job_store import facefusion.jobs.job_store
import facefusion.processors.core as processors import facefusion.processors.core as processors
from facefusion import config, content_analyser, inference_manager, logger, process_manager, state_manager, wording from facefusion import config, content_analyser, inference_manager, logger, process_manager, state_manager, video_manager, wording
from facefusion.common_helper import create_int_metavar from facefusion.common_helper import create_int_metavar
from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url
from facefusion.execution import has_execution_provider from facefusion.execution import has_execution_provider
@@ -450,6 +450,7 @@ def pre_process(mode : ProcessMode) -> bool:
def post_process() -> None: def post_process() -> None:
read_static_image.cache_clear() read_static_image.cache_clear()
video_manager.clear_video_pool()
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]: if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
clear_inference_pool() clear_inference_pool()
if state_manager.get_item('video_memory_strategy') == 'strict': if state_manager.get_item('video_memory_strategy') == 'strict':

View File

@@ -8,7 +8,7 @@ import numpy
import facefusion.jobs.job_manager import facefusion.jobs.job_manager
import facefusion.jobs.job_store import facefusion.jobs.job_store
import facefusion.processors.core as processors import facefusion.processors.core as processors
from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, inference_manager, logger, process_manager, state_manager, voice_extractor, wording from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, inference_manager, logger, process_manager, state_manager, video_manager, voice_extractor, wording
from facefusion.audio import create_empty_audio_frame, get_voice_frame, read_static_voice from facefusion.audio import create_empty_audio_frame, get_voice_frame, read_static_voice
from facefusion.common_helper import get_first from facefusion.common_helper import get_first
from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url
@@ -127,6 +127,7 @@ def pre_process(mode : ProcessMode) -> bool:
def post_process() -> None: def post_process() -> None:
read_static_image.cache_clear() read_static_image.cache_clear()
read_static_voice.cache_clear() read_static_voice.cache_clear()
video_manager.clear_video_pool()
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]: if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
clear_inference_pool() clear_inference_pool()
if state_manager.get_item('video_memory_strategy') == 'strict': if state_manager.get_item('video_memory_strategy') == 'strict':

View File

@@ -1,6 +1,7 @@
from collections import namedtuple from collections import namedtuple
from typing import Any, Callable, Dict, List, Literal, Optional, Tuple, TypeAlias, TypedDict from typing import Any, Callable, Dict, List, Literal, Optional, Tuple, TypeAlias, TypedDict
import cv2
import numpy import numpy
from numpy.typing import NDArray from numpy.typing import NDArray
from onnxruntime import InferenceSession from onnxruntime import InferenceSession
@@ -49,6 +50,7 @@ FaceStore = TypedDict('FaceStore',
'static_faces' : FaceSet, 'static_faces' : FaceSet,
'reference_faces' : FaceSet 'reference_faces' : FaceSet
}) })
VideoPoolSet : TypeAlias = Dict[str, cv2.VideoCapture]
VisionFrame : TypeAlias = NDArray[Any] VisionFrame : TypeAlias = NDArray[Any]
Mask : TypeAlias = NDArray[Any] Mask : TypeAlias = NDArray[Any]

View File

@@ -0,0 +1,19 @@
import cv2
from facefusion.types import VideoPoolSet
VIDEO_POOL_SET : VideoPoolSet = {}
def get_video_capture(video_path : str) -> cv2.VideoCapture:
if video_path not in VIDEO_POOL_SET:
VIDEO_POOL_SET[video_path] = cv2.VideoCapture(video_path)
return VIDEO_POOL_SET.get(video_path)
def clear_video_pool() -> None:
for video_capture in VIDEO_POOL_SET.values():
video_capture.release()
VIDEO_POOL_SET.clear()

View File

@@ -9,7 +9,9 @@ from cv2.typing import Size
import facefusion.choices import facefusion.choices
from facefusion.common_helper import is_windows from facefusion.common_helper import is_windows
from facefusion.filesystem import get_file_extension, is_image, is_video from facefusion.filesystem import get_file_extension, is_image, is_video
from facefusion.thread_helper import thread_semaphore
from facefusion.types import Duration, Fps, Orientation, Resolution, VisionFrame from facefusion.types import Duration, Fps, Orientation, Resolution, VisionFrame
from facefusion.video_manager import get_video_capture
@lru_cache() @lru_cache()
@@ -81,24 +83,30 @@ def create_image_resolutions(resolution : Resolution) -> List[str]:
def read_video_frame(video_path : str, frame_number : int = 0) -> Optional[VisionFrame]: def read_video_frame(video_path : str, frame_number : int = 0) -> Optional[VisionFrame]:
if is_video(video_path): if is_video(video_path):
video_capture = cv2.VideoCapture(video_path) video_capture = get_video_capture(video_path)
if video_capture.isOpened(): if video_capture.isOpened():
frame_total = video_capture.get(cv2.CAP_PROP_FRAME_COUNT) frame_total = video_capture.get(cv2.CAP_PROP_FRAME_COUNT)
video_capture.set(cv2.CAP_PROP_POS_FRAMES, min(frame_total, frame_number - 1))
has_vision_frame, vision_frame = video_capture.read() with thread_semaphore():
video_capture.release() video_capture.set(cv2.CAP_PROP_POS_FRAMES, min(frame_total, frame_number - 1))
has_vision_frame, vision_frame = video_capture.read()
if has_vision_frame: if has_vision_frame:
return vision_frame return vision_frame
return None return None
def count_video_frame_total(video_path : str) -> int: def count_video_frame_total(video_path : str) -> int:
if is_video(video_path): if is_video(video_path):
video_capture = cv2.VideoCapture(video_path) video_capture = get_video_capture(video_path)
if video_capture.isOpened(): if video_capture.isOpened():
video_frame_total = int(video_capture.get(cv2.CAP_PROP_FRAME_COUNT)) with thread_semaphore():
video_capture.release() video_frame_total = int(video_capture.get(cv2.CAP_PROP_FRAME_COUNT))
return video_frame_total return video_frame_total
return 0 return 0
@@ -112,11 +120,13 @@ def predict_video_frame_total(video_path : str, fps : Fps, trim_frame_start : in
def detect_video_fps(video_path : str) -> Optional[float]: def detect_video_fps(video_path : str) -> Optional[float]:
if is_video(video_path): if is_video(video_path):
video_capture = cv2.VideoCapture(video_path) video_capture = get_video_capture(video_path)
if video_capture.isOpened(): if video_capture.isOpened():
video_fps = video_capture.get(cv2.CAP_PROP_FPS) with thread_semaphore():
video_capture.release() video_fps = video_capture.get(cv2.CAP_PROP_FPS)
return video_fps return video_fps
return None return None
@@ -163,12 +173,14 @@ def restrict_trim_frame(video_path : str, trim_frame_start : Optional[int], trim
def detect_video_resolution(video_path : str) -> Optional[Resolution]: def detect_video_resolution(video_path : str) -> Optional[Resolution]:
if is_video(video_path): if is_video(video_path):
video_capture = cv2.VideoCapture(video_path) video_capture = get_video_capture(video_path)
if video_capture.isOpened(): if video_capture.isOpened():
width = video_capture.get(cv2.CAP_PROP_FRAME_WIDTH) with thread_semaphore():
height = video_capture.get(cv2.CAP_PROP_FRAME_HEIGHT) width = video_capture.get(cv2.CAP_PROP_FRAME_WIDTH)
video_capture.release() height = video_capture.get(cv2.CAP_PROP_FRAME_HEIGHT)
return int(width), int(height) return int(width), int(height)
return None return None