Files
facefusion/facefusion/uis/components/face_selector.py
T
da0da3a4b4 Next (#945)
* Rename calcXXX to calculateXXX

* Add migraphx support

* Add migraphx support

* Add migraphx support

* Add migraphx support

* Add migraphx support

* Add migraphx support

* Use True for the flags

* Add migraphx support

* add face-swapper-weight

* add face-swapper-weight to facefusion.ini

* changes

* change choice

* Fix typing for xxxWeight

* Feat/log inference session (#906)

* Log inference session, Introduce time helper

* Log inference session, Introduce time helper

* Log inference session, Introduce time helper

* Log inference session, Introduce time helper

* Mark as NEXT

* Follow industry standard x1, x2, y1 and y2

* Follow industry standard x1, x2, y1 and y2

* Follow industry standard in terms of naming (#908)

* Follow industry standard in terms of naming

* Improve xxx_embedding naming

* Fix norm vs. norms

* Reduce timeout to 5

* Sort out voice_extractor once again

* changes

* Introduce many to the occlusion mask (#910)

* Introduce many to the occlusion mask

* Then we use minimum

* Add support for wmv

* Run platform tests before has_execution_provider (#911)

* Add support for wmv

* Introduce benchmark mode (#912)

* Honestly makes no difference to me

* Honestly makes no difference to me

* Fix wording

* Bring back YuNet (#922)

* Reintroduce YuNet without cv2 dependency

* Fix variable naming

* Avoid RGB to YUV colorshift using libx264rgb

* Avoid RGB to YUV colorshift using libx264rgb

* Make libx264 the default again

* Make libx264 the default again

* Fix types in ffmpeg builder

* Fix quality stuff in ffmpeg builder

* Fix quality stuff in ffmpeg builder

* Add libx264rgb to test

* Revamp Processors (#923)

* Introduce new concept of pure target frames

* Radical refactoring of process flow

* Introduce new concept of pure target frames

* Fix webcam

* Minor improvements

* Minor improvements

* Use deque for video processing

* Use deque for video processing

* Extend the video manager

* Polish deque

* Polish deque

* Deque is not even used

* Improve speed with multiple futures

* Fix temp frame mutation and

* Fix RAM usage

* Remove old types and manage method

* Remove execution_queue_count

* Use init_state for benchmarker to avoid issues

* add voice extractor option

* Change the order of voice extractor in code

* Use official download urls

* Use official download urls

* add gui

* fix preview

* Add remote updates for voice extractor

* fix crash on headless-run

* update test_job_helper.py

* Fix it for good

* Remove pointless method

* Fix types and unused imports

* Revamp reference (#925)

* Initial revamp of face references

* Initial revamp of face references

* Initial revamp of face references

* Terminate find_similar_faces

* Improve find mutant faces

* Improve find mutant faces

* Move sort where it belongs

* Forward reference vision frame

* Forward reference vision frame also in preview

* Fix reference selection

* Use static video frame

* Fix CI

* Remove reference type from frame processors

* Improve some naming

* Fix types and unused imports

* Fix find mutant faces

* Fix find mutant faces

* Fix imports

* Correct naming

* Correct naming

* simplify pad

* Improve webcam performance on highres

* Camera manager (#932)

* Introduce webcam manager

* Fix order

* Rename to camera manager, improve video manager

* Fix CI

* Remove optional

* Fix naming in webcam options

* Avoid using temp faces (#933)

* output video scale

* Fix imports

* output image scale

* upscale fix (not limiter)

* add unit test scale_resolution & remove unused methods

* fix and add test

* fix

* change pack_resolution

* fix tests

* Simplify output scale testing

* Fix benchmark UI

* Fix benchmark UI

* Update dependencies

* Introduce REAL multi gpu support using multi dimensional inference pool (#935)

* Introduce REAL multi gpu support using multi dimensional inference pool

* Remove the MULTI:GPU flag

* Restore "processing stop"

* Restore "processing stop"

* Remove old templates

* Go fill in with caching

* add expression restorer areas

* re-arrange

* rename method

* Fix stop for extract frames and merge video

* Replace arcface_converter models with latest crossface models

* Replace arcface_converter models with latest crossface models

* Move module logs to debug mode

* Refactor/streamer (#938)

* Introduce webcam manager

* Fix order

* Rename to camera manager, improve video manager

* Fix CI

* Fix naming in webcam options

* Move logic over to streamer

* Fix streamer, improve webcam experience

* Improve webcam experience

* Revert method

* Revert method

* Improve webcam again

* Use release on capture instead

* Only forward valid frames

* Fix resolution logging

* Add AVIF support

* Add AVIF support

* Limit avif to unix systems

* Drop avif

* Drop avif

* Drop avif

* Default to Documents in the UI if output path is not set

* Update wording.py (#939)

"succeed" is grammatically incorrect in the given context. To succeed is the infinitive form of the verb. Correct would be either "succeeded" or alternatively a form involving the noun "success".

* Fix more grammar issue

* Fix more grammar issue

* Sort out caching

* Move webcam choices back to UI

* Move preview options to own file (#940)

* Fix Migraphx execution provider

* Fix benchmark

* Reuse blend frame method

* Fix CI

* Fix CI

* Fix CI

* Hotfix missing check in face debugger, Enable logger for preview

* Fix reference selection (#942)

* Fix reference selection

* Fix reference selection

* Fix reference selection

* Fix reference selection

* Side by side preview (#941)

* Initial side by side preview

* More work on preview, remove UI only stuff from vision.py

* Improve more

* Use fit frame

* Add different fit methods for vision

* Improve preview part2

* Improve preview part3

* Improve preview part4

* Remove none as choice

* Remove useless methods

* Fix CI

* Fix naming

* use 1024 as preview resolution default

* Fix fit_cover_frame

* Uniform fit_xxx_frame methods

* Add back disabled logger

* Use ui choices alias

* Extract select face logic from processors (#943)

* Extract select face logic from processors to use it for face by face in preview

* Fix order

* Remove old code

* Merge methods

* Refactor face debugger (#944)

* Refactor huge method of face debugger

* Remove text metrics from face debugger

* Remove useless copy of temp frame

* Resort methods

* Fix spacing

* Remove old method

* Fix hard exit to work without signals

* Prevent upscaling for face-by-face

* Switch to version

* Improve exiting

---------

Co-authored-by: harisreedhar <h4harisreedhar.s.s@gmail.com>
Co-authored-by: Harisreedhar <46858047+harisreedhar@users.noreply.github.com>
Co-authored-by: Rafael Tappe Maestro <rafael@tappemaestro.com>
2025-09-08 10:43:58 +02:00

232 lines
11 KiB
Python

from typing import List, Optional, Tuple
import cv2
import gradio
from gradio_rangeslider import RangeSlider
import facefusion.choices
from facefusion import state_manager, wording
from facefusion.common_helper import calculate_float_step, calculate_int_step
from facefusion.face_analyser import get_many_faces
from facefusion.face_selector import sort_and_filter_faces
from facefusion.face_store import clear_static_faces
from facefusion.filesystem import is_image, is_video
from facefusion.types import FaceSelectorMode, FaceSelectorOrder, Gender, Race, VisionFrame
from facefusion.uis.core import get_ui_component, get_ui_components, register_ui_component
from facefusion.uis.types import ComponentOptions
from facefusion.uis.ui_helper import convert_str_none
from facefusion.vision import fit_cover_frame, read_static_image, read_video_frame
FACE_SELECTOR_MODE_DROPDOWN : Optional[gradio.Dropdown] = None
FACE_SELECTOR_ORDER_DROPDOWN : Optional[gradio.Dropdown] = None
FACE_SELECTOR_GENDER_DROPDOWN : Optional[gradio.Dropdown] = None
FACE_SELECTOR_RACE_DROPDOWN : Optional[gradio.Dropdown] = None
FACE_SELECTOR_AGE_RANGE_SLIDER : Optional[RangeSlider] = None
REFERENCE_FACE_POSITION_GALLERY : Optional[gradio.Gallery] = None
REFERENCE_FACE_DISTANCE_SLIDER : Optional[gradio.Slider] = None
def render() -> None:
global FACE_SELECTOR_MODE_DROPDOWN
global FACE_SELECTOR_ORDER_DROPDOWN
global FACE_SELECTOR_GENDER_DROPDOWN
global FACE_SELECTOR_RACE_DROPDOWN
global FACE_SELECTOR_AGE_RANGE_SLIDER
global REFERENCE_FACE_POSITION_GALLERY
global REFERENCE_FACE_DISTANCE_SLIDER
reference_face_gallery_options : ComponentOptions =\
{
'label': wording.get('uis.reference_face_gallery'),
'object_fit': 'cover',
'columns': 7,
'allow_preview': False,
'elem_classes': 'box-face-selector',
'visible': 'reference' in state_manager.get_item('face_selector_mode')
}
if is_image(state_manager.get_item('target_path')):
target_vision_frame = read_static_image(state_manager.get_item('target_path'))
reference_face_gallery_options['value'] = extract_gallery_frames(target_vision_frame)
if is_video(state_manager.get_item('target_path')):
target_vision_frame = read_video_frame(state_manager.get_item('target_path'), state_manager.get_item('reference_frame_number'))
reference_face_gallery_options['value'] = extract_gallery_frames(target_vision_frame)
FACE_SELECTOR_MODE_DROPDOWN = gradio.Dropdown(
label = wording.get('uis.face_selector_mode_dropdown'),
choices = facefusion.choices.face_selector_modes,
value = state_manager.get_item('face_selector_mode')
)
REFERENCE_FACE_POSITION_GALLERY = gradio.Gallery(**reference_face_gallery_options)
with gradio.Group():
with gradio.Row():
FACE_SELECTOR_ORDER_DROPDOWN = gradio.Dropdown(
label = wording.get('uis.face_selector_order_dropdown'),
choices = facefusion.choices.face_selector_orders,
value = state_manager.get_item('face_selector_order')
)
FACE_SELECTOR_GENDER_DROPDOWN = gradio.Dropdown(
label = wording.get('uis.face_selector_gender_dropdown'),
choices = [ 'none' ] + facefusion.choices.face_selector_genders,
value = state_manager.get_item('face_selector_gender') or 'none'
)
FACE_SELECTOR_RACE_DROPDOWN = gradio.Dropdown(
label = wording.get('uis.face_selector_race_dropdown'),
choices = [ 'none' ] + facefusion.choices.face_selector_races,
value = state_manager.get_item('face_selector_race') or 'none'
)
with gradio.Row():
face_selector_age_start = state_manager.get_item('face_selector_age_start') or facefusion.choices.face_selector_age_range[0]
face_selector_age_end = state_manager.get_item('face_selector_age_end') or facefusion.choices.face_selector_age_range[-1]
FACE_SELECTOR_AGE_RANGE_SLIDER = RangeSlider(
label = wording.get('uis.face_selector_age_range_slider'),
minimum = facefusion.choices.face_selector_age_range[0],
maximum = facefusion.choices.face_selector_age_range[-1],
value = (face_selector_age_start, face_selector_age_end),
step = calculate_int_step(facefusion.choices.face_selector_age_range)
)
REFERENCE_FACE_DISTANCE_SLIDER = gradio.Slider(
label = wording.get('uis.reference_face_distance_slider'),
value = state_manager.get_item('reference_face_distance'),
step = calculate_float_step(facefusion.choices.reference_face_distance_range),
minimum = facefusion.choices.reference_face_distance_range[0],
maximum = facefusion.choices.reference_face_distance_range[-1],
visible = 'reference' in state_manager.get_item('face_selector_mode')
)
register_ui_component('face_selector_mode_dropdown', FACE_SELECTOR_MODE_DROPDOWN)
register_ui_component('face_selector_order_dropdown', FACE_SELECTOR_ORDER_DROPDOWN)
register_ui_component('face_selector_gender_dropdown', FACE_SELECTOR_GENDER_DROPDOWN)
register_ui_component('face_selector_race_dropdown', FACE_SELECTOR_RACE_DROPDOWN)
register_ui_component('face_selector_age_range_slider', FACE_SELECTOR_AGE_RANGE_SLIDER)
register_ui_component('reference_face_position_gallery', REFERENCE_FACE_POSITION_GALLERY)
register_ui_component('reference_face_distance_slider', REFERENCE_FACE_DISTANCE_SLIDER)
def listen() -> None:
FACE_SELECTOR_MODE_DROPDOWN.change(update_face_selector_mode, inputs = FACE_SELECTOR_MODE_DROPDOWN, outputs = [ REFERENCE_FACE_POSITION_GALLERY, REFERENCE_FACE_DISTANCE_SLIDER ])
FACE_SELECTOR_ORDER_DROPDOWN.change(update_face_selector_order, inputs = FACE_SELECTOR_ORDER_DROPDOWN, outputs = REFERENCE_FACE_POSITION_GALLERY)
FACE_SELECTOR_GENDER_DROPDOWN.change(update_face_selector_gender, inputs = FACE_SELECTOR_GENDER_DROPDOWN, outputs = REFERENCE_FACE_POSITION_GALLERY)
FACE_SELECTOR_RACE_DROPDOWN.change(update_face_selector_race, inputs = FACE_SELECTOR_RACE_DROPDOWN, outputs = REFERENCE_FACE_POSITION_GALLERY)
FACE_SELECTOR_AGE_RANGE_SLIDER.release(update_face_selector_age_range, inputs = FACE_SELECTOR_AGE_RANGE_SLIDER, outputs = REFERENCE_FACE_POSITION_GALLERY)
REFERENCE_FACE_DISTANCE_SLIDER.release(update_reference_face_distance, inputs = REFERENCE_FACE_DISTANCE_SLIDER)
preview_frame_slider = get_ui_component('preview_frame_slider')
if preview_frame_slider:
REFERENCE_FACE_POSITION_GALLERY.select(update_reference_frame_number, inputs = preview_frame_slider)
REFERENCE_FACE_POSITION_GALLERY.select(update_reference_face_position)
for ui_component in get_ui_components(
[
'target_image',
'target_video'
]):
for method in [ 'change', 'clear' ]:
getattr(ui_component, method)(clear_reference_frame_number)
getattr(ui_component, method)(clear_reference_face_position)
getattr(ui_component, method)(update_reference_position_gallery, outputs = REFERENCE_FACE_POSITION_GALLERY)
for ui_component in get_ui_components(
[
'face_detector_model_dropdown',
'face_detector_size_dropdown',
'face_detector_angles_checkbox_group'
]):
ui_component.change(clear_and_update_reference_position_gallery, outputs = REFERENCE_FACE_POSITION_GALLERY)
face_detector_score_slider = get_ui_component('face_detector_score_slider')
if face_detector_score_slider:
face_detector_score_slider.release(update_reference_position_gallery, outputs = REFERENCE_FACE_POSITION_GALLERY)
preview_frame_slider = get_ui_component('preview_frame_slider')
if preview_frame_slider:
for method in [ 'change', 'release' ]:
getattr(preview_frame_slider, method)(update_reference_position_gallery, inputs = preview_frame_slider, outputs = REFERENCE_FACE_POSITION_GALLERY, show_progress = 'hidden')
def update_face_selector_mode(face_selector_mode : FaceSelectorMode) -> Tuple[gradio.Gallery, gradio.Slider]:
state_manager.set_item('face_selector_mode', face_selector_mode)
if face_selector_mode == 'many':
return gradio.Gallery(visible = False), gradio.Slider(visible = False)
if face_selector_mode == 'one':
return gradio.Gallery(visible = False), gradio.Slider(visible = False)
if face_selector_mode == 'reference':
return gradio.Gallery(visible = True), gradio.Slider(visible = True)
def update_face_selector_order(face_analyser_order : FaceSelectorOrder) -> gradio.Gallery:
state_manager.set_item('face_selector_order', convert_str_none(face_analyser_order))
return update_reference_position_gallery()
def update_face_selector_gender(face_selector_gender : Gender) -> gradio.Gallery:
state_manager.set_item('face_selector_gender', convert_str_none(face_selector_gender))
return update_reference_position_gallery()
def update_face_selector_race(face_selector_race : Race) -> gradio.Gallery:
state_manager.set_item('face_selector_race', convert_str_none(face_selector_race))
return update_reference_position_gallery()
def update_face_selector_age_range(face_selector_age_range : Tuple[float, float]) -> gradio.Gallery:
face_selector_age_start, face_selector_age_end = face_selector_age_range
state_manager.set_item('face_selector_age_start', int(face_selector_age_start))
state_manager.set_item('face_selector_age_end', int(face_selector_age_end))
return update_reference_position_gallery()
def update_reference_face_position(event : gradio.SelectData) -> None:
state_manager.set_item('reference_face_position', event.index)
def clear_reference_face_position() -> None:
state_manager.set_item('reference_face_position', 0)
def update_reference_face_distance(reference_face_distance : float) -> None:
state_manager.set_item('reference_face_distance', reference_face_distance)
def update_reference_frame_number(reference_frame_number : int = 0) -> None:
state_manager.set_item('reference_frame_number', reference_frame_number)
def clear_reference_frame_number() -> None:
state_manager.set_item('reference_frame_number', 0)
def clear_and_update_reference_position_gallery() -> gradio.Gallery:
clear_static_faces()
return update_reference_position_gallery()
def update_reference_position_gallery(frame_number : int = 0) -> gradio.Gallery:
gallery_vision_frames = []
if is_image(state_manager.get_item('target_path')):
target_vision_frame = read_static_image(state_manager.get_item('target_path'))
gallery_vision_frames = extract_gallery_frames(target_vision_frame)
if is_video(state_manager.get_item('target_path')):
target_vision_frame = read_video_frame(state_manager.get_item('target_path'), frame_number)
gallery_vision_frames = extract_gallery_frames(target_vision_frame)
if gallery_vision_frames:
return gradio.Gallery(value = gallery_vision_frames)
return gradio.Gallery(value = None)
def extract_gallery_frames(target_vision_frame : VisionFrame) -> List[VisionFrame]:
gallery_vision_frames = []
faces = get_many_faces([ target_vision_frame ])
faces = sort_and_filter_faces(faces)
for face in faces:
start_x, start_y, end_x, end_y = map(int, face.bounding_box)
padding_x = int((end_x - start_x) * 0.25)
padding_y = int((end_y - start_y) * 0.25)
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)
crop_vision_frame = target_vision_frame[start_y:end_y, start_x:end_x]
crop_vision_frame = fit_cover_frame(crop_vision_frame, (128, 128))
crop_vision_frame = cv2.cvtColor(crop_vision_frame, cv2.COLOR_BGR2RGB)
gallery_vision_frames.append(crop_vision_frame)
return gallery_vision_frames