Akjava commited on
Commit
4c7255e
·
1 Parent(s): 661ec13
app.py CHANGED
@@ -6,16 +6,17 @@ import json
6
  import os
7
  import time
8
  import mp_box
9
- from mp_utils import get_pixel_cordinate_list,extract_landmark,get_pixel_cordinate,get_pixel_xyz
 
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 estimate_head_pose,draw_head_pose
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
- camera_fov,double_check_offset_center,
87
- draw_base_model,fit_base_model,
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,image_indices[0],w,h)#nose tip
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
- image = mp_box.draw_landmarks_on_image(face_landmarker_result,image)
174
- cv2_image = pil_to_bgr_image(image)
175
-
176
- chin_distance = calculate_distance(cordinates[0],cordinates[1])
177
- #trying detect pnp from same pose,but seeems not working
178
- #fitted_cordinates = fit_cordinates(model_cordinates,cordinates[0][0],cordinates[0][1],chin_distance)
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
- focal_length: float = calculate_distance(cordinates[0],cordinates[1])
193
- focal_length = focal_length*camera_fov
 
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
- scaled_translation_vector =(translation_vector[0]*1024,translation_vector[1]*1024,translation_vector[2]*1024)
 
243
  #scaled_translation_vector = (-512,-512,-1024)
244
- if draw_mediapipe_result:
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 {order} (degrees): {euler_angles}"
251
- return label
 
 
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
- if first_pnp!="None":
261
- if first_pnp == "EPNP":
262
- flags = cv2.SOLVEPNP_EPNP
263
- elif first_pnp == "ITERATIVE":
264
- flags = cv2.SOLVEPNP_ITERATIVE
265
- elif first_pnp == "IPPE":
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
- if draw_base_model:
313
- if fit_base_model:
314
- im_with_pose=plot_model(im_with_pose,cordinates[0][0],cordinates[0][1],chin_distance)
315
- else:
316
- im_with_pose=plot_model(im_with_pose)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
317
 
318
- return cv2.cvtColor(im_with_pose,cv2.COLOR_BGR2RGB),result_label,mediapipe_first_text,mediapipe_second_text
 
 
 
 
 
 
 
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
- camera_fov = gr.Slider(info="not effect mediapipe,nose-chin x multiply",
373
- label="Multiply value",
374
- minimum=0.1,
375
- maximum=2.0,
376
- step=0.01,
377
- value=1.2)
378
- double_check_offset_center = gr.Checkbox(label="offset center point",value=True,info="move center and detect again(usually more accurate)")
379
- z_multiply = gr.Slider(info="nose depth",
380
- label="Z-Multiply",
 
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
- final_iterative = gr.Checkbox(label="PnP final iterative",value=False,info="sometime good")
393
- debug_process = gr.Checkbox(label="Debug Process",value=False)
394
- draw_mediapipe_mesh = gr.Checkbox(label="Draw mediapipe mesh",value=False)
395
- draw_mediapipe_result = gr.Checkbox(label="Draw mediapipe result",value=False)
396
- plot_button = gr.Button("Plot Model", elem_id="run_button")
 
397
 
398
  with gr.Column():
399
  result_image = gr.Image(height=760,label="Result", elem_id="output-animation",image_mode='RGB')
400
- result_text = gr.Textbox(label="cv2 result")
401
- mediapipe_first_text = gr.Textbox(label="first mediapipe result")
402
- mediapipe_last_text = gr.Textbox(label="2nd or last mediapipe result")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
403
 
404
  btn.click(fn=process_images, inputs=[image,base_image,
405
- camera_fov,double_check_offset_center,
406
- draw_base_model,fit_base_model,
407
- first_pnp,second_refine,final_iterative,debug_process,draw_mediapipe_mesh,draw_mediapipe_result
408
- ],outputs=[result_image,result_text,mediapipe_first_text,mediapipe_last_text] ,api_name='infer')
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 Face-Pose Estimation plus OpenCV
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
- A new tool or method is needed to achieve improved accuracy."<br>
12
- OpenCV:I've tried it out, but there's still room for improvement in how the features work together.<br>
13
- TODO:change base-model
 
 
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
+