init
Browse files- app.py +298 -211
- average.py +102 -0
- cv2_pose_estimate.py +0 -273
- demo_header.html +6 -4
- glibvision +1 -0
- glibvision/common_utils.py +0 -112
- glibvision/cv2_utils.py +0 -175
- glibvision/draw_utils.py +0 -42
- glibvision/glandmark_utils.py +0 -48
- glibvision/numpy_utils.py +0 -110
- glibvision/pil_utils.py +0 -35
- models/hyper-hgr-random15.joblib +3 -0
- models/hyper-hgr-random15_polynomial_features.joblib +3 -0
- models/hyper-hgr-random15_scaler.joblib +3 -0
- models/hyper-hgr-random15_selectkbest.joblib +3 -0
- models/hyper-hgr-random45.joblib +3 -0
- models/hyper-hgr-random45_polynomial_features.joblib +3 -0
- models/hyper-hgr-random45_scaler.joblib +3 -0
- models/hyper-hgr-random45_selectkbest.joblib +3 -0
- models/hyper-hgr-random90.joblib +3 -0
- models/hyper-hgr-random90_polynomial_features.joblib +3 -0
- models/hyper-hgr-random90_scaler.joblib +3 -0
- models/hyper-hgr-random90_selectkbest.joblib +3 -0
- models/lgbm-optimizer_15_random.joblib +3 -0
- models/lgbm-optimizer_15_random_scaler.joblib +3 -0
- models/lgbm-optimizer_15dart_random.joblib +3 -0
- models/lgbm-optimizer_15dart_random_scaler.joblib +3 -0
- models/lgbm-optimizer_45_random.joblib +3 -0
- models/lgbm-optimizer_45_random_scaler.joblib +3 -0
- models/lgbm-optimizer_90_random.joblib +3 -0
- models/lgbm-optimizer_90_random_scaler.joblib +3 -0
- mp_estimate.py +253 -0
- mp_utils.py +25 -1
app.py
CHANGED
@@ -6,16 +6,17 @@ import json
|
|
6 |
import os
|
7 |
import time
|
8 |
import mp_box
|
9 |
-
from
|
|
|
10 |
from glibvision.draw_utils import points_to_box,box_to_xy,plus_point
|
11 |
|
12 |
|
13 |
from glibvision.cv2_utils import plot_points,create_color_image,pil_to_bgr_image,set_plot_text,copy_image
|
14 |
-
|
15 |
from gradio_utils import save_image,save_buffer,clear_old_files ,read_file
|
16 |
|
17 |
import cv2
|
18 |
-
from cv2_pose_estimate import
|
19 |
|
20 |
import numpy as np
|
21 |
from numpy.typing import NDArray
|
@@ -25,86 +26,21 @@ from numpy.typing import NDArray
|
|
25 |
iris_mask_blur - final iris edge blur
|
26 |
'''
|
27 |
|
28 |
-
set_plot_text(False,0.5,(200,200,200))
|
29 |
-
|
30 |
-
depath_ratio = 1.0
|
31 |
-
|
32 |
-
model_cordinates = [ (0.0, 0.0, 0.0), # Nose tip
|
33 |
-
(0.0, 344.0, -40.0 * depath_ratio), # Chin
|
34 |
-
#(0.0, -160.0, -50.0),#center of eye
|
35 |
-
#INNER
|
36 |
-
(-110.0, -215.0, -60.0 * depath_ratio), #inner Left eye left corner
|
37 |
-
(110.0, -215.0, -60.0 * depath_ratio), #inner Right eye right corne
|
38 |
-
|
39 |
-
(-300.0, -250.0, -90.0 * depath_ratio), # Left eye left corner
|
40 |
-
(300.0, -250.0, -90.0 * depath_ratio), # Right eye right corne
|
41 |
-
|
42 |
-
(-125.0, 180.0, -70.0 * depath_ratio), # Left Mouth corner
|
43 |
-
(125.0, 180.0, -70.0 * depath_ratio) ] # Right mouth corner
|
44 |
-
|
45 |
-
def fit_cordinates(cordinates,center_x=512,center_y=512,base_distance = 344):
|
46 |
-
ratio = base_distance/(cordinates[1][1])
|
47 |
-
fitted_cordinates = []
|
48 |
-
|
49 |
-
for cordinate in model_cordinates:
|
50 |
-
fitted_cordinate = [
|
51 |
-
cordinate[0]*ratio+center_x,
|
52 |
-
cordinate[1]*ratio+center_y,
|
53 |
-
cordinate[2]*ratio
|
54 |
-
]
|
55 |
-
fitted_cordinates.append(fitted_cordinate)
|
56 |
-
return fitted_cordinates
|
57 |
-
|
58 |
-
|
59 |
-
def plot_model(cv2_image=None,center_x=512,center_y=512,base_distance = 344):
|
60 |
-
if cv2_image is None:
|
61 |
-
#TODO add arg
|
62 |
-
cv2_image=create_color_image(np.zeros((1024, 1024,3),dtype=np.uint8))
|
63 |
-
fitted_cordinates = fit_cordinates(model_cordinates,center_x,center_y,base_distance)
|
64 |
-
ratio = base_distance/model_cordinates[1][1]
|
65 |
-
|
66 |
-
def adjust_cordinate(point):
|
67 |
-
|
68 |
-
|
69 |
-
return point
|
70 |
-
|
71 |
-
plot_points(cv2_image,[adjust_cordinate(fitted_cordinates[0])],False,6,(0,0,255),3,(255,0,0))
|
72 |
-
plot_points(cv2_image,[adjust_cordinate((fitted_cordinates[1]))],False,6,(0,0,255),3,(255,0,0))
|
73 |
-
|
74 |
-
plot_points(cv2_image,[adjust_cordinate((fitted_cordinates[2])),adjust_cordinate((fitted_cordinates[4]))],False,6,(0,0,255),3,(255,0,0))
|
75 |
-
plot_points(cv2_image,[adjust_cordinate((fitted_cordinates[3])),adjust_cordinate((fitted_cordinates[5]))],False,6,(0,0,255),3,(255,0,0))
|
76 |
-
plot_points(cv2_image,[adjust_cordinate((fitted_cordinates[6])),adjust_cordinate((fitted_cordinates[7]))],False,6,(0,0,255),3,(255,0,0))
|
77 |
-
|
78 |
-
return cv2_image
|
79 |
-
|
80 |
-
|
81 |
-
def set_model_cordinates(cordinates):
|
82 |
-
global model_cordinates
|
83 |
-
model_cordinates = cordinates
|
84 |
-
|
85 |
def process_images(image,base_image,
|
86 |
-
|
87 |
-
|
88 |
-
first_pnp,second_refine,final_iterative,debug_process,draw_mediapipe_mesh,draw_mediapipe_result,z_multiply=0.8,
|
89 |
progress=gr.Progress(track_tqdm=True)):
|
90 |
clear_old_files()
|
91 |
-
|
92 |
image_indices = [4,199,#6,#center of eye
|
93 |
133,362,#inner eye
|
94 |
33,263, #outer eye
|
95 |
61,291]#mouth
|
96 |
-
|
97 |
-
|
98 |
-
chin = 344
|
99 |
-
global model_cordinates
|
100 |
-
|
101 |
-
""" normalize ?
|
102 |
-
model_cordinates =[
|
103 |
-
[pt[0]/chin,pt[1]/chin,pt[2]/chin] for pt in model_cordinates
|
104 |
-
]
|
105 |
"""
|
106 |
|
107 |
|
|
|
|
|
108 |
def landmarks_to_model_corsinates(face_landmarks,indices,w,h):
|
109 |
cordinates = []
|
110 |
z_depth = w if w<h else h
|
@@ -123,24 +59,6 @@ def process_images(image,base_image,
|
|
123 |
size = cv2_image.shape
|
124 |
center: tuple[float, float] = (size[1] / 2, size[0] / 2)
|
125 |
|
126 |
-
if base_image is not None:#additiona base image
|
127 |
-
|
128 |
-
base_image_indices = [
|
129 |
-
6,197,195,5,4,#nose center
|
130 |
-
122,196, 3, 51, 45,
|
131 |
-
351,419,248,281,275,
|
132 |
-
|
133 |
-
122,245,244,243,133, #eyes
|
134 |
-
351,465,464,463,362 #eyes
|
135 |
-
]
|
136 |
-
# TODO check same?
|
137 |
-
cv2_base_image = pil_to_bgr_image(base_image)
|
138 |
-
mp_image,face_landmarker_result = extract_landmark(cv2_base_image,"face_landmarker.task",0,0,True)
|
139 |
-
h,w = cv2_base_image.shape[:2]
|
140 |
-
|
141 |
-
image_indices = base_image_indices
|
142 |
-
set_model_cordinates(landmarks_to_model_corsinates(face_landmarker_result.face_landmarks,image_indices,w,h))
|
143 |
-
print(image_indices)
|
144 |
|
145 |
import math
|
146 |
def calculate_distance(xy, xy2):
|
@@ -151,70 +69,101 @@ def process_images(image,base_image,
|
|
151 |
h,w = im.shape[:2]
|
152 |
|
153 |
first_landmarker_result = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
154 |
if double_check_offset_center:
|
155 |
-
root_cordinate = get_pixel_cordinate(face_landmarker_result.face_landmarks,
|
156 |
diff_center_x = center[0] - root_cordinate[0]
|
157 |
diff_center_y = center[1] - root_cordinate[1]
|
158 |
base = np.zeros_like(cv2_image)
|
159 |
copy_image(base,cv2_image,diff_center_x,diff_center_y)
|
|
|
160 |
first_landmarker_result = face_landmarker_result
|
161 |
mp_image,face_landmarker_result = extract_landmark(base,"face_landmarker.task",0,0,True)
|
162 |
im = mp_image.numpy_view()
|
|
|
|
|
163 |
else:
|
164 |
diff_center_x=0
|
165 |
diff_center_y=0
|
166 |
#return base,"",""
|
167 |
|
168 |
-
cordinates = get_pixel_cordinate_list(face_landmarker_result.face_landmarks,image_indices,w,h)
|
169 |
|
170 |
|
171 |
|
|
|
|
|
172 |
if draw_mediapipe_mesh:
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
if fit_base_model:
|
180 |
-
#not get good result
|
181 |
-
#model_points: NDArray = np.array(fitted_cordinates, dtype="double")
|
182 |
-
model_points: NDArray = np.array(model_cordinates, dtype="double")
|
183 |
-
else:
|
184 |
-
model_points: NDArray = np.array(model_cordinates, dtype="double")
|
185 |
-
|
186 |
|
187 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
188 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
189 |
|
|
|
|
|
|
|
|
|
190 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
191 |
|
192 |
-
|
193 |
-
|
|
|
194 |
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
#image_size = size[0] #TODO
|
199 |
-
#f = (image_size / 2) / np.tan(np.deg2rad(camera_fov / 2))
|
200 |
-
#focal_length = f
|
201 |
-
#print(f"fov ={camera_fov} size = {image_size} focal_length = {focal_length}")
|
202 |
|
203 |
-
|
|
|
|
|
204 |
|
205 |
camera_matrix: NDArray = np.array([
|
206 |
[focal_length, 0, center[0]],
|
207 |
-
[0, focal_length, center[1]],
|
208 |
[0, 0, 1]
|
209 |
], dtype="double")
|
210 |
dist_coeffs: NDArray = np.zeros((4, 1))
|
211 |
|
212 |
# offset center usually improve result
|
213 |
|
214 |
-
|
215 |
image_points: NDArray = np.array(cordinates, dtype="double")
|
216 |
|
217 |
-
|
218 |
from scipy.spatial.transform import Rotation as R
|
219 |
def print_euler(rotation_vector,label=""):
|
220 |
order = "yxz"
|
@@ -223,13 +172,11 @@ def process_images(image,base_image,
|
|
223 |
r = R.from_matrix(rotation_matrix)
|
224 |
euler_angles = r.as_euler(order, degrees=True)
|
225 |
label = f"{label} Euler Angles {order} (degrees): {euler_angles}"
|
226 |
-
|
227 |
return label
|
228 |
|
229 |
rotation_vector = None
|
230 |
translation_vector = None
|
231 |
im_with_pose = cv2_image
|
232 |
-
result_label = None
|
233 |
mediapipe_text = None
|
234 |
|
235 |
def face_landmarker_result_to_angle_label(face_landmarker_result,order="yxz"):
|
@@ -239,85 +186,203 @@ def process_images(image,base_image,
|
|
239 |
|
240 |
rotation_matrix, translation_vector = transformation_matrix[:3, :3],transformation_matrix[:3, 3]
|
241 |
#TODO change base-size
|
242 |
-
|
|
|
243 |
#scaled_translation_vector = (-512,-512,-1024)
|
244 |
-
|
245 |
-
im_with_pose = draw_head_pose(im_with_pose, image_points, rotation_matrix, scaled_translation_vector, camera_matrix, dist_coeffs,32,-diff_center_x,-diff_center_y)
|
246 |
#print("mediapipe",scaled_translation_vector)
|
247 |
#mediapipe_label = print_euler(rotation_vector,"MediaPipe")
|
248 |
r = R.from_matrix(rotation_matrix)
|
249 |
euler_angles = r.as_euler(order, degrees=True)
|
250 |
-
label = f"Media pipe Euler Angles
|
251 |
-
|
|
|
|
|
252 |
|
253 |
if first_landmarker_result != None:
|
254 |
-
mediapipe_first_text = face_landmarker_result_to_angle_label(first_landmarker_result)
|
255 |
else:
|
256 |
mediapipe_first_text = ""
|
257 |
|
258 |
-
mediapipe_second_text = face_landmarker_result_to_angle_label(face_landmarker_result)
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
flags = cv2.SOLVEPNP_IPPE
|
267 |
-
else:
|
268 |
-
flags = cv2.SOLVEPNP_SQPNP
|
269 |
-
if first_pnp == "Mediapipe":
|
270 |
-
rotation_vector, _ = cv2.Rodrigues(rotation_matrix)
|
271 |
-
translation_vector = scaled_translation_vector
|
272 |
-
else:
|
273 |
-
translation_vector = None
|
274 |
-
#translation_vector = np.array([cordinates[0][0],cordinates[0][1],focal_length],dtype="double")
|
275 |
-
#translation_vector = scaled_translation_vector
|
276 |
-
#print("initial",translation_vector,)
|
277 |
-
rotation_vector, translation_vector = estimate_head_pose(cv2_image, model_points,image_points, camera_matrix, dist_coeffs,flags,None,translation_vector)
|
278 |
-
#print(translation_vector)
|
279 |
-
im_with_pose = cv2_image
|
280 |
-
result_label = print_euler(rotation_vector,first_pnp)
|
281 |
-
print("firstpnp",translation_vector)
|
282 |
-
if debug_process:
|
283 |
-
im_with_pose = draw_head_pose(cv2_image, image_points, rotation_vector, translation_vector, camera_matrix, dist_coeffs,128,-diff_center_x,-diff_center_y)
|
284 |
-
|
285 |
-
if first_pnp!="None" and second_refine!="None":
|
286 |
-
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 1000, 1e-8) # 反復終了条件
|
287 |
-
if second_refine == "LM":
|
288 |
-
rotation_vector, translation_vector = cv2.solvePnPRefineLM(model_points, image_points, camera_matrix, dist_coeffs, rotation_vector, translation_vector, criteria=criteria)
|
289 |
-
else:
|
290 |
-
rotation_vector, translation_vector = cv2.solvePnPRefineVVS(model_points, image_points, camera_matrix, dist_coeffs, rotation_vector, translation_vector, criteria=criteria)
|
291 |
-
if debug_process:
|
292 |
-
im_with_pose = draw_head_pose(im_with_pose, image_points, rotation_vector, translation_vector, camera_matrix, dist_coeffs,128+64,-diff_center_x,-diff_center_y)
|
293 |
-
result_label = print_euler(rotation_vector,second_refine)
|
294 |
-
#print("refine",translation_vector)
|
295 |
-
|
296 |
-
if final_iterative:
|
297 |
-
(success, rotation_vector, translation_vector) = cv2.solvePnP(
|
298 |
-
model_points, image_points, camera_matrix, dist_coeffs,rotation_vector ,translation_vector,flags=cv2.SOLVEPNP_ITERATIVE)
|
299 |
-
if success:
|
300 |
-
result_label = print_euler(rotation_vector,"SOLVEPNP_ITERATIVE")
|
301 |
-
else:
|
302 |
-
|
303 |
-
raise gr.Warning("final_iterative faild")
|
304 |
-
#draw final one
|
305 |
-
if rotation_vector is not None:
|
306 |
-
im_with_pose = draw_head_pose(im_with_pose, image_points, rotation_vector, translation_vector, camera_matrix, dist_coeffs,255,-diff_center_x,-diff_center_y)
|
307 |
|
308 |
# mediapipe metrix
|
309 |
#print("opencv",translation_vector)
|
|
|
|
|
|
|
|
|
310 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
311 |
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
317 |
|
318 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
319 |
|
|
|
|
|
|
|
|
|
320 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
321 |
|
322 |
css="""
|
323 |
#col-left {
|
@@ -360,7 +425,7 @@ with gr.Blocks(css=css, elem_id="demo-container") as demo:
|
|
360 |
|
361 |
with gr.Row(elem_id="prompt-container", equal_height=False):
|
362 |
with gr.Row():
|
363 |
-
btn = gr.Button("Pose Estimate", elem_id="run_button",variant="primary")
|
364 |
|
365 |
|
366 |
|
@@ -369,45 +434,67 @@ with gr.Blocks(css=css, elem_id="demo-container") as demo:
|
|
369 |
base_image = gr.Image(sources=['upload','clipboard'],image_mode='RGB',elem_id="image_upload", type="pil", label="Image",visible=False)
|
370 |
|
371 |
with gr.Row( equal_height=True):
|
372 |
-
|
373 |
-
label="
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
|
|
381 |
minimum=0.1,
|
382 |
maximum=1.5,
|
383 |
step=0.01,
|
384 |
value=0.8)
|
385 |
-
with gr.Row( equal_height=True):
|
386 |
-
draw_base_model = gr.Checkbox(label="draw base model",value=False,info="draw base model")
|
387 |
-
fit_base_model = gr.Checkbox(label="fit base model",value=False,info="This is just for visual,not use as model")
|
388 |
|
389 |
-
first_pnp =gr.Radio(label="PnP",choices=["None","EPNP","SQPNP","IPPE","ITERATIVE","Mediapipe"],value="EPNP")
|
390 |
-
second_refine =gr.Radio(label="PnP refine",choices=["None","LM","VVS"],value="LM")
|
391 |
with gr.Row( equal_height=True):
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
|
|
397 |
|
398 |
with gr.Column():
|
399 |
result_image = gr.Image(height=760,label="Result", elem_id="output-animation",image_mode='RGB')
|
400 |
-
|
401 |
-
|
402 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
403 |
|
404 |
btn.click(fn=process_images, inputs=[image,base_image,
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
plot_button.click(fn=plot_model,inputs=[],outputs=[result_image])
|
410 |
-
|
411 |
example_images = [
|
412 |
["examples/02316230.jpg"],
|
413 |
["examples/00003245_00.jpg"],
|
|
|
6 |
import os
|
7 |
import time
|
8 |
import mp_box
|
9 |
+
from mp_estimate import ratios_cordinates,estimate_horizontal,estimate_vertical,mean_std_label,normalized_to_pixel,get_feature_angles_cordinate,create_detail_labels,get_feature_ratios_cordinate
|
10 |
+
from mp_utils import get_pixel_cordinate_list,extract_landmark,get_pixel_cordinate,get_pixel_xyz,get_normalized_landmarks
|
11 |
from glibvision.draw_utils import points_to_box,box_to_xy,plus_point
|
12 |
|
13 |
|
14 |
from glibvision.cv2_utils import plot_points,create_color_image,pil_to_bgr_image,set_plot_text,copy_image
|
15 |
+
from glibvision.numpy_utils import rotate_point_euler,load_data
|
16 |
from gradio_utils import save_image,save_buffer,clear_old_files ,read_file
|
17 |
|
18 |
import cv2
|
19 |
+
from cv2_pose_estimate import draw_head_pose
|
20 |
|
21 |
import numpy as np
|
22 |
from numpy.typing import NDArray
|
|
|
26 |
iris_mask_blur - final iris edge blur
|
27 |
'''
|
28 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
def process_images(image,base_image,
|
30 |
+
double_check_offset_center,center_index,
|
31 |
+
draw_mediapipe_mesh,z_multiply=0.8,draw_mediapipe_angle=False,draw_hozizontal_line=False,draw_vertical_line=False,draw_faceratio_line=False,
|
|
|
32 |
progress=gr.Progress(track_tqdm=True)):
|
33 |
clear_old_files()
|
34 |
+
"""
|
35 |
image_indices = [4,199,#6,#center of eye
|
36 |
133,362,#inner eye
|
37 |
33,263, #outer eye
|
38 |
61,291]#mouth
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
"""
|
40 |
|
41 |
|
42 |
+
|
43 |
+
|
44 |
def landmarks_to_model_corsinates(face_landmarks,indices,w,h):
|
45 |
cordinates = []
|
46 |
z_depth = w if w<h else h
|
|
|
59 |
size = cv2_image.shape
|
60 |
center: tuple[float, float] = (size[1] / 2, size[0] / 2)
|
61 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
|
63 |
import math
|
64 |
def calculate_distance(xy, xy2):
|
|
|
69 |
h,w = im.shape[:2]
|
70 |
|
71 |
first_landmarker_result = None
|
72 |
+
def get_first_landmarker_result():
|
73 |
+
if first_landmarker_result:
|
74 |
+
return first_landmarker_result
|
75 |
+
else:
|
76 |
+
return face_landmarker_result
|
77 |
+
|
78 |
+
first_translation_vector = None
|
79 |
if double_check_offset_center:
|
80 |
+
root_cordinate = get_pixel_cordinate(face_landmarker_result.face_landmarks,center_index,w,h)#nose tip
|
81 |
diff_center_x = center[0] - root_cordinate[0]
|
82 |
diff_center_y = center[1] - root_cordinate[1]
|
83 |
base = np.zeros_like(cv2_image)
|
84 |
copy_image(base,cv2_image,diff_center_x,diff_center_y)
|
85 |
+
#cv2.imwrite("center.jpg",base)
|
86 |
first_landmarker_result = face_landmarker_result
|
87 |
mp_image,face_landmarker_result = extract_landmark(base,"face_landmarker.task",0,0,True)
|
88 |
im = mp_image.numpy_view()
|
89 |
+
transformation_matrix=first_landmarker_result.facial_transformation_matrixes[0]
|
90 |
+
rotation_matrix, first_translation_vector = transformation_matrix[:3, :3],transformation_matrix[:3, 3]
|
91 |
else:
|
92 |
diff_center_x=0
|
93 |
diff_center_y=0
|
94 |
#return base,"",""
|
95 |
|
96 |
+
#cordinates = get_pixel_cordinate_list(face_landmarker_result.face_landmarks,image_indices,w,h)
|
97 |
|
98 |
|
99 |
|
100 |
+
|
101 |
+
|
102 |
if draw_mediapipe_mesh:
|
103 |
+
result = first_landmarker_result
|
104 |
+
if result == None:
|
105 |
+
result = face_landmarker_result
|
106 |
+
image = mp_box.draw_landmarks_on_image(result,image)
|
107 |
+
cv2_image = pil_to_bgr_image(image)#here must be bug,but somehow working
|
108 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
109 |
|
110 |
|
111 |
+
|
112 |
+
|
113 |
+
|
114 |
+
# draw lines
|
115 |
+
|
116 |
+
#x_ratios = []
|
117 |
+
z_angles,y_ratios,h_cordinates,_ = estimate_horizontal(get_first_landmarker_result().face_landmarks)
|
118 |
|
119 |
+
if draw_hozizontal_line:
|
120 |
+
for cordinates in h_cordinates:
|
121 |
+
print(cordinates)
|
122 |
+
points = normalized_to_pixel(cordinates,w,h)
|
123 |
+
print(points)
|
124 |
+
plot_points(cv2_image,points[:2],False,5,(255,0,0),3)#last one is middle point on horizontal
|
125 |
+
|
126 |
+
|
127 |
|
128 |
+
_,x_ratios,v_cordinates,_ = estimate_vertical(get_first_landmarker_result().face_landmarks)
|
129 |
+
if draw_vertical_line:
|
130 |
+
for cordinates in v_cordinates:
|
131 |
+
plot_points(cv2_image,normalized_to_pixel(cordinates,w,h),False,5,(0,0,255),3,(255,0,0))#second one is middle point on vertical
|
132 |
|
133 |
+
#these are for training feature
|
134 |
+
key_cordinates,angles = get_feature_angles_cordinate(get_first_landmarker_result().face_landmarks)
|
135 |
+
for cordinates in key_cordinates:
|
136 |
+
pass
|
137 |
+
#plot_points(cv2_image,normalized_to_pixel(cordinates,w,h),False,5,(0,0,255),3,(255,0,0))
|
138 |
+
key_cordinates,angles = get_feature_ratios_cordinate(get_first_landmarker_result().face_landmarks)
|
139 |
+
for cordinates in key_cordinates:
|
140 |
+
pass
|
141 |
+
#plot_points(cv2_image,normalized_to_pixel(cordinates,w,h),False,5,(0,0,255),3,(255,0,0))
|
142 |
+
|
143 |
|
144 |
+
z_angle_text = mean_std_label(z_angles,True)
|
145 |
+
y_ratio_text = mean_std_label(y_ratios)
|
146 |
+
x_ratio_text = mean_std_label(x_ratios)
|
147 |
|
148 |
+
z_angle_detail = create_detail_labels(z_angles,True)
|
149 |
+
y_ratio_detail = create_detail_labels(y_ratios)
|
150 |
+
x_ratio_detail = f"forehead-chin = {np.mean(x_ratios)}"
|
|
|
|
|
|
|
|
|
151 |
|
152 |
+
|
153 |
+
focal_length: float = calculate_distance(cordinates[0],cordinates[1])
|
154 |
+
focal_length = focal_length*1
|
155 |
|
156 |
camera_matrix: NDArray = np.array([
|
157 |
[focal_length, 0, center[0]],
|
158 |
+
[0, -focal_length, center[1]],
|
159 |
[0, 0, 1]
|
160 |
], dtype="double")
|
161 |
dist_coeffs: NDArray = np.zeros((4, 1))
|
162 |
|
163 |
# offset center usually improve result
|
164 |
|
|
|
165 |
image_points: NDArray = np.array(cordinates, dtype="double")
|
166 |
|
|
|
167 |
from scipy.spatial.transform import Rotation as R
|
168 |
def print_euler(rotation_vector,label=""):
|
169 |
order = "yxz"
|
|
|
172 |
r = R.from_matrix(rotation_matrix)
|
173 |
euler_angles = r.as_euler(order, degrees=True)
|
174 |
label = f"{label} Euler Angles {order} (degrees): {euler_angles}"
|
|
|
175 |
return label
|
176 |
|
177 |
rotation_vector = None
|
178 |
translation_vector = None
|
179 |
im_with_pose = cv2_image
|
|
|
180 |
mediapipe_text = None
|
181 |
|
182 |
def face_landmarker_result_to_angle_label(face_landmarker_result,order="yxz"):
|
|
|
186 |
|
187 |
rotation_matrix, translation_vector = transformation_matrix[:3, :3],transformation_matrix[:3, 3]
|
188 |
#TODO change base-size
|
189 |
+
vector_multiply=10
|
190 |
+
scaled_translation_vector =(translation_vector[0]*vector_multiply,translation_vector[1]*vector_multiply,translation_vector[2]*vector_multiply)
|
191 |
#scaled_translation_vector = (-512,-512,-1024)
|
192 |
+
#im_with_pose = draw_head_pose(im_with_pose, image_points, rotation_matrix, scaled_translation_vector, camera_matrix, dist_coeffs,32,-diff_center_x,-diff_center_y)
|
|
|
193 |
#print("mediapipe",scaled_translation_vector)
|
194 |
#mediapipe_label = print_euler(rotation_vector,"MediaPipe")
|
195 |
r = R.from_matrix(rotation_matrix)
|
196 |
euler_angles = r.as_euler(order, degrees=True)
|
197 |
+
#label = f"Media pipe {order}-Euler Angles [x,y,z] (degrees): [{euler_angles[1]:.2f},{euler_angles[0]:.2f},{euler_angles[2]:.2f}]"
|
198 |
+
label = f"[x:{euler_angles[1]:.2f},y:{-euler_angles[0]:.2f},z:{-euler_angles[2]:.2f}]"
|
199 |
+
|
200 |
+
return label,rotation_matrix,scaled_translation_vector
|
201 |
|
202 |
if first_landmarker_result != None:
|
203 |
+
mediapipe_first_text,_,_ = face_landmarker_result_to_angle_label(first_landmarker_result)
|
204 |
else:
|
205 |
mediapipe_first_text = ""
|
206 |
|
207 |
+
mediapipe_second_text,rotation_matrix,scaled_translation_vector = face_landmarker_result_to_angle_label(face_landmarker_result)
|
208 |
+
|
209 |
+
rotation_vector, _ = cv2.Rodrigues(rotation_matrix)
|
210 |
+
translation_vector = scaled_translation_vector
|
211 |
+
|
212 |
+
#if first_translation_vector.all():
|
213 |
+
# translation_vector = first_translation_vector
|
214 |
+
#im_with_pose = draw_head_pose(im_with_pose, image_points, rotation_vector, translation_vector, camera_matrix, dist_coeffs,255,-diff_center_x,-diff_center_y)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
215 |
|
216 |
# mediapipe metrix
|
217 |
#print("opencv",translation_vector)
|
218 |
+
|
219 |
+
|
220 |
+
if draw_mediapipe_angle:
|
221 |
+
root_cordinate = get_pixel_xyz(get_first_landmarker_result().face_landmarks,4,w,h)
|
222 |
|
223 |
+
r = R.from_matrix(rotation_matrix)
|
224 |
+
euler_angles = r.as_euler("yxz", degrees=False)
|
225 |
+
print(r.as_euler("yxz", degrees=True))
|
226 |
+
draw_cordinate1=rotate_point_euler((0,0,-100),[-euler_angles[1],euler_angles[0],euler_angles[2]],"yzx")
|
227 |
+
draw_cordinate2=rotate_point_euler((0,0,-200),[-euler_angles[1],euler_angles[0],euler_angles[2]],"yzx")
|
228 |
+
|
229 |
+
plot_points(im_with_pose,[root_cordinate[:2]+draw_cordinate1[:2],root_cordinate[:2]+draw_cordinate2[:2],root_cordinate[:2]],False,5,(0,128,0),3,(0,255,0))
|
230 |
+
|
231 |
+
#analyze face ratios
|
232 |
+
landmarks = get_normalized_landmarks(get_first_landmarker_result().face_landmarks)
|
233 |
+
face_ratio_infos = []
|
234 |
+
|
235 |
+
|
236 |
+
print("landmark",[landmarks[37],landmarks[267]])
|
237 |
+
print("numpy",np.array([landmarks[37],landmarks[267]]))
|
238 |
+
print("mean",np.mean(np.array([landmarks[37],landmarks[267]]),axis=0))
|
239 |
+
v_cordinates=[
|
240 |
+
["philtrum",landmarks[175],landmarks[13],np.mean((landmarks[164],landmarks[2]),axis=0).tolist()],
|
241 |
+
["straight",landmarks[175],landmarks[94],landmarks[9]],
|
242 |
+
["face",landmarks[175],landmarks[9],landmarks[127],landmarks[356]],
|
243 |
+
["r-eyes",landmarks[33],landmarks[190],landmarks[414]],
|
244 |
+
["r-contour",landmarks[127],landmarks[33],landmarks[190]],
|
245 |
+
["l-eyes",landmarks[263],landmarks[414],landmarks[190]],
|
246 |
+
["l-contour",landmarks[356],landmarks[263],landmarks[414]],
|
247 |
+
["lips",landmarks[17],landmarks[13],np.mean((landmarks[37],landmarks[267]),axis=0).tolist()],
|
248 |
+
["mouth-eye",landmarks[61],landmarks[291],landmarks[133],landmarks[362]],
|
249 |
+
]
|
250 |
|
251 |
+
for cordinates in v_cordinates:
|
252 |
+
ratio=ratios_cordinates(cordinates[1:])
|
253 |
+
if draw_faceratio_line:
|
254 |
+
plot_points(cv2_image,normalized_to_pixel(cordinates[1:],w,h),False,5,(0,255,255),3,(255,255,0))
|
255 |
+
label = f"{cordinates[0]}:{ratio:.2f}"
|
256 |
+
face_ratio_infos.append(label)
|
257 |
+
face_ratio_info=",".join(face_ratio_infos)
|
258 |
+
return cv2.cvtColor(im_with_pose,cv2.COLOR_BGR2RGB),mediapipe_first_text,mediapipe_second_text,z_angle_text,y_ratio_text,x_ratio_text,z_angle_detail,y_ratio_detail,x_ratio_detail,face_ratio_info
|
259 |
+
|
260 |
+
|
261 |
+
|
262 |
+
def find_nearest_weighted_euclidean_2d(target_angles_full, all_angles_full, weights):
|
263 |
+
target_angles = target_angles_full[:5] # 最初の3つの角度を使用
|
264 |
+
all_angles = all_angles_full[:, :5] # 最初の3列を使用
|
265 |
+
|
266 |
+
weighted_diff = (all_angles - target_angles) * weights
|
267 |
+
distances = np.linalg.norm(weighted_diff, axis=1)
|
268 |
+
nearest_index = np.argmin(distances)
|
269 |
+
return nearest_index, all_angles_full[nearest_index]
|
270 |
+
|
271 |
+
from mp_estimate import estimate_horizontal_points ,estimate_vertical_points,estimate_rotations_v2
|
272 |
+
def find_angles(image):
|
273 |
+
if image is None:
|
274 |
+
raise gr.Error("need image")
|
275 |
+
cv2_image = pil_to_bgr_image(image)
|
276 |
+
size = cv2_image.shape
|
277 |
+
mp_image,face_landmarker_result = extract_landmark(cv2_image,"face_landmarker.task",0,0,True)
|
278 |
+
|
279 |
+
|
280 |
+
features_text = estimate_rotations_v2(face_landmarker_result)
|
281 |
+
features_value_origin = [float(value) for value in features_text.split(",")]
|
282 |
+
features_value = features_value_origin.copy()
|
283 |
+
#print(features_value)
|
284 |
+
#weights = np.array([0.2, 0.2,0.3,0.3])
|
285 |
+
|
286 |
+
#index,matched = find_nearest_weighted_euclidean_2d(target_angles,all_angles,weights)
|
287 |
+
#index,matched = find_nearest_euclidean_2d(target_angles,all_angles)
|
288 |
+
#formatted_arr = [np.format_float_positional(x) for x in matched]
|
289 |
+
#print(formatted_arr)
|
290 |
+
x_ratios = 11 #magic vertical ratios
|
291 |
+
|
292 |
+
#short
|
293 |
+
features_values = [
|
294 |
+
[np.add(features_value[-x_ratios:],features_value[0:1])],
|
295 |
+
[features_value[:-x_ratios]],
|
296 |
+
[np.hstack([features_value[ 3:5],features_value[ 6:-x_ratios]])]
|
297 |
+
#[features_value[:-x_ratios]]
|
298 |
+
]
|
299 |
+
import joblib
|
300 |
+
def estimate(model_path,scaler_path,features_values):
|
301 |
+
scalers = joblib.load("models/"+scaler_path)
|
302 |
+
if not isinstance(scalers,list):
|
303 |
+
scalers=(scalers,scalers,scalers)
|
304 |
+
for i,scaler in enumerate(scalers):
|
305 |
+
print(i,scaler)
|
306 |
+
features_values[i] = scaler.transform(features_values[i].copy())
|
307 |
+
|
308 |
+
|
309 |
+
result_preds=[]
|
310 |
+
models = joblib.load("models/"+model_path)
|
311 |
+
for i,model in enumerate(models):
|
312 |
+
y_pred = model.predict(features_values[i])
|
313 |
+
result_preds.append(y_pred.round(2))
|
314 |
+
return result_preds
|
315 |
+
def estimate2(model_key,features_values):
|
316 |
+
model_path=f"models/{model_key}.joblib"
|
317 |
+
scaler_path=f"models/{model_key}_scaler.joblib"
|
318 |
+
polynomial_path=f"models/{model_key}_polynomial_features.joblib"
|
319 |
+
selectkbest_path=f"models/{model_key}_selectkbest.joblib"
|
320 |
+
model = joblib.load(model_path)
|
321 |
+
scaler = joblib.load(scaler_path)
|
322 |
+
polynomial = joblib.load(polynomial_path)
|
323 |
+
selectkbest = joblib.load(selectkbest_path)
|
324 |
+
result_preds=[]
|
325 |
+
for i in range(3):
|
326 |
+
x = polynomial[i].transform(features_values[i].copy())
|
327 |
+
x = selectkbest[i].transform(x)
|
328 |
+
x = scaler[i].transform(x)
|
329 |
+
y_pred = model[i].predict(x)
|
330 |
+
result_preds.append(y_pred.round(2))
|
331 |
+
return result_preds
|
332 |
+
|
333 |
+
|
334 |
+
#short_result = estimate('linear-svr-xyz_5.joblib','linear-svr-xyz_5_scaler.joblib',features_values)
|
335 |
+
|
336 |
+
features_value = features_value_origin.copy()
|
337 |
+
features_values = [
|
338 |
+
[features_value],[features_value],[features_value]
|
339 |
+
]
|
340 |
+
#short_result = estimate('lgbm-optimizer_15.joblib','lgbm-optimizer_15_scaler.joblib',features_values.copy())
|
341 |
+
short_result = estimate2('hyper-hgr-random15',features_values.copy())
|
342 |
|
343 |
+
|
344 |
+
|
345 |
+
|
346 |
+
#middle_result = estimate('lgbm-xyz_90-rand47.joblib','lgbm-xyz_90-rand47_scaler.joblib',features_values.copy())
|
347 |
+
middle_result = estimate2('hyper-hgr-random45',features_values.copy())
|
348 |
+
|
349 |
+
long_result = estimate2('hyper-hgr-random90',features_values.copy())
|
350 |
+
|
351 |
|
352 |
+
e1_key="lgbm-optimizer_15dart_random"
|
353 |
+
short_result2a = estimate(f'{e1_key}.joblib',f'{e1_key}_scaler.joblib',features_values.copy())
|
354 |
+
e1_key="lgbm-optimizer_15_random"
|
355 |
+
short_result2 = estimate(f'{e1_key}.joblib',f'{e1_key}_scaler.joblib',features_values.copy())
|
356 |
|
357 |
+
e1_key="lgbm-optimizer_45_random"
|
358 |
+
|
359 |
+
middle_result2 = estimate(f'{e1_key}.joblib',f'{e1_key}_scaler.joblib',features_values.copy())
|
360 |
+
e1_key="lgbm-optimizer_90_random"
|
361 |
+
long_result2 = estimate(f'{e1_key}.joblib',f'{e1_key}_scaler.joblib',features_values.copy())
|
362 |
+
def flatten_for(lst):
|
363 |
+
return [round(item, 3) for sublist in lst for item in sublist]
|
364 |
+
|
365 |
+
def average(values):
|
366 |
+
flat_values=[]
|
367 |
+
for value in values:
|
368 |
+
flat_values += [flatten_for(value)]
|
369 |
+
print(np.mean(flat_values,axis=0))
|
370 |
+
|
371 |
+
import average
|
372 |
+
data={
|
373 |
+
"hgbr-15":flatten_for(short_result),
|
374 |
+
"hgbr-45":flatten_for(middle_result),
|
375 |
+
"hgbr-90":flatten_for(long_result),
|
376 |
+
"lgbm-15dart":flatten_for(short_result2a),
|
377 |
+
"lgbm-15":flatten_for(short_result2),
|
378 |
+
"lgbm-45":flatten_for(middle_result2),
|
379 |
+
"lgbm-90":flatten_for(long_result2),
|
380 |
+
}
|
381 |
+
#print(data)
|
382 |
+
average_data=average.analyze_3d_data(data.values())
|
383 |
+
print(average_data)
|
384 |
+
#average((short_result,middle_result,long_result,short_result2a,short_result2,middle_result2,long_result2))
|
385 |
+
return average_data['trimmed_mean'],flatten_for(short_result),flatten_for(middle_result),flatten_for(long_result),flatten_for(short_result2a),flatten_for(short_result2),flatten_for(middle_result2),flatten_for(long_result2)
|
386 |
|
387 |
css="""
|
388 |
#col-left {
|
|
|
425 |
|
426 |
with gr.Row(elem_id="prompt-container", equal_height=False):
|
427 |
with gr.Row():
|
428 |
+
btn = gr.Button("Head-Pose Estimate", elem_id="run_button",variant="primary")
|
429 |
|
430 |
|
431 |
|
|
|
434 |
base_image = gr.Image(sources=['upload','clipboard'],image_mode='RGB',elem_id="image_upload", type="pil", label="Image",visible=False)
|
435 |
|
436 |
with gr.Row( equal_height=True):
|
437 |
+
|
438 |
+
double_check = gr.Checkbox(label="Double Check",value=True,info="move center and detect again(usually more accurate).recommend choose 195")
|
439 |
+
center_index = gr.Slider(info="center-index",
|
440 |
+
label="Center-index",
|
441 |
+
minimum=0,
|
442 |
+
maximum=467,
|
443 |
+
step=1,
|
444 |
+
value=195)
|
445 |
+
z_multiply = gr.Slider(info="nose height",
|
446 |
+
label="Depth-Multiply",
|
447 |
minimum=0.1,
|
448 |
maximum=1.5,
|
449 |
step=0.01,
|
450 |
value=0.8)
|
|
|
|
|
|
|
451 |
|
|
|
|
|
452 |
with gr.Row( equal_height=True):
|
453 |
+
draw_mediapipe_mesh = gr.Checkbox(label="Draw mediapipe mesh",value=True)
|
454 |
+
draw_mediapipe_angle = gr.Checkbox(label="Draw mediapipe angle(green)",value=True)
|
455 |
+
with gr.Row( equal_height=True):
|
456 |
+
draw_hozizontal_line = gr.Checkbox(label="Draw horizontal line(red)",value=True)
|
457 |
+
draw_vertical_line = gr.Checkbox(label="Draw vertical line(blue)",value=True)
|
458 |
+
draw_faceratio_line = gr.Checkbox(label="Draw Face-Ratio line(blue)",value=False)
|
459 |
|
460 |
with gr.Column():
|
461 |
result_image = gr.Image(height=760,label="Result", elem_id="output-animation",image_mode='RGB')
|
462 |
+
with gr.Row( equal_height=True):
|
463 |
+
mediapipe_last_text = gr.Textbox(label="2nd or last mediapipe result(yzx-eulder[x,y,z])")
|
464 |
+
mediapipe_first_text = gr.Textbox(label="first mediapipe result(yzx-eulder[x,y,z])")
|
465 |
+
|
466 |
+
with gr.Row( equal_height=True):
|
467 |
+
z_angle_text = gr.Textbox(label="Z angle by horizontal-line",info="start with 0,exactly Z-Angle")
|
468 |
+
y_ratio_text = gr.Textbox(label="Y Left-Right length ratio",info="start 0.49-0.51")
|
469 |
+
x_ratio_text = gr.Textbox(label="X Up-down length ratio",info="start near 0.49,look at nose-hole-shape")
|
470 |
+
with gr.Accordion(label="Angle Ratio Details", open=False):
|
471 |
+
with gr.Row( equal_height=True):
|
472 |
+
z_angle_detail_text = gr.TextArea(label="Z-angle detail")
|
473 |
+
y_ratio_detail = gr.TextArea(label="Y-ratio detail")
|
474 |
+
x_ratio_detail = gr.TextArea(label="X-ratio detail",value="")
|
475 |
+
with gr.Row( equal_height=True):
|
476 |
+
face_ratio_info = gr.Text(label="Face Ratio",info="Average philtrum:1.82(std 0.13),straight:0.82(std 0.04),face:0.91(std 0.02),r-eyes:0.86(std 0.03),r-contour:0.77(std 0.05),l-eyes:0.86(std 0.03),l-contour:0.75(std 0.05),lips:1.43(std 0.16),mouth-eye:1.21(std 0.07)")
|
477 |
+
gr.HTML("<h5>For Rotation sometime differenct to mediapipe's result(Especially X usually minus 4-7)</h5>")
|
478 |
+
bt_test = gr.Button("Model-Estimate")
|
479 |
+
gr.HTML("<p>YXZ-Euler [x,y,z] hgbr is stable,lgbm is accurate(dart is more).trimmed works well on small angles</p>")
|
480 |
+
with gr.Row( equal_height=True):
|
481 |
+
average_result = gr.Text(label="trimmed-mean")
|
482 |
+
short_result = gr.Text(label="hgbr-15")
|
483 |
+
middle_result = gr.Text(label="hgbr-45")
|
484 |
+
long_result = gr.Text(label="hgbr-90")
|
485 |
+
with gr.Row( equal_height=True):
|
486 |
+
short_result2a = gr.Text(label="lgbm-15dart")
|
487 |
+
short_result2 = gr.Text(label="lgbm-15")
|
488 |
+
middle_result2 = gr.Text(label="lgbm-45")
|
489 |
+
long_result2 = gr.Text(label="lgbm-90")
|
490 |
+
#,
|
491 |
+
bt_test.click(fn=find_angles,inputs=image,outputs=[average_result,short_result,middle_result,long_result,short_result2a,short_result2,middle_result2,long_result2])
|
492 |
|
493 |
btn.click(fn=process_images, inputs=[image,base_image,
|
494 |
+
double_check,center_index,
|
495 |
+
draw_mediapipe_mesh,z_multiply,draw_mediapipe_angle,draw_hozizontal_line,draw_vertical_line,draw_faceratio_line,
|
496 |
+
],outputs=[result_image,mediapipe_first_text,mediapipe_last_text,z_angle_text,y_ratio_text,x_ratio_text,z_angle_detail_text,y_ratio_detail,x_ratio_detail,face_ratio_info] ,api_name='infer')
|
497 |
+
|
|
|
|
|
498 |
example_images = [
|
499 |
["examples/02316230.jpg"],
|
500 |
["examples/00003245_00.jpg"],
|
average.py
ADDED
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import numpy as np
|
2 |
+
|
3 |
+
def analyze_3d_data(data):
|
4 |
+
"""
|
5 |
+
3Dデータポイントのリストを受け取り、トリム平均とSTD法による外れ値を返す
|
6 |
+
|
7 |
+
Args:
|
8 |
+
data: [(x1, y1, z1), (x2, y2, z2), ...] 形式のリスト
|
9 |
+
|
10 |
+
Returns:
|
11 |
+
dict:
|
12 |
+
{
|
13 |
+
'trimmed_mean': (x_mean, y_mean, z_mean), # トリム平均
|
14 |
+
'std_outliers': {
|
15 |
+
'x': [x_outlier1, x_outlier2, ...], # x座標の外れ値
|
16 |
+
'y': [y_outlier1, y_outlier2, ...], # y座標の外れ値
|
17 |
+
'z': [z_outlier1, z_outlier2, ...] # z座標の外れ値
|
18 |
+
},
|
19 |
+
'trimmed_data': {
|
20 |
+
'x': [x1, x2, ...], # トリム平均に使用したx座標データ
|
21 |
+
'y': [y1, y2, ...], # トリム平均に使用したy座標データ
|
22 |
+
'z': [z1, z2, ...] # トリム平均に使用したz座標データ
|
23 |
+
}
|
24 |
+
}
|
25 |
+
"""
|
26 |
+
|
27 |
+
# データを x, y, z 座標ごとに整理
|
28 |
+
x_coords = [point[0] for point in data]
|
29 |
+
y_coords = [point[1] for point in data]
|
30 |
+
z_coords = [point[2] for point in data]
|
31 |
+
|
32 |
+
def trimmed_data_and_mean(coords):
|
33 |
+
"""
|
34 |
+
最小値と最大値を除外したデータとトリム平均を返す
|
35 |
+
"""
|
36 |
+
coords_sorted = sorted(coords)
|
37 |
+
trimmed_coords = coords_sorted[1:-1] # 最小値と最大値を除外
|
38 |
+
return trimmed_coords, np.mean(trimmed_coords)
|
39 |
+
|
40 |
+
# 各座標のトリム平均を計算
|
41 |
+
x_trimmed, x_trimmed_mean = trimmed_data_and_mean(x_coords)
|
42 |
+
y_trimmed, y_trimmed_mean = trimmed_data_and_mean(y_coords)
|
43 |
+
z_trimmed, z_trimmed_mean = trimmed_data_and_mean(z_coords)
|
44 |
+
|
45 |
+
def detect_outliers_std(data, multiplier=2):
|
46 |
+
"""
|
47 |
+
標準偏差に基づく外れ値検出 (multiplier=2)
|
48 |
+
"""
|
49 |
+
mean = np.mean(data)
|
50 |
+
std = np.std(data)
|
51 |
+
lower_bound = mean - multiplier * std
|
52 |
+
upper_bound = mean + multiplier * std
|
53 |
+
outliers = [x for x in data if x < lower_bound or x > upper_bound]
|
54 |
+
return outliers
|
55 |
+
|
56 |
+
# トリム後のデータに対して STD 法を適用
|
57 |
+
x_outliers_std = detect_outliers_std(x_trimmed, multiplier=2)
|
58 |
+
y_outliers_std = detect_outliers_std(y_trimmed, multiplier=2)
|
59 |
+
z_outliers_std = detect_outliers_std(z_trimmed, multiplier=2)
|
60 |
+
|
61 |
+
return {
|
62 |
+
'trimmed_mean': (round(x_trimmed_mean,2), round(y_trimmed_mean,2), round(z_trimmed_mean,2)),
|
63 |
+
'std_outliers': {
|
64 |
+
'x': x_outliers_std,
|
65 |
+
'y': y_outliers_std,
|
66 |
+
'z': z_outliers_std,
|
67 |
+
},
|
68 |
+
'trimmed_data': {
|
69 |
+
'x': x_trimmed,
|
70 |
+
'y': y_trimmed,
|
71 |
+
'z': z_trimmed
|
72 |
+
}
|
73 |
+
}
|
74 |
+
"""
|
75 |
+
# 使用例 (元のデータを使用)
|
76 |
+
data = {
|
77 |
+
'hgbr-15': [-5.4, 1.56, -2.92],
|
78 |
+
'hgbr-45': [-4.5, 1.2, -1.76],
|
79 |
+
'hgbr-90': [1.58, 1.82, -3.35],
|
80 |
+
'lgbm-15dart': [-6.18, 3.11, -2.46],
|
81 |
+
'lgbm-15': [-5.65, 1.76, -2.59],
|
82 |
+
'lgbm-45': [-7.18, 1.42, -2.71],
|
83 |
+
'lgbm-90': [-3.58, 3.94, -2.5],
|
84 |
+
}
|
85 |
+
|
86 |
+
# 辞書型のデータをリスト型に変換
|
87 |
+
data_list = list(data.values())
|
88 |
+
|
89 |
+
# 関数を呼び出し
|
90 |
+
result = analyze_3d_data(data_list)
|
91 |
+
|
92 |
+
# 結果を表示
|
93 |
+
print("トリム平均:", result['trimmed_mean'])
|
94 |
+
print("STD法による外れ値 (multiplier=2):")
|
95 |
+
print(" x:", result['std_outliers']['x'])
|
96 |
+
print(" y:", result['std_outliers']['y'])
|
97 |
+
print(" z:", result['std_outliers']['z'])
|
98 |
+
print("トリム平均に使用したデータ:")
|
99 |
+
print(" x:", result['trimmed_data']['x'])
|
100 |
+
print(" y:", result['trimmed_data']['y'])
|
101 |
+
print(" z:", result['trimmed_data']['z'])
|
102 |
+
"""
|
cv2_pose_estimate.py
DELETED
@@ -1,273 +0,0 @@
|
|
1 |
-
import cv2
|
2 |
-
import numpy as np
|
3 |
-
from numpy.typing import NDArray
|
4 |
-
import sys
|
5 |
-
from mp_utils import get_pixel_cordinate_list,extract_landmark
|
6 |
-
def estimate_head_pose(im: NDArray, model_points: NDArray, image_points,camera_matrix: NDArray, dist_coeffs: NDArray,flags = cv2.SOLVEPNP_ITERATIVE,rotation_vector=None,translation_vector=None) -> tuple[NDArray, NDArray]:
|
7 |
-
"""
|
8 |
-
Estimates the head pose from an image.
|
9 |
-
|
10 |
-
Args:
|
11 |
-
image_path: Path to the image file.
|
12 |
-
model_points: 3D model points.
|
13 |
-
camera_matrix: Camera intrinsic matrix.
|
14 |
-
dist_coeffs: Lens distortion coefficients.
|
15 |
-
|
16 |
-
Returns:
|
17 |
-
rotation_vector: Estimated rotation vector.
|
18 |
-
translation_vector: Estimated translation vector.
|
19 |
-
"""
|
20 |
-
size = im.shape
|
21 |
-
|
22 |
-
'''
|
23 |
-
image_points: NDArray = np.array([
|
24 |
-
(359, 391), # Nose tip
|
25 |
-
(399, 561), # Chin
|
26 |
-
(337, 297), # Left eye left corner
|
27 |
-
(513, 301), # Right eye right corne
|
28 |
-
(345, 465), # Left Mouth corner
|
29 |
-
(453, 469) # Right mouth corner
|
30 |
-
], dtype="double")
|
31 |
-
'''
|
32 |
-
|
33 |
-
model_points = model_points +500
|
34 |
-
(success, rotation_vector, translation_vector) = cv2.solvePnP(
|
35 |
-
model_points, image_points, camera_matrix, dist_coeffs,flags=flags,
|
36 |
-
)
|
37 |
-
print(model_points)
|
38 |
-
print(image_points)
|
39 |
-
print(camera_matrix)
|
40 |
-
|
41 |
-
if not success:
|
42 |
-
raise RuntimeError("solvePnP failed.")
|
43 |
-
|
44 |
-
return rotation_vector, translation_vector
|
45 |
-
|
46 |
-
import cv2
|
47 |
-
import numpy as np
|
48 |
-
from numpy.typing import NDArray
|
49 |
-
|
50 |
-
def draw_head_pose(image: NDArray, image_points: NDArray, rotation_vector: NDArray, translation_vector: NDArray, camera_matrix: NDArray, dist_coeffs: NDArray,color_max=255,offset_x=0,offset_y=0) -> NDArray:
|
51 |
-
"""
|
52 |
-
Draws the head pose (XYZ axes) on the image.
|
53 |
-
|
54 |
-
Args:
|
55 |
-
image: Input image.
|
56 |
-
image_points: 2D image points.
|
57 |
-
rotation_vector: Estimated rotation vector.
|
58 |
-
translation_vector: Estimated translation vector.
|
59 |
-
camera_matrix: Camera intrinsic matrix.
|
60 |
-
dist_coeffs: Lens distortion coefficients.
|
61 |
-
|
62 |
-
Returns:
|
63 |
-
Image with head pose drawn.
|
64 |
-
"""
|
65 |
-
|
66 |
-
# Define the 3D points for the XYZ axes
|
67 |
-
axis_length = 500.0 # Length of the axes
|
68 |
-
axis_points_3D: NDArray = np.array([
|
69 |
-
[0, 0, 0], # Origin
|
70 |
-
[axis_length, 0, 0], # X axis
|
71 |
-
[0, axis_length, 0], # Y axis
|
72 |
-
[0, 0, axis_length] # Z axis
|
73 |
-
], dtype='float32')
|
74 |
-
|
75 |
-
# Project the 3D points to the 2D image plane
|
76 |
-
(axis_points_2D, _) = cv2.projectPoints(
|
77 |
-
axis_points_3D, rotation_vector, translation_vector, camera_matrix, dist_coeffs
|
78 |
-
)
|
79 |
-
axis_points_2D = axis_points_2D.astype(int)
|
80 |
-
|
81 |
-
# Draw the axes on the image
|
82 |
-
origin = tuple(axis_points_2D[0].ravel())
|
83 |
-
cv2.line(image, origin, tuple(axis_points_2D[1].ravel()), (0, 0, color_max), 3) # X axis (Red)
|
84 |
-
cv2.line(image, origin, tuple(axis_points_2D[2].ravel()), (0, color_max, 0), 3) # Y axis (Green)
|
85 |
-
cv2.line(image, origin, tuple(axis_points_2D[3].ravel()), (color_max, 0, 0), 3) # Z axis (Blue)
|
86 |
-
|
87 |
-
for p in image_points:
|
88 |
-
cv2.circle(image, (int(p[0]+offset_x), int(p[1]+offset_y)), 3, (0, 0, 255), -1)
|
89 |
-
|
90 |
-
return image
|
91 |
-
|
92 |
-
def main():
|
93 |
-
# 3D model points.
|
94 |
-
'''
|
95 |
-
model_points: NDArray = np.array([
|
96 |
-
(0.0, 0.0, 0.0), # Nose tip
|
97 |
-
(0.0, 300.0, -65.0), # Chin
|
98 |
-
(-225.0, -170.0, -135.0), # Left eye left corner
|
99 |
-
(225.0, -170.0, -135.0), # Right eye right corne
|
100 |
-
(-150.0, -150.0, -125.0), # Left Mouth corner
|
101 |
-
(150.0, -150.0, -125.0) # Right mouth corner
|
102 |
-
])
|
103 |
-
'''
|
104 |
-
|
105 |
-
model_points: NDArray = np.array([
|
106 |
-
(0.0, 0.0, 0.0), # Nose tip
|
107 |
-
(0.0, -344.0, -40.0), # Chin
|
108 |
-
#(0.0, -160.0, -50.0),#center of eye
|
109 |
-
(-110.0, 215.0, -60.0), #inner Left eye left corner
|
110 |
-
(110.0, 215.0, -60.0), #inner Right eye right corne
|
111 |
-
(-300.0, 250.0, -90.0), # Left eye left corner
|
112 |
-
(300.0, 250.0, -90.0), # Right eye right corne
|
113 |
-
(-185.0, -180.0, -70.0), # Left Mouth corner
|
114 |
-
(185.0, -180.0, -70.0) # Right mouth corner
|
115 |
-
])
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
"""
|
120 |
-
|
121 |
-
model_points: NDArray = np.array([
|
122 |
-
(0.0, 0.0, 0.0), # Nose tip
|
123 |
-
(0.0, -450.0, 0.0), # Chin
|
124 |
-
(-110.0, 175.0, -20.0), #inner Left eye left corner
|
125 |
-
(110.0, 175.0, -20.0), #inner Right eye right corne
|
126 |
-
(-300.0, 200.0, -40.0), # Left eye left corner
|
127 |
-
(300.0, 200.0, -40.0), # Right eye right corne
|
128 |
-
(-176.0, -200.0, -20.0), # Left Mouth corner
|
129 |
-
(175.0, -200.0, -20.0) # Right mouth corner
|
130 |
-
])
|
131 |
-
"""
|
132 |
-
|
133 |
-
square_model_points: NDArray = np.array([
|
134 |
-
(-100.0, -100.0, 0), # Left eye left corner
|
135 |
-
(100.0, -100.0, 0), # Right eye right corne
|
136 |
-
(-100.0, 100.0, 0), # Left Mouth corner
|
137 |
-
(100.0, 100.0, 0) # Right mouth corner
|
138 |
-
])
|
139 |
-
|
140 |
-
|
141 |
-
# Example image and camera parameters (replace with actual values)
|
142 |
-
image_path = sys.argv[1]
|
143 |
-
mp_image,face_landmarker_result = extract_landmark(image_path)
|
144 |
-
im = mp_image.numpy_view()
|
145 |
-
h,w = im.shape[:2]
|
146 |
-
cordinates = get_pixel_cordinate_list(face_landmarker_result.face_landmarks,[4,199,#6,#center of eye
|
147 |
-
33,263,133,362,61,291],w,h)
|
148 |
-
print(cordinates)
|
149 |
-
image_points: NDArray = np.array(cordinates, dtype="double")
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
import math
|
154 |
-
def calculate_distance(xy, xy2):
|
155 |
-
return math.sqrt((xy2[0] - xy[0])**2 + (xy2[1] - xy[1])**2)
|
156 |
-
|
157 |
-
if im is None:
|
158 |
-
raise FileNotFoundError(f"Could not open or find the image file: {image_path}")
|
159 |
-
size = im.shape
|
160 |
-
focal_length: float = calculate_distance(cordinates[0],cordinates[1])
|
161 |
-
focal_length = focal_length*1.5
|
162 |
-
print("focal length",focal_length)
|
163 |
-
center: tuple[float, float] = (size[1] / 2, size[0] / 2)
|
164 |
-
center = cordinates[0]
|
165 |
-
camera_matrix: NDArray = np.array([
|
166 |
-
[focal_length, 0, center[0]],
|
167 |
-
[0, focal_length, center[1]],
|
168 |
-
[0, 0, 1]
|
169 |
-
], dtype="double")
|
170 |
-
dist_coeffs: NDArray = np.zeros((4, 1)) # Assuming no lens distortion
|
171 |
-
|
172 |
-
# 2D image points. If you change the image, you need to change vector
|
173 |
-
'''
|
174 |
-
image_points: NDArray = np.array([
|
175 |
-
(321, 571), # Nose tip
|
176 |
-
(423, 852), # Chin
|
177 |
-
(201, 406), # Left eye left corner
|
178 |
-
(529, 363), # Right eye right corne
|
179 |
-
(336, 705), # Left Mouth corner
|
180 |
-
(483, 693) # Right mouth corner
|
181 |
-
], dtype="double")
|
182 |
-
'''
|
183 |
-
"""
|
184 |
-
image_points: NDArray = np.array([
|
185 |
-
#(663, 325), # Nose tip
|
186 |
-
(655,388),
|
187 |
-
(705, 555), # Chin
|
188 |
-
(549, 296), # inner Left eye left corner
|
189 |
-
(651, 291), # inner Right eye right corne
|
190 |
-
(453, 303), # Left eye left corner
|
191 |
-
(718, 294), # Right eye right corne
|
192 |
-
(591, 474), # Left Mouth corner
|
193 |
-
(715, 472) # Right mouth corner
|
194 |
-
], dtype="double")
|
195 |
-
"""
|
196 |
-
|
197 |
-
|
198 |
-
square_image_points: NDArray = np.array([
|
199 |
-
(549, 296), # Nose tip
|
200 |
-
(651, 291), # Chin
|
201 |
-
(573, 386), # Left eye left corner
|
202 |
-
(691, 370), # Right eye right corne
|
203 |
-
], dtype="double")
|
204 |
-
|
205 |
-
flags_list = [
|
206 |
-
cv2.SOLVEPNP_EPNP#cv2.SOLVEPNP_ITERATIVE#,cv2.SOLVEPNP_SQPNP,cv2.SOLVEPNP_EPNP
|
207 |
-
]
|
208 |
-
im_with_pose = im.copy()
|
209 |
-
for flags in flags_list:
|
210 |
-
rotation_vector, translation_vector = estimate_head_pose(image_path, model_points,image_points, camera_matrix, dist_coeffs,flags)
|
211 |
-
#print(f"Rotation Vector:\n {rotation_vector}")
|
212 |
-
#print(f"Translation Vector:\n {translation_vector}")
|
213 |
-
#initial
|
214 |
-
#im_with_pose = draw_head_pose(im_with_pose, image_points, rotation_vector, translation_vector, camera_matrix, dist_coeffs)
|
215 |
-
|
216 |
-
from scipy.spatial.transform import Rotation as R
|
217 |
-
def print_euler(rotation_vector):
|
218 |
-
order = "yxz"
|
219 |
-
rotation_matrix, _ = cv2.Rodrigues(rotation_vector)
|
220 |
-
|
221 |
-
r = R.from_matrix(rotation_matrix)
|
222 |
-
euler_angles = r.as_euler(order, degrees=True)
|
223 |
-
print(f"Euler Angles {order} (degrees): {euler_angles}")
|
224 |
-
|
225 |
-
print_euler(rotation_vector)
|
226 |
-
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 1000, 1e-8) # 反復終了条件
|
227 |
-
|
228 |
-
rotation_vector, translation_vector = cv2.solvePnPRefineLM(model_points, image_points, camera_matrix, dist_coeffs, rotation_vector, translation_vector, criteria=criteria)
|
229 |
-
im_with_pose = draw_head_pose(im_with_pose, image_points, rotation_vector, translation_vector, camera_matrix, dist_coeffs,128)
|
230 |
-
print_euler(rotation_vector)
|
231 |
-
|
232 |
-
#rotation_vector[0]=0
|
233 |
-
#rotation_vector[1]=0
|
234 |
-
#rotation_vector[2]=0
|
235 |
-
|
236 |
-
#(success, rotation_vector, translation_vector) = cv2.solvePnP(
|
237 |
-
# model_points, image_points, camera_matrix, dist_coeffs,rotation_vector ,translation_vector,flags=cv2.SOLVEPNP_ITERATIVE)
|
238 |
-
|
239 |
-
im_with_pose = draw_head_pose(im_with_pose, image_points, rotation_vector, translation_vector, camera_matrix, dist_coeffs)
|
240 |
-
|
241 |
-
#print_euler(rotation_vector)
|
242 |
-
|
243 |
-
(rotation_matrix, jacobian) = cv2.Rodrigues(rotation_vector)
|
244 |
-
mat = np.hstack((rotation_matrix, translation_vector))
|
245 |
-
|
246 |
-
#yaw,pitch,rollの取り出し
|
247 |
-
(_, _, _, _, _, _, eulerAngles) = cv2.decomposeProjectionMatrix(mat)
|
248 |
-
print(eulerAngles)
|
249 |
-
#rvec, tvec = cv2.solvePnPRefineVVS(model_points, image_points, camera_matrix, dist_coeffs, rotation_vector, translation_vector, criteria=criteria)
|
250 |
-
#im_with_pose = draw_head_pose(im_with_pose, image_points, rvec, tvec, camera_matrix, dist_coeffs)
|
251 |
-
|
252 |
-
|
253 |
-
#square
|
254 |
-
#rvec, tvec = estimate_head_pose(image_path, square_model_points,square_image_points, camera_matrix, dist_coeffs,cv2.SOLVEPNP_IPPE_SQUARE)
|
255 |
-
#not so good
|
256 |
-
#im_with_pose = draw_head_pose(im_with_pose, square_image_points, rvec, tvec, camera_matrix, dist_coeffs)
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
#print(rotation_matrix)
|
261 |
-
# 回転行列をオイラー角に変換
|
262 |
-
#euler_angles = cv2.decomposeProjectionMatrix(rotation_matrix)[-1]
|
263 |
-
|
264 |
-
# オイラー角の表示 (x, y, z)
|
265 |
-
|
266 |
-
# Display image
|
267 |
-
cv2.imshow("Output", cv2.cvtColor(im_with_pose, cv2.COLOR_BGR2RGB))
|
268 |
-
cv2.waitKey(0)
|
269 |
-
cv2.destroyAllWindows()
|
270 |
-
cv2.imwrite("result.jpg",cv2.cvtColor(im_with_pose, cv2.COLOR_BGR2RGB))
|
271 |
-
|
272 |
-
if __name__ == "__main__":
|
273 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
demo_header.html
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
<div style="text-align: center;">
|
2 |
<h1>
|
3 |
-
Mediapipe
|
4 |
</h1>
|
5 |
<div class="grid-container">
|
6 |
<img src="https://akjava.github.io/AIDiagramChatWithVoice-FaceCharacter/webp/128/00544245.webp" alt="Mediapipe Face Detection" class="image">
|
@@ -8,9 +8,11 @@
|
|
8 |
<p class="text">
|
9 |
This Space use <a href="http://www.apache.org/licenses/LICENSE-2.0">the Apache 2.0</a> Licensed <a href="https://ai.google.dev/edge/mediapipe/solutions/vision/face_landmarker">Mediapipe FaceLandmarker</a> <br>
|
10 |
"Current MediaPipe face-landmark detection struggle with faces rotated more than 45 degrees (due to limitations in training data).<br>
|
11 |
-
|
12 |
-
|
13 |
-
|
|
|
|
|
14 |
</p>
|
15 |
</div>
|
16 |
|
|
|
1 |
<div style="text-align: center;">
|
2 |
<h1>
|
3 |
+
Mediapipe Head-Pose Estimation
|
4 |
</h1>
|
5 |
<div class="grid-container">
|
6 |
<img src="https://akjava.github.io/AIDiagramChatWithVoice-FaceCharacter/webp/128/00544245.webp" alt="Mediapipe Face Detection" class="image">
|
|
|
8 |
<p class="text">
|
9 |
This Space use <a href="http://www.apache.org/licenses/LICENSE-2.0">the Apache 2.0</a> Licensed <a href="https://ai.google.dev/edge/mediapipe/solutions/vision/face_landmarker">Mediapipe FaceLandmarker</a> <br>
|
10 |
"Current MediaPipe face-landmark detection struggle with faces rotated more than 45 degrees (due to limitations in training data).<br>
|
11 |
+
|
12 |
+
Accurate detection in MediaPipe requires correct positioning, but I don't know the exact position needed. This is a known <a href="https://github.com/google-ai-edge/mediapipe/issues/4759">issue</a><br>
|
13 |
+
I start to test hgbr and lgbm models,this estimate result help to make aligned image.<br>
|
14 |
+
center index see <a href="https://github.com/google-ai-edge/mediapipe/blob/a908d668c730da128dfa8d9f6bd25d519d006692/mediapipe/modules/face_geometry/data/canonical_face_model_uv_visualization.png">mediapipe face index image</a><br>
|
15 |
+
Recently Choose center index and mediapipe-estimate + models trained with face-features extracted by mediapipe
|
16 |
</p>
|
17 |
</div>
|
18 |
|
glibvision
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
C:/Users/owner/Documents/pythons/glibvision/glibvision
|
glibvision/common_utils.py
DELETED
@@ -1,112 +0,0 @@
|
|
1 |
-
import os
|
2 |
-
def check_exists_files(files,dirs,exit_on_error=True):
|
3 |
-
if files is not None:
|
4 |
-
if isinstance(files, str):
|
5 |
-
files = [files]
|
6 |
-
for file in files:
|
7 |
-
if not os.path.isfile(file):
|
8 |
-
print(f"File {file} not found")
|
9 |
-
if exit_on_error:
|
10 |
-
exit(1)
|
11 |
-
else:
|
12 |
-
return 1
|
13 |
-
if dirs is not None:
|
14 |
-
if isinstance(dirs, str):
|
15 |
-
dirs = [dirs]
|
16 |
-
for dir in dirs:
|
17 |
-
if not os.path.isdir(dir):
|
18 |
-
print(f"Dir {dir} not found")
|
19 |
-
if exit_on_error:
|
20 |
-
exit(1)
|
21 |
-
else:
|
22 |
-
return 1
|
23 |
-
return 0
|
24 |
-
|
25 |
-
image_extensions =[".jpg"]
|
26 |
-
|
27 |
-
def add_name_suffix(file_name,suffix,replace_suffix=False):
|
28 |
-
if not suffix.startswith("_"):#force add
|
29 |
-
suffix="_"+suffix
|
30 |
-
|
31 |
-
name,ext = os.path.splitext(file_name)
|
32 |
-
if replace_suffix:
|
33 |
-
index = name.rfind("_")
|
34 |
-
if index!=-1:
|
35 |
-
return f"{name[0:index]}{suffix}{ext}"
|
36 |
-
|
37 |
-
return f"{name}{suffix}{ext}"
|
38 |
-
|
39 |
-
def replace_extension(file_name,new_extension,suffix=None,replace_suffix=False):
|
40 |
-
if not new_extension.startswith("."):
|
41 |
-
new_extension="."+new_extension
|
42 |
-
|
43 |
-
name,ext = os.path.splitext(file_name)
|
44 |
-
new_file = f"{name}{new_extension}"
|
45 |
-
if suffix:
|
46 |
-
return add_name_suffix(name+new_extension,suffix,replace_suffix)
|
47 |
-
return new_file
|
48 |
-
|
49 |
-
def list_digit_images(input_dir,sort=True):
|
50 |
-
digit_images = []
|
51 |
-
global image_extensions
|
52 |
-
files = os.listdir(input_dir)
|
53 |
-
for file in files:
|
54 |
-
if file.endswith(".jpg"):#TODO check image
|
55 |
-
base,ext = os.path.splitext(file)
|
56 |
-
if not base.isdigit():
|
57 |
-
continue
|
58 |
-
digit_images.append(file)
|
59 |
-
|
60 |
-
if sort:
|
61 |
-
digit_images.sort()
|
62 |
-
|
63 |
-
return digit_images
|
64 |
-
def list_suffix_images(input_dir,suffix,is_digit=True,sort=True):
|
65 |
-
digit_images = []
|
66 |
-
global image_extensions
|
67 |
-
files = os.listdir(input_dir)
|
68 |
-
for file in files:
|
69 |
-
if file.endswith(".jpg"):#TODO check image
|
70 |
-
base,ext = os.path.splitext(file)
|
71 |
-
if base.endswith(suffix):
|
72 |
-
if is_digit:
|
73 |
-
if not base.replace(suffix,"").isdigit():
|
74 |
-
continue
|
75 |
-
digit_images.append(file)
|
76 |
-
|
77 |
-
if sort:
|
78 |
-
digit_images.sort()
|
79 |
-
|
80 |
-
return digit_images
|
81 |
-
|
82 |
-
import time
|
83 |
-
|
84 |
-
class ProgressTracker:
|
85 |
-
"""
|
86 |
-
処理の進捗状況を追跡し、経過時間と残り時間を表示するクラス。
|
87 |
-
"""
|
88 |
-
|
89 |
-
def __init__(self,key, total_target):
|
90 |
-
"""
|
91 |
-
コンストラクタ
|
92 |
-
|
93 |
-
Args:
|
94 |
-
total_target (int): 処理対象の総数
|
95 |
-
"""
|
96 |
-
self.key = key
|
97 |
-
self.total_target = total_target
|
98 |
-
self.complete_target = 0
|
99 |
-
self.start_time = time.time()
|
100 |
-
|
101 |
-
def update(self):
|
102 |
-
"""
|
103 |
-
進捗を1つ進める。
|
104 |
-
経過時間と残り時間を表示する。
|
105 |
-
"""
|
106 |
-
self.complete_target += 1
|
107 |
-
current_time = time.time()
|
108 |
-
consumed_time = current_time - self.start_time
|
109 |
-
remain_time = (consumed_time / self.complete_target) * (self.total_target - self.complete_target) if self.complete_target > 0 else 0
|
110 |
-
print(f"stepped {self.key} {self.total_target} of {self.complete_target}, consumed {(consumed_time / 60):.1f} min, remain {(remain_time / 60):.1f} min")
|
111 |
-
|
112 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
glibvision/cv2_utils.py
DELETED
@@ -1,175 +0,0 @@
|
|
1 |
-
import cv2
|
2 |
-
import numpy as np
|
3 |
-
|
4 |
-
#2024-11-27 add copy image
|
5 |
-
|
6 |
-
def draw_bbox(image,box,color=(255,0,0),thickness=1):
|
7 |
-
if thickness==0:
|
8 |
-
return
|
9 |
-
|
10 |
-
left = int(box[0])
|
11 |
-
top = int(box[1])
|
12 |
-
right = int(box[0]+box[2])
|
13 |
-
bottom = int(box[1]+box[3])
|
14 |
-
box_points =[(left,top),(right,top),(right,bottom),(left,bottom)]
|
15 |
-
|
16 |
-
cv2.polylines(image, [np.array(box_points)], isClosed=True, color=color, thickness=thickness)
|
17 |
-
|
18 |
-
|
19 |
-
def to_int_points(points):
|
20 |
-
int_points=[]
|
21 |
-
for point in points:
|
22 |
-
int_points.append([int(point[0]),int(point[1])])
|
23 |
-
return int_points
|
24 |
-
|
25 |
-
def draw_text(img, text, point, font_scale=0.5, color=(200, 200, 200), thickness=1):
|
26 |
-
font = cv2.FONT_HERSHEY_SIMPLEX
|
27 |
-
cv2.putText(img, str(text), point, font, font_scale, color, thickness, cv2.LINE_AA)
|
28 |
-
|
29 |
-
plot_text_color = (200, 200, 200)
|
30 |
-
plot_text_font_scale = 0.5
|
31 |
-
plot_index = 1
|
32 |
-
plot_text = True
|
33 |
-
|
34 |
-
def set_plot_text(is_plot,text_font_scale,text_color):
|
35 |
-
global plot_index,plot_text,plot_text_font_scale,plot_text_color
|
36 |
-
plot_text = is_plot
|
37 |
-
plot_index = 1
|
38 |
-
plot_text_font_scale = text_font_scale
|
39 |
-
plot_text_color = text_color
|
40 |
-
|
41 |
-
def plot_points(image,points,isClosed=False,circle_size=3,circle_color=(255,0,0),line_size=1,line_color=(0,0,255)):
|
42 |
-
|
43 |
-
global plot_index,plot_text
|
44 |
-
int_points = to_int_points(points)
|
45 |
-
if circle_size>0:
|
46 |
-
for point in int_points:
|
47 |
-
cv2.circle(image,point,circle_size,circle_color,-1)
|
48 |
-
if plot_text:
|
49 |
-
draw_text(image,plot_index,point,plot_text_font_scale,plot_text_color)
|
50 |
-
plot_index+=1
|
51 |
-
if line_size>0:
|
52 |
-
cv2.polylines(image, [np.array(int_points)], isClosed=isClosed, color=line_color, thickness=line_size)
|
53 |
-
|
54 |
-
def fill_points(image,points,thickness=1,line_color=(255,255,255),fill_color = (255,255,255)):
|
55 |
-
np_points = np.array(points,dtype=np.int32)
|
56 |
-
cv2.fillPoly(image, [np_points], fill_color)
|
57 |
-
cv2.polylines(image, [np_points], isClosed=True, color=line_color, thickness=thickness)
|
58 |
-
|
59 |
-
def get_image_size(cv2_image):
|
60 |
-
return cv2_image.shape[:2]
|
61 |
-
|
62 |
-
def get_channel(np_array):
|
63 |
-
return np_array.shape[2] if np_array.ndim == 3 else 1
|
64 |
-
|
65 |
-
def get_numpy_text(np_array,key=""):
|
66 |
-
channel = get_channel(np_array)
|
67 |
-
return f"{key} shape = {np_array.shape} channel = {channel} ndim = {np_array.ndim} size = {np_array.size}"
|
68 |
-
|
69 |
-
|
70 |
-
def gray3d_to_2d(grayscale: np.ndarray) -> np.ndarray:
|
71 |
-
channel = get_channel(grayscale)
|
72 |
-
if channel!=1:
|
73 |
-
raise ValueError(f"color maybe rgb or rgba {get_numpy_text(grayscale)}")
|
74 |
-
"""
|
75 |
-
3 次元グレースケール画像 (チャンネル数 1) を 2 次元に変換する。
|
76 |
-
|
77 |
-
Args:
|
78 |
-
grayscale (np.ndarray): 3 次元グレースケール画像 (チャンネル数 1)。
|
79 |
-
|
80 |
-
Returns:
|
81 |
-
np.ndarray: 2 次元グレースケール画像。
|
82 |
-
"""
|
83 |
-
|
84 |
-
if grayscale.ndim == 2:
|
85 |
-
return grayscale
|
86 |
-
return np.squeeze(grayscale)
|
87 |
-
|
88 |
-
def blend_rgb_images(image1: np.ndarray, image2: np.ndarray, mask: np.ndarray) -> np.ndarray:
|
89 |
-
"""
|
90 |
-
2 つの RGB 画像をマスク画像を使用してブレンドする。
|
91 |
-
|
92 |
-
Args:
|
93 |
-
image1 (np.ndarray): 最初の画像 (RGB)。
|
94 |
-
image2 (np.ndarray): 2 番目の画像 (RGB)。
|
95 |
-
mask (np.ndarray): マスク画像 (グレースケール)。
|
96 |
-
|
97 |
-
Returns:
|
98 |
-
np.ndarray: ブレンドされた画像 (RGB)。
|
99 |
-
|
100 |
-
Raises:
|
101 |
-
ValueError: 入力画像の形状が一致しない場合。
|
102 |
-
"""
|
103 |
-
|
104 |
-
if image1.shape != image2.shape or image1.shape[:2] != mask.shape:
|
105 |
-
raise ValueError("入力画像の形状が一致しません。")
|
106 |
-
|
107 |
-
# 画像を float 型に変換
|
108 |
-
image1 = image1.astype(float)
|
109 |
-
image2 = image2.astype(float)
|
110 |
-
|
111 |
-
# マスクを 3 チャンネルに変換し、0-1 の範囲にスケール
|
112 |
-
alpha = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR).astype(float) / 255.0
|
113 |
-
|
114 |
-
# ブレンド計算
|
115 |
-
blended = (1 - alpha) * image1 + alpha * image2
|
116 |
-
|
117 |
-
return blended.astype(np.uint8)
|
118 |
-
|
119 |
-
def create_color_image(img,color=(255,255,255)):
|
120 |
-
mask = np.zeros_like(img)
|
121 |
-
|
122 |
-
h, w = img.shape[:2]
|
123 |
-
cv2.rectangle(mask, (0, 0), (w, h), color, -1)
|
124 |
-
return mask
|
125 |
-
|
126 |
-
def pil_to_bgr_image(image):
|
127 |
-
np_image = np.array(image, dtype=np.uint8)
|
128 |
-
if np_image.shape[2] == 4:
|
129 |
-
bgr_img = cv2.cvtColor(np_image, cv2.COLOR_RGBA2BGRA)
|
130 |
-
else:
|
131 |
-
bgr_img = cv2.cvtColor(np_image, cv2.COLOR_RGB2BGR)
|
132 |
-
return bgr_img
|
133 |
-
|
134 |
-
def bgr_to_rgb(np_image):
|
135 |
-
if np_image.shape[2] == 4:
|
136 |
-
bgr_img = cv2.cvtColor(np_image, cv2.COLOR_RBGRA2RGBA)
|
137 |
-
else:
|
138 |
-
bgr_img = cv2.cvtColor(np_image, cv2.COLOR_BGR2RGB)
|
139 |
-
return bgr_img
|
140 |
-
|
141 |
-
def copy_image(img1: np.ndarray, img2: np.ndarray, x: int, y: int) -> None:
|
142 |
-
# チャネル数と次元数のチェック
|
143 |
-
if img1.ndim != 3 or img2.ndim != 3:
|
144 |
-
raise ValueError("Both img1 and img2 must be 3-dimensional arrays.")
|
145 |
-
elif img1.shape[2] != img2.shape[2]:
|
146 |
-
raise ValueError(f"img1 and img2 must have the same number of channels. img1 has {img1.shape[2]} channels, but img2 has {img2.shape[1]} channels.")
|
147 |
-
|
148 |
-
# Type check
|
149 |
-
if not isinstance(img1, np.ndarray) or not isinstance(img2, np.ndarray):
|
150 |
-
raise TypeError("img1 and img2 must be NumPy arrays.")
|
151 |
-
|
152 |
-
if x>=0:
|
153 |
-
offset_x=0
|
154 |
-
w = min(img1.shape[1]-x,img2.shape[1])
|
155 |
-
else:
|
156 |
-
w = min(img1.shape[1],img2.shape[1]+x)
|
157 |
-
offset_x=int(-x)
|
158 |
-
x = 0
|
159 |
-
|
160 |
-
if y>=0:
|
161 |
-
h = min(img1.shape[0]-y,img2.shape[0])
|
162 |
-
offset_y=0
|
163 |
-
else:
|
164 |
-
h = min(img1.shape[0]-y,img2.shape[0]+y)
|
165 |
-
offset_y=int(-y)
|
166 |
-
y = 0
|
167 |
-
x=int(x)
|
168 |
-
y=int(y)
|
169 |
-
h=int(h)
|
170 |
-
w=int(w)
|
171 |
-
|
172 |
-
|
173 |
-
print(f"img1 {img1.shape} img2{img2.shape} x={x} y={y} w={w} h={h}")
|
174 |
-
# Paste the overlapping part
|
175 |
-
img1[y:y+h, x:x+w] = img2[offset_y:h+offset_y, offset_x:w+offset_x]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
glibvision/draw_utils.py
DELETED
@@ -1,42 +0,0 @@
|
|
1 |
-
# DrawUtils
|
2 |
-
# not PIL,CV2,Numpy drawing method
|
3 |
-
import math
|
4 |
-
# 2024-11-29 add calculate_distance
|
5 |
-
def points_to_box(points):
|
6 |
-
x1=float('inf')
|
7 |
-
x2=0
|
8 |
-
y1=float('inf')
|
9 |
-
y2=0
|
10 |
-
for point in points:
|
11 |
-
if point[0]<x1:
|
12 |
-
x1=point[0]
|
13 |
-
if point[0]>x2:
|
14 |
-
x2=point[0]
|
15 |
-
if point[1]<y1:
|
16 |
-
y1=point[1]
|
17 |
-
if point[1]>y2:
|
18 |
-
y2=point[1]
|
19 |
-
return [x1,y1,x2-x1,y2-y1]
|
20 |
-
|
21 |
-
def box_to_point(box):
|
22 |
-
return [
|
23 |
-
[box[0],box[1]],
|
24 |
-
[box[0]+box[2],box[1]],
|
25 |
-
[box[0]+box[2],box[1]+box[3]],
|
26 |
-
[box[0],box[1]+box[3]]
|
27 |
-
]
|
28 |
-
|
29 |
-
def plus_point(base_pt,add_pt):
|
30 |
-
return [base_pt[0]+add_pt[0],base_pt[1]+add_pt[1]]
|
31 |
-
|
32 |
-
def box_to_xy(box):
|
33 |
-
return [box[0],box[1],box[2]+box[0],box[3]+box[1]]
|
34 |
-
|
35 |
-
def to_int_points(points):
|
36 |
-
int_points=[]
|
37 |
-
for point in points:
|
38 |
-
int_points.append([int(point[0]),int(point[1])])
|
39 |
-
return int_points
|
40 |
-
|
41 |
-
def calculate_distance(xy, xy2):
|
42 |
-
return math.sqrt((xy2[0] - xy[0])**2 + (xy2[1] - xy[1])**2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
glibvision/glandmark_utils.py
DELETED
@@ -1,48 +0,0 @@
|
|
1 |
-
|
2 |
-
import os
|
3 |
-
|
4 |
-
#simple single version
|
5 |
-
def bbox_to_glandmarks(file_name,bbox,points = None):
|
6 |
-
base,ext = os.path.splitext(file_name)
|
7 |
-
glandmark = {"image":{
|
8 |
-
"boxes":[{
|
9 |
-
"left":int(bbox[0]),"top":int(bbox[1]),"width":int(bbox[2]),"height":int(bbox[3])
|
10 |
-
}],
|
11 |
-
"file":file_name,
|
12 |
-
"id":int(base)
|
13 |
-
# width,height ignore here
|
14 |
-
}}
|
15 |
-
if points is not None:
|
16 |
-
parts=[
|
17 |
-
]
|
18 |
-
for point in points:
|
19 |
-
parts.append({"x":int(point[0]),"y":int(point[1])})
|
20 |
-
glandmark["image"]["boxes"][0]["parts"] = parts
|
21 |
-
return glandmark
|
22 |
-
|
23 |
-
#technically this is not g-landmark/dlib ,
|
24 |
-
def convert_to_landmark_group_json(points):
|
25 |
-
if len(points)!=68:
|
26 |
-
print(f"points must be 68 but {len(points)}")
|
27 |
-
return None
|
28 |
-
new_points=list(points)
|
29 |
-
|
30 |
-
result = [ # possible multi person ,just possible any func support multi person
|
31 |
-
|
32 |
-
{ # index start 0 but index-number start 1
|
33 |
-
"chin":new_points[0:17],
|
34 |
-
"left_eyebrow":new_points[17:22],
|
35 |
-
"right_eyebrow":new_points[22:27],
|
36 |
-
"nose_bridge":new_points[27:31],
|
37 |
-
"nose_tip":new_points[31:36],
|
38 |
-
"left_eye":new_points[36:42],
|
39 |
-
"right_eye":new_points[42:48],
|
40 |
-
|
41 |
-
# lip points customized structure
|
42 |
-
# MIT licensed face_recognition
|
43 |
-
# https://github.com/ageitgey/face_recognition
|
44 |
-
"top_lip":new_points[48:55]+[new_points[64]]+[new_points[63]]+[new_points[62]]+[new_points[61]]+[new_points[60]],
|
45 |
-
"bottom_lip":new_points[54:60]+[new_points[48]]+[new_points[60]]+[new_points[67]]+[new_points[66]]+[new_points[65]]+[new_points[64]],
|
46 |
-
}
|
47 |
-
]
|
48 |
-
return result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
glibvision/numpy_utils.py
DELETED
@@ -1,110 +0,0 @@
|
|
1 |
-
import numpy as np
|
2 |
-
|
3 |
-
|
4 |
-
def apply_binary_mask_to_color(base_image,color,mask):
|
5 |
-
"""
|
6 |
-
二値マスクを使用して、画像の一部を別の画像にコピーする。
|
7 |
-
|
8 |
-
Args:
|
9 |
-
base_image (np.ndarray): コピー先の画像。
|
10 |
-
paste_image (np.ndarray): コピー元の画像。
|
11 |
-
mask (np.ndarray): 二値マスク画像。
|
12 |
-
|
13 |
-
Returns:
|
14 |
-
np.ndarray: マスクを適用した画像。
|
15 |
-
|
16 |
-
"""
|
17 |
-
# TODO check all shape
|
18 |
-
#print_numpy(base_image)
|
19 |
-
#print_numpy(paste_image)
|
20 |
-
#print_numpy(mask)
|
21 |
-
if mask.ndim == 2:
|
22 |
-
condition = mask == 255
|
23 |
-
else:
|
24 |
-
condition = mask[:,:,0] == 255
|
25 |
-
|
26 |
-
base_image[condition] = color
|
27 |
-
return base_image
|
28 |
-
|
29 |
-
def apply_binary_mask_to_image(base_image,paste_image,mask):
|
30 |
-
"""
|
31 |
-
二値マスクを使用して、画像の一部を別の画像にコピーする。
|
32 |
-
|
33 |
-
Args:
|
34 |
-
base_image (np.ndarray): コピー先の画像。
|
35 |
-
paste_image (np.ndarray): コピー元の画像。
|
36 |
-
mask (np.ndarray): 二値マスク画像。
|
37 |
-
|
38 |
-
Returns:
|
39 |
-
np.ndarray: マスクを適用した画像。
|
40 |
-
|
41 |
-
"""
|
42 |
-
# TODO check all shape
|
43 |
-
#print_numpy(base_image)
|
44 |
-
#print_numpy(paste_image)
|
45 |
-
#print_numpy(mask)
|
46 |
-
if mask.ndim == 2:
|
47 |
-
condition = mask == 255
|
48 |
-
else:
|
49 |
-
condition = mask[:,:,0] == 255
|
50 |
-
|
51 |
-
base_image[condition] = paste_image[condition]
|
52 |
-
return base_image
|
53 |
-
|
54 |
-
def pil_to_numpy(image):
|
55 |
-
return np.array(image, dtype=np.uint8)
|
56 |
-
|
57 |
-
def extruce_points(points,index,ratio=1.5):
|
58 |
-
"""
|
59 |
-
indexのポイントをratio倍だけ、点群の中心から、外側に膨らます。
|
60 |
-
"""
|
61 |
-
center_point = np.mean(points, axis=0)
|
62 |
-
if index < 0 or index > len(points):
|
63 |
-
raise ValueError(f"index must be range(0,{len(points)} but value = {index})")
|
64 |
-
point1 =points[index]
|
65 |
-
print(f"center = {center_point}")
|
66 |
-
vec_to_center = point1 - center_point
|
67 |
-
return vec_to_center*ratio + center_point
|
68 |
-
|
69 |
-
|
70 |
-
def bulge_polygon(points, bulge_factor=0.1,isClosed=True):
|
71 |
-
"""
|
72 |
-
ポリゴンの辺の中間に点を追加し、外側に膨らませる
|
73 |
-
ndarrayを返すので注意
|
74 |
-
"""
|
75 |
-
# 入力 points を NumPy 配列に変換
|
76 |
-
points = np.array(points)
|
77 |
-
|
78 |
-
# ポリゴン全体の重心を求める
|
79 |
-
center_point = np.mean(points, axis=0)
|
80 |
-
#print(f"center = {center_point}")
|
81 |
-
new_points = []
|
82 |
-
num_points = len(points)
|
83 |
-
for i in range(num_points):
|
84 |
-
if i == num_points -1 and not isClosed:
|
85 |
-
break
|
86 |
-
p1 = points[i]
|
87 |
-
#print(f"p{i} = {p1}")
|
88 |
-
# 重心から頂点へのベクトル
|
89 |
-
#vec_to_center = p1 - center_point
|
90 |
-
|
91 |
-
# 辺のベクトルを求める
|
92 |
-
mid_diff = points[(i + 1) % num_points] - p1
|
93 |
-
mid = p1+(mid_diff/2)
|
94 |
-
|
95 |
-
#print(f"mid = {mid}")
|
96 |
-
out_vec = mid - center_point
|
97 |
-
|
98 |
-
# 重心からのベクトルに bulge_vec を加算
|
99 |
-
new_point = mid + out_vec * bulge_factor
|
100 |
-
|
101 |
-
new_points.append(p1)
|
102 |
-
new_points.append(new_point.astype(np.int32))
|
103 |
-
|
104 |
-
return np.array(new_points)
|
105 |
-
|
106 |
-
|
107 |
-
# image.shape rgb are (1024,1024,3) use 1024,1024 as 2-dimensional
|
108 |
-
def create_2d_image(shape):
|
109 |
-
grayscale_image = np.zeros(shape[:2], dtype=np.uint8)
|
110 |
-
return grayscale_image
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
glibvision/pil_utils.py
DELETED
@@ -1,35 +0,0 @@
|
|
1 |
-
from PIL import Image,ImageDraw
|
2 |
-
from .draw_utils import box_to_xy,to_int_points,box_to_point
|
3 |
-
#ver-2024-11-18
|
4 |
-
def create_color_image(width, height, color=(255,255,255)):
|
5 |
-
if color == None:
|
6 |
-
color = (0,0,0)
|
7 |
-
|
8 |
-
if len(color )== 3:
|
9 |
-
mode ="RGB"
|
10 |
-
elif len(color )== 4:
|
11 |
-
mode ="RGBA"
|
12 |
-
|
13 |
-
img = Image.new(mode, (width, height), color)
|
14 |
-
return img
|
15 |
-
|
16 |
-
# deprecated
|
17 |
-
def fill_points(image,points,color=(255,255,255)):
|
18 |
-
return draw_points(image,points,fill=color)
|
19 |
-
|
20 |
-
def draw_points(image,points,outline=None,fill=None,width=1):
|
21 |
-
|
22 |
-
draw = ImageDraw.Draw(image)
|
23 |
-
int_points = [(int(x), int(y)) for x, y in points]
|
24 |
-
|
25 |
-
if outline is not None or fill is not None:
|
26 |
-
draw.polygon(int_points, outline=outline,fill=fill,width=width)
|
27 |
-
|
28 |
-
return image
|
29 |
-
|
30 |
-
def draw_box(image,box,outline=None,fill=None):
|
31 |
-
points = to_int_points(box_to_point(box))
|
32 |
-
return draw_points(image,points,outline,fill)
|
33 |
-
|
34 |
-
def from_numpy(numpy_array):
|
35 |
-
return Image.fromarray(numpy_array)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
models/hyper-hgr-random15.joblib
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:61faed39a9175818ce419775a85cc8ad3a32710f770a4f235221f280c3098876
|
3 |
+
size 14615903
|
models/hyper-hgr-random15_polynomial_features.joblib
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:29f0cb0ea2b44f42eac4270e913617693fcb41dca950ca86417f27f450169c71
|
3 |
+
size 361
|
models/hyper-hgr-random15_scaler.joblib
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:d64692adb1ab4593f035b7fcbd1d15ce64f6d0196046c9a639ac40a42355ae67
|
3 |
+
size 43881
|
models/hyper-hgr-random15_selectkbest.joblib
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:a150b731677290056735baf204c9abb0287676f5235e416740e9cdfa6a64fceb
|
3 |
+
size 29337
|
models/hyper-hgr-random45.joblib
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:b03b1ceb2629ddbbc52019fb8bf90279d211938da545bd0fce34575080ef70cf
|
3 |
+
size 14615903
|
models/hyper-hgr-random45_polynomial_features.joblib
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:29f0cb0ea2b44f42eac4270e913617693fcb41dca950ca86417f27f450169c71
|
3 |
+
size 361
|
models/hyper-hgr-random45_scaler.joblib
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:a7b8b0f3b8be8386ef5fb95ce41d7df962138fd894943c97cca2b2fada49d9d5
|
3 |
+
size 43881
|
models/hyper-hgr-random45_selectkbest.joblib
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:8b76c0e458602bf9a2a99e8293cf8b370aab3b6ba6daaf326df434a547504283
|
3 |
+
size 29337
|
models/hyper-hgr-random90.joblib
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:abf1f0844f355860e7d0a16c13900f52e30414b7a592b3e72e7083baceb32390
|
3 |
+
size 12316183
|
models/hyper-hgr-random90_polynomial_features.joblib
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:29f0cb0ea2b44f42eac4270e913617693fcb41dca950ca86417f27f450169c71
|
3 |
+
size 361
|
models/hyper-hgr-random90_scaler.joblib
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:c5086bcd611dc93afbbc2f48b98d34d955da0de4820c920f897830850b852048
|
3 |
+
size 43881
|
models/hyper-hgr-random90_selectkbest.joblib
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:6065c99e9919125705f0c7f5867c8dfa5d90869aff3cf035dbc3ddfb8431478d
|
3 |
+
size 29337
|
models/lgbm-optimizer_15_random.joblib
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:8a3c7a8f13513e82a58246e15d16117bca71eebaf42aca0b02da952b0945edc6
|
3 |
+
size 6815952
|
models/lgbm-optimizer_15_random_scaler.joblib
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:29d9c78081a6842483621fdde85114e65aed8e667787bf7e37e1acb8bad12149
|
3 |
+
size 2857
|
models/lgbm-optimizer_15dart_random.joblib
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:dd7347ddc3384d0f031602ba12d74dedd0ef642711bf52f38d575ef678c77765
|
3 |
+
size 5635161
|
models/lgbm-optimizer_15dart_random_scaler.joblib
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:be8bee58658969f1c8d94d00301ab7d2e0654f11a8a66cfaf7c71ce91f07074c
|
3 |
+
size 2841
|
models/lgbm-optimizer_45_random.joblib
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:f9bf95c6b1dd0008987431ec470ab92e039155d89aa72ed271f478a38fc4374e
|
3 |
+
size 6516617
|
models/lgbm-optimizer_45_random_scaler.joblib
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:701b13f7141d30b4ceec3e79b66f7e23da524e82c515e00120c3c229e6afbc58
|
3 |
+
size 3177
|
models/lgbm-optimizer_90_random.joblib
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:c18042d853049aef5d754b89cfd09ef2512303ec9b17326b99548287c29d8efe
|
3 |
+
size 6653496
|
models/lgbm-optimizer_90_random_scaler.joblib
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:c185a859763906f4a6fc67e8855228a20e6ab44aa54aad6460115b16db614e0a
|
3 |
+
size 3401
|
mp_estimate.py
ADDED
@@ -0,0 +1,253 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#2024-12-04 add forehead_chin_points_pair,estimate_rotatios
|
2 |
+
#formart is first,second,middle
|
3 |
+
#2024-12-05 deg to rad
|
4 |
+
#2024-12-06 get_feature_ratios_cordinate
|
5 |
+
#2024-12-08 create_detail_labels
|
6 |
+
#2024-12-14 ratio support 4point
|
7 |
+
horizontal_points_pair = [
|
8 |
+
[
|
9 |
+
"inner-eye",133,362,6
|
10 |
+
],
|
11 |
+
[
|
12 |
+
"outer-eye",33,263,168
|
13 |
+
],
|
14 |
+
[
|
15 |
+
"mouth",61,291,13
|
16 |
+
],
|
17 |
+
[
|
18 |
+
"eyeblow",105,334,9
|
19 |
+
],[
|
20 |
+
"nose",98,327,2
|
21 |
+
],[
|
22 |
+
"contour",143,372,6
|
23 |
+
],
|
24 |
+
[
|
25 |
+
"chin",32,262,200
|
26 |
+
], [
|
27 |
+
"cheek",123,352,5
|
28 |
+
], [
|
29 |
+
"cheek2",192,416,0
|
30 |
+
], [
|
31 |
+
"nose1",129,358,1
|
32 |
+
], [
|
33 |
+
"nose2",47,277,195
|
34 |
+
], [
|
35 |
+
"cheek3",206,426,2
|
36 |
+
], [
|
37 |
+
"cheek4",101,330,5
|
38 |
+
], [
|
39 |
+
"cheek5",153,380,6
|
40 |
+
]
|
41 |
+
]
|
42 |
+
def angle_between_points_and_x_axis(A, B):
|
43 |
+
"""
|
44 |
+
2点A, Bを結ぶ線分とx軸の正方向との角度を計算する
|
45 |
+
|
46 |
+
Args:
|
47 |
+
A: A点の座標 (x, y) のタプルまたはNumPy配列
|
48 |
+
B: B点の座標 (x, y) のタプルまたはNumPy配列
|
49 |
+
|
50 |
+
Returns:
|
51 |
+
角度(ラジアン)
|
52 |
+
"""
|
53 |
+
x = B[0] - A[0]
|
54 |
+
y = B[1] - A[1]
|
55 |
+
return np.arctan2(y, x)
|
56 |
+
|
57 |
+
vertical_points_pair=[
|
58 |
+
["forehead-chin",8,1,199]
|
59 |
+
]
|
60 |
+
#formart is first,second,third
|
61 |
+
feature_ratios_indices=[
|
62 |
+
["forehead",67,69,66],
|
63 |
+
["forehead",10,151,9],
|
64 |
+
["forehead",297,299,296],
|
65 |
+
#["forehead-chin",8,1,199],
|
66 |
+
#["middle-chin",168,199,2],
|
67 |
+
["middle",168,195,2],
|
68 |
+
["right",153,101,206],
|
69 |
+
["right2",133,47,129],
|
70 |
+
["left",380,330,426],
|
71 |
+
["left2",362,277,358],
|
72 |
+
["right-contour",143,123,192],
|
73 |
+
["left-contour",372,352,416],
|
74 |
+
["nose",4,1,2],
|
75 |
+
]
|
76 |
+
|
77 |
+
feature_angles_indices =[
|
78 |
+
["forehead1",9,6],
|
79 |
+
["forehead2",69,299],
|
80 |
+
["eyes1",133,362],
|
81 |
+
["eyes2",133,33],
|
82 |
+
["eyes3",362,263],
|
83 |
+
["nose1",6,2],
|
84 |
+
["nose1",98,327],
|
85 |
+
["nose1",2,1],
|
86 |
+
["nose1",1,6],
|
87 |
+
["lip",61,291],
|
88 |
+
["lip",0,17],
|
89 |
+
["jaw",152,199],
|
90 |
+
["jaw",194,418],
|
91 |
+
["cheek",118,214],
|
92 |
+
["cheek",347,434],
|
93 |
+
["contour",389,397],
|
94 |
+
["contour",127,172],
|
95 |
+
]
|
96 |
+
def get_feature_angles_cordinate(face_landmarks,angles=feature_angles_indices):
|
97 |
+
points = [get_normalized_cordinate(face_landmarks,i) for i in range(468)]
|
98 |
+
return get_feature_angles_cordinate_points(points,angles)
|
99 |
+
|
100 |
+
def get_feature_angles_cordinate_points(points,angles=feature_angles_indices):
|
101 |
+
cordinates=[]
|
102 |
+
result_angles = []
|
103 |
+
for indices in angles:
|
104 |
+
points_cordinate = get_points_by_indices(points,indices[1:])#first one is label
|
105 |
+
angle_rad =angle_between_points_and_x_axis(points_cordinate[0][:2],points_cordinate[1][:2])
|
106 |
+
result_angles.append(angle_rad)
|
107 |
+
cordinates.append(points_cordinate)
|
108 |
+
return cordinates,result_angles
|
109 |
+
|
110 |
+
def get_feature_ratios_cordinate(face_landmarks,ratios=feature_ratios_indices):
|
111 |
+
points = [get_normalized_cordinate(face_landmarks,i) for i in range(468)]
|
112 |
+
return get_feature_angles_cordinate_points(points,ratios)
|
113 |
+
|
114 |
+
def ratios_cordinates(cordinates):
|
115 |
+
|
116 |
+
distance_a = calculate_distance(cordinates[0],cordinates[1])
|
117 |
+
print(distance_a)
|
118 |
+
distance_b = calculate_distance(cordinates[-2],cordinates[-1])
|
119 |
+
print(distance_b)
|
120 |
+
if distance_a == 0 or distance_b == 0:
|
121 |
+
return 0
|
122 |
+
else:
|
123 |
+
return distance_a/distance_b
|
124 |
+
|
125 |
+
def get_feature_ratios_cordinate_points(points,ratios=feature_ratios_indices):
|
126 |
+
cordinates=[]
|
127 |
+
result_ratios = []
|
128 |
+
for indices in ratios:
|
129 |
+
points_cordinate = get_points_by_indices(points,indices[1:])#first one is label
|
130 |
+
result_ratios.append(ratios_cordinates(points_cordinate))
|
131 |
+
cordinates.append(points_cordinate)
|
132 |
+
return cordinates,result_ratios
|
133 |
+
|
134 |
+
|
135 |
+
#vertical-format
|
136 |
+
forehead_chin_points_pair=[
|
137 |
+
[
|
138 |
+
"forehead-chin",8,1,199
|
139 |
+
]
|
140 |
+
]
|
141 |
+
horizontal_contour_points_pair=[
|
142 |
+
[
|
143 |
+
"contour",143,6,372
|
144 |
+
]
|
145 |
+
]
|
146 |
+
import math
|
147 |
+
def calculate_distance(xy, xy2):
|
148 |
+
return math.sqrt((xy2[0] - xy[0])**2 + (xy2[1] - xy[1])**2)
|
149 |
+
|
150 |
+
def create_detail_labels(values,radian=False,pair_data=horizontal_points_pair):
|
151 |
+
assert len(values) == len(pair_data)
|
152 |
+
lines = []
|
153 |
+
for i,value in enumerate(values):
|
154 |
+
if radian:
|
155 |
+
value=math.degrees(value)
|
156 |
+
lines.append(f"{pair_data[i][0]} = {value:.2f}")
|
157 |
+
return "\n".join(lines)
|
158 |
+
|
159 |
+
import numpy as np
|
160 |
+
from mp_utils import get_normalized_cordinate
|
161 |
+
def estimate_horizontal(face_landmarks,pair_data = horizontal_points_pair):
|
162 |
+
points = [get_normalized_cordinate(face_landmarks,i) for i in range(468)]
|
163 |
+
return estimate_horizontal_points(points,pair_data)
|
164 |
+
|
165 |
+
def get_points_by_indices(face_landmark_points,indices):
|
166 |
+
points = [face_landmark_points[index] for index in indices]
|
167 |
+
return points
|
168 |
+
|
169 |
+
def normalized_to_pixel(cordinates,width,height):
|
170 |
+
pixel_point = [[pt[0]*width,pt[1]*height] for pt in cordinates]
|
171 |
+
return pixel_point
|
172 |
+
|
173 |
+
def estimate_horizontal_points(face_landmark_points,pair_data = horizontal_points_pair):
|
174 |
+
z_angles=[]
|
175 |
+
y_ratios = []
|
176 |
+
cordinates = []
|
177 |
+
for compare_point in pair_data:
|
178 |
+
points_cordinate = get_points_by_indices(face_landmark_points,compare_point[1:])#first one is label
|
179 |
+
cordinates.append(points_cordinate)
|
180 |
+
angle_rad =angle_between_points_and_x_axis(points_cordinate[0][:2],points_cordinate[1][:2])
|
181 |
+
#angle_deg = np.degrees(angle_rad)
|
182 |
+
z_angles.append(angle_rad)
|
183 |
+
right_distance = calculate_distance(points_cordinate[0],points_cordinate[2])
|
184 |
+
left_distance = calculate_distance(points_cordinate[1],points_cordinate[2])
|
185 |
+
y_ratios.append(left_distance/(right_distance+left_distance))
|
186 |
+
return z_angles,y_ratios,cordinates,pair_data
|
187 |
+
|
188 |
+
def estimate_vertical(face_landmarks,pair_data = vertical_points_pair):
|
189 |
+
points = [get_normalized_cordinate(face_landmarks,i) for i in range(468)]
|
190 |
+
return estimate_vertical_points(points,pair_data)
|
191 |
+
|
192 |
+
|
193 |
+
def estimate_rotations_v2(face_landmarker_result):
|
194 |
+
points = get_normalized_landmarks(face_landmarker_result.face_landmarks,True)
|
195 |
+
values1_text=estimate_rotations_point(points)
|
196 |
+
result3,ratios = get_feature_ratios_cordinate_points(points)
|
197 |
+
key_cordinates,angles = get_feature_angles_cordinate_points(points)
|
198 |
+
angles_str=[str(angle) for angle in angles]
|
199 |
+
ratios_str=[str(ratio) for ratio in ratios]
|
200 |
+
return f"{values1_text},{','.join(angles_str)},{','.join(ratios_str)}"
|
201 |
+
|
202 |
+
from mp_utils import get_normalized_landmarks
|
203 |
+
def estimate_rotations(face_landmarker_result):
|
204 |
+
points = get_normalized_landmarks(face_landmarker_result.face_landmarks,True)
|
205 |
+
return estimate_rotations_point(points)
|
206 |
+
def estimate_rotations_point(points):
|
207 |
+
z_angles,y_ratios,h_cordinates,_ =estimate_horizontal_points(points)
|
208 |
+
z_angle = np.mean(z_angles)
|
209 |
+
y_ratio = np.mean(y_ratios)
|
210 |
+
_,x_ratios,h_cordinates,_ =estimate_vertical_points(points)
|
211 |
+
x_ratio = np.mean(x_ratios)
|
212 |
+
|
213 |
+
x_angle,_,_,_ =estimate_vertical_points(points,forehead_chin_points_pair)
|
214 |
+
x_angle=np.mean(x_angle)
|
215 |
+
|
216 |
+
length_ratio = estimate_ratio(points)
|
217 |
+
|
218 |
+
result = f"{x_ratio:.6f},{y_ratio:.6f},{z_angle:.6f},{x_angle:.6f},{length_ratio:.6f}"
|
219 |
+
return result
|
220 |
+
|
221 |
+
def estimate_ratio(face_landmark_points,a_line=forehead_chin_points_pair,b_line=horizontal_contour_points_pair):
|
222 |
+
points_cordinate_a = get_points_by_indices(face_landmark_points,a_line[0][1:])#for campatible
|
223 |
+
points_cordinate_b = get_points_by_indices(face_landmark_points,b_line[0][1:])
|
224 |
+
|
225 |
+
distance_a = calculate_distance(points_cordinate_a[0],points_cordinate_a[2])
|
226 |
+
distance_b = calculate_distance(points_cordinate_b[0],points_cordinate_b[2])
|
227 |
+
if distance_a == 0 or distance_b == 0:
|
228 |
+
return 0
|
229 |
+
else:
|
230 |
+
return distance_a/distance_b
|
231 |
+
|
232 |
+
def estimate_vertical_points(face_landmarks,pair_data = vertical_points_pair):
|
233 |
+
angles = []
|
234 |
+
ratios = []
|
235 |
+
cordinates = []
|
236 |
+
for compare_point in pair_data:
|
237 |
+
points_cordinate = get_points_by_indices(face_landmarks,compare_point[1:])#first one is label
|
238 |
+
cordinates.append(points_cordinate)
|
239 |
+
angle_rad =angle_between_points_and_x_axis(points_cordinate[0][:2],points_cordinate[2][:2])
|
240 |
+
#angle_deg = np.degrees(angle_rad)
|
241 |
+
angles.append(angle_rad)
|
242 |
+
up_distance = calculate_distance(points_cordinate[0],points_cordinate[1])
|
243 |
+
down_distance = calculate_distance(points_cordinate[1],points_cordinate[2])
|
244 |
+
ratios.append(down_distance/(down_distance+up_distance))
|
245 |
+
return angles,ratios,cordinates,pair_data
|
246 |
+
def mean_std_label(values,radian=False):
|
247 |
+
mean_value = np.mean(values)
|
248 |
+
std_value = np.std(values)
|
249 |
+
if radian:
|
250 |
+
mean_value = math.degrees(mean_value)
|
251 |
+
std_value = math.degrees(std_value)
|
252 |
+
value_text = f"mean:{mean_value:.3f} std:{std_value:.3f}"
|
253 |
+
return value_text
|
mp_utils.py
CHANGED
@@ -10,11 +10,16 @@ import numpy as np
|
|
10 |
# 2024-11-27 -extract_landmark :add args
|
11 |
# add get_pixel_xyz
|
12 |
# 2024-11-28 add get_normalized_xyz
|
|
|
|
|
13 |
def calculate_distance(p1, p2):
|
14 |
"""
|
15 |
|
16 |
"""
|
17 |
return math.sqrt((p2[0] - p1[0])**2 + (p2[1] - p1[1])**2)
|
|
|
|
|
|
|
18 |
def to_int_points(points):
|
19 |
ints=[]
|
20 |
for pt in points:
|
@@ -102,6 +107,24 @@ def get_normalized_xyz(face_landmarks_list,index):
|
|
102 |
z=face_landmarks_list[0][index].z
|
103 |
return x,y,z
|
104 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
105 |
# z is normalized
|
106 |
def get_pixel_xyz(face_landmarks_list,landmark,width,height):
|
107 |
point = get_normalized_cordinate(face_landmarks_list,landmark)
|
@@ -137,4 +160,5 @@ def extract_landmark(image_data,model_path="face_landmarker.task",min_face_detec
|
|
137 |
else:
|
138 |
mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=np.asarray(image_data))
|
139 |
face_landmarker_result = landmarker.detect(mp_image)
|
140 |
-
return mp_image,face_landmarker_result
|
|
|
|
10 |
# 2024-11-27 -extract_landmark :add args
|
11 |
# add get_pixel_xyz
|
12 |
# 2024-11-28 add get_normalized_xyz
|
13 |
+
# 2024-11-30 add get_normalized_landmarks,sort_triangles_by_depth
|
14 |
+
# 2024-12-04 add get_normalized_landmarks args
|
15 |
def calculate_distance(p1, p2):
|
16 |
"""
|
17 |
|
18 |
"""
|
19 |
return math.sqrt((p2[0] - p1[0])**2 + (p2[1] - p1[1])**2)
|
20 |
+
|
21 |
+
|
22 |
+
|
23 |
def to_int_points(points):
|
24 |
ints=[]
|
25 |
for pt in points:
|
|
|
107 |
z=face_landmarks_list[0][index].z
|
108 |
return x,y,z
|
109 |
|
110 |
+
def get_normalized_landmarks(face_landmarks_list,recentering=False,recentering_index=4,z_multiply=0.8):
|
111 |
+
cordinates = [get_normalized_xyz(face_landmarks_list,i) for i in range(0,468)]
|
112 |
+
if recentering:
|
113 |
+
normalized_center_point = cordinates[recentering_index]
|
114 |
+
offset_x = normalized_center_point[0]
|
115 |
+
offset_y = normalized_center_point[1]
|
116 |
+
|
117 |
+
#need aspect?
|
118 |
+
cordinates = [[point[0]-offset_x,point[1]-offset_y,point[2]*z_multiply] for point in cordinates]
|
119 |
+
|
120 |
+
return cordinates
|
121 |
+
|
122 |
+
|
123 |
+
|
124 |
+
def sort_triangles_by_depth(landmark_points,mesh_triangle_indices):
|
125 |
+
assert len(landmark_points) == 468
|
126 |
+
mesh_triangle_indices.sort(key=lambda triangle: sum(landmark_points[index][2] for index in triangle) / len(triangle)
|
127 |
+
,reverse=True)
|
128 |
# z is normalized
|
129 |
def get_pixel_xyz(face_landmarks_list,landmark,width,height):
|
130 |
point = get_normalized_cordinate(face_landmarks_list,landmark)
|
|
|
160 |
else:
|
161 |
mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=np.asarray(image_data))
|
162 |
face_landmarker_result = landmarker.detect(mp_image)
|
163 |
+
return mp_image,face_landmarker_result
|
164 |
+
|