update
Browse files- .gitattributes +1 -0
- .gitignore +2 -0
- app.py +207 -0
- demo_footer.html +3 -0
- demo_header.html +17 -0
- demo_tools.html +4 -0
- draw_landmarks68.py +516 -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
- mp_box.py +133 -0
- mp_constants.py +296 -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,207 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
import mp_box
|
| 9 |
+
import draw_landmarks68
|
| 10 |
+
'''
|
| 11 |
+
Face landmark detection based Face Detection.
|
| 12 |
+
https://ai.google.dev/edge/mediapipe/solutions/vision/face_landmarker
|
| 13 |
+
from model card
|
| 14 |
+
https://storage.googleapis.com/mediapipe-assets/MediaPipe%20BlazeFace%20Model%20Card%20(Short%20Range).pdf
|
| 15 |
+
Licensed Apache License, Version 2.0
|
| 16 |
+
Train with google's dataset(more detail see model card)
|
| 17 |
+
|
| 18 |
+
'''
|
| 19 |
+
|
| 20 |
+
dir_name ="files"
|
| 21 |
+
passed_time = 60*60
|
| 22 |
+
def clear_old_files(dir,passed_time):
|
| 23 |
+
try:
|
| 24 |
+
files = os.listdir(dir)
|
| 25 |
+
current_time = time.time()
|
| 26 |
+
for file in files:
|
| 27 |
+
file_path = os.path.join(dir,file)
|
| 28 |
+
|
| 29 |
+
ctime = os.stat(file_path).st_ctime
|
| 30 |
+
diff = current_time - ctime
|
| 31 |
+
#print(f"ctime={ctime},current_time={current_time},passed_time={passed_time},diff={diff}")
|
| 32 |
+
if diff > passed_time:
|
| 33 |
+
os.remove(file_path)
|
| 34 |
+
except:
|
| 35 |
+
print("maybe still gallery using error")
|
| 36 |
+
|
| 37 |
+
def picker_color_to_rgba(picker_color):
|
| 38 |
+
color_value = picker_color.strip("rgba()").split(",")
|
| 39 |
+
color_value[0] = int(float(color_value[0]))
|
| 40 |
+
color_value[1] = int(float(color_value[1]))
|
| 41 |
+
color_value[2] = int(float(color_value[2]))
|
| 42 |
+
color_value[3] = int(float(color_value[3]))
|
| 43 |
+
return color_value
|
| 44 |
+
|
| 45 |
+
#@spaces.GPU(duration=120)
|
| 46 |
+
def process_images(image,draw_number,font_scale,text_color_text,dot_size,dot_color_text,line_size,line_color_text,box_size,box_color_text,json_format,progress=gr.Progress(track_tqdm=True)):
|
| 47 |
+
|
| 48 |
+
if not os.path.exists(dir_name):
|
| 49 |
+
os.mkdir(dir_name)
|
| 50 |
+
clear_old_files(dir_name,passed_time)
|
| 51 |
+
|
| 52 |
+
if image == None:
|
| 53 |
+
raise gr.Error("Need Image")
|
| 54 |
+
|
| 55 |
+
progress(0, desc="Start Mediapipe")
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
boxes,mp_image,face_landmarker_result = mp_box.mediapipe_to_box(image)
|
| 59 |
+
#need only result
|
| 60 |
+
text_color = picker_color_to_rgba(text_color_text)
|
| 61 |
+
line_color = picker_color_to_rgba(line_color_text)
|
| 62 |
+
dot_color = picker_color_to_rgba(dot_color_text)
|
| 63 |
+
box_color = picker_color_to_rgba(box_color_text)
|
| 64 |
+
annotated_image,bbox,landmark_points = draw_landmarks68.draw_landmarks_on_image(image,face_landmarker_result,draw_number,font_scale,text_color,
|
| 65 |
+
dot_size,dot_color,line_size,line_color,
|
| 66 |
+
box_size,box_color)
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
if json_format=="raw":
|
| 70 |
+
jsons = landmark_points
|
| 71 |
+
else:
|
| 72 |
+
jsons=draw_landmarks68.convert_to_landmark_group_json(landmark_points)
|
| 73 |
+
|
| 74 |
+
#print(annotation_boxes)
|
| 75 |
+
formatted_json = json.dumps(jsons)
|
| 76 |
+
json_path=create_json_download(formatted_json)
|
| 77 |
+
#return image
|
| 78 |
+
return annotated_image,jsons,json_path
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def write_file(file_path,text):
|
| 82 |
+
with open(file_path, 'w', encoding='utf-8') as f:
|
| 83 |
+
f.write(text)
|
| 84 |
+
|
| 85 |
+
def read_file(file_path):
|
| 86 |
+
"""read the text of target file
|
| 87 |
+
"""
|
| 88 |
+
with open(file_path, 'r', encoding='utf-8') as f:
|
| 89 |
+
content = f.read()
|
| 90 |
+
|
| 91 |
+
return content
|
| 92 |
+
|
| 93 |
+
css="""
|
| 94 |
+
#col-left {
|
| 95 |
+
margin: 0 auto;
|
| 96 |
+
max-width: 640px;
|
| 97 |
+
}
|
| 98 |
+
#col-right {
|
| 99 |
+
margin: 0 auto;
|
| 100 |
+
max-width: 640px;
|
| 101 |
+
}
|
| 102 |
+
.grid-container {
|
| 103 |
+
display: flex;
|
| 104 |
+
align-items: center;
|
| 105 |
+
justify-content: center;
|
| 106 |
+
gap:10px
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
.image {
|
| 110 |
+
width: 128px;
|
| 111 |
+
height: 128px;
|
| 112 |
+
object-fit: cover;
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
.text {
|
| 116 |
+
font-size: 16px;
|
| 117 |
+
}
|
| 118 |
+
"""
|
| 119 |
+
|
| 120 |
+
#css=css,
|
| 121 |
+
|
| 122 |
+
import hashlib
|
| 123 |
+
|
| 124 |
+
def text_to_sha256(text):
|
| 125 |
+
text_bytes = text.encode('utf-8')
|
| 126 |
+
hash_object = hashlib.sha256()
|
| 127 |
+
hash_object.update(text_bytes)
|
| 128 |
+
sha256_hex = hash_object.hexdigest()
|
| 129 |
+
return sha256_hex
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
def create_json_download(text):
|
| 133 |
+
file_id = f"{dir_name}/landmark_{text_to_sha256(text)[:32]}.json"
|
| 134 |
+
write_file(file_id,text)
|
| 135 |
+
# try to save
|
| 136 |
+
return file_id
|
| 137 |
+
|
| 138 |
+
with gr.Blocks(css=css, elem_id="demo-container") as demo:
|
| 139 |
+
with gr.Column():
|
| 140 |
+
gr.HTML(read_file("demo_header.html"))
|
| 141 |
+
gr.HTML(read_file("demo_tools.html"))
|
| 142 |
+
with gr.Row():
|
| 143 |
+
with gr.Column():
|
| 144 |
+
image = gr.Image(height=800,sources=['upload','clipboard'],image_mode='RGB',elem_id="image_upload", type="pil", label="Upload")
|
| 145 |
+
with gr.Row(elem_id="prompt-container", equal_height=False):
|
| 146 |
+
with gr.Row():
|
| 147 |
+
btn = gr.Button("Extract Landmark 68", elem_id="run_button",variant="primary")
|
| 148 |
+
|
| 149 |
+
with gr.Accordion(label="Advanced Settings", open=False):
|
| 150 |
+
with gr.Row( equal_height=True):
|
| 151 |
+
draw_number = gr.Checkbox(label="draw Number")
|
| 152 |
+
|
| 153 |
+
font_scale = gr.Slider(
|
| 154 |
+
label="Font Scale",
|
| 155 |
+
minimum=0.1,
|
| 156 |
+
maximum=2,
|
| 157 |
+
step=0.1,
|
| 158 |
+
value=0.5)
|
| 159 |
+
|
| 160 |
+
text_color = gr.ColorPicker(value="rgba(200,200,200,1)",label="text color")
|
| 161 |
+
#square_shape = gr.Checkbox(label="Square shape")
|
| 162 |
+
with gr.Row( equal_height=True):
|
| 163 |
+
|
| 164 |
+
line_color = gr.ColorPicker(value="rgba(0,0,255,1)",label="line color")
|
| 165 |
+
line_size = gr.Slider(
|
| 166 |
+
label="Line Size",
|
| 167 |
+
minimum=0,
|
| 168 |
+
maximum=20,
|
| 169 |
+
step=1,
|
| 170 |
+
value=1)
|
| 171 |
+
with gr.Row( equal_height=True):
|
| 172 |
+
dot_color = gr.ColorPicker(value="rgba(255,0,0,1)",label="dot color")
|
| 173 |
+
dot_size = gr.Slider(
|
| 174 |
+
label="Dot Size",
|
| 175 |
+
minimum=0,
|
| 176 |
+
maximum=40,
|
| 177 |
+
step=1,
|
| 178 |
+
value=3)
|
| 179 |
+
with gr.Row( equal_height=True):
|
| 180 |
+
box_color = gr.ColorPicker(value="rgba(200,200,200,1)",label="box color")
|
| 181 |
+
box_size = gr.Slider(
|
| 182 |
+
label="Box Size",
|
| 183 |
+
minimum=0,
|
| 184 |
+
maximum=20,
|
| 185 |
+
step=1,
|
| 186 |
+
value=1)
|
| 187 |
+
with gr.Row( equal_height=True):
|
| 188 |
+
json_format = gr.Radio(choices=["raw","face-detection"],value="face-detection",label="json-output format")
|
| 189 |
+
|
| 190 |
+
with gr.Column():
|
| 191 |
+
image_out = gr.Image(label="Output", elem_id="output-img")
|
| 192 |
+
text_out = gr.TextArea(label="JSON-Output")
|
| 193 |
+
download_button = gr.DownloadButton(label="Download JSON" )
|
| 194 |
+
#download_button.click(fn=json_download,inputs=text_out,outputs=download_button)
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
btn.click(fn=process_images, inputs=[image,draw_number,font_scale,text_color,
|
| 198 |
+
dot_size,dot_color,line_size,line_color,
|
| 199 |
+
box_size,box_color,json_format], outputs =[image_out,text_out,download_button], api_name='infer')
|
| 200 |
+
gr.Examples(
|
| 201 |
+
examples =["examples/00004200.jpg","examples/00005259.jpg","examples/00018022.jpg","examples/img-above.jpg","examples/img-below.jpg","examples/img-side.jpg"],
|
| 202 |
+
inputs=[image]
|
| 203 |
+
)
|
| 204 |
+
gr.HTML(read_file("demo_footer.html"))
|
| 205 |
+
|
| 206 |
+
if __name__ == "__main__":
|
| 207 |
+
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 |
+
Contour 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,font_scale,text_color,dot_size,dot_color,line_size,line_color,box_size,box_color):
|
| 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_EYE,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_EYE,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/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
|
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,296 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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,POINT_RIGHT_LOWER_INNER_EYE_1]
|
| 168 |
+
]
|
| 169 |
+
|
| 170 |
+
POINT_RIGHT_LOWER_OUTER_EYE_1 = 112
|
| 171 |
+
POINT_RIGHT_LOWER_OUTER_EYE_2 = 26
|
| 172 |
+
POINT_RIGHT_LOWER_OUTER_EYE_3 = 22
|
| 173 |
+
POINT_RIGHT_LOWER_OUTER_EYE_4 = 23
|
| 174 |
+
POINT_RIGHT_LOWER_OUTER_EYE_5 = 24
|
| 175 |
+
POINT_RIGHT_LOWER_OUTER_EYE_6 = 110
|
| 176 |
+
POINT_RIGHT_LOWER_OUTER_EYE_7 = 25
|
| 177 |
+
|
| 178 |
+
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]
|
| 179 |
+
|
| 180 |
+
LINE_RIGHT_LOWER_MIXED_EYE =[
|
| 181 |
+
[POINT_RIGHT_UPPER_INNER_EYE_8,POINT_RIGHT_UPPER_INNER_EYE_9,POINT_RIGHT_LOWER_INNER_EYE_1]
|
| 182 |
+
,[POINT_RIGHT_LOWER_INNER_EYE_2]
|
| 183 |
+
,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
|
| 184 |
+
,[POINT_RIGHT_UPPER_INNER_EYE_1,POINT_RIGHT_UPPER_OUTER_EYE_2] #combine 1 and 2 for move up
|
| 185 |
+
]
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
POINT_LEFT_UPPER_INNER_EYE_1 = 362
|
| 189 |
+
POINT_LEFT_UPPER_INNER_EYE_2 = 398
|
| 190 |
+
POINT_LEFT_UPPER_INNER_EYE_3 = 384
|
| 191 |
+
POINT_LEFT_UPPER_INNER_EYE_4 = 385
|
| 192 |
+
POINT_LEFT_UPPER_INNER_EYE_5 = 386
|
| 193 |
+
POINT_LEFT_UPPER_INNER_EYE_6 = 387
|
| 194 |
+
POINT_LEFT_UPPER_INNER_EYE_7 = 388
|
| 195 |
+
POINT_LEFT_UPPER_INNER_EYE_8 = 466
|
| 196 |
+
POINT_LEFT_UPPER_INNER_EYE_9 = 263
|
| 197 |
+
|
| 198 |
+
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]
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
POINT_LEFT_LOWER_INNER_EYE_1 = 249
|
| 203 |
+
POINT_LEFT_LOWER_INNER_EYE_2 = 390
|
| 204 |
+
POINT_LEFT_LOWER_INNER_EYE_3 = 373
|
| 205 |
+
POINT_LEFT_LOWER_INNER_EYE_4 = 374
|
| 206 |
+
POINT_LEFT_LOWER_INNER_EYE_5 = 380
|
| 207 |
+
POINT_LEFT_LOWER_INNER_EYE_6 = 381
|
| 208 |
+
POINT_LEFT_LOWER_INNER_EYE_7 = 382
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
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]
|
| 212 |
+
|
| 213 |
+
#outer
|
| 214 |
+
|
| 215 |
+
POINT_LEFT_UPPER_OUTER_EYE_1 = 463
|
| 216 |
+
POINT_LEFT_UPPER_OUTER_EYE_2 = 414
|
| 217 |
+
POINT_LEFT_UPPER_OUTER_EYE_3 = 286
|
| 218 |
+
POINT_LEFT_UPPER_OUTER_EYE_4 = 258
|
| 219 |
+
POINT_LEFT_UPPER_OUTER_EYE_5 = 257
|
| 220 |
+
POINT_LEFT_UPPER_OUTER_EYE_6 = 259
|
| 221 |
+
POINT_LEFT_UPPER_OUTER_EYE_7 = 260
|
| 222 |
+
POINT_LEFT_UPPER_OUTER_EYE_8 = 467
|
| 223 |
+
POINT_LEFT_UPPER_OUTER_EYE_9 = 359
|
| 224 |
+
|
| 225 |
+
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]
|
| 226 |
+
|
| 227 |
+
|
| 228 |
+
POINT_LEFT_LOWER_OUTER_EYE_1 = 255
|
| 229 |
+
POINT_LEFT_LOWER_OUTER_EYE_2 = 339
|
| 230 |
+
POINT_LEFT_LOWER_OUTER_EYE_3 = 254
|
| 231 |
+
POINT_LEFT_LOWER_OUTER_EYE_4 = 253
|
| 232 |
+
POINT_LEFT_LOWER_OUTER_EYE_5 = 252
|
| 233 |
+
POINT_LEFT_LOWER_OUTER_EYE_6 = 256
|
| 234 |
+
POINT_LEFT_LOWER_OUTER_EYE_7 = 341
|
| 235 |
+
|
| 236 |
+
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]
|
| 237 |
+
|
| 238 |
+
LINE_LEFT_UPPER_MIXED_EYE =[#firs eye1 and eye2 is intesionaly for moveup
|
| 239 |
+
[POINT_LEFT_UPPER_INNER_EYE_1,POINT_LEFT_UPPER_INNER_EYE_2,POINT_LEFT_LOWER_INNER_EYE_7],
|
| 240 |
+
[POINT_LEFT_UPPER_INNER_EYE_2,POINT_LEFT_UPPER_OUTER_EYE_2], [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]
|
| 241 |
+
,[POINT_LEFT_UPPER_INNER_EYE_8],[POINT_LEFT_UPPER_OUTER_EYE_8,POINT_LEFT_UPPER_INNER_EYE_9]
|
| 242 |
+
]
|
| 243 |
+
LINE_LEFT_LOWER_MIXED_EYE =[
|
| 244 |
+
[POINT_LEFT_UPPER_OUTER_EYE_8,POINT_LEFT_UPPER_INNER_EYE_9]
|
| 245 |
+
,[POINT_LEFT_LOWER_INNER_EYE_2]
|
| 246 |
+
,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
|
| 247 |
+
, [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
|
| 248 |
+
]
|
| 249 |
+
|
| 250 |
+
|
| 251 |
+
#LIP
|
| 252 |
+
LINE_RIGHT_UPPER_OUTER_LIP=[
|
| 253 |
+
61,185,40,39,37,0
|
| 254 |
+
]
|
| 255 |
+
LINE_LEFT_UPPER_OUTER_LIP=[
|
| 256 |
+
0,267,269,270,409,291
|
| 257 |
+
]
|
| 258 |
+
|
| 259 |
+
|
| 260 |
+
LINE_LOWER_OUTER_LIP=[291,#upper
|
| 261 |
+
375,321,405,314,17,84,181,91,146
|
| 262 |
+
,61 #upper
|
| 263 |
+
]
|
| 264 |
+
|
| 265 |
+
LINE_UPPER_INNER_LIP=[
|
| 266 |
+
61,185,40,39,37,0,267,269,270,409,291
|
| 267 |
+
]
|
| 268 |
+
|
| 269 |
+
LINE_LOWER_INNER_LIP=[291,#upper
|
| 270 |
+
375,321,405,314,17,84,181,91,146
|
| 271 |
+
,61 #upper
|
| 272 |
+
]
|
| 273 |
+
|
| 274 |
+
LANDMARK_68_UPPER_OUTER_LIP_49 =[61]
|
| 275 |
+
LANDMARK_68_UPPER_OUTER_LIP_50 =[40,39]
|
| 276 |
+
LANDMARK_68_UPPER_OUTER_LIP_51 =[37]
|
| 277 |
+
LANDMARK_68_UPPER_OUTER_LIP_52 =[0]
|
| 278 |
+
LANDMARK_68_UPPER_OUTER_LIP_53 =[267]
|
| 279 |
+
LANDMARK_68_UPPER_OUTER_LIP_54 =[270,269]
|
| 280 |
+
LANDMARK_68_UPPER_OUTER_LIP_55 =[291]
|
| 281 |
+
|
| 282 |
+
LANDMARK_68_LOWER_OUTER_LIP_56 =[375,321]
|
| 283 |
+
LANDMARK_68_LOWER_OUTER_LIP_57 =[405,314]
|
| 284 |
+
LANDMARK_68_LOWER_OUTER_LIP_58 =[17]
|
| 285 |
+
LANDMARK_68_LOWER_OUTER_LIP_59 =[84,181]
|
| 286 |
+
LANDMARK_68_LOWER_OUTER_LIP_60 =[146,91]
|
| 287 |
+
|
| 288 |
+
LANDMARK_68_UPPER_INNER_LIP_61 =[78]
|
| 289 |
+
LANDMARK_68_UPPER_INNER_LIP_62 =[81]
|
| 290 |
+
LANDMARK_68_UPPER_INNER_LIP_63 =[13]
|
| 291 |
+
LANDMARK_68_UPPER_INNER_LIP_64 =[311]
|
| 292 |
+
LANDMARK_68_UPPER_INNER_LIP_65 =[308]
|
| 293 |
+
|
| 294 |
+
LANDMARK_68_LOWER_INNER_LIP_66 =[402]
|
| 295 |
+
LANDMARK_68_LOWER_INNER_LIP_67 =[14]
|
| 296 |
+
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
|