iniit
Browse files- .gitattributes +1 -0
- .gitignore +2 -0
- app.py +250 -0
- demo_footer.html +3 -0
- demo_header.html +17 -0
- demo_tools.html +4 -0
- draw_landmarks68.py +516 -0
- examples/00002200.jpg +0 -0
- examples/00003245_00.jpg +0 -0
- examples/00004200.jpg +0 -0
- examples/00005259.jpg +0 -0
- examples/00018022.jpg +0 -0
- examples/img-above.jpg +0 -0
- examples/img-below.jpg +0 -0
- examples/img-side.jpg +0 -0
- face_landmarker.task +3 -0
- face_landmarker.task.txt +8 -0
- glibvision/cv2_utils.py +49 -0
- glibvision/glandmark_utils.py +48 -0
- landmarks68_utils.py +70 -0
- mp_box.py +133 -0
- mp_constants.py +320 -0
- mp_utils.py +94 -0
- requirements.txt +5 -0
.gitattributes
CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
*.task filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
__pycache__
|
2 |
+
files
|
app.py
ADDED
@@ -0,0 +1,250 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import spaces
|
2 |
+
import gradio as gr
|
3 |
+
import subprocess
|
4 |
+
from PIL import Image
|
5 |
+
import json
|
6 |
+
import os
|
7 |
+
import time
|
8 |
+
|
9 |
+
import mp_box
|
10 |
+
import draw_landmarks68
|
11 |
+
import landmarks68_utils
|
12 |
+
import io
|
13 |
+
import numpy as np
|
14 |
+
'''
|
15 |
+
Face landmark detection based Face Detection.
|
16 |
+
https://ai.google.dev/edge/mediapipe/solutions/vision/face_landmarker
|
17 |
+
from model card
|
18 |
+
https://storage.googleapis.com/mediapipe-assets/MediaPipe%20BlazeFace%20Model%20Card%20(Short%20Range).pdf
|
19 |
+
Licensed Apache License, Version 2.0
|
20 |
+
Train with google's dataset(more detail see model card)
|
21 |
+
|
22 |
+
'''
|
23 |
+
|
24 |
+
dir_name ="files"
|
25 |
+
passed_time = 60*60
|
26 |
+
def clear_old_files(dir,passed_time):
|
27 |
+
try:
|
28 |
+
files = os.listdir(dir)
|
29 |
+
current_time = time.time()
|
30 |
+
for file in files:
|
31 |
+
file_path = os.path.join(dir,file)
|
32 |
+
|
33 |
+
ctime = os.stat(file_path).st_ctime
|
34 |
+
diff = current_time - ctime
|
35 |
+
#print(f"ctime={ctime},current_time={current_time},passed_time={passed_time},diff={diff}")
|
36 |
+
if diff > passed_time:
|
37 |
+
os.remove(file_path)
|
38 |
+
except:
|
39 |
+
print("maybe still gallery using error")
|
40 |
+
|
41 |
+
def get_image_id(image,length=32):
|
42 |
+
buffer = io.BytesIO()
|
43 |
+
image.save(buffer, format='PNG')
|
44 |
+
hash_object = hashlib.sha256(buffer.getvalue())
|
45 |
+
hex_dig = hash_object.hexdigest()
|
46 |
+
unique_id = hex_dig[:length]
|
47 |
+
return unique_id
|
48 |
+
|
49 |
+
def save_image(image,extension="jpg"):
|
50 |
+
id = get_image_id(image)
|
51 |
+
os.makedirs(dir_name,exist_ok=True)
|
52 |
+
file_path = f"{dir_name}/{id}.{extension}"
|
53 |
+
|
54 |
+
image.save(file_path)
|
55 |
+
return file_path
|
56 |
+
|
57 |
+
def picker_color_to_rgba(picker_color):
|
58 |
+
color_value = picker_color.strip("rgba()").split(",")
|
59 |
+
color_value[0] = int(float(color_value[0]))
|
60 |
+
color_value[1] = int(float(color_value[1]))
|
61 |
+
color_value[2] = int(float(color_value[2]))
|
62 |
+
color_value[3] = int(float(color_value[3]))
|
63 |
+
return color_value
|
64 |
+
|
65 |
+
#@spaces.GPU(duration=120)
|
66 |
+
def process_images(image,progress=gr.Progress(track_tqdm=True)):
|
67 |
+
if image == None:
|
68 |
+
raise gr.Error("Need Image")
|
69 |
+
|
70 |
+
progress(0, desc="Start Mediapipe")
|
71 |
+
|
72 |
+
boxes,mp_image,face_landmarker_result = mp_box.mediapipe_to_box(image)
|
73 |
+
annotated_image,bbox,landmark_points = draw_landmarks68.draw_landmarks_on_image(image,face_landmarker_result)
|
74 |
+
landmark_list = draw_landmarks68.convert_to_landmark_group_json(landmark_points)
|
75 |
+
|
76 |
+
annotations = []
|
77 |
+
galleries = []
|
78 |
+
|
79 |
+
def append(mask,label):
|
80 |
+
file_path = save_image(mask)
|
81 |
+
galleries.append((file_path,label))
|
82 |
+
annotations.append((np.array(mask.convert("1")),label))
|
83 |
+
|
84 |
+
def fill_points(points,base_image=None):
|
85 |
+
if base_image == None:
|
86 |
+
base_image = landmarks68_utils.create_color_image(image.width,image.height,(0,0,0))
|
87 |
+
landmarks68_utils.fill_points(base_image,points)
|
88 |
+
return base_image
|
89 |
+
|
90 |
+
# TODO support type
|
91 |
+
left_eye_points = landmarks68_utils.get_landmark_points(landmark_list,landmarks68_utils.PARTS_LEFT_EYE)
|
92 |
+
right_eye_points = landmarks68_utils.get_landmark_points(landmark_list,landmarks68_utils.PARTS_RIGHT_EYE)
|
93 |
+
eyes_mask = fill_points(left_eye_points)
|
94 |
+
eyes_mask = fill_points(right_eye_points,eyes_mask)
|
95 |
+
append(eyes_mask,"eyes")
|
96 |
+
|
97 |
+
|
98 |
+
upper_lip_points = landmarks68_utils.get_landmark_points(landmark_list,landmarks68_utils.PARTS_UPPER_LIP)
|
99 |
+
upper_lip_mask = fill_points(upper_lip_points)
|
100 |
+
append(upper_lip_mask,"upper-lip")
|
101 |
+
|
102 |
+
lower_lip_points = landmarks68_utils.get_landmark_points(landmark_list,landmarks68_utils.PARTS_LOWER_LIP)
|
103 |
+
lower_lip_mask = fill_points(lower_lip_points)
|
104 |
+
append(lower_lip_mask,"lower-lip")
|
105 |
+
|
106 |
+
inner_mouth_points = landmarks68_utils.get_innner_mouth_points(landmark_list)
|
107 |
+
inner_mouth_mask = fill_points(inner_mouth_points)
|
108 |
+
append(inner_mouth_mask,"inner-mouth")
|
109 |
+
|
110 |
+
|
111 |
+
# TODO support type
|
112 |
+
contour_points = landmarks68_utils.get_landmark_points(landmark_list,landmarks68_utils.PARTS_CONTOUR)
|
113 |
+
|
114 |
+
contour_points=landmarks68_utils.get_face_points(landmark_list)
|
115 |
+
|
116 |
+
contour_mask = fill_points(contour_points)
|
117 |
+
append(contour_mask,"contour")
|
118 |
+
|
119 |
+
mixed = Image.composite(eyes_mask,upper_lip_mask,eyes_mask.convert("L"))
|
120 |
+
mixed = Image.composite(mixed,lower_lip_mask,mixed.convert("L"))
|
121 |
+
mixed = Image.composite(mixed,inner_mouth_mask,mixed.convert("L"))
|
122 |
+
append(mixed,"mixed")
|
123 |
+
|
124 |
+
return [image,annotations],galleries
|
125 |
+
|
126 |
+
|
127 |
+
def write_file(file_path,text):
|
128 |
+
with open(file_path, 'w', encoding='utf-8') as f:
|
129 |
+
f.write(text)
|
130 |
+
|
131 |
+
def read_file(file_path):
|
132 |
+
"""read the text of target file
|
133 |
+
"""
|
134 |
+
with open(file_path, 'r', encoding='utf-8') as f:
|
135 |
+
content = f.read()
|
136 |
+
|
137 |
+
return content
|
138 |
+
|
139 |
+
css="""
|
140 |
+
#col-left {
|
141 |
+
margin: 0 auto;
|
142 |
+
max-width: 640px;
|
143 |
+
}
|
144 |
+
#col-right {
|
145 |
+
margin: 0 auto;
|
146 |
+
max-width: 640px;
|
147 |
+
}
|
148 |
+
.grid-container {
|
149 |
+
display: flex;
|
150 |
+
align-items: center;
|
151 |
+
justify-content: center;
|
152 |
+
gap:10px
|
153 |
+
}
|
154 |
+
|
155 |
+
.image {
|
156 |
+
width: 128px;
|
157 |
+
height: 128px;
|
158 |
+
object-fit: cover;
|
159 |
+
}
|
160 |
+
|
161 |
+
.text {
|
162 |
+
font-size: 16px;
|
163 |
+
}
|
164 |
+
"""
|
165 |
+
|
166 |
+
#css=css,
|
167 |
+
|
168 |
+
import hashlib
|
169 |
+
|
170 |
+
def text_to_sha256(text):
|
171 |
+
text_bytes = text.encode('utf-8')
|
172 |
+
hash_object = hashlib.sha256()
|
173 |
+
hash_object.update(text_bytes)
|
174 |
+
sha256_hex = hash_object.hexdigest()
|
175 |
+
return sha256_hex
|
176 |
+
|
177 |
+
|
178 |
+
def create_json_download(text):
|
179 |
+
file_id = f"{dir_name}/landmark_{text_to_sha256(text)[:32]}.json"
|
180 |
+
write_file(file_id,text)
|
181 |
+
# try to save
|
182 |
+
return file_id
|
183 |
+
|
184 |
+
with gr.Blocks(css=css, elem_id="demo-container") as demo:
|
185 |
+
with gr.Column():
|
186 |
+
gr.HTML(read_file("demo_header.html"))
|
187 |
+
gr.HTML(read_file("demo_tools.html"))
|
188 |
+
with gr.Row():
|
189 |
+
with gr.Column():
|
190 |
+
image = gr.Image(height=800,sources=['upload','clipboard'],image_mode='RGB',elem_id="image_upload", type="pil", label="Upload")
|
191 |
+
with gr.Row(elem_id="prompt-container", equal_height=False):
|
192 |
+
with gr.Row():
|
193 |
+
btn = gr.Button("Create Landmark 68 Mask", elem_id="run_button",variant="primary")
|
194 |
+
|
195 |
+
with gr.Accordion(label="Advanced Settings", open=False):
|
196 |
+
with gr.Row( equal_height=True):
|
197 |
+
draw_number = gr.Checkbox(label="draw Number")
|
198 |
+
|
199 |
+
font_scale = gr.Slider(
|
200 |
+
label="Font Scale",
|
201 |
+
minimum=0.1,
|
202 |
+
maximum=2,
|
203 |
+
step=0.1,
|
204 |
+
value=0.5)
|
205 |
+
|
206 |
+
text_color = gr.ColorPicker(value="rgba(200,200,200,1)",label="text color")
|
207 |
+
#square_shape = gr.Checkbox(label="Square shape")
|
208 |
+
with gr.Row( equal_height=True):
|
209 |
+
|
210 |
+
line_color = gr.ColorPicker(value="rgba(0,0,255,1)",label="line color")
|
211 |
+
line_size = gr.Slider(
|
212 |
+
label="Line Size",
|
213 |
+
minimum=0,
|
214 |
+
maximum=20,
|
215 |
+
step=1,
|
216 |
+
value=1)
|
217 |
+
with gr.Row( equal_height=True):
|
218 |
+
dot_color = gr.ColorPicker(value="rgba(255,0,0,1)",label="dot color")
|
219 |
+
dot_size = gr.Slider(
|
220 |
+
label="Dot Size",
|
221 |
+
minimum=0,
|
222 |
+
maximum=40,
|
223 |
+
step=1,
|
224 |
+
value=3)
|
225 |
+
with gr.Row( equal_height=True):
|
226 |
+
box_color = gr.ColorPicker(value="rgba(200,200,200,1)",label="box color")
|
227 |
+
box_size = gr.Slider(
|
228 |
+
label="Box Size",
|
229 |
+
minimum=0,
|
230 |
+
maximum=20,
|
231 |
+
step=1,
|
232 |
+
value=1)
|
233 |
+
with gr.Row( equal_height=True):
|
234 |
+
json_format = gr.Radio(choices=["raw","face-detection"],value="face-detection",label="json-output format")
|
235 |
+
|
236 |
+
with gr.Column():
|
237 |
+
image_out = gr.AnnotatedImage(label="Output", elem_id="output-img")
|
238 |
+
image_gallery = gr.Gallery(label="masks",preview=True)
|
239 |
+
#download_button.click(fn=json_download,inputs=text_out,outputs=download_button)
|
240 |
+
|
241 |
+
|
242 |
+
btn.click(fn=process_images, inputs=[image],outputs=[image_out,image_gallery] ,api_name='infer')
|
243 |
+
gr.Examples(
|
244 |
+
examples =["examples/00003245_00.jpg","examples/00004200.jpg","examples/00002200.jpg","examples/00005259.jpg","examples/00018022.jpg","examples/img-above.jpg","examples/img-below.jpg","examples/img-side.jpg"],
|
245 |
+
inputs=[image]
|
246 |
+
)
|
247 |
+
gr.HTML(read_file("demo_footer.html"))
|
248 |
+
|
249 |
+
if __name__ == "__main__":
|
250 |
+
demo.launch()
|
demo_footer.html
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
<div>
|
2 |
+
<P> Images are generated with <a href="https://huggingface.co/black-forest-labs/FLUX.1-schnell">FLUX.1-schnell</a> and licensed under <a href="http://www.apache.org/licenses/LICENSE-2.0">the Apache 2.0 License</a>
|
3 |
+
</div>
|
demo_header.html
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<div style="text-align: center;">
|
2 |
+
<h1>
|
3 |
+
Mediapipe 68-points facial landmark
|
4 |
+
</h1>
|
5 |
+
<div class="grid-container">
|
6 |
+
<img src="https://akjava.github.io/AIDiagramChatWithVoice-FaceCharacter/webp/128/00191245_09_00002200.webp" alt="Mediapipe Face Detection" class="image">
|
7 |
+
|
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 |
+
One of json format is from MIT licensed <a href="https://github.com/ageitgey/face_recognition">face_recognition</a><br>
|
11 |
+
I should clarify because it is confusing: I'm not using dlib's non-MIT licensed 68-point model at all.<br>
|
12 |
+
Contour points are not point-to-point mapping,because when face rotated some point move inside.<br>
|
13 |
+
Most of points are always divided equal for balancing when face rotated<br>
|
14 |
+
</p>
|
15 |
+
</div>
|
16 |
+
|
17 |
+
</div>
|
demo_tools.html
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<div style="text-align: center;">
|
2 |
+
<p><a href="https://huggingface.co/spaces/Akjava/mediapipe-face-detect">Mediapipe Face detector</a></p>
|
3 |
+
<p></p>
|
4 |
+
</div>
|
draw_landmarks68.py
ADDED
@@ -0,0 +1,516 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import mediapipe as mp
|
3 |
+
from mediapipe.tasks import python
|
4 |
+
from mediapipe.tasks.python import vision
|
5 |
+
from mediapipe.framework.formats import landmark_pb2
|
6 |
+
from mediapipe import solutions
|
7 |
+
import numpy as np
|
8 |
+
import time
|
9 |
+
import cv2
|
10 |
+
import argparse
|
11 |
+
import os
|
12 |
+
import math
|
13 |
+
|
14 |
+
# modified in gradio
|
15 |
+
|
16 |
+
from mp_constants import *
|
17 |
+
from mp_utils import divide_line_to_points,points_to_bbox,expand_bbox
|
18 |
+
|
19 |
+
import logging
|
20 |
+
|
21 |
+
# for share lib,TODO make module
|
22 |
+
#import sys
|
23 |
+
#sys.path.append("C:\\Users\\owner\\Documents\\pythons\\glibvision")
|
24 |
+
from glibvision.glandmark_utils import bbox_to_glandmarks,convert_to_landmark_group_json
|
25 |
+
from glibvision.cv2_utils import draw_bbox,plot_points,set_plot_text
|
26 |
+
|
27 |
+
def parse_arguments():
|
28 |
+
"""
|
29 |
+
引数
|
30 |
+
|
31 |
+
"""
|
32 |
+
parser = argparse.ArgumentParser(
|
33 |
+
description="draw 68 points"
|
34 |
+
)
|
35 |
+
parser.add_argument(
|
36 |
+
"--input_file","-i",required=True,help="Input file"
|
37 |
+
)
|
38 |
+
parser.add_argument(
|
39 |
+
"--model_path","-m",default="face_landmarker.task",help="model path"
|
40 |
+
)
|
41 |
+
parser.add_argument(
|
42 |
+
"--save_glandmark","-g",action="store_true",help="save godot-landmark json"
|
43 |
+
)
|
44 |
+
parser.add_argument(
|
45 |
+
"--save_group_landmark","-landmark",action="store_true",help="save group-landmark json"
|
46 |
+
)
|
47 |
+
return parser.parse_args()
|
48 |
+
|
49 |
+
|
50 |
+
|
51 |
+
|
52 |
+
|
53 |
+
def draw_landmarks_on_image(rgb_image, detection_result,draw_number=True,font_scale=0.5,text_color=(200,200,200),dot_size=3,dot_color=(255,0,0),line_size=1,line_color=(0,0,355),box_size=1,box_color=(200,200,200)):
|
54 |
+
#print(f"dot_size={dot_size},dot_color={dot_color},line_size={line_size},line_color={line_color}")
|
55 |
+
image_width,iamge_height = rgb_image.size
|
56 |
+
face_landmarks_list = detection_result.face_landmarks
|
57 |
+
annotated_image = np.copy(rgb_image)
|
58 |
+
|
59 |
+
def get_cordinate(index):
|
60 |
+
x=face_landmarks_list[0][index].x
|
61 |
+
y=face_landmarks_list[0][index].y
|
62 |
+
return x,y
|
63 |
+
|
64 |
+
def get_distance(x1,y1,x2,y2):
|
65 |
+
return math.sqrt((x2 - x1)**2 + (y2 - y1)**2)
|
66 |
+
|
67 |
+
def get_centers():
|
68 |
+
center_indices =[
|
69 |
+
#(POINT_LEFT_HEAD_OUTER,POINT_RIGHT_HEAD_OUTER,POINT_FOREHEAD_TOP),
|
70 |
+
#(POINT_LEFT_HEAD_OUTER,POINT_RIGHT_HEAD_OUTER,POINT_CHIN_BOTTOM),
|
71 |
+
[POINT_NOSE_CENTER_MIDDLE],
|
72 |
+
#[POINT_LOWER_LIP_CENTER_BOTTOM]
|
73 |
+
#(POINT_UPPER_LIP_CENTER_BOTTOM,POINT_LOWER_LIP_CENTER_TOP)
|
74 |
+
]
|
75 |
+
centers = []
|
76 |
+
for indices in center_indices:
|
77 |
+
total_x = 0
|
78 |
+
total_y = 0
|
79 |
+
for index in indices:
|
80 |
+
x,y = get_cordinate(index)
|
81 |
+
total_x+=x
|
82 |
+
total_y+=y
|
83 |
+
centers.append ((total_x/len(indices),total_y/len(indices)))
|
84 |
+
return centers
|
85 |
+
|
86 |
+
centers = get_centers()
|
87 |
+
for center in centers:
|
88 |
+
center_x,center_y = center
|
89 |
+
|
90 |
+
pt = int(center_x*image_width),int(center_y*iamge_height)
|
91 |
+
|
92 |
+
#cv2.circle(annotated_image,pt,20,(0,0,255),-1)
|
93 |
+
|
94 |
+
def get_closed_center(x,y):
|
95 |
+
closed = None
|
96 |
+
closed_distance = 0
|
97 |
+
for center in centers:
|
98 |
+
distance = get_distance(center[0],center[1],x,y)
|
99 |
+
if closed == None:
|
100 |
+
closed = center
|
101 |
+
closed_distance = distance
|
102 |
+
else:
|
103 |
+
if distance<closed_distance:
|
104 |
+
closed_distance = distance
|
105 |
+
closed = center
|
106 |
+
return closed
|
107 |
+
|
108 |
+
|
109 |
+
#landmark is [index-upper,index-lower]
|
110 |
+
def get_mean_point(landmark,width=image_width,height=iamge_height):
|
111 |
+
xs=[]
|
112 |
+
ys=[]
|
113 |
+
for index in landmark:
|
114 |
+
x,y = get_cordinate(index) #inner cordinate
|
115 |
+
xs.append(x)
|
116 |
+
ys.append(y)
|
117 |
+
|
118 |
+
return int(np.mean(xs)*width),int(np.mean(ys)*height)
|
119 |
+
|
120 |
+
def get_cordinate_point(landmark,width=image_width,height=iamge_height):
|
121 |
+
point = get_cordinate(landmark)
|
122 |
+
|
123 |
+
return int(point[0]*width),int(point[1]*height)
|
124 |
+
# TODO rename and explain this is for contour choose most outer point
|
125 |
+
def get_point(landmark,width=image_width,height=iamge_height):
|
126 |
+
xs=[]
|
127 |
+
ys=[]
|
128 |
+
|
129 |
+
|
130 |
+
def get_outer_point(indexes):
|
131 |
+
outer_point = None
|
132 |
+
max_distance = None
|
133 |
+
if len(indexes) == 0:
|
134 |
+
return None
|
135 |
+
|
136 |
+
ratio = 0.5
|
137 |
+
x,y = get_cordinate(indexes[-1]) #on contour 3 lines outer,center,inner cordinate
|
138 |
+
|
139 |
+
#x,y = get_cordinate(indexes[0])
|
140 |
+
center_x,center_y = get_closed_center(x,y)
|
141 |
+
x-=(center_x-x)*ratio
|
142 |
+
y-=(center_y-y)*ratio
|
143 |
+
|
144 |
+
outer_x = x
|
145 |
+
outer_y = y
|
146 |
+
|
147 |
+
for index in indexes:
|
148 |
+
x,y = get_cordinate(index)
|
149 |
+
|
150 |
+
distance = get_distance(outer_x,outer_y,x,y)
|
151 |
+
#print(f"{distance} index={index} x={x},y={y}")
|
152 |
+
if outer_point == None:
|
153 |
+
outer_point = (x,y)
|
154 |
+
max_distance = distance
|
155 |
+
else:
|
156 |
+
if distance<max_distance:
|
157 |
+
outer_point = (x,y)
|
158 |
+
return outer_point
|
159 |
+
|
160 |
+
|
161 |
+
|
162 |
+
for group in landmark:
|
163 |
+
outer_point = get_outer_point(group)
|
164 |
+
xs.append(outer_point[0])
|
165 |
+
ys.append(outer_point[1])
|
166 |
+
|
167 |
+
|
168 |
+
return int(np.mean(xs)*width),int(np.mean(ys)*height)
|
169 |
+
|
170 |
+
# Loop through the detected faces to visualize.
|
171 |
+
for idx in range(len(face_landmarks_list)):
|
172 |
+
face_landmarks = face_landmarks_list[idx]
|
173 |
+
|
174 |
+
# Draw the face landmarks. #something change format
|
175 |
+
face_landmarks_proto = landmark_pb2.NormalizedLandmarkList()
|
176 |
+
face_landmarks_proto.landmark.extend([
|
177 |
+
landmark_pb2.NormalizedLandmark(x=landmark.x, y=landmark.y, z=landmark.z) for landmark in face_landmarks
|
178 |
+
])
|
179 |
+
|
180 |
+
|
181 |
+
def draw_sets(draw_set,color=(0,255,0)):
|
182 |
+
solutions.drawing_utils.draw_landmarks(
|
183 |
+
image=annotated_image,
|
184 |
+
landmark_list=face_landmarks_proto,
|
185 |
+
connections=draw_set,
|
186 |
+
landmark_drawing_spec=None,
|
187 |
+
connection_drawing_spec=mp.solutions.drawing_styles.DrawingSpec(color=color, thickness=1 ))
|
188 |
+
def draw_triangle(index1,index2,index3):
|
189 |
+
draw_sets({(index1,index2),(index2,index3),(index3,index1)})
|
190 |
+
|
191 |
+
def draw_lines(array,color=(0,0,128)):
|
192 |
+
my_set = set()
|
193 |
+
for i in range(len(array)-1):
|
194 |
+
v = (array[i],array[i+1])
|
195 |
+
my_set.add(v)
|
196 |
+
draw_sets(my_set,color)
|
197 |
+
|
198 |
+
def convert_to_box(face_landmarks_list,indices,w=1024,h=1024):
|
199 |
+
x1=0
|
200 |
+
y1=0
|
201 |
+
x2=w
|
202 |
+
y2=h
|
203 |
+
for index in indices:
|
204 |
+
x=min(w,max(0,(face_landmarks_list[0][index].x*w)))
|
205 |
+
y=min(h,max(0,(face_landmarks_list[0][index].y*h)))
|
206 |
+
if x>x1:
|
207 |
+
x1=x
|
208 |
+
if y>y1:
|
209 |
+
y1=y
|
210 |
+
|
211 |
+
if x<x2:
|
212 |
+
x2=x
|
213 |
+
if y<y2:
|
214 |
+
y2=y
|
215 |
+
|
216 |
+
return [x1,y1,x2-x1,y2-y1]
|
217 |
+
|
218 |
+
|
219 |
+
my_set ={(362,382),(382,398),(398,362)}
|
220 |
+
my_set = mp.solutions.face_mesh.FACEMESH_RIGHT_EYE
|
221 |
+
|
222 |
+
#mediapipe to 5point
|
223 |
+
"""
|
224 |
+
draw_triangle(362,382,398)
|
225 |
+
draw_triangle(173,133,155)
|
226 |
+
draw_triangle(33,246,7)
|
227 |
+
draw_triangle(249,263,466)
|
228 |
+
|
229 |
+
draw_triangle(94,2,164)
|
230 |
+
|
231 |
+
draw_triangle(61,76,61)
|
232 |
+
draw_triangle(291,306,291)
|
233 |
+
|
234 |
+
draw_lines([17,18,200,199,175,152])
|
235 |
+
|
236 |
+
draw_lines([127,234,93,132,58,172,136,150,149,176,148,152],(255,0,0))
|
237 |
+
#draw_lines([127,234,132,172,150,176,152],(0,0,255))
|
238 |
+
"""
|
239 |
+
|
240 |
+
#
|
241 |
+
#draw_lines([9,107])
|
242 |
+
"""
|
243 |
+
draw_lines([148,171,208])
|
244 |
+
draw_lines([176,140])
|
245 |
+
draw_lines([149,170,211])
|
246 |
+
draw_lines([150,169,])
|
247 |
+
|
248 |
+
draw_lines([150,169])
|
249 |
+
draw_lines([136,135,214])
|
250 |
+
draw_lines([172,138,192])
|
251 |
+
draw_lines([58,215])
|
252 |
+
draw_lines([132,177,147])
|
253 |
+
draw_lines([58,215,213])
|
254 |
+
draw_lines([93,137,123])
|
255 |
+
#draw_lines([234,227])
|
256 |
+
#draw_lines([127,34,143])
|
257 |
+
|
258 |
+
"""
|
259 |
+
#draw_lines([378,288,356,251,151,21,127,58,150,152])
|
260 |
+
#draw_lines(LINE_RIGHT_CONTOUR_OUTER_EYE_TO_CHIN)
|
261 |
+
#draw_lines(LINE_RIGHT_CONTOUR_EYE_TO_CHIN)
|
262 |
+
#draw_lines(LINE_RIGHT_CONTOUR_INNER_EYE_TO_CHIN,(0,255,0))
|
263 |
+
"""
|
264 |
+
draw_lines(LINE_RIGHT_CONTOUR_0)
|
265 |
+
draw_lines(LINE_RIGHT_CONTOUR_1)
|
266 |
+
draw_lines(LINE_RIGHT_CONTOUR_2)
|
267 |
+
draw_lines(LINE_RIGHT_CONTOUR_3)
|
268 |
+
draw_lines(LINE_RIGHT_CONTOUR_4)
|
269 |
+
draw_lines(LINE_RIGHT_CONTOUR_5)
|
270 |
+
draw_lines(LINE_RIGHT_CONTOUR_6)
|
271 |
+
draw_lines(LINE_RIGHT_CONTOUR_7)
|
272 |
+
draw_lines(LINE_RIGHT_CONTOUR_8)
|
273 |
+
draw_lines(LINE_RIGHT_CONTOUR_9)
|
274 |
+
draw_lines(LINE_RIGHT_CONTOUR_10)
|
275 |
+
draw_lines(LINE_RIGHT_CONTOUR_11)
|
276 |
+
|
277 |
+
|
278 |
+
draw_lines(LINE_LEFT_CONTOUR_1)
|
279 |
+
draw_lines(LINE_LEFT_CONTOUR_2)
|
280 |
+
draw_lines(LINE_LEFT_CONTOUR_3)
|
281 |
+
draw_lines(LINE_LEFT_CONTOUR_4)
|
282 |
+
draw_lines(LINE_LEFT_CONTOUR_5)
|
283 |
+
draw_lines(LINE_LEFT_CONTOUR_6)
|
284 |
+
draw_lines(LINE_LEFT_CONTOUR_7)
|
285 |
+
draw_lines(LINE_LEFT_CONTOUR_8)
|
286 |
+
draw_lines(LINE_LEFT_CONTOUR_9)
|
287 |
+
draw_lines(LINE_LEFT_CONTOUR_10)
|
288 |
+
draw_lines(LINE_LEFT_CONTOUR_11)
|
289 |
+
#draw_lines(LINE_LEFT_CONTOUR_12)
|
290 |
+
"""
|
291 |
+
|
292 |
+
#draw_lines(LINE_RIGHT_CONTOUR_6,(255,0,0))
|
293 |
+
|
294 |
+
def get_eye_brow_points(landmarks):
|
295 |
+
result_points= []
|
296 |
+
for landmark in landmarks:
|
297 |
+
point=get_mean_point(landmark)
|
298 |
+
result_points.append(point)
|
299 |
+
|
300 |
+
return result_points
|
301 |
+
|
302 |
+
def get_mean_points(landmarks):
|
303 |
+
result_points= []
|
304 |
+
for landmark in landmarks:
|
305 |
+
point=get_mean_point(landmark)
|
306 |
+
result_points.append(point)
|
307 |
+
|
308 |
+
return result_points
|
309 |
+
|
310 |
+
def get_divided_points(landmarks,divided=3):
|
311 |
+
result_points= []
|
312 |
+
landmark_points = []
|
313 |
+
for landmark in landmarks:
|
314 |
+
if isinstance(landmark, int):
|
315 |
+
pt=get_cordinate_point(landmark)
|
316 |
+
else:
|
317 |
+
pt =get_mean_point(landmark)
|
318 |
+
landmark_points.append(pt)
|
319 |
+
|
320 |
+
divided_points = divide_line_to_points(landmark_points,divided)
|
321 |
+
|
322 |
+
#print(centers[0][0]*1024,",",centers[0][1]*1024)
|
323 |
+
return divided_points
|
324 |
+
|
325 |
+
|
326 |
+
def get_half_contour(landmarks):
|
327 |
+
result_points= []
|
328 |
+
landmark_points = []
|
329 |
+
for landmark in landmarks:
|
330 |
+
pt =get_point(landmark)
|
331 |
+
landmark_points.append(pt)
|
332 |
+
|
333 |
+
divided_points = divide_line_to_points(landmark_points,8)#9
|
334 |
+
#for pt in divided_points:
|
335 |
+
# cv2.circle(annotated_image,pt,3,(255,0,0),-1)
|
336 |
+
# result_points.append((pt[0],pt[1]))
|
337 |
+
|
338 |
+
#print(centers[0][0]*1024,",",centers[0][1]*1024)
|
339 |
+
return divided_points
|
340 |
+
|
341 |
+
right_landmarks =[
|
342 |
+
#[LANDMARK_68_CONTOUR_5]
|
343 |
+
[LANDMARK_68_CONTOUR_1],[LANDMARK_68_CONTOUR_2_PART1,LANDMARK_68_CONTOUR_2_PART2],[LANDMARK_68_CONTOUR_3],[LANDMARK_68_CONTOUR_4],[LANDMARK_68_CONTOUR_5],[LANDMARK_68_CONTOUR_6_PART1,LANDMARK_68_CONTOUR_6_PART2],[LANDMARK_68_CONTOUR_7],[LANDMARK_68_CONTOUR_8_PART1,LANDMARK_68_CONTOUR_8_PART2],[LANDMARK_68_CONTOUR_9],
|
344 |
+
|
345 |
+
]
|
346 |
+
contour_right_points=get_half_contour(right_landmarks)
|
347 |
+
|
348 |
+
left_landmarks =[
|
349 |
+
[LANDMARK_68_CONTOUR_9], [LINE_LEFT_CONTOUR_1], [LINE_LEFT_CONTOUR_2], [LINE_LEFT_CONTOUR_3], [LINE_LEFT_CONTOUR_4],[LINE_LEFT_CONTOUR_5],[LINE_LEFT_CONTOUR_6],[LINE_LEFT_CONTOUR_7],[LINE_LEFT_CONTOUR_8],[LINE_LEFT_CONTOUR_9],[LINE_LEFT_CONTOUR_10],[LINE_LEFT_CONTOUR_11]
|
350 |
+
]
|
351 |
+
contour_left_points=get_half_contour(left_landmarks)
|
352 |
+
|
353 |
+
set_plot_text(draw_number,font_scale,text_color) # for reset
|
354 |
+
plot_points(annotated_image,contour_right_points+contour_left_points[1:],False,dot_size,dot_color,line_size,line_color)
|
355 |
+
|
356 |
+
right_eye_brow_points=get_eye_brow_points([
|
357 |
+
LANDMARK_68_RIGHT_EYEBROW_18,LANDMARK_68_RIGHT_EYEBROW_19,LANDMARK_68_RIGHT_EYEBROW_20,LANDMARK_68_RIGHT_EYEBROW_21,LANDMARK_68_RIGHT_EYEBROW_22
|
358 |
+
])
|
359 |
+
plot_points(annotated_image,right_eye_brow_points,False,dot_size,dot_color,line_size,line_color)
|
360 |
+
left_eye_brow_points=get_eye_brow_points([
|
361 |
+
LANDMARK_68_LEFT_EYEBROW_23,LANDMARK_68_LEFT_EYEBROW_24,LANDMARK_68_LEFT_EYEBROW_25,LANDMARK_68_LEFT_EYEBROW_26,LANDMARK_68_LEFT_EYEBROW_27
|
362 |
+
])
|
363 |
+
plot_points(annotated_image,left_eye_brow_points,False,dot_size,dot_color,line_size,line_color)
|
364 |
+
|
365 |
+
vertical_nose_points = get_divided_points([LANDMARK_68_VERTICAL_NOSE_28,LANDMARK_68_VERTICAL_NOSE_29,LANDMARK_68_VERTICAL_NOSE_30,LANDMARK_68_VERTICAL_NOSE_31],3)
|
366 |
+
plot_points(annotated_image,vertical_nose_points,False,dot_size,dot_color,line_size,line_color)
|
367 |
+
|
368 |
+
horizontal_nose_points = get_mean_points([LANDMARK_68_HORIZONTAL_NOSE_32,LANDMARK_68_HORIZONTAL_NOSE_33,LANDMARK_68_HORIZONTAL_NOSE_34,LANDMARK_68_HORIZONTAL_NOSE_35,LANDMARK_68_HORIZONTAL_NOSE_36])
|
369 |
+
plot_points(annotated_image,horizontal_nose_points,False,dot_size,dot_color,line_size,line_color)
|
370 |
+
|
371 |
+
right_upper_eye_points = get_divided_points(LINE_RIGHT_UPPER_MIXED_EYE2,3)
|
372 |
+
right_lower_eye_points = get_divided_points(LINE_RIGHT_LOWER_MIXED_EYE,3)
|
373 |
+
#right_eye_points = right_upper_eye_points+right_lower_eye_points # first and last is same as above
|
374 |
+
right_eye_points = right_upper_eye_points+right_lower_eye_points[1:-1]
|
375 |
+
plot_points(annotated_image,right_eye_points,True,dot_size,dot_color,line_size,line_color)
|
376 |
+
|
377 |
+
#draw_lines(LINE_RIGHT_LOWER_OUTER_EYE,(0,255,0))
|
378 |
+
#draw_lines(LINE_RIGHT_LOWER_INNER_EYE,(0,255,0))
|
379 |
+
#draw_lines(LINE_RIGHT_UPPER_OUTER_EYE,(0,255,0))
|
380 |
+
#draw_lines(LINE_RIGHT_UPPER_INNER_EYE,(0,255,0))
|
381 |
+
|
382 |
+
left_upper_eye_points = get_divided_points(LINE_LEFT_UPPER_MIXED_EYE2,3)
|
383 |
+
left_lower_eye_points = get_divided_points(LINE_LEFT_LOWER_MIXED_EYE,3)
|
384 |
+
#left_eye_points = left_upper_eye_points+left_lower_eye_points# first and last is same as above
|
385 |
+
left_eye_points = left_upper_eye_points+left_lower_eye_points[1:-1]
|
386 |
+
plot_points(annotated_image,left_eye_points,True,dot_size,dot_color,line_size,line_color)
|
387 |
+
# first and last is same as above
|
388 |
+
|
389 |
+
#draw_lines(LINE_LEFT_LOWER_OUTER_EYE,(0,255,0))
|
390 |
+
#draw_lines(LINE_LEFT_LOWER_INNER_EYE,(0,255,0))
|
391 |
+
#draw_lines(LINE_LEFT_UPPER_OUTER_EYE,(0,255,0))
|
392 |
+
#draw_lines(LINE_LEFT_UPPER_INNER_EYE,(0,255,0))
|
393 |
+
|
394 |
+
left_upper_outer_lip_points = get_divided_points(LINE_RIGHT_UPPER_OUTER_LIP,3)
|
395 |
+
right_upper_outer_lip_points = get_divided_points(LINE_LEFT_UPPER_OUTER_LIP,3)
|
396 |
+
upper_outer_lip_points = left_upper_outer_lip_points+right_upper_outer_lip_points[1:]# first and last is same as above
|
397 |
+
#plot_points(annotated_image,upper_outer_lip_points)
|
398 |
+
upper_outer_lip_points = get_mean_points([LANDMARK_68_UPPER_OUTER_LIP_49,LANDMARK_68_UPPER_OUTER_LIP_50,LANDMARK_68_UPPER_OUTER_LIP_51,LANDMARK_68_UPPER_OUTER_LIP_52,LANDMARK_68_UPPER_OUTER_LIP_53,LANDMARK_68_UPPER_OUTER_LIP_54,LANDMARK_68_UPPER_OUTER_LIP_55])
|
399 |
+
#plot_points(annotated_image,upper_outer_lip_points)
|
400 |
+
|
401 |
+
lower_outer_lip_points = get_mean_points([LANDMARK_68_UPPER_OUTER_LIP_55,LANDMARK_68_LOWER_OUTER_LIP_56,LANDMARK_68_LOWER_OUTER_LIP_57,LANDMARK_68_LOWER_OUTER_LIP_58,LANDMARK_68_LOWER_OUTER_LIP_59,LANDMARK_68_LOWER_OUTER_LIP_60,LANDMARK_68_UPPER_OUTER_LIP_49])
|
402 |
+
outer_lip_points = upper_outer_lip_points+lower_outer_lip_points[1:-1]
|
403 |
+
plot_points(annotated_image,outer_lip_points,True,dot_size,dot_color,line_size,line_color)
|
404 |
+
|
405 |
+
upper_inner_lip_points = get_mean_points([LANDMARK_68_UPPER_INNER_LIP_61,LANDMARK_68_UPPER_INNER_LIP_62,LANDMARK_68_UPPER_INNER_LIP_63,LANDMARK_68_UPPER_INNER_LIP_64,LANDMARK_68_UPPER_INNER_LIP_65])
|
406 |
+
#plot_points(annotated_image,upper_inner_lip_points)
|
407 |
+
|
408 |
+
lower_inner_lip_points = get_mean_points([LANDMARK_68_UPPER_INNER_LIP_65,LANDMARK_68_LOWER_INNER_LIP_66,LANDMARK_68_LOWER_INNER_LIP_67,LANDMARK_68_LOWER_INNER_LIP_68,LANDMARK_68_UPPER_INNER_LIP_61])
|
409 |
+
inner_lip_points = upper_inner_lip_points+lower_inner_lip_points[1:-1]
|
410 |
+
plot_points(annotated_image,inner_lip_points,True,dot_size,dot_color,line_size,line_color)
|
411 |
+
|
412 |
+
landmark_points = contour_right_points+contour_left_points[1:]
|
413 |
+
landmark_points += right_eye_brow_points + left_eye_brow_points
|
414 |
+
landmark_points += vertical_nose_points + horizontal_nose_points
|
415 |
+
landmark_points += right_eye_points + left_eye_points
|
416 |
+
landmark_points += outer_lip_points + inner_lip_points
|
417 |
+
|
418 |
+
#plot_points(annotated_image,landmark_points,20) # for debug
|
419 |
+
bbox = points_to_bbox(landmark_points)
|
420 |
+
bbox = expand_bbox(bbox,5,7,5,5)
|
421 |
+
|
422 |
+
draw_bbox(annotated_image,bbox,box_color,box_size)
|
423 |
+
#draw_lines([POINT_LEFT_HEAD_OUTER_EX,POINT_LEFT_EYE_OUTER_EX,POINT_LEFT_MOUTH_OUTER_EX,POINT_LEFT_CHIN_OUTER,POINT_CHIN_BOTTOM])
|
424 |
+
#draw_lines([LANDMARK_68_CONTOUR_1,LANDMARK_68_CONTOUR_2,LANDMARK_68_CONTOUR_3,LANDMARK_68_CONTOUR_4,LANDMARK_68_CONTOUR_5,LANDMARK_68_CONTOUR_6,LANDMARK_68_CONTOUR_7,LANDMARK_68_CONTOUR_8,LANDMARK_68_CONTOUR_9])
|
425 |
+
"""solutions.drawing_utils.draw_landmarks(
|
426 |
+
image=annotated_image,
|
427 |
+
landmark_list=face_landmarks_proto,
|
428 |
+
connections=mp.solutions.face_mesh.FACEMESH_LEFT_EYE,#FACE_OVAL
|
429 |
+
landmark_drawing_spec=None,
|
430 |
+
connection_drawing_spec=mp.solutions.drawing_styles
|
431 |
+
.get_default_face_mesh_contours_style())"""
|
432 |
+
|
433 |
+
"""solutions.drawing_utils.draw_landmarks(
|
434 |
+
image=annotated_image,
|
435 |
+
landmark_list=face_landmarks_proto,
|
436 |
+
connections=mp.solutions.face_mesh.FACEMESH_CONTOURS,# mix all
|
437 |
+
landmark_drawing_spec=None,
|
438 |
+
connection_drawing_spec=mp.solutions.drawing_styles
|
439 |
+
.get_default_face_mesh_contours_style())
|
440 |
+
"""
|
441 |
+
"""solutions.drawing_utils.draw_landmarks(
|
442 |
+
image=annotated_image,
|
443 |
+
landmark_list=face_landmarks_proto,
|
444 |
+
connections=mp.solutions.face_mesh.FACEMESH_IRISES,
|
445 |
+
landmark_drawing_spec=None,
|
446 |
+
connection_drawing_spec=mp.solutions.drawing_styles
|
447 |
+
.get_default_face_mesh_iris_connections_style()) """
|
448 |
+
|
449 |
+
return annotated_image,bbox,landmark_points
|
450 |
+
|
451 |
+
|
452 |
+
if __name__ == "__main__":
|
453 |
+
args = parse_arguments()
|
454 |
+
input_file = args.input_file
|
455 |
+
|
456 |
+
#file checks
|
457 |
+
if not os.path.isfile(input_file):
|
458 |
+
print(f"input is not file '{input_file}'")
|
459 |
+
exit(0)
|
460 |
+
|
461 |
+
model_path = args.model_path
|
462 |
+
|
463 |
+
BaseOptions = mp.tasks.BaseOptions
|
464 |
+
FaceLandmarker = mp.tasks.vision.FaceLandmarker
|
465 |
+
FaceLandmarkerOptions = mp.tasks.vision.FaceLandmarkerOptions
|
466 |
+
VisionRunningMode = mp.tasks.vision.RunningMode
|
467 |
+
|
468 |
+
options = FaceLandmarkerOptions(
|
469 |
+
base_options=BaseOptions(model_asset_path=model_path),
|
470 |
+
running_mode=VisionRunningMode.IMAGE
|
471 |
+
,min_face_detection_confidence=0, min_face_presence_confidence=0
|
472 |
+
)
|
473 |
+
|
474 |
+
|
475 |
+
with FaceLandmarker.create_from_options(options) as landmarker:
|
476 |
+
|
477 |
+
start = time.time()
|
478 |
+
mp_image = mp.Image.create_from_file(input_file)
|
479 |
+
face_landmarker_result = landmarker.detect(mp_image)
|
480 |
+
detect_time = time.time()-start
|
481 |
+
print(detect_time)
|
482 |
+
|
483 |
+
annotated_image,bbox,landmark_points = draw_landmarks_on_image(mp_image.numpy_view(), face_landmarker_result)
|
484 |
+
#print(annotated_image)
|
485 |
+
#annotated_image=cv2.resize(annotated_image, (800, 800))
|
486 |
+
annotated_image=cv2.cvtColor(annotated_image, cv2.COLOR_RGB2BGR)
|
487 |
+
cv2.imwrite(input_file.replace(".jpg","_la68.jpg"),annotated_image)
|
488 |
+
|
489 |
+
if args.save_glandmark:
|
490 |
+
parent_path,file = os.path.split(input_file)
|
491 |
+
glandmark = bbox_to_glandmarks(file,bbox,landmark_points)
|
492 |
+
glandmark_path = input_file.replace(".jpg",f".json")
|
493 |
+
if os.path.exists(glandmark_path):
|
494 |
+
print(f"glandmark exist skipped {glandmark_path}")
|
495 |
+
else:
|
496 |
+
import json
|
497 |
+
with open(glandmark_path,"w") as f:
|
498 |
+
json.dump(glandmark,f)
|
499 |
+
|
500 |
+
# _landmark.json always overwrite because not design for edit
|
501 |
+
if args.save_group_landmark:
|
502 |
+
result=convert_to_landmark_group_json(landmark_points)
|
503 |
+
total = 0
|
504 |
+
for key in result[0].keys():
|
505 |
+
total += len(result[0][key])
|
506 |
+
|
507 |
+
print(total)
|
508 |
+
import json
|
509 |
+
group_landmark_path = input_file.replace(".jpg",f"_landmark.json")
|
510 |
+
with open(group_landmark_path,"w") as f:
|
511 |
+
json.dump(result,f)
|
512 |
+
|
513 |
+
#cv2.imshow("image",)
|
514 |
+
#cv2.waitKey(0)
|
515 |
+
#cv2.destroyAllWindows()
|
516 |
+
|
examples/00002200.jpg
ADDED
examples/00003245_00.jpg
ADDED
examples/00004200.jpg
ADDED
examples/00005259.jpg
ADDED
examples/00018022.jpg
ADDED
examples/img-above.jpg
ADDED
examples/img-below.jpg
ADDED
examples/img-side.jpg
ADDED
face_landmarker.task
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:64184e229b263107bc2b804c6625db1341ff2bb731874b0bcc2fe6544e0bc9ff
|
3 |
+
size 3758596
|
face_landmarker.task.txt
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Face landmark detection
|
2 |
+
https://ai.google.dev/edge/mediapipe/solutions/vision/face_landmarker
|
3 |
+
|
4 |
+
model card page is
|
5 |
+
https://storage.googleapis.com/mediapipe-assets/MediaPipe%20BlazeFace%20Model%20Card%20(Short%20Range).pdf
|
6 |
+
|
7 |
+
license is Apache2.0
|
8 |
+
https://www.apache.org/licenses/LICENSE-2.0.html
|
glibvision/cv2_utils.py
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import cv2
|
2 |
+
import numpy as np
|
3 |
+
|
4 |
+
def draw_bbox(image,box,color=(255,0,0),thickness=1):
|
5 |
+
if thickness==0:
|
6 |
+
return
|
7 |
+
|
8 |
+
left = int(box[0])
|
9 |
+
top = int(box[1])
|
10 |
+
right = int(box[0]+box[2])
|
11 |
+
bottom = int(box[1]+box[3])
|
12 |
+
box_points =[(left,top),(right,top),(right,bottom),(left,bottom)]
|
13 |
+
|
14 |
+
cv2.polylines(image, [np.array(box_points)], isClosed=True, color=color, thickness=thickness)
|
15 |
+
|
16 |
+
|
17 |
+
def to_int_points(points):
|
18 |
+
int_points=[]
|
19 |
+
for point in points:
|
20 |
+
int_points.append([int(point[0]),int(point[1])])
|
21 |
+
return int_points
|
22 |
+
|
23 |
+
def draw_text(img, text, point, font_scale=0.5, color=(200, 200, 200), thickness=1):
|
24 |
+
font = cv2.FONT_HERSHEY_SIMPLEX
|
25 |
+
cv2.putText(img, str(text), point, font, font_scale, color, thickness, cv2.LINE_AA)
|
26 |
+
|
27 |
+
plot_text_color = (200, 200, 200)
|
28 |
+
plot_text_font_scale = 0.5
|
29 |
+
plot_index = 1
|
30 |
+
plot_text = True
|
31 |
+
|
32 |
+
def set_plot_text(is_plot,text_font_scale,text_color):
|
33 |
+
global plot_index,plot_text,plot_text_font_scale,plot_text_color
|
34 |
+
plot_text = is_plot
|
35 |
+
plot_index = 1
|
36 |
+
plot_text_font_scale = text_font_scale
|
37 |
+
plot_text_color = text_color
|
38 |
+
|
39 |
+
def plot_points(image,points,isClosed=False,circle_size=3,circle_color=(255,0,0),line_size=1,line_color=(0,0,255)):
|
40 |
+
global plot_index,plot_text
|
41 |
+
int_points = to_int_points(points)
|
42 |
+
if circle_size>0:
|
43 |
+
for point in int_points:
|
44 |
+
cv2.circle(image,point,circle_size,circle_color,-1)
|
45 |
+
if plot_text:
|
46 |
+
draw_text(image,plot_index,point,plot_text_font_scale,plot_text_color)
|
47 |
+
plot_index+=1
|
48 |
+
if line_size>0:
|
49 |
+
cv2.polylines(image, [np.array(int_points)], isClosed=isClosed, color=line_color, thickness=line_size)
|
glibvision/glandmark_utils.py
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
landmarks68_utils.py
ADDED
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from PIL import Image,ImageDraw
|
2 |
+
|
3 |
+
def create_color_image(width, height, color=(255,255,255)):
|
4 |
+
img = Image.new('RGB', (width, height), color)
|
5 |
+
return img
|
6 |
+
|
7 |
+
def fill_points(image,points,color=(255,255,255)):
|
8 |
+
draw = ImageDraw.Draw(image)
|
9 |
+
int_points = [(int(x), int(y)) for x, y in points]
|
10 |
+
draw.polygon(int_points, fill=color)
|
11 |
+
return image
|
12 |
+
|
13 |
+
|
14 |
+
def minus_point(pt1,pt2):
|
15 |
+
return [pt1[0]-pt2[0],pt1[1]-pt2[1]]
|
16 |
+
|
17 |
+
def lerp_point(pt1,pt2,pt2_ratio):
|
18 |
+
return [int(pt1[0]*(1.0-pt2_ratio)+pt2[0]*pt2_ratio),pt1[1]*(1.0-pt2_ratio)+pt2[1]*pt2_ratio]
|
19 |
+
|
20 |
+
def mean_point(points):
|
21 |
+
xs = 0
|
22 |
+
ys = 0
|
23 |
+
for pt in points:
|
24 |
+
xs +=pt[0]
|
25 |
+
ys +=pt[1]
|
26 |
+
return [int(xs/len(points)),int(ys/len(points))]
|
27 |
+
|
28 |
+
def get_face_points(face_landmarks_list):
|
29 |
+
contour_points=get_landmark_points(face_landmarks_list,PARTS_CONTOUR)
|
30 |
+
left_eyebrow_points=get_landmark_points(face_landmarks_list,PARTS_LEFT_EYEBROW)
|
31 |
+
|
32 |
+
right_eyebrow_points=get_landmark_points(face_landmarks_list,PARTS_RIGHT_EYEBROW)
|
33 |
+
|
34 |
+
nose_points=get_landmark_points(face_landmarks_list,PARTS_NOSE_BRIDGE)
|
35 |
+
|
36 |
+
diff_right = minus_point(contour_points[1],contour_points[0])
|
37 |
+
right_minus_corner = minus_point(contour_points[0] , diff_right)
|
38 |
+
right_contour = lerp_point(right_minus_corner,left_eyebrow_points[0],0.3)
|
39 |
+
|
40 |
+
diff_left = minus_point(contour_points[15],contour_points[16])
|
41 |
+
left_minus_corner = minus_point(contour_points[16] , diff_left)
|
42 |
+
left_contour = lerp_point(left_minus_corner,right_eyebrow_points[-1],0.3)
|
43 |
+
|
44 |
+
middle_face = mean_point([nose_points[0],right_eyebrow_points[0],left_eyebrow_points[-1]])
|
45 |
+
return [right_contour]+list(contour_points)+[left_contour,middle_face]
|
46 |
+
|
47 |
+
|
48 |
+
def get_innner_mouth_points(face_landmarks_list):
|
49 |
+
top_points=get_landmark_points(face_landmarks_list,PARTS_UPPER_LIP)
|
50 |
+
bottom_points=get_landmark_points(face_landmarks_list,PARTS_LOWER_LIP)
|
51 |
+
return top_points[7:]+bottom_points[7:]#[::-1]
|
52 |
+
|
53 |
+
|
54 |
+
PARTS_UPPER_LIP = "top_lip"
|
55 |
+
PARTS_LOWER_LIP = "bottom_lip"
|
56 |
+
PARTS_CONTOUR ="chin"
|
57 |
+
PARTS_LEFT_EYEBROW ="left_eyebrow"
|
58 |
+
PARTS_RIGHT_EYEBROW ="right_eyebrow"
|
59 |
+
PARTS_LEFT_EYE ="left_eye"
|
60 |
+
PARTS_RIGHT_EYE ="right_eye"
|
61 |
+
PARTS_NOSE_TIP ="nose_tip"
|
62 |
+
PARTS_NOSE_BRIDGE ="nose_bridge"
|
63 |
+
|
64 |
+
def get_landmark_points(face_landmarks_list,key):
|
65 |
+
matching_landmark_points = []
|
66 |
+
for face_landmarks in face_landmarks_list:
|
67 |
+
for landmark_name, landmark_points in face_landmarks.items():
|
68 |
+
matching_landmark_points = landmark_points.copy()
|
69 |
+
if landmark_name ==key:
|
70 |
+
return tuple(matching_landmark_points)
|
mp_box.py
ADDED
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import mediapipe as mp
|
2 |
+
from mediapipe.tasks import python
|
3 |
+
from mediapipe.tasks.python import vision
|
4 |
+
from mediapipe.framework.formats import landmark_pb2
|
5 |
+
from mediapipe import solutions
|
6 |
+
import numpy as np
|
7 |
+
|
8 |
+
# for X,Y,W,H to x1,y1,x2,y2(Left-top,right-bottom style)
|
9 |
+
def xywh_to_xyxy(box):
|
10 |
+
return [box[0],box[1],box[0]+box[2],box[1]+box[3]]
|
11 |
+
|
12 |
+
def convert_to_box(face_landmarks_list,indices,w=1024,h=1024):
|
13 |
+
x1=w
|
14 |
+
y1=h
|
15 |
+
x2=0
|
16 |
+
y2=0
|
17 |
+
for index in indices:
|
18 |
+
x=min(w,max(0,(face_landmarks_list[0][index].x*w)))
|
19 |
+
y=min(h,max(0,(face_landmarks_list[0][index].y*h)))
|
20 |
+
if x<x1:
|
21 |
+
x1=x
|
22 |
+
|
23 |
+
if y<y1:
|
24 |
+
y1=y
|
25 |
+
|
26 |
+
if x>x2:
|
27 |
+
x2=x
|
28 |
+
if y>y2:
|
29 |
+
y2=y
|
30 |
+
|
31 |
+
|
32 |
+
return [int(x1),int(y1),int(x2-x1),int(y2-y1)]
|
33 |
+
|
34 |
+
|
35 |
+
def box_to_square(bbox):
|
36 |
+
box=list(bbox)
|
37 |
+
if box[2]>box[3]:
|
38 |
+
diff = box[2]-box[3]
|
39 |
+
box[3]+=diff
|
40 |
+
box[1]-=diff/2
|
41 |
+
elif box[3]>box[2]:
|
42 |
+
diff = box[3]-box[2]
|
43 |
+
box[2]+=diff
|
44 |
+
box[0]-=diff/2
|
45 |
+
return box
|
46 |
+
|
47 |
+
|
48 |
+
def face_landmark_result_to_box(face_landmarker_result,width=1024,height=1024):
|
49 |
+
face_landmarks_list = face_landmarker_result.face_landmarks
|
50 |
+
|
51 |
+
|
52 |
+
full_indices = list(range(456))
|
53 |
+
|
54 |
+
MIDDLE_FOREHEAD = 151
|
55 |
+
BOTTOM_CHIN_EX = 152
|
56 |
+
BOTTOM_CHIN = 175
|
57 |
+
CHIN_TO_MIDDLE_FOREHEAD = [200,14,1,6,18,9]
|
58 |
+
MOUTH_BOTTOM = [202,200,422]
|
59 |
+
EYEBROW_CHEEK_LEFT_RIGHT = [46,226,50,1,280,446,276]
|
60 |
+
|
61 |
+
LEFT_HEAD_OUTER_EX = 251 #on side face almost same as full
|
62 |
+
LEFT_HEAD_OUTER = 301
|
63 |
+
LEFT_EYE_OUTER_EX = 356
|
64 |
+
LEFT_EYE_OUTER = 264
|
65 |
+
LEFT_MOUTH_OUTER_EX = 288
|
66 |
+
LEFT_MOUTH_OUTER = 288
|
67 |
+
LEFT_CHIN_OUTER = 435
|
68 |
+
RIGHT_HEAD_OUTER_EX = 21
|
69 |
+
RIGHT_HEAD_OUTER = 71
|
70 |
+
RIGHT_EYE_OUTER_EX = 127
|
71 |
+
RIGHT_EYE_OUTER = 34
|
72 |
+
RIGHT_MOUTH_OUTER_EX = 58
|
73 |
+
RIGHT_MOUTH_OUTER = 215
|
74 |
+
RIGHT_CHIN_OUTER = 150
|
75 |
+
|
76 |
+
# TODO naming line
|
77 |
+
min_indices=CHIN_TO_MIDDLE_FOREHEAD+EYEBROW_CHEEK_LEFT_RIGHT+MOUTH_BOTTOM
|
78 |
+
|
79 |
+
chin_to_brow_indices = [LEFT_CHIN_OUTER,LEFT_MOUTH_OUTER,LEFT_EYE_OUTER,LEFT_HEAD_OUTER,MIDDLE_FOREHEAD,RIGHT_HEAD_OUTER,RIGHT_EYE_OUTER,RIGHT_MOUTH_OUTER,RIGHT_CHIN_OUTER,BOTTOM_CHIN]+min_indices
|
80 |
+
|
81 |
+
box1 = convert_to_box(face_landmarks_list,min_indices,width,height)
|
82 |
+
box2 = convert_to_box(face_landmarks_list,chin_to_brow_indices,width,height)
|
83 |
+
box3 = convert_to_box(face_landmarks_list,full_indices,width,height)
|
84 |
+
#print(box)
|
85 |
+
|
86 |
+
return [box1,box2,box3,box_to_square(box1),box_to_square(box2),box_to_square(box3)]
|
87 |
+
|
88 |
+
|
89 |
+
def draw_landmarks_on_image(detection_result,rgb_image):
|
90 |
+
face_landmarks_list = detection_result.face_landmarks
|
91 |
+
annotated_image = np.copy(rgb_image)
|
92 |
+
|
93 |
+
# Loop through the detected faces to visualize.
|
94 |
+
for idx in range(len(face_landmarks_list)):
|
95 |
+
face_landmarks = face_landmarks_list[idx]
|
96 |
+
|
97 |
+
# Draw the face landmarks.
|
98 |
+
face_landmarks_proto = landmark_pb2.NormalizedLandmarkList()
|
99 |
+
face_landmarks_proto.landmark.extend([
|
100 |
+
landmark_pb2.NormalizedLandmark(x=landmark.x, y=landmark.y, z=landmark.z) for landmark in face_landmarks
|
101 |
+
])
|
102 |
+
|
103 |
+
solutions.drawing_utils.draw_landmarks(
|
104 |
+
image=annotated_image,
|
105 |
+
landmark_list=face_landmarks_proto,
|
106 |
+
connections=mp.solutions.face_mesh.FACEMESH_TESSELATION,
|
107 |
+
landmark_drawing_spec=None,
|
108 |
+
connection_drawing_spec=mp.solutions.drawing_styles
|
109 |
+
.get_default_face_mesh_tesselation_style())
|
110 |
+
|
111 |
+
return annotated_image
|
112 |
+
|
113 |
+
def mediapipe_to_box(image_data,model_path="face_landmarker.task"):
|
114 |
+
BaseOptions = mp.tasks.BaseOptions
|
115 |
+
FaceLandmarker = mp.tasks.vision.FaceLandmarker
|
116 |
+
FaceLandmarkerOptions = mp.tasks.vision.FaceLandmarkerOptions
|
117 |
+
VisionRunningMode = mp.tasks.vision.RunningMode
|
118 |
+
|
119 |
+
options = FaceLandmarkerOptions(
|
120 |
+
base_options=BaseOptions(model_asset_path=model_path),
|
121 |
+
running_mode=VisionRunningMode.IMAGE
|
122 |
+
,min_face_detection_confidence=0, min_face_presence_confidence=0
|
123 |
+
)
|
124 |
+
|
125 |
+
|
126 |
+
with FaceLandmarker.create_from_options(options) as landmarker:
|
127 |
+
if isinstance(image_data,str):
|
128 |
+
mp_image = mp.Image.create_from_file(image_data)
|
129 |
+
else:
|
130 |
+
mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=np.asarray(image_data))
|
131 |
+
face_landmarker_result = landmarker.detect(mp_image)
|
132 |
+
boxes = face_landmark_result_to_box(face_landmarker_result,mp_image.width,mp_image.height)
|
133 |
+
return boxes,mp_image,face_landmarker_result
|
mp_constants.py
ADDED
@@ -0,0 +1,320 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
# contour
|
3 |
+
POINT_LEFT_HEAD_OUTER_EX = 251 #on side face almost same as full
|
4 |
+
POINT_LEFT_HEAD_OUTER = 301
|
5 |
+
POINT_LEFT_EYE_OUTER_EX = 356
|
6 |
+
POINT_LEFT_EYE_OUTER = 264
|
7 |
+
POINT_LEFT_MOUTH_OUTER_EX = 288
|
8 |
+
POINT_LEFT_MOUTH_OUTER = 435
|
9 |
+
POINT_LEFT_CHIN_OUTER = 379
|
10 |
+
POINT_RIGHT_HEAD_OUTER_EX = 21
|
11 |
+
POINT_RIGHT_HEAD_OUTER = 71
|
12 |
+
POINT_RIGHT_EYE_OUTER_EX = 127
|
13 |
+
POINT_RIGHT_EYE_OUTER = 34
|
14 |
+
POINT_RIGHT_MOUTH_OUTER_EX = 58
|
15 |
+
POINT_RIGHT_MOUTH_OUTER = 215
|
16 |
+
POINT_RIGHT_CHIN_OUTER = 150
|
17 |
+
POINT_CHIN_BOTTOM = 152
|
18 |
+
|
19 |
+
POINT_FOREHEAD_TOP = 10
|
20 |
+
|
21 |
+
POINT_UPPER_LIP_CENTER_BOTTOM=13
|
22 |
+
POINT_LOWER_LIP_CENTER_TOP=14
|
23 |
+
POINT_LOWER_LIP_CENTER_BOTTOM=17
|
24 |
+
POINT_NOSE_CENTER_MIDDLE=5
|
25 |
+
|
26 |
+
LINE_RIGHT_CONTOUR_OUTER_EYE_TO_CHIN =[127,234,93,132,58,172,136,150,149,176,148,152]
|
27 |
+
LINE_RIGHT_CONTOUR_EYE_TO_CHIN = [34,227,137,177,215,138,135,169,170,140,171,175]
|
28 |
+
LINE_RIGHT_CONTOUR_INNER_EYE_TO_CHIN =[143,116,123,147,213,192,214,210,211,32,208,199]
|
29 |
+
|
30 |
+
|
31 |
+
LINE_RIGHT_CONTOUR_0 = [152,175,199]
|
32 |
+
LINE_RIGHT_CONTOUR_1 = [148,171,208]
|
33 |
+
LINE_RIGHT_CONTOUR_2 = [176,140,32]
|
34 |
+
LINE_RIGHT_CONTOUR_3 = [149,170,211]
|
35 |
+
LINE_RIGHT_CONTOUR_4 = [150,169,210]
|
36 |
+
LINE_RIGHT_CONTOUR_5 = [136,135,214]
|
37 |
+
LINE_RIGHT_CONTOUR_6 = [172,138,192]
|
38 |
+
LINE_RIGHT_CONTOUR_7 = [58,215,213]
|
39 |
+
LINE_RIGHT_CONTOUR_8 = [132,177,147]
|
40 |
+
LINE_RIGHT_CONTOUR_9 = [93,137,123]
|
41 |
+
LINE_RIGHT_CONTOUR_10 = [234,227,116]
|
42 |
+
LINE_RIGHT_CONTOUR_11 = [127,34,143]
|
43 |
+
|
44 |
+
LANDMARK_68_CONTOUR_1 = LINE_RIGHT_CONTOUR_11
|
45 |
+
LANDMARK_68_CONTOUR_2_PART1 = LINE_RIGHT_CONTOUR_10
|
46 |
+
LANDMARK_68_CONTOUR_2_PART2 = LINE_RIGHT_CONTOUR_9
|
47 |
+
LANDMARK_68_CONTOUR_3 = LINE_RIGHT_CONTOUR_8
|
48 |
+
LANDMARK_68_CONTOUR_4 = LINE_RIGHT_CONTOUR_7
|
49 |
+
LANDMARK_68_CONTOUR_5 = LINE_RIGHT_CONTOUR_6
|
50 |
+
LANDMARK_68_CONTOUR_6_PART1 = LINE_RIGHT_CONTOUR_5
|
51 |
+
LANDMARK_68_CONTOUR_6_PART2 = LINE_RIGHT_CONTOUR_4
|
52 |
+
|
53 |
+
LANDMARK_68_CONTOUR_7 = LINE_RIGHT_CONTOUR_3
|
54 |
+
LANDMARK_68_CONTOUR_8_PART1 = LINE_RIGHT_CONTOUR_2
|
55 |
+
LANDMARK_68_CONTOUR_8_PART2 = LINE_RIGHT_CONTOUR_1
|
56 |
+
LANDMARK_68_CONTOUR_9 = LINE_RIGHT_CONTOUR_0
|
57 |
+
|
58 |
+
|
59 |
+
LINE_LEFT_CONTOUR_1 = [377,396,428]
|
60 |
+
LINE_LEFT_CONTOUR_2 = [400,369,262]
|
61 |
+
LINE_LEFT_CONTOUR_3 = [378,395,431]
|
62 |
+
LINE_LEFT_CONTOUR_4 = [379,394,430]
|
63 |
+
LINE_LEFT_CONTOUR_5 = [365,364,434]
|
64 |
+
LINE_LEFT_CONTOUR_6 = [397,367,416]
|
65 |
+
LINE_LEFT_CONTOUR_7 = [288,435,433]
|
66 |
+
LINE_LEFT_CONTOUR_8 = [361,401,376]
|
67 |
+
LINE_LEFT_CONTOUR_9 = [323,366,352]
|
68 |
+
LINE_LEFT_CONTOUR_10 = [454,447,345]
|
69 |
+
LINE_LEFT_CONTOUR_11 = [356,264,372]
|
70 |
+
LINE_LEFT_CONTOUR_12 = [389,368,383]
|
71 |
+
|
72 |
+
LANDMARK_68_CONTOUR_10 = LINE_LEFT_CONTOUR_1
|
73 |
+
LANDMARK_68_CONTOUR_11_PART1 = LINE_LEFT_CONTOUR_2
|
74 |
+
LANDMARK_68_CONTOUR_11_PART2 = LINE_LEFT_CONTOUR_3
|
75 |
+
LANDMARK_68_CONTOUR_12 = LINE_LEFT_CONTOUR_4
|
76 |
+
LANDMARK_68_CONTOUR_13 = LINE_LEFT_CONTOUR_5
|
77 |
+
LANDMARK_68_CONTOUR_14 = LINE_LEFT_CONTOUR_6
|
78 |
+
LANDMARK_68_CONTOUR_15_PART1 = LINE_LEFT_CONTOUR_7
|
79 |
+
LANDMARK_68_CONTOUR_15_PART2 = LINE_LEFT_CONTOUR_8
|
80 |
+
|
81 |
+
LANDMARK_68_CONTOUR_16 = LINE_LEFT_CONTOUR_9
|
82 |
+
LANDMARK_68_CONTOUR_17_PART1 = LINE_LEFT_CONTOUR_10
|
83 |
+
LANDMARK_68_CONTOUR_17_PART2 = LINE_LEFT_CONTOUR_11
|
84 |
+
|
85 |
+
LANDMARK_68_RIGHT_EYEBROW_18 = [70,46] #upper,lower
|
86 |
+
LANDMARK_68_RIGHT_EYEBROW_19 = [63,53]
|
87 |
+
LANDMARK_68_RIGHT_EYEBROW_20 = [105,52]
|
88 |
+
LANDMARK_68_RIGHT_EYEBROW_21 = [66,65]
|
89 |
+
LANDMARK_68_RIGHT_EYEBROW_22 = [107,55]
|
90 |
+
|
91 |
+
LANDMARK_68_LEFT_EYEBROW_23 = [336,285] #upper,lower
|
92 |
+
LANDMARK_68_LEFT_EYEBROW_24 = [296,295]
|
93 |
+
LANDMARK_68_LEFT_EYEBROW_25 = [334,282]
|
94 |
+
LANDMARK_68_LEFT_EYEBROW_26 = [293,283]
|
95 |
+
LANDMARK_68_LEFT_EYEBROW_27 = [300,276]
|
96 |
+
|
97 |
+
POINT_NOSE_0 = 8
|
98 |
+
POINT_NOSE_1 = 168
|
99 |
+
POINT_NOSE_2 = 6
|
100 |
+
POINT_NOSE_3 = 197
|
101 |
+
POINT_NOSE_4 = 195
|
102 |
+
POINT_NOSE_5 = 5
|
103 |
+
POINT_NOSE_6 = 4
|
104 |
+
POINT_NOSE_7 = 19
|
105 |
+
POINT_NOSE_8 = 94
|
106 |
+
POINT_NOSE_9 = 2
|
107 |
+
|
108 |
+
#side
|
109 |
+
POINT_NOSE_10 = 98
|
110 |
+
POINT_NOSE_11 = 97
|
111 |
+
POINT_NOSE_12 = 326
|
112 |
+
POINT_NOSE_13 = 327
|
113 |
+
|
114 |
+
LANDMARK_68_VERTICAL_NOSE_28 =[8,168]
|
115 |
+
LANDMARK_68_VERTICAL_NOSE_29 = [6]
|
116 |
+
LANDMARK_68_VERTICAL_NOSE_30=[197,195]
|
117 |
+
LANDMARK_68_VERTICAL_NOSE_31=[5,4]
|
118 |
+
|
119 |
+
LANDMARK_68_HORIZONTAL_NOSE_32 =[POINT_NOSE_10]
|
120 |
+
LANDMARK_68_HORIZONTAL_NOSE_33 = [POINT_NOSE_11]
|
121 |
+
LANDMARK_68_HORIZONTAL_NOSE_34=[POINT_NOSE_9]
|
122 |
+
LANDMARK_68_HORIZONTAL_NOSE_35=[POINT_NOSE_12]
|
123 |
+
LANDMARK_68_HORIZONTAL_NOSE_36=[POINT_NOSE_13]
|
124 |
+
|
125 |
+
|
126 |
+
LINE_VERTICAL_NOSE = [POINT_NOSE_0,POINT_NOSE_1,POINT_NOSE_2,POINT_NOSE_3,POINT_NOSE_4,POINT_NOSE_5,POINT_NOSE_6,POINT_NOSE_7,POINT_NOSE_8,POINT_NOSE_9]
|
127 |
+
LINE_HORIZONTAL_NOSE =[POINT_NOSE_10,POINT_NOSE_11,POINT_NOSE_9,POINT_NOSE_12,POINT_NOSE_13]
|
128 |
+
|
129 |
+
### EYES
|
130 |
+
POINT_RIGHT_UPPER_INNER_EYE_1 = 33
|
131 |
+
POINT_RIGHT_UPPER_INNER_EYE_2 = 246
|
132 |
+
POINT_RIGHT_UPPER_INNER_EYE_3 = 161
|
133 |
+
POINT_RIGHT_UPPER_INNER_EYE_4 = 160
|
134 |
+
POINT_RIGHT_UPPER_INNER_EYE_5 = 159
|
135 |
+
POINT_RIGHT_UPPER_INNER_EYE_6 = 158
|
136 |
+
POINT_RIGHT_UPPER_INNER_EYE_7 = 157
|
137 |
+
POINT_RIGHT_UPPER_INNER_EYE_8 = 173
|
138 |
+
POINT_RIGHT_UPPER_INNER_EYE_9 = 133
|
139 |
+
|
140 |
+
LINE_RIGHT_UPPER_INNER_EYE=[POINT_RIGHT_UPPER_INNER_EYE_1,POINT_RIGHT_UPPER_INNER_EYE_2,POINT_RIGHT_UPPER_INNER_EYE_3,POINT_RIGHT_UPPER_INNER_EYE_4,POINT_RIGHT_UPPER_INNER_EYE_5,POINT_RIGHT_UPPER_INNER_EYE_6,POINT_RIGHT_UPPER_INNER_EYE_7,POINT_RIGHT_UPPER_INNER_EYE_8,POINT_RIGHT_UPPER_INNER_EYE_9]
|
141 |
+
|
142 |
+
POINT_RIGHT_LOWER_INNER_EYE_1 = 155
|
143 |
+
POINT_RIGHT_LOWER_INNER_EYE_2 = 154
|
144 |
+
POINT_RIGHT_LOWER_INNER_EYE_3 = 153
|
145 |
+
POINT_RIGHT_LOWER_INNER_EYE_4 = 145
|
146 |
+
POINT_RIGHT_LOWER_INNER_EYE_5 = 144
|
147 |
+
POINT_RIGHT_LOWER_INNER_EYE_6 = 163
|
148 |
+
POINT_RIGHT_LOWER_INNER_EYE_7 = 7
|
149 |
+
|
150 |
+
LINE_RIGHT_LOWER_INNER_EYE=[POINT_RIGHT_UPPER_INNER_EYE_9,POINT_RIGHT_LOWER_INNER_EYE_1,POINT_RIGHT_LOWER_INNER_EYE_2,POINT_RIGHT_LOWER_INNER_EYE_3,POINT_RIGHT_LOWER_INNER_EYE_4,POINT_RIGHT_LOWER_INNER_EYE_5,POINT_RIGHT_LOWER_INNER_EYE_6,POINT_RIGHT_LOWER_INNER_EYE_7,POINT_RIGHT_UPPER_INNER_EYE_1]
|
151 |
+
|
152 |
+
|
153 |
+
POINT_RIGHT_UPPER_OUTER_EYE_1 = 130
|
154 |
+
POINT_RIGHT_UPPER_OUTER_EYE_2 = 247
|
155 |
+
POINT_RIGHT_UPPER_OUTER_EYE_3 = 30
|
156 |
+
POINT_RIGHT_UPPER_OUTER_EYE_4 = 29
|
157 |
+
POINT_RIGHT_UPPER_OUTER_EYE_5 = 27
|
158 |
+
POINT_RIGHT_UPPER_OUTER_EYE_6 = 28
|
159 |
+
POINT_RIGHT_UPPER_OUTER_EYE_7 = 56
|
160 |
+
POINT_RIGHT_UPPER_OUTER_EYE_8 = 190
|
161 |
+
POINT_RIGHT_UPPER_OUTER_EYE_9 = 243
|
162 |
+
|
163 |
+
LINE_RIGHT_UPPER_OUTER_EYE=[POINT_RIGHT_UPPER_OUTER_EYE_1,POINT_RIGHT_UPPER_OUTER_EYE_2,POINT_RIGHT_UPPER_OUTER_EYE_3,POINT_RIGHT_UPPER_OUTER_EYE_4,POINT_RIGHT_UPPER_OUTER_EYE_5,POINT_RIGHT_UPPER_OUTER_EYE_6,POINT_RIGHT_UPPER_OUTER_EYE_7,POINT_RIGHT_UPPER_OUTER_EYE_8,POINT_RIGHT_UPPER_OUTER_EYE_9]
|
164 |
+
|
165 |
+
LINE_RIGHT_UPPER_MIXED_EYE =[#firs eye1 and eye2 is intesionaly for moveup
|
166 |
+
[POINT_RIGHT_UPPER_INNER_EYE_1,POINT_RIGHT_UPPER_OUTER_EYE_2], [POINT_RIGHT_UPPER_INNER_EYE_2,POINT_RIGHT_UPPER_OUTER_EYE_2], [POINT_RIGHT_UPPER_INNER_EYE_3,POINT_RIGHT_UPPER_OUTER_EYE_3], [POINT_RIGHT_UPPER_INNER_EYE_4,POINT_RIGHT_UPPER_OUTER_EYE_4], [POINT_RIGHT_UPPER_INNER_EYE_5,POINT_RIGHT_UPPER_OUTER_EYE_5], [POINT_RIGHT_UPPER_INNER_EYE_6,POINT_RIGHT_UPPER_OUTER_EYE_6]
|
167 |
+
,[POINT_RIGHT_UPPER_INNER_EYE_8],[POINT_RIGHT_UPPER_INNER_EYE_8,POINT_RIGHT_UPPER_INNER_EYE_9] #I'm not sure need this one or not POINT_RIGHT_LOWER_INNER_EYE_1
|
168 |
+
]
|
169 |
+
|
170 |
+
LINE_RIGHT_UPPER_MIXED_EYE2 =[#firs eye1 and eye2 is intesionaly for moveup
|
171 |
+
[POINT_RIGHT_UPPER_INNER_EYE_1,POINT_RIGHT_UPPER_INNER_EYE_1,POINT_RIGHT_UPPER_OUTER_EYE_2],
|
172 |
+
[POINT_RIGHT_UPPER_INNER_EYE_2,POINT_RIGHT_UPPER_INNER_EYE_2,POINT_RIGHT_UPPER_OUTER_EYE_2],
|
173 |
+
[POINT_RIGHT_UPPER_INNER_EYE_3,POINT_RIGHT_UPPER_INNER_EYE_3,POINT_RIGHT_UPPER_OUTER_EYE_3],
|
174 |
+
[POINT_RIGHT_UPPER_INNER_EYE_4,POINT_RIGHT_UPPER_INNER_EYE_4,POINT_RIGHT_UPPER_OUTER_EYE_4],
|
175 |
+
[POINT_RIGHT_UPPER_INNER_EYE_5,POINT_RIGHT_UPPER_INNER_EYE_5,POINT_RIGHT_UPPER_OUTER_EYE_5],
|
176 |
+
[POINT_RIGHT_UPPER_INNER_EYE_6,POINT_RIGHT_UPPER_INNER_EYE_6,POINT_RIGHT_UPPER_OUTER_EYE_6]
|
177 |
+
,[POINT_RIGHT_UPPER_INNER_EYE_8],
|
178 |
+
[POINT_RIGHT_UPPER_INNER_EYE_8,POINT_RIGHT_UPPER_INNER_EYE_9] #I'm not sure need this one or not POINT_RIGHT_LOWER_INNER_EYE_1
|
179 |
+
]
|
180 |
+
|
181 |
+
POINT_RIGHT_LOWER_OUTER_EYE_1 = 112
|
182 |
+
POINT_RIGHT_LOWER_OUTER_EYE_2 = 26
|
183 |
+
POINT_RIGHT_LOWER_OUTER_EYE_3 = 22
|
184 |
+
POINT_RIGHT_LOWER_OUTER_EYE_4 = 23
|
185 |
+
POINT_RIGHT_LOWER_OUTER_EYE_5 = 24
|
186 |
+
POINT_RIGHT_LOWER_OUTER_EYE_6 = 110
|
187 |
+
POINT_RIGHT_LOWER_OUTER_EYE_7 = 25
|
188 |
+
|
189 |
+
LINE_RIGHT_LOWER_OUTER_EYE=[POINT_RIGHT_UPPER_OUTER_EYE_9,POINT_RIGHT_LOWER_OUTER_EYE_1,POINT_RIGHT_LOWER_OUTER_EYE_2,POINT_RIGHT_LOWER_OUTER_EYE_3,POINT_RIGHT_LOWER_OUTER_EYE_4,POINT_RIGHT_LOWER_OUTER_EYE_5,POINT_RIGHT_LOWER_OUTER_EYE_6,POINT_RIGHT_LOWER_OUTER_EYE_7,POINT_RIGHT_UPPER_OUTER_EYE_1]
|
190 |
+
|
191 |
+
LINE_RIGHT_LOWER_MIXED_EYE =[
|
192 |
+
[POINT_RIGHT_UPPER_INNER_EYE_8,POINT_RIGHT_UPPER_INNER_EYE_9,POINT_RIGHT_LOWER_INNER_EYE_1]
|
193 |
+
,[POINT_RIGHT_LOWER_INNER_EYE_2]
|
194 |
+
,POINT_RIGHT_LOWER_INNER_EYE_3,POINT_RIGHT_LOWER_INNER_EYE_4,POINT_RIGHT_LOWER_INNER_EYE_5,POINT_RIGHT_LOWER_INNER_EYE_6,POINT_RIGHT_LOWER_INNER_EYE_7
|
195 |
+
,[POINT_RIGHT_UPPER_INNER_EYE_1,POINT_RIGHT_UPPER_OUTER_EYE_2] #combine 1 and 2 for move up
|
196 |
+
]
|
197 |
+
|
198 |
+
|
199 |
+
POINT_LEFT_UPPER_INNER_EYE_1 = 362
|
200 |
+
POINT_LEFT_UPPER_INNER_EYE_2 = 398
|
201 |
+
POINT_LEFT_UPPER_INNER_EYE_3 = 384
|
202 |
+
POINT_LEFT_UPPER_INNER_EYE_4 = 385
|
203 |
+
POINT_LEFT_UPPER_INNER_EYE_5 = 386
|
204 |
+
POINT_LEFT_UPPER_INNER_EYE_6 = 387
|
205 |
+
POINT_LEFT_UPPER_INNER_EYE_7 = 388
|
206 |
+
POINT_LEFT_UPPER_INNER_EYE_8 = 466
|
207 |
+
POINT_LEFT_UPPER_INNER_EYE_9 = 263
|
208 |
+
|
209 |
+
LINE_LEFT_UPPER_INNER_EYE=[POINT_LEFT_UPPER_INNER_EYE_1,POINT_LEFT_UPPER_INNER_EYE_2,POINT_LEFT_UPPER_INNER_EYE_3,POINT_LEFT_UPPER_INNER_EYE_4,POINT_LEFT_UPPER_INNER_EYE_5,POINT_LEFT_UPPER_INNER_EYE_6,POINT_LEFT_UPPER_INNER_EYE_7,POINT_LEFT_UPPER_INNER_EYE_8,POINT_LEFT_UPPER_INNER_EYE_9]
|
210 |
+
LINE_LEFT_UPPER_INNER_EYE2=[POINT_LEFT_UPPER_INNER_EYE_1,POINT_LEFT_UPPER_INNER_EYE_2,POINT_LEFT_UPPER_INNER_EYE_3,POINT_LEFT_UPPER_INNER_EYE_4,POINT_LEFT_UPPER_INNER_EYE_5,POINT_LEFT_UPPER_INNER_EYE_6,POINT_LEFT_UPPER_INNER_EYE_7,POINT_LEFT_UPPER_INNER_EYE_8,POINT_LEFT_UPPER_INNER_EYE_9]
|
211 |
+
|
212 |
+
|
213 |
+
|
214 |
+
POINT_LEFT_LOWER_INNER_EYE_1 = 249
|
215 |
+
POINT_LEFT_LOWER_INNER_EYE_2 = 390
|
216 |
+
POINT_LEFT_LOWER_INNER_EYE_3 = 373
|
217 |
+
POINT_LEFT_LOWER_INNER_EYE_4 = 374
|
218 |
+
POINT_LEFT_LOWER_INNER_EYE_5 = 380
|
219 |
+
POINT_LEFT_LOWER_INNER_EYE_6 = 381
|
220 |
+
POINT_LEFT_LOWER_INNER_EYE_7 = 382
|
221 |
+
|
222 |
+
|
223 |
+
LINE_LEFT_LOWER_INNER_EYE=[POINT_LEFT_UPPER_INNER_EYE_9,POINT_LEFT_LOWER_INNER_EYE_2,POINT_LEFT_LOWER_INNER_EYE_3,POINT_LEFT_LOWER_INNER_EYE_4,POINT_LEFT_LOWER_INNER_EYE_5,POINT_LEFT_LOWER_INNER_EYE_6,POINT_LEFT_LOWER_INNER_EYE_7,POINT_LEFT_UPPER_INNER_EYE_1]
|
224 |
+
|
225 |
+
#outer
|
226 |
+
|
227 |
+
POINT_LEFT_UPPER_OUTER_EYE_1 = 463
|
228 |
+
POINT_LEFT_UPPER_OUTER_EYE_2 = 414
|
229 |
+
POINT_LEFT_UPPER_OUTER_EYE_3 = 286
|
230 |
+
POINT_LEFT_UPPER_OUTER_EYE_4 = 258
|
231 |
+
POINT_LEFT_UPPER_OUTER_EYE_5 = 257
|
232 |
+
POINT_LEFT_UPPER_OUTER_EYE_6 = 259
|
233 |
+
POINT_LEFT_UPPER_OUTER_EYE_7 = 260
|
234 |
+
POINT_LEFT_UPPER_OUTER_EYE_8 = 467
|
235 |
+
POINT_LEFT_UPPER_OUTER_EYE_9 = 359
|
236 |
+
|
237 |
+
LINE_LEFT_UPPER_OUTER_EYE=[POINT_LEFT_UPPER_OUTER_EYE_1,POINT_LEFT_UPPER_OUTER_EYE_2,POINT_LEFT_UPPER_OUTER_EYE_3,POINT_LEFT_UPPER_OUTER_EYE_4,POINT_LEFT_UPPER_OUTER_EYE_5,POINT_LEFT_UPPER_OUTER_EYE_6,POINT_LEFT_UPPER_OUTER_EYE_7,POINT_LEFT_UPPER_OUTER_EYE_8,POINT_LEFT_UPPER_OUTER_EYE_9]
|
238 |
+
|
239 |
+
|
240 |
+
POINT_LEFT_LOWER_OUTER_EYE_1 = 255
|
241 |
+
POINT_LEFT_LOWER_OUTER_EYE_2 = 339
|
242 |
+
POINT_LEFT_LOWER_OUTER_EYE_3 = 254
|
243 |
+
POINT_LEFT_LOWER_OUTER_EYE_4 = 253
|
244 |
+
POINT_LEFT_LOWER_OUTER_EYE_5 = 252
|
245 |
+
POINT_LEFT_LOWER_OUTER_EYE_6 = 256
|
246 |
+
POINT_LEFT_LOWER_OUTER_EYE_7 = 341
|
247 |
+
|
248 |
+
LINE_LEFT_LOWER_OUTER_EYE=[POINT_LEFT_UPPER_OUTER_EYE_9,POINT_LEFT_LOWER_OUTER_EYE_1,POINT_LEFT_LOWER_OUTER_EYE_2,POINT_LEFT_LOWER_OUTER_EYE_3,POINT_LEFT_LOWER_OUTER_EYE_4,POINT_LEFT_LOWER_OUTER_EYE_5,POINT_LEFT_LOWER_OUTER_EYE_6,POINT_LEFT_LOWER_OUTER_EYE_7,POINT_LEFT_UPPER_OUTER_EYE_1]
|
249 |
+
|
250 |
+
LINE_LEFT_UPPER_MIXED_EYE =[#firs eye1 and eye2 is intesionaly for moveup
|
251 |
+
[POINT_LEFT_UPPER_INNER_EYE_1,POINT_LEFT_UPPER_INNER_EYE_2,POINT_LEFT_LOWER_INNER_EYE_7],
|
252 |
+
[POINT_LEFT_UPPER_INNER_EYE_2,POINT_LEFT_UPPER_OUTER_EYE_2], [POINT_LEFT_UPPER_INNER_EYE_3,POINT_LEFT_UPPER_INNER_EYE_3,POINT_LEFT_UPPER_OUTER_EYE_3], [POINT_LEFT_UPPER_INNER_EYE_4,POINT_LEFT_UPPER_OUTER_EYE_4], [POINT_LEFT_UPPER_INNER_EYE_5,POINT_LEFT_UPPER_OUTER_EYE_5], [POINT_LEFT_UPPER_INNER_EYE_6,POINT_LEFT_UPPER_OUTER_EYE_6]
|
253 |
+
,[POINT_LEFT_UPPER_INNER_EYE_8],[POINT_LEFT_UPPER_OUTER_EYE_8,POINT_LEFT_UPPER_INNER_EYE_9]
|
254 |
+
]
|
255 |
+
|
256 |
+
LINE_LEFT_UPPER_MIXED_EYE2 =[#firs eye1 and eye2 is intesionaly for moveup
|
257 |
+
[POINT_LEFT_UPPER_INNER_EYE_1,POINT_LEFT_UPPER_INNER_EYE_1,POINT_LEFT_UPPER_INNER_EYE_2,POINT_LEFT_LOWER_INNER_EYE_7],
|
258 |
+
[POINT_LEFT_UPPER_INNER_EYE_2,POINT_LEFT_UPPER_INNER_EYE_2,POINT_LEFT_UPPER_OUTER_EYE_2],
|
259 |
+
[POINT_LEFT_UPPER_INNER_EYE_3,POINT_LEFT_UPPER_INNER_EYE_3,POINT_LEFT_UPPER_INNER_EYE_3,POINT_LEFT_UPPER_OUTER_EYE_3],
|
260 |
+
[POINT_LEFT_UPPER_INNER_EYE_4,POINT_LEFT_UPPER_INNER_EYE_4,POINT_LEFT_UPPER_OUTER_EYE_4],
|
261 |
+
[POINT_LEFT_UPPER_INNER_EYE_5,POINT_LEFT_UPPER_INNER_EYE_5,POINT_LEFT_UPPER_OUTER_EYE_5],
|
262 |
+
[POINT_LEFT_UPPER_INNER_EYE_6,POINT_LEFT_UPPER_INNER_EYE_6,POINT_LEFT_UPPER_OUTER_EYE_6]
|
263 |
+
,[POINT_LEFT_UPPER_INNER_EYE_8],
|
264 |
+
[POINT_LEFT_UPPER_OUTER_EYE_8,POINT_LEFT_UPPER_INNER_EYE_9]
|
265 |
+
]
|
266 |
+
|
267 |
+
LINE_LEFT_LOWER_MIXED_EYE =[
|
268 |
+
[POINT_LEFT_UPPER_OUTER_EYE_8,POINT_LEFT_UPPER_INNER_EYE_9]
|
269 |
+
,[POINT_LEFT_LOWER_INNER_EYE_2]
|
270 |
+
,POINT_LEFT_LOWER_INNER_EYE_3,POINT_LEFT_LOWER_INNER_EYE_4,POINT_LEFT_LOWER_INNER_EYE_5,POINT_LEFT_LOWER_INNER_EYE_6,POINT_LEFT_LOWER_INNER_EYE_7
|
271 |
+
, [POINT_LEFT_UPPER_INNER_EYE_1,POINT_LEFT_UPPER_INNER_EYE_2,POINT_LEFT_LOWER_INNER_EYE_7] #combine 1 and 2 for move up
|
272 |
+
]
|
273 |
+
|
274 |
+
|
275 |
+
#LIP
|
276 |
+
LINE_RIGHT_UPPER_OUTER_LIP=[
|
277 |
+
61,185,40,39,37,0
|
278 |
+
]
|
279 |
+
LINE_LEFT_UPPER_OUTER_LIP=[
|
280 |
+
0,267,269,270,409,291
|
281 |
+
]
|
282 |
+
|
283 |
+
|
284 |
+
LINE_LOWER_OUTER_LIP=[291,#upper
|
285 |
+
375,321,405,314,17,84,181,91,146
|
286 |
+
,61 #upper
|
287 |
+
]
|
288 |
+
|
289 |
+
LINE_UPPER_INNER_LIP=[
|
290 |
+
61,185,40,39,37,0,267,269,270,409,291
|
291 |
+
]
|
292 |
+
|
293 |
+
LINE_LOWER_INNER_LIP=[291,#upper
|
294 |
+
375,321,405,314,17,84,181,91,146
|
295 |
+
,61 #upper
|
296 |
+
]
|
297 |
+
|
298 |
+
LANDMARK_68_UPPER_OUTER_LIP_49 =[61]
|
299 |
+
LANDMARK_68_UPPER_OUTER_LIP_50 =[40,39]
|
300 |
+
LANDMARK_68_UPPER_OUTER_LIP_51 =[37]
|
301 |
+
LANDMARK_68_UPPER_OUTER_LIP_52 =[0]
|
302 |
+
LANDMARK_68_UPPER_OUTER_LIP_53 =[267]
|
303 |
+
LANDMARK_68_UPPER_OUTER_LIP_54 =[270,269]
|
304 |
+
LANDMARK_68_UPPER_OUTER_LIP_55 =[291]
|
305 |
+
|
306 |
+
LANDMARK_68_LOWER_OUTER_LIP_56 =[375,321]
|
307 |
+
LANDMARK_68_LOWER_OUTER_LIP_57 =[405,314]
|
308 |
+
LANDMARK_68_LOWER_OUTER_LIP_58 =[17]
|
309 |
+
LANDMARK_68_LOWER_OUTER_LIP_59 =[84,181]
|
310 |
+
LANDMARK_68_LOWER_OUTER_LIP_60 =[146,91]
|
311 |
+
|
312 |
+
LANDMARK_68_UPPER_INNER_LIP_61 =[78]
|
313 |
+
LANDMARK_68_UPPER_INNER_LIP_62 =[81]
|
314 |
+
LANDMARK_68_UPPER_INNER_LIP_63 =[13]
|
315 |
+
LANDMARK_68_UPPER_INNER_LIP_64 =[311]
|
316 |
+
LANDMARK_68_UPPER_INNER_LIP_65 =[308]
|
317 |
+
|
318 |
+
LANDMARK_68_LOWER_INNER_LIP_66 =[402]
|
319 |
+
LANDMARK_68_LOWER_INNER_LIP_67 =[14]
|
320 |
+
LANDMARK_68_LOWER_INNER_LIP_68 =[178]
|
mp_utils.py
ADDED
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import math
|
2 |
+
def calculate_distance(p1, p2):
|
3 |
+
"""
|
4 |
+
|
5 |
+
"""
|
6 |
+
return math.sqrt((p2[0] - p1[0])**2 + (p2[1] - p1[1])**2)
|
7 |
+
def to_int_points(points):
|
8 |
+
ints=[]
|
9 |
+
for pt in points:
|
10 |
+
#print(pt)
|
11 |
+
value = [int(pt[0]),int(pt[1])]
|
12 |
+
#print(value)
|
13 |
+
ints.append(value)
|
14 |
+
return ints
|
15 |
+
|
16 |
+
debug = False
|
17 |
+
def divide_line_to_points(points,divided): # return divided + 1
|
18 |
+
total_length = 0
|
19 |
+
line_length_list = []
|
20 |
+
for i in range(len(points)-1):
|
21 |
+
pt_length = calculate_distance(points[i],points[i+1])
|
22 |
+
total_length += pt_length
|
23 |
+
line_length_list.append(pt_length)
|
24 |
+
|
25 |
+
splited_length = total_length/divided
|
26 |
+
|
27 |
+
def get_new_point(index,lerp):
|
28 |
+
pt1 = points[index]
|
29 |
+
pt2 = points[index+1]
|
30 |
+
diff = [pt2[0] - pt1[0], pt2[1]-pt1[1]]
|
31 |
+
new_point = [pt1[0]+diff[0]*lerp,pt1[1]+diff[1]*lerp]
|
32 |
+
if debug:
|
33 |
+
print(f"pt1 ={pt1} pt2 ={pt2} diff={diff} new_point={new_point}")
|
34 |
+
|
35 |
+
return new_point
|
36 |
+
|
37 |
+
if debug:
|
38 |
+
print(f"{total_length} splitted = {splited_length} line-length-list = {len(line_length_list)}")
|
39 |
+
splited_points=[points[0]]
|
40 |
+
for i in range(1,divided):
|
41 |
+
need_length = splited_length*i
|
42 |
+
if debug:
|
43 |
+
print(f"{i} need length = {need_length}")
|
44 |
+
current_length = 0
|
45 |
+
for j in range(len(line_length_list)):
|
46 |
+
line_length = line_length_list[j]
|
47 |
+
current_length+=line_length
|
48 |
+
if current_length>need_length:
|
49 |
+
if debug:
|
50 |
+
print(f"over need length index = {j} current={current_length}")
|
51 |
+
diff = current_length - need_length
|
52 |
+
|
53 |
+
lerp_point = 1.0 - (diff/line_length)
|
54 |
+
if debug:
|
55 |
+
print(f"over = {diff} lerp ={lerp_point}")
|
56 |
+
new_point = get_new_point(j,lerp_point)
|
57 |
+
|
58 |
+
splited_points.append(new_point)
|
59 |
+
break
|
60 |
+
|
61 |
+
splited_points.append(points[-1]) # last one
|
62 |
+
splited_points=to_int_points(splited_points)
|
63 |
+
|
64 |
+
if debug:
|
65 |
+
print(f"sp={len(splited_points)}")
|
66 |
+
return splited_points
|
67 |
+
|
68 |
+
def points_to_bbox(points):
|
69 |
+
x1=float('inf')
|
70 |
+
x2=0
|
71 |
+
y1=float('inf')
|
72 |
+
y2=0
|
73 |
+
for point in points:
|
74 |
+
if point[0]<x1:
|
75 |
+
x1=point[0]
|
76 |
+
if point[0]>x2:
|
77 |
+
x2=point[0]
|
78 |
+
if point[1]<y1:
|
79 |
+
y1=point[1]
|
80 |
+
if point[1]>y2:
|
81 |
+
y2=point[1]
|
82 |
+
return [x1,y1,x2-x1,y2-y1]
|
83 |
+
|
84 |
+
def expand_bbox(bbox,left=5,top=5,right=5,bottom=5):
|
85 |
+
left_pixel = bbox[2]*(float(left)/100)
|
86 |
+
top_pixel = bbox[3]*(float(top)/100)
|
87 |
+
right_pixel = bbox[2]*(float(right)/100)
|
88 |
+
bottom_pixel = bbox[3]*(float(bottom)/100)
|
89 |
+
new_box = list(bbox)
|
90 |
+
new_box[0] -=left_pixel
|
91 |
+
new_box[1] -=top_pixel
|
92 |
+
new_box[2] +=left_pixel+right_pixel
|
93 |
+
new_box[3] +=top_pixel+bottom_pixel
|
94 |
+
return new_box
|
requirements.txt
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
numpy
|
2 |
+
torch
|
3 |
+
spaces
|
4 |
+
mediapipe
|
5 |
+
opencv-python
|