Feat/webcam improvements (#838)

* Detect available webcams

* Fix CI, Move webcam id dropdown to the sidebar, Disable warnings

* Fix CI
This commit is contained in:
Henry Ruhs 2024-12-23 09:10:24 +01:00 committed by GitHub
parent aa60da7e9a
commit df96eb51e7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 43 additions and 10 deletions

View File

@ -2,7 +2,7 @@ import os
import subprocess import subprocess
from collections import deque from collections import deque
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
from typing import Deque, Generator, Optional from typing import Deque, Generator, List, Optional
import cv2 import cv2
import gradio import gradio
@ -10,7 +10,7 @@ from tqdm import tqdm
from facefusion import logger, state_manager, wording from facefusion import logger, state_manager, wording
from facefusion.audio import create_empty_audio_frame from facefusion.audio import create_empty_audio_frame
from facefusion.common_helper import is_windows from facefusion.common_helper import get_first, is_windows
from facefusion.content_analyser import analyse_stream from facefusion.content_analyser import analyse_stream
from facefusion.face_analyser import get_average_face, get_many_faces from facefusion.face_analyser import get_average_face, get_many_faces
from facefusion.ffmpeg import open_ffmpeg from facefusion.ffmpeg import open_ffmpeg
@ -27,14 +27,17 @@ WEBCAM_START_BUTTON : Optional[gradio.Button] = None
WEBCAM_STOP_BUTTON : Optional[gradio.Button] = None WEBCAM_STOP_BUTTON : Optional[gradio.Button] = None
def get_webcam_capture() -> Optional[cv2.VideoCapture]: def get_webcam_capture(index : int) -> Optional[cv2.VideoCapture]:
global WEBCAM_CAPTURE global WEBCAM_CAPTURE
if WEBCAM_CAPTURE is None: if WEBCAM_CAPTURE is None:
cv2.setLogLevel(0)
if is_windows(): if is_windows():
webcam_capture = cv2.VideoCapture(0, cv2.CAP_DSHOW) webcam_capture = cv2.VideoCapture(index, cv2.CAP_DSHOW)
else: else:
webcam_capture = cv2.VideoCapture(0) webcam_capture = cv2.VideoCapture(index)
cv2.setLogLevel(3)
if webcam_capture and webcam_capture.isOpened(): if webcam_capture and webcam_capture.isOpened():
WEBCAM_CAPTURE = webcam_capture WEBCAM_CAPTURE = webcam_capture
return WEBCAM_CAPTURE return WEBCAM_CAPTURE
@ -68,31 +71,35 @@ def render() -> None:
def listen() -> None: def listen() -> None:
webcam_device_id_dropdown = get_ui_component('webcam_device_id_dropdown')
webcam_mode_radio = get_ui_component('webcam_mode_radio') webcam_mode_radio = get_ui_component('webcam_mode_radio')
webcam_resolution_dropdown = get_ui_component('webcam_resolution_dropdown') webcam_resolution_dropdown = get_ui_component('webcam_resolution_dropdown')
webcam_fps_slider = get_ui_component('webcam_fps_slider') webcam_fps_slider = get_ui_component('webcam_fps_slider')
source_image = get_ui_component('source_image') source_image = get_ui_component('source_image')
if webcam_mode_radio and webcam_resolution_dropdown and webcam_fps_slider: if webcam_device_id_dropdown and webcam_mode_radio and webcam_resolution_dropdown and webcam_fps_slider:
start_event = WEBCAM_START_BUTTON.click(start, inputs = [ webcam_mode_radio, webcam_resolution_dropdown, webcam_fps_slider ], outputs = WEBCAM_IMAGE) start_event = WEBCAM_START_BUTTON.click(start, inputs = [ webcam_device_id_dropdown, webcam_mode_radio, webcam_resolution_dropdown, webcam_fps_slider ], outputs = WEBCAM_IMAGE)
WEBCAM_STOP_BUTTON.click(stop, cancels = start_event, outputs = WEBCAM_IMAGE) WEBCAM_STOP_BUTTON.click(stop, cancels = start_event, outputs = WEBCAM_IMAGE)
if source_image: if source_image:
source_image.change(stop, cancels = start_event, outputs = WEBCAM_IMAGE) source_image.change(stop, cancels = start_event, outputs = WEBCAM_IMAGE)
def start(webcam_mode : WebcamMode, webcam_resolution : str, webcam_fps : Fps) -> Generator[VisionFrame, None, None]: def start(webcam_device_id : int, webcam_mode : WebcamMode, webcam_resolution : str, webcam_fps : Fps) -> Generator[VisionFrame, None, None]:
state_manager.set_item('face_selector_mode', 'one') state_manager.set_item('face_selector_mode', 'one')
source_image_paths = filter_image_paths(state_manager.get_item('source_paths')) source_image_paths = filter_image_paths(state_manager.get_item('source_paths'))
source_frames = read_static_images(source_image_paths) source_frames = read_static_images(source_image_paths)
source_faces = get_many_faces(source_frames) source_faces = get_many_faces(source_frames)
source_face = get_average_face(source_faces) source_face = get_average_face(source_faces)
stream = None stream = None
webcam_capture = None
if webcam_mode in [ 'udp', 'v4l2' ]: if webcam_mode in [ 'udp', 'v4l2' ]:
stream = open_stream(webcam_mode, webcam_resolution, webcam_fps) #type:ignore[arg-type] stream = open_stream(webcam_mode, webcam_resolution, webcam_fps) #type:ignore[arg-type]
webcam_width, webcam_height = unpack_resolution(webcam_resolution) webcam_width, webcam_height = unpack_resolution(webcam_resolution)
webcam_capture = get_webcam_capture()
if isinstance(webcam_device_id, int):
webcam_capture = get_webcam_capture(webcam_device_id)
if webcam_capture and webcam_capture.isOpened(): if webcam_capture and webcam_capture.isOpened():
webcam_capture.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'MJPG')) #type:ignore[attr-defined] webcam_capture.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'MJPG')) #type:ignore[attr-defined]
@ -163,9 +170,22 @@ def open_stream(stream_mode : StreamMode, stream_resolution : str, stream_fps :
commands.extend([ '-b:v', '2000k', '-f', 'mpegts', 'udp://localhost:27000?pkt_size=1316' ]) commands.extend([ '-b:v', '2000k', '-f', 'mpegts', 'udp://localhost:27000?pkt_size=1316' ])
if stream_mode == 'v4l2': if stream_mode == 'v4l2':
try: try:
device_name = os.listdir('/sys/devices/virtual/video4linux')[0] device_name = get_first(os.listdir('/sys/devices/virtual/video4linux'))
if device_name: if device_name:
commands.extend([ '-f', 'v4l2', '/dev/' + device_name ]) commands.extend([ '-f', 'v4l2', '/dev/' + device_name ])
except FileNotFoundError: except FileNotFoundError:
logger.error(wording.get('stream_not_loaded').format(stream_mode = stream_mode), __name__) logger.error(wording.get('stream_not_loaded').format(stream_mode = stream_mode), __name__)
return open_ffmpeg(commands) return open_ffmpeg(commands)
def get_available_webcam_ids(webcam_id_start : int, webcam_id_end : int) -> List[int]:
available_webcam_ids = []
for index in range(webcam_id_start, webcam_id_end):
webcam_capture = get_webcam_capture(index)
if webcam_capture and webcam_capture.isOpened():
available_webcam_ids.append(index)
clear_webcam_capture()
return available_webcam_ids

View File

@ -3,19 +3,29 @@ from typing import Optional
import gradio import gradio
from facefusion import wording from facefusion import wording
from facefusion.common_helper import get_first
from facefusion.uis import choices as uis_choices from facefusion.uis import choices as uis_choices
from facefusion.uis.components.webcam import get_available_webcam_ids
from facefusion.uis.core import register_ui_component from facefusion.uis.core import register_ui_component
WEBCAM_DEVICE_ID_DROPDOWN : Optional[gradio.Dropdown] = None
WEBCAM_MODE_RADIO : Optional[gradio.Radio] = None WEBCAM_MODE_RADIO : Optional[gradio.Radio] = None
WEBCAM_RESOLUTION_DROPDOWN : Optional[gradio.Dropdown] = None WEBCAM_RESOLUTION_DROPDOWN : Optional[gradio.Dropdown] = None
WEBCAM_FPS_SLIDER : Optional[gradio.Slider] = None WEBCAM_FPS_SLIDER : Optional[gradio.Slider] = None
def render() -> None: def render() -> None:
global WEBCAM_DEVICE_ID_DROPDOWN
global WEBCAM_MODE_RADIO global WEBCAM_MODE_RADIO
global WEBCAM_RESOLUTION_DROPDOWN global WEBCAM_RESOLUTION_DROPDOWN
global WEBCAM_FPS_SLIDER global WEBCAM_FPS_SLIDER
available_webcam_ids = get_available_webcam_ids(0, 10) or [ 'none' ] #type:ignore[list-item]
WEBCAM_DEVICE_ID_DROPDOWN = gradio.Dropdown(
value = get_first(available_webcam_ids),
label = wording.get('uis.webcam_device_id_dropdown'),
choices = available_webcam_ids
)
WEBCAM_MODE_RADIO = gradio.Radio( WEBCAM_MODE_RADIO = gradio.Radio(
label = wording.get('uis.webcam_mode_radio'), label = wording.get('uis.webcam_mode_radio'),
choices = uis_choices.webcam_modes, choices = uis_choices.webcam_modes,
@ -33,6 +43,7 @@ def render() -> None:
minimum = 1, minimum = 1,
maximum = 60 maximum = 60
) )
register_ui_component('webcam_device_id_dropdown', WEBCAM_DEVICE_ID_DROPDOWN)
register_ui_component('webcam_mode_radio', WEBCAM_MODE_RADIO) register_ui_component('webcam_mode_radio', WEBCAM_MODE_RADIO)
register_ui_component('webcam_resolution_dropdown', WEBCAM_RESOLUTION_DROPDOWN) register_ui_component('webcam_resolution_dropdown', WEBCAM_RESOLUTION_DROPDOWN)
register_ui_component('webcam_fps_slider', WEBCAM_FPS_SLIDER) register_ui_component('webcam_fps_slider', WEBCAM_FPS_SLIDER)

View File

@ -73,6 +73,7 @@ ComponentName = Literal\
'target_image', 'target_image',
'target_video', 'target_video',
'ui_workflow_dropdown', 'ui_workflow_dropdown',
'webcam_device_id_dropdown',
'webcam_fps_slider', 'webcam_fps_slider',
'webcam_mode_radio', 'webcam_mode_radio',
'webcam_resolution_dropdown' 'webcam_resolution_dropdown'

View File

@ -330,6 +330,7 @@ WORDING : Dict[str, Any] =\
'video_memory_strategy_dropdown': 'VIDEO MEMORY STRATEGY', 'video_memory_strategy_dropdown': 'VIDEO MEMORY STRATEGY',
'webcam_fps_slider': 'WEBCAM FPS', 'webcam_fps_slider': 'WEBCAM FPS',
'webcam_image': 'WEBCAM', 'webcam_image': 'WEBCAM',
'webcam_device_id_dropdown': 'WEBCAM DEVICE ID',
'webcam_mode_radio': 'WEBCAM MODE', 'webcam_mode_radio': 'WEBCAM MODE',
'webcam_resolution_dropdown': 'WEBCAM RESOLUTION' 'webcam_resolution_dropdown': 'WEBCAM RESOLUTION'
} }