andro1241 commited on
Commit
a65ae47
·
verified ·
1 Parent(s): 886f9b2

Upload 4 files

Browse files
processors/modules/face_debugger/choices.py ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ from typing import List, get_args
2
+
3
+ from facefusion.processors.modules.face_debugger.types import FaceDebuggerItem
4
+
5
+ face_debugger_items : List[FaceDebuggerItem] = list(get_args(FaceDebuggerItem))
processors/modules/face_debugger/core.py ADDED
@@ -0,0 +1,270 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from argparse import ArgumentParser
2
+ from types import ModuleType
3
+ from typing import List
4
+
5
+ import cv2
6
+ import numpy
7
+
8
+ import facefusion.jobs.job_manager
9
+ import facefusion.jobs.job_store
10
+ from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, logger, state_manager, translator, video_manager
11
+ from facefusion.common_helper import get_middle
12
+ from facefusion.face_creator import scale_face
13
+ from facefusion.face_helper import warp_face_by_face_landmark_5
14
+ from facefusion.face_masker import create_area_mask, create_box_mask, create_occlusion_mask, create_region_mask
15
+ from facefusion.face_selector import select_faces
16
+ from facefusion.filesystem import in_directory, is_image, is_video, same_file_extension
17
+ from facefusion.processors.modules.face_debugger import choices as face_debugger_choices
18
+ from facefusion.processors.modules.face_debugger.types import FaceDebuggerInputs
19
+ from facefusion.processors.types import ProcessorOutputs
20
+ from facefusion.program_helper import find_argument_group
21
+ from facefusion.types import ApplyStateItem, Args, Face, InferencePool, ProcessMode, VisionFrame
22
+ from facefusion.vision import read_static_image, read_static_video_frame
23
+
24
+
25
+ def get_inference_pool() -> InferencePool:
26
+ pass
27
+
28
+
29
+ def clear_inference_pool() -> None:
30
+ pass
31
+
32
+
33
+ def register_args(program : ArgumentParser) -> None:
34
+ group_processors = find_argument_group(program, 'processors')
35
+ if group_processors:
36
+ group_processors.add_argument('--face-debugger-items', help = translator.get('help.items', __package__).format(choices = ', '.join(face_debugger_choices.face_debugger_items)), default = config.get_str_list('processors', 'face_debugger_items', 'face-landmark-5/68 face-mask'), choices = face_debugger_choices.face_debugger_items, nargs = '+', metavar = 'FACE_DEBUGGER_ITEMS')
37
+ facefusion.jobs.job_store.register_step_keys([ 'face_debugger_items' ])
38
+
39
+
40
+ def apply_args(args : Args, apply_state_item : ApplyStateItem) -> None:
41
+ apply_state_item('face_debugger_items', args.get('face_debugger_items'))
42
+
43
+
44
+ def get_common_modules() -> List[ModuleType]:
45
+ return [ content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer ]
46
+
47
+
48
+ def pre_check() -> bool:
49
+ for common_module in get_common_modules():
50
+ if not common_module.pre_check():
51
+ return False
52
+ return True
53
+
54
+
55
+ def pre_process(mode : ProcessMode) -> bool:
56
+ if mode in [ 'output', 'preview' ] and not is_image(state_manager.get_item('target_path')) and not is_video(state_manager.get_item('target_path')):
57
+ logger.error(translator.get('choose_image_or_video_target') + translator.get('exclamation_mark'), __name__)
58
+ return False
59
+ if mode == 'output' and not in_directory(state_manager.get_item('output_path')):
60
+ logger.error(translator.get('specify_image_or_video_output') + translator.get('exclamation_mark'), __name__)
61
+ return False
62
+ if mode == 'output' and not same_file_extension(state_manager.get_item('target_path'), state_manager.get_item('output_path')):
63
+ logger.error(translator.get('match_target_and_output_extension') + translator.get('exclamation_mark'), __name__)
64
+ return False
65
+ return True
66
+
67
+
68
+ def post_process() -> None:
69
+ read_static_image.cache_clear()
70
+ read_static_video_frame.cache_clear()
71
+ video_manager.clear_video_pool()
72
+
73
+ if state_manager.get_item('video_memory_strategy') == 'strict':
74
+ for common_module in get_common_modules():
75
+ common_module.clear_inference_pool()
76
+
77
+
78
+ def debug_face(target_face : Face, temp_vision_frame : VisionFrame) -> VisionFrame:
79
+ face_debugger_items = state_manager.get_item('face_debugger_items')
80
+
81
+ if 'bounding-box' in face_debugger_items:
82
+ temp_vision_frame = draw_bounding_box(target_face, temp_vision_frame)
83
+
84
+ if 'face-mask' in face_debugger_items:
85
+ temp_vision_frame = draw_face_mask(target_face, temp_vision_frame)
86
+
87
+ if 'face-landmark-5' in face_debugger_items:
88
+ temp_vision_frame = draw_face_landmark_5(target_face, temp_vision_frame)
89
+
90
+ if 'face-landmark-5/68' in face_debugger_items:
91
+ temp_vision_frame = draw_face_landmark_5_68(target_face, temp_vision_frame)
92
+
93
+ if 'face-landmark-68' in face_debugger_items:
94
+ temp_vision_frame = draw_face_landmark_68(target_face, temp_vision_frame)
95
+
96
+ if 'face-landmark-68/5' in face_debugger_items:
97
+ temp_vision_frame = draw_face_landmark_68_5(target_face, temp_vision_frame)
98
+
99
+ return temp_vision_frame
100
+
101
+
102
+ def draw_bounding_box(target_face : Face, temp_vision_frame : VisionFrame) -> VisionFrame:
103
+ temp_vision_frame = numpy.ascontiguousarray(temp_vision_frame)
104
+ bounding_box = target_face.bounding_box.astype(numpy.int32)
105
+ x1, y1, x2, y2 = bounding_box
106
+ box_color = 0, 0, 255
107
+ border_scale = calculate_scale(temp_vision_frame)
108
+ border_color = 100, 100, 255
109
+
110
+ cv2.rectangle(temp_vision_frame, (x1, y1), (x2, y2), box_color, border_scale)
111
+
112
+ if target_face.angle == 0:
113
+ cv2.line(temp_vision_frame, (x1, y1), (x2, y1), border_color, border_scale + 1)
114
+ if target_face.angle == 180:
115
+ cv2.line(temp_vision_frame, (x1, y2), (x2, y2), border_color, border_scale + 1)
116
+ if target_face.angle == 90:
117
+ cv2.line(temp_vision_frame, (x2, y1), (x2, y2), border_color, border_scale + 1)
118
+ if target_face.angle == 270:
119
+ cv2.line(temp_vision_frame, (x1, y1), (x1, y2), border_color, border_scale + 1)
120
+
121
+ return temp_vision_frame
122
+
123
+
124
+ def draw_face_mask(target_face : Face, temp_vision_frame : VisionFrame) -> VisionFrame:
125
+ crop_masks = []
126
+ temp_vision_frame = numpy.ascontiguousarray(temp_vision_frame)
127
+ face_landmark_5 = target_face.landmark_set.get('5')
128
+ face_landmark_68 = target_face.landmark_set.get('68')
129
+ face_landmark_5_68 = target_face.landmark_set.get('5/68')
130
+ crop_vision_frame, affine_matrix = warp_face_by_face_landmark_5(temp_vision_frame, face_landmark_5_68, 'arcface_128', (512, 512))
131
+ inverse_matrix = cv2.invertAffineTransform(affine_matrix)
132
+ temp_size = temp_vision_frame.shape[:2][::-1]
133
+ mask_scale = calculate_scale(temp_vision_frame)
134
+ mask_color = 0, 255, 0
135
+
136
+ if numpy.array_equal(face_landmark_5, face_landmark_5_68):
137
+ mask_color = 255, 255, 0
138
+
139
+ if target_face.origin == 'refill':
140
+ mask_color = 0, 165, 255
141
+
142
+ if 'box' in state_manager.get_item('face_mask_types'):
143
+ box_mask = create_box_mask(crop_vision_frame, 0, state_manager.get_item('face_mask_padding'))
144
+ crop_masks.append(box_mask)
145
+
146
+ if 'occlusion' in state_manager.get_item('face_mask_types'):
147
+ occlusion_mask = create_occlusion_mask(crop_vision_frame)
148
+ crop_masks.append(occlusion_mask)
149
+
150
+ if 'area' in state_manager.get_item('face_mask_types'):
151
+ face_landmark_68 = cv2.transform(face_landmark_68.reshape(1, -1, 2), affine_matrix).reshape(-1, 2)
152
+ area_mask = create_area_mask(crop_vision_frame, face_landmark_68, state_manager.get_item('face_mask_areas'))
153
+ crop_masks.append(area_mask)
154
+
155
+ if 'region' in state_manager.get_item('face_mask_types'):
156
+ region_mask = create_region_mask(crop_vision_frame, state_manager.get_item('face_mask_regions'))
157
+ crop_masks.append(region_mask)
158
+
159
+ crop_mask = numpy.minimum.reduce(crop_masks).clip(0, 1)
160
+ crop_mask = (crop_mask * 255).astype(numpy.uint8)
161
+ inverse_vision_frame = cv2.warpAffine(crop_mask, inverse_matrix, temp_size)
162
+ inverse_vision_frame = cv2.threshold(inverse_vision_frame, 100, 255, cv2.THRESH_BINARY)[1]
163
+ inverse_contours, _ = cv2.findContours(inverse_vision_frame, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
164
+ cv2.drawContours(temp_vision_frame, inverse_contours, -1, mask_color, mask_scale)
165
+
166
+ return temp_vision_frame
167
+
168
+
169
+ def draw_face_landmark_5(target_face : Face, temp_vision_frame : VisionFrame) -> VisionFrame:
170
+ temp_vision_frame = numpy.ascontiguousarray(temp_vision_frame)
171
+ face_landmark_5 = target_face.landmark_set.get('5')
172
+ point_scale = calculate_scale(temp_vision_frame)
173
+ point_color = 0, 0, 255
174
+
175
+ if target_face.origin == 'refill':
176
+ point_color = 0, 165, 255
177
+
178
+ if numpy.any(face_landmark_5):
179
+ face_landmark_5 = face_landmark_5.astype(numpy.int32)
180
+
181
+ for point in face_landmark_5:
182
+ cv2.circle(temp_vision_frame, tuple(point), point_scale, point_color, -1)
183
+
184
+ return temp_vision_frame
185
+
186
+
187
+ def draw_face_landmark_5_68(target_face : Face, temp_vision_frame : VisionFrame) -> VisionFrame:
188
+ temp_vision_frame = numpy.ascontiguousarray(temp_vision_frame)
189
+ face_landmark_5 = target_face.landmark_set.get('5')
190
+ face_landmark_5_68 = target_face.landmark_set.get('5/68')
191
+ point_scale = calculate_scale(temp_vision_frame)
192
+ point_color = 0, 255, 0
193
+
194
+ if numpy.array_equal(face_landmark_5, face_landmark_5_68):
195
+ point_color = 255, 255, 0
196
+
197
+ if target_face.origin == 'refill':
198
+ point_color = 0, 165, 255
199
+
200
+ if numpy.any(face_landmark_5_68):
201
+ face_landmark_5_68 = face_landmark_5_68.astype(numpy.int32)
202
+
203
+ for point in face_landmark_5_68:
204
+ cv2.circle(temp_vision_frame, tuple(point), point_scale, point_color, -1)
205
+
206
+ return temp_vision_frame
207
+
208
+
209
+ def draw_face_landmark_68(target_face : Face, temp_vision_frame : VisionFrame) -> VisionFrame:
210
+ temp_vision_frame = numpy.ascontiguousarray(temp_vision_frame)
211
+ face_landmark_68 = target_face.landmark_set.get('68')
212
+ face_landmark_68_5 = target_face.landmark_set.get('68/5')
213
+ point_scale = calculate_scale(temp_vision_frame)
214
+ point_color = 0, 255, 0
215
+
216
+ if numpy.array_equal(face_landmark_68, face_landmark_68_5):
217
+ point_color = 255, 255, 0
218
+
219
+ if target_face.origin == 'refill':
220
+ point_color = 0, 165, 255
221
+
222
+ if numpy.any(face_landmark_68):
223
+ face_landmark_68 = face_landmark_68.astype(numpy.int32)
224
+
225
+ for point in face_landmark_68:
226
+ cv2.circle(temp_vision_frame, tuple(point), point_scale, point_color, -1)
227
+
228
+ return temp_vision_frame
229
+
230
+
231
+ def draw_face_landmark_68_5(target_face : Face, temp_vision_frame : VisionFrame) -> VisionFrame:
232
+ temp_vision_frame = numpy.ascontiguousarray(temp_vision_frame)
233
+ face_landmark_68_5 = target_face.landmark_set.get('68/5')
234
+ point_scale = calculate_scale(temp_vision_frame)
235
+ point_color = 255, 255, 0
236
+
237
+ if target_face.origin == 'refill':
238
+ point_color = 0, 165, 255
239
+
240
+ if numpy.any(face_landmark_68_5):
241
+ face_landmark_68_5 = face_landmark_68_5.astype(numpy.int32)
242
+
243
+ for point in face_landmark_68_5:
244
+ cv2.circle(temp_vision_frame, tuple(point), point_scale, point_color, -1)
245
+
246
+ return temp_vision_frame
247
+
248
+
249
+ def calculate_scale(temp_vision_frame : VisionFrame) -> int:
250
+ frame_height, _ = temp_vision_frame.shape[:2]
251
+ frame_scale = round(frame_height / 270)
252
+ return max(1, min(10, frame_scale))
253
+
254
+
255
+ def process_frame(inputs : FaceDebuggerInputs) -> ProcessorOutputs:
256
+ reference_vision_frame = inputs.get('reference_vision_frame')
257
+ source_vision_frames = inputs.get('source_vision_frames')
258
+ target_vision_frames = inputs.get('target_vision_frames')
259
+ temp_vision_frame = inputs.get('temp_vision_frame')
260
+ temp_vision_mask = inputs.get('temp_vision_mask')
261
+
262
+ target_vision_frame = get_middle(target_vision_frames)
263
+ target_faces = select_faces(reference_vision_frame, source_vision_frames, target_vision_frames)
264
+
265
+ if target_faces:
266
+ for target_face in target_faces:
267
+ target_face = scale_face(target_face, target_vision_frame, temp_vision_frame)
268
+ temp_vision_frame = debug_face(target_face, temp_vision_frame)
269
+
270
+ return temp_vision_frame, temp_vision_mask
processors/modules/face_debugger/locales.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from facefusion.types import Locales
2
+
3
+ LOCALES : Locales =\
4
+ {
5
+ 'en':
6
+ {
7
+ 'help':
8
+ {
9
+ 'items': 'load a single or multiple processors (choices: {choices})'
10
+ },
11
+ 'uis':
12
+ {
13
+ 'items_checkbox_group': 'FACE DEBUGGER ITEMS'
14
+ }
15
+ }
16
+ }
processors/modules/face_debugger/types.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List, Literal, TypedDict
2
+
3
+ from facefusion.types import Mask, VisionFrame
4
+
5
+ FaceDebuggerInputs = TypedDict('FaceDebuggerInputs',
6
+ {
7
+ 'reference_vision_frame' : VisionFrame,
8
+ 'source_vision_frames' : List[VisionFrame],
9
+ 'target_vision_frames' : List[VisionFrame],
10
+ 'temp_vision_frame' : VisionFrame,
11
+ 'temp_vision_mask' : Mask
12
+ })
13
+
14
+ FaceDebuggerItem = Literal['bounding-box', 'face-landmark-5', 'face-landmark-5/68', 'face-landmark-68', 'face-landmark-68/5', 'face-mask']