mirror of
https://github.com/facefusion/facefusion.git
synced 2026-11-03 01:24:26 +01:00
* revamp reference system * revamp reference system * revamp reference system * revamp reference system * rename to reference face maps * rename to reference face maps * rename to reference face maps * fix ci * fix logic part1 * fix logic part2 * fix logic part2 * fix logic part3 * sort out ini, program and normalize * sort out ini, program and normalize * fix lint * get rid of matrix reader * revamp groups again * revamp groups again part2 * revamp groups again part2a * revamp groups again part2a * minor typo * switch to selector map to support source averaging * save map different in state * adjust the normalize of maps * refactor * refactor * refactor * refactor * better test * resolve find_match_group_index * minor improvements * find closes faces * find closes faces * fix according to code review
338 lines
13 KiB
Python
338 lines
13 KiB
Python
from typing import List
|
|
|
|
import cv2
|
|
import numpy
|
|
import pytest
|
|
|
|
from facefusion import face_aligner, face_classifier, face_detector, face_recognizer, face_store, ffmpeg, ffmpeg_builder, inference_manager, process_manager, state_manager
|
|
from facefusion.common_helper import get_first, get_last
|
|
from facefusion.download import conditional_download
|
|
from facefusion.face_creator import get_static_faces
|
|
from facefusion.face_selector import calculate_face_distance, collect_source_faces, compare_faces, decode_face_selector_map, decode_face_selector_maps, filter_faces_by_age, filter_faces_by_gender, filter_faces_by_race, find_match_faces, select_faces, sort_and_filter_faces, sort_faces_by_order
|
|
from facefusion.types import Face
|
|
from facefusion.vision import read_static_image
|
|
from tests.assert_helper import get_test_example_file, get_test_examples_directory
|
|
|
|
|
|
@pytest.fixture(scope = 'module', autouse = True)
|
|
def before_all() -> None:
|
|
state_manager.init()
|
|
inference_manager.init()
|
|
|
|
face_store.init()
|
|
|
|
process_manager.start()
|
|
conditional_download(get_test_examples_directory(),
|
|
[
|
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg',
|
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
|
])
|
|
|
|
ffmpeg.run_ffmpeg(
|
|
ffmpeg_builder.chain(
|
|
ffmpeg_builder.set_input(get_test_example_file('target-240p.mp4')),
|
|
[
|
|
'-vframes',
|
|
'1'
|
|
],
|
|
ffmpeg_builder.set_output(get_test_example_file('target-240p.jpg'))
|
|
)
|
|
).wait()
|
|
|
|
state_manager.init_item('execution_device_ids', [ 0 ])
|
|
state_manager.init_item('execution_providers', [ 'cpu' ])
|
|
state_manager.init_item('download_providers', [ 'github' ])
|
|
state_manager.init_item('face_detector_angles', [ 0 ])
|
|
state_manager.init_item('face_detector_model', 'many')
|
|
state_manager.init_item('face_detector_size', '640x640')
|
|
state_manager.init_item('face_detector_margin', (0, 0, 0, 0))
|
|
state_manager.init_item('face_detector_score', 0.5)
|
|
state_manager.init_item('face_aligner_model', 'many')
|
|
state_manager.init_item('face_aligner_score', 0.5)
|
|
state_manager.init_item('face_tracker_score', 0)
|
|
state_manager.init_item('reference_face_distance', 0.3)
|
|
|
|
face_classifier.pre_check()
|
|
face_detector.pre_check()
|
|
face_aligner.pre_check()
|
|
face_recognizer.pre_check()
|
|
|
|
|
|
def create_faces() -> List[Face]:
|
|
landmark_generator = numpy.random.default_rng(0)
|
|
|
|
return\
|
|
[
|
|
Face(
|
|
origin = 'detect',
|
|
bounding_box = numpy.array([ 0, 0, 10, 10 ]),
|
|
score_set =
|
|
{
|
|
'detector': 0.6,
|
|
'aligner': 0.0
|
|
},
|
|
landmark_set =
|
|
{
|
|
'5': landmark_generator.random((5, 2)),
|
|
'5/68': landmark_generator.random((5, 2)),
|
|
'68': landmark_generator.random((68, 2))
|
|
},
|
|
angle = 0,
|
|
embedding = numpy.array([ 1.0, 0.0 ]),
|
|
embedding_norm = numpy.array([ 1.0, 0.0 ]),
|
|
age = range(20, 29),
|
|
gender = 'female',
|
|
race = 'white'
|
|
),
|
|
Face(
|
|
origin = 'detect',
|
|
bounding_box = numpy.array([ 50, 10, 80, 40 ]),
|
|
score_set =
|
|
{
|
|
'detector': 0.9,
|
|
'aligner': 0.0
|
|
},
|
|
landmark_set =
|
|
{
|
|
'5': landmark_generator.random((5, 2)),
|
|
'5/68': landmark_generator.random((5, 2)),
|
|
'68': landmark_generator.random((68, 2))
|
|
},
|
|
angle = 0,
|
|
embedding = numpy.array([ 0.0, 1.0 ]),
|
|
embedding_norm = numpy.array([ 0.0, 1.0 ]),
|
|
age = range(30, 39),
|
|
gender = 'male',
|
|
race = 'black'
|
|
),
|
|
Face(
|
|
origin = 'detect',
|
|
bounding_box = numpy.array([ 20, 60, 25, 65 ]),
|
|
score_set =
|
|
{
|
|
'detector': 0.7,
|
|
'aligner': 0.0
|
|
},
|
|
landmark_set =
|
|
{
|
|
'5': landmark_generator.random((5, 2)),
|
|
'5/68': landmark_generator.random((5, 2)),
|
|
'68': landmark_generator.random((68, 2))
|
|
},
|
|
angle = 0,
|
|
embedding = numpy.array([ -1.0, 0.0 ]),
|
|
embedding_norm = numpy.array([ -1.0, 0.0 ]),
|
|
age = range(60, 69),
|
|
gender = 'female',
|
|
race = 'asian'
|
|
),
|
|
Face(
|
|
origin = 'detect',
|
|
bounding_box = numpy.array([ 30, 30, 50, 50 ]),
|
|
score_set =
|
|
{
|
|
'detector': 0.5,
|
|
'aligner': 0.0
|
|
},
|
|
landmark_set =
|
|
{
|
|
'5': landmark_generator.random((5, 2)),
|
|
'5/68': landmark_generator.random((5, 2)),
|
|
'68': landmark_generator.random((68, 2))
|
|
},
|
|
angle = 0,
|
|
embedding = numpy.array([ 0.6, 0.8 ]),
|
|
embedding_norm = numpy.array([ 0.6, 0.8 ]),
|
|
age = range(10, 19),
|
|
gender = 'male',
|
|
race = 'white'
|
|
)
|
|
]
|
|
|
|
|
|
def test_select_faces() -> None:
|
|
source_vision_frame = read_static_image(get_test_example_file('source.jpg'))
|
|
target_vision_frame = read_static_image(get_test_example_file('target-240p.jpg'))
|
|
reference_vision_frame = numpy.hstack([ cv2.resize(source_vision_frame, (226, 226)), target_vision_frame ])
|
|
|
|
state_manager.set_item('face_selector_mode', 'many')
|
|
state_manager.set_item('face_selector_maps', [ 'invalid' ])
|
|
|
|
source_faces, target_faces = select_faces([], [ source_vision_frame ], [ source_vision_frame ])
|
|
|
|
assert len(source_faces) == 1
|
|
assert len(target_faces) == 1
|
|
|
|
state_manager.set_item('face_selector_mode', 'reference')
|
|
state_manager.set_item('face_selector_maps', [ '0:0 to 0:0', '1:1 to 1:1' ])
|
|
|
|
source_faces, target_faces = select_faces([ source_vision_frame, reference_vision_frame ], [ source_vision_frame, reference_vision_frame ], [ reference_vision_frame ])
|
|
|
|
assert compare_faces(get_first(source_faces), get_first(get_static_faces([ source_vision_frame ])), 0.3) is True
|
|
assert compare_faces(get_last(source_faces), get_first(get_static_faces([ target_vision_frame ])), 0.3) is True
|
|
assert compare_faces(get_first(target_faces), get_first(get_static_faces([ source_vision_frame ])), 0.3) is True
|
|
assert compare_faces(get_last(target_faces), get_first(get_static_faces([ target_vision_frame ])), 0.3) is True
|
|
|
|
state_manager.set_item('face_selector_maps', [ '1:1 to 0:0', '0:0 to 1:1' ])
|
|
|
|
source_faces, target_faces = select_faces([ source_vision_frame, reference_vision_frame ], [ source_vision_frame, reference_vision_frame ], [ reference_vision_frame ])
|
|
|
|
assert compare_faces(get_first(source_faces), get_first(get_static_faces([ target_vision_frame ])), 0.3) is True
|
|
assert compare_faces(get_last(source_faces), get_first(get_static_faces([ source_vision_frame ])), 0.3) is True
|
|
assert compare_faces(get_first(target_faces), get_first(get_static_faces([ source_vision_frame ])), 0.3) is True
|
|
assert compare_faces(get_last(target_faces), get_first(get_static_faces([ target_vision_frame ])), 0.3) is True
|
|
|
|
state_manager.set_item('face_selector_maps', [ '0:0 to 0:0', '1:1 to 1:1' ])
|
|
state_manager.set_item('reference_face_distance', 1.0)
|
|
|
|
source_faces, target_faces = select_faces([ source_vision_frame, reference_vision_frame ], [ source_vision_frame, reference_vision_frame ], [ reference_vision_frame ])
|
|
|
|
assert len(source_faces) == 2
|
|
assert len(target_faces) == 2
|
|
assert compare_faces(get_first(source_faces), get_first(get_static_faces([ source_vision_frame ])), 0.3) is True
|
|
assert compare_faces(get_last(source_faces), get_first(get_static_faces([ target_vision_frame ])), 0.3) is True
|
|
assert compare_faces(get_first(target_faces), get_first(get_static_faces([ source_vision_frame ])), 0.3) is True
|
|
assert compare_faces(get_last(target_faces), get_first(get_static_faces([ target_vision_frame ])), 0.3) is True
|
|
|
|
state_manager.set_item('face_selector_maps', [ '0:2 to 0:1' ])
|
|
state_manager.set_item('reference_face_distance', 0.3)
|
|
|
|
source_faces, target_faces = select_faces([ reference_vision_frame ], [ reference_vision_frame ], [ reference_vision_frame ])
|
|
|
|
assert source_faces == [ None ]
|
|
assert compare_faces(get_first(target_faces), get_first(get_static_faces([ target_vision_frame ])), 0.3) is True
|
|
|
|
state_manager.set_item('face_selector_maps', [ '0:1 to 0:2' ])
|
|
|
|
source_faces, target_faces = select_faces([ reference_vision_frame ], [ reference_vision_frame ], [ reference_vision_frame ])
|
|
|
|
assert source_faces == []
|
|
assert target_faces == []
|
|
|
|
state_manager.clear_item('face_selector_mode')
|
|
state_manager.clear_item('face_selector_maps')
|
|
|
|
|
|
def test_decode_face_selector_maps() -> None:
|
|
assert decode_face_selector_maps([ '0:0 1:0 to 100:1', '2:0' ]) == [ { 'source_faces': [ { 'frame_index': 0, 'face_position': 0 }, { 'frame_index': 1, 'face_position': 0 } ], 'reference_faces': [ { 'frame_index': 100, 'face_position': 1 } ] }, { 'source_faces': [ { 'frame_index': 2, 'face_position': 0 } ], 'reference_faces': [] } ]
|
|
assert decode_face_selector_maps([ 'to 100:1', 'invalid' ]) == [ { 'source_faces': [], 'reference_faces': [ { 'frame_index': 100, 'face_position': 1 } ] } ]
|
|
assert decode_face_selector_maps([]) == []
|
|
assert decode_face_selector_maps(None) == []
|
|
|
|
|
|
def test_decode_face_selector_map() -> None:
|
|
assert decode_face_selector_map('0:0 to 100:1 200:0') == { 'source_faces': [ { 'frame_index': 0, 'face_position': 0 } ], 'reference_faces': [ { 'frame_index': 100, 'face_position': 1 }, { 'frame_index': 200, 'face_position': 0 } ] }
|
|
assert decode_face_selector_map('0:0 1 1,2 -1:0 0:0:0 to') == { 'source_faces': [ { 'frame_index': 0, 'face_position': 0 } ], 'reference_faces': [] }
|
|
|
|
|
|
def test_collect_source_faces() -> None:
|
|
assert collect_source_faces(decode_face_selector_maps([ '0:0 to 100:1', '1:0 2:1 to 300:0' ])) == [ { 'frame_index': 0, 'face_position': 0 }, { 'frame_index': 1, 'face_position': 0 }, { 'frame_index': 2, 'face_position': 1 } ]
|
|
assert collect_source_faces(decode_face_selector_maps([ 'to 0:0' ])) == []
|
|
|
|
|
|
def test_find_match_faces() -> None:
|
|
faces = face_a, face_b, face_c, face_d = create_faces()
|
|
|
|
assert find_match_faces([ face_a ], faces, 0.3) == [ face_a, face_d ]
|
|
assert find_match_faces([ face_a ], faces, 0.6) == [ face_a, face_b, face_d ]
|
|
assert find_match_faces([ face_a, face_b ], [ face_a, face_b, face_c ], 0.1) == [ face_a, face_b ]
|
|
assert find_match_faces([ face_a ], [], 0.3) == []
|
|
|
|
|
|
def test_compare_faces() -> None:
|
|
face_a, face_b, face_c, face_d = create_faces()
|
|
|
|
assert compare_faces(face_a, face_a, 0.1) is True
|
|
assert compare_faces(face_a, face_d, 0.3) is True
|
|
assert compare_faces(face_a, face_d, 0.2) is False
|
|
assert compare_faces(face_a, face_b, 0.5) is False
|
|
assert compare_faces(face_a, face_b, 0.6) is True
|
|
assert compare_faces(face_a, face_c, 1.0) is False
|
|
|
|
|
|
def test_calculate_face_distance() -> None:
|
|
face_a, face_b, face_c, face_d = create_faces()
|
|
|
|
assert calculate_face_distance(face_a, face_a) == 0.0
|
|
assert calculate_face_distance(face_a, face_b) == 1.0
|
|
assert calculate_face_distance(face_a, face_c) == 2.0
|
|
assert calculate_face_distance(face_d, face_a) == 0.4
|
|
|
|
|
|
def test_sort_and_filter_faces() -> None:
|
|
faces = face_a, face_b, face_c, face_d = create_faces()
|
|
|
|
assert sort_and_filter_faces([ face_a ], []) == []
|
|
assert sort_and_filter_faces([], faces) == faces
|
|
|
|
state_manager.set_item('face_selector_order', 'left-right')
|
|
|
|
assert sort_and_filter_faces([], faces) == [ face_a, face_c, face_d, face_b ]
|
|
|
|
state_manager.set_item('face_selector_gender', 'female')
|
|
|
|
assert sort_and_filter_faces([], faces) == [ face_a, face_c ]
|
|
|
|
state_manager.set_item('face_selector_gender', 'auto')
|
|
|
|
assert sort_and_filter_faces([ face_c, face_b ], faces) == [ face_d, face_b ]
|
|
assert sort_and_filter_faces([], faces) == [ face_a, face_c, face_d, face_b ]
|
|
|
|
state_manager.clear_item('face_selector_gender')
|
|
state_manager.set_item('face_selector_race', 'white')
|
|
|
|
assert sort_and_filter_faces([], faces) == [ face_a, face_d ]
|
|
|
|
state_manager.set_item('face_selector_race', 'auto')
|
|
|
|
assert sort_and_filter_faces([ face_c, face_b ], faces) == [ face_b ]
|
|
assert sort_and_filter_faces([], faces) == [ face_a, face_c, face_d, face_b ]
|
|
|
|
state_manager.clear_item('face_selector_race')
|
|
state_manager.set_item('face_selector_order', 'large-small')
|
|
state_manager.set_item('face_selector_age_start', 25)
|
|
state_manager.set_item('face_selector_age_end', 35)
|
|
|
|
assert sort_and_filter_faces([], faces) == [ face_b, face_a ]
|
|
|
|
|
|
def test_sort_faces_by_order() -> None:
|
|
faces = face_a, face_b, face_c, face_d = create_faces()
|
|
|
|
assert sort_faces_by_order(faces, 'left-right') == [ face_a, face_c, face_d, face_b ]
|
|
assert sort_faces_by_order(faces, 'right-left') == [ face_b, face_d, face_c, face_a ]
|
|
assert sort_faces_by_order(faces, 'top-bottom') == [ face_a, face_b, face_d, face_c ]
|
|
assert sort_faces_by_order(faces, 'bottom-top') == [ face_c, face_d, face_b, face_a ]
|
|
assert sort_faces_by_order(faces, 'small-large') == [ face_c, face_a, face_d, face_b ]
|
|
assert sort_faces_by_order(faces, 'large-small') == [ face_b, face_d, face_a, face_c ]
|
|
assert sort_faces_by_order(faces, 'best-worst') == [ face_b, face_c, face_a, face_d ]
|
|
assert sort_faces_by_order(faces, 'worst-best') == [ face_d, face_a, face_c, face_b ]
|
|
assert sort_faces_by_order(faces, 'invalid') == faces #type:ignore[arg-type]
|
|
|
|
|
|
def test_filter_faces_by_gender() -> None:
|
|
faces = face_a, face_b, face_c, face_d = create_faces()
|
|
|
|
assert filter_faces_by_gender(faces, 'female') == [ face_a, face_c ]
|
|
assert filter_faces_by_gender(faces, 'male') == [ face_b, face_d ]
|
|
assert filter_faces_by_gender([ face_a, face_c ], 'male') == []
|
|
|
|
|
|
def test_filter_faces_by_age() -> None:
|
|
faces = face_a, face_b, face_c, face_d = create_faces()
|
|
|
|
assert filter_faces_by_age(faces, 25, 35) == [ face_a, face_b ]
|
|
assert filter_faces_by_age(faces, 0, 100) == faces
|
|
assert filter_faces_by_age(faces, 0, 10) == []
|
|
assert filter_faces_by_age(faces, 0, 11) == [ face_d ]
|
|
assert filter_faces_by_age(faces, 68, 100) == [ face_c ]
|
|
assert filter_faces_by_age(faces, 69, 100) == []
|
|
|
|
|
|
def test_filter_faces_by_race() -> None:
|
|
faces = face_a, face_b, face_c, face_d = create_faces()
|
|
|
|
assert filter_faces_by_race(faces, 'white') == [ face_a, face_d ]
|
|
assert filter_faces_by_race(faces, 'asian') == [ face_c ]
|
|
assert filter_faces_by_race(faces, 'indian') == []
|