lukassso commited on
Commit
d3e8abf
·
1 Parent(s): 3607858

feat: add complete Gradio pose detection app using MoveNet

Browse files
Files changed (3) hide show
  1. app.py +46 -0
  2. download_movenet.py +0 -9
  3. poser.py +67 -0
app.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import PIL.Image
3
+ from poser import draw_bones, movenet
4
+ import tensorflow as tf
5
+ import numpy as np
6
+
7
+ def predict(image: PIL.Image.Image):
8
+ input_size = 256
9
+ image = image.resize((1280, 1280))
10
+ image_tf = tf.keras.preprocessing.image.img_to_array(image)
11
+
12
+ # Prepare input for the model
13
+ input_image = tf.expand_dims(image_tf, axis=0)
14
+ input_image = tf.image.resize_with_pad(input_image, input_size, input_size)
15
+
16
+ # Run MoveNet pose estimation
17
+ keypoints = movenet(input_image)
18
+
19
+ # Draw bones on the image
20
+ joints = draw_bones(image, keypoints)
21
+
22
+ # Format points as text
23
+ points = [f"{label} → ({int(float(x))}, {int(float(y))})" for label, x, y in joints]
24
+
25
+ return image, joints, points
26
+
27
+ with gr.Blocks(title="MoveNet Pose Estimation") as demo:
28
+ gr.Markdown("# 🧍‍♀️ Human Pose Estimation with MoveNet")
29
+ gr.Markdown("Upload an image to detect body keypoints and view the skeleton overlay.")
30
+
31
+ with gr.Row():
32
+ with gr.Column():
33
+ input_image = gr.Image(type="pil", label="Input Image")
34
+ run_button = gr.Button("Detect Pose", variant="primary")
35
+ with gr.Column():
36
+ output_image = gr.Image(type="numpy", label="Skeleton Output")
37
+ joints_table = gr.Dataframe(headers=["Label", "X", "Y"], row_count=17, col_count=(3, "fixed"))
38
+ point_text = gr.Textbox(label="Formatted Keypoints", lines=8)
39
+
40
+ run_button.click(
41
+ fn=predict,
42
+ inputs=[input_image],
43
+ outputs=[output_image, joints_table, point_text]
44
+ )
45
+
46
+ demo.launch()
download_movenet.py DELETED
@@ -1,9 +0,0 @@
1
- import tensorflow as tf
2
- import tensorflow_hub as hub
3
- import os
4
-
5
- save_path = "movenet_saved_model"
6
- model_url = "https://tfhub.dev/google/movenet/singlepose/thunder/4"
7
- model = hub.load(model_url)
8
- tf.saved_model.save(model, export_dir=save_path)
9
- print(f"Model saved to: {os.path.abspath(save_path)}")
 
 
 
 
 
 
 
 
 
 
poser.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import tensorflow as tf
2
+ import numpy as np
3
+ from PIL import ImageDraw, ImageFont
4
+ from huggingface_hub import snapshot_download
5
+
6
+ # Keypoint labels for MoveNet (17 human body parts)
7
+ KEYPOINT_LABELS = {
8
+ 'nose': 0, 'left_eye': 1, 'right_eye': 2, 'left_ear': 3, 'right_ear': 4,
9
+ 'left_shoulder': 5, 'right_shoulder': 6, 'left_elbow': 7, 'right_elbow': 8,
10
+ 'left_wrist': 9, 'right_wrist': 10, 'left_hip': 11, 'right_hip': 12,
11
+ 'left_knee': 13, 'right_knee': 14, 'left_ankle': 15, 'right_ankle': 16
12
+ }
13
+
14
+ # Stylish connection colors for the skeleton bones
15
+ SKELETON_EDGES = {
16
+ (0, 1): '#FF66CC', (0, 2): '#66FFFF', (1, 3): '#FF66CC', (2, 4): '#66FFFF',
17
+ (0, 5): '#FF99CC', (0, 6): '#99FFFF', (5, 7): '#FF6699', (7, 9): '#FF3366',
18
+ (6, 8): '#66CCCC', (8, 10): '#33CCCC', (5, 6): '#CCCC00', (5, 11): '#FF9966',
19
+ (6, 12): '#66FF99', (11, 12): '#999900', (11, 13): '#FF6600', (13, 15): '#FF3300',
20
+ (12, 14): '#00CC99', (14, 16): '#009966'
21
+ }
22
+
23
+ def process_keypoints(prediction, img_height, img_width, confidence=0.12):
24
+ all_joints = []
25
+ all_bones = []
26
+
27
+ instance_count = prediction.shape[1]
28
+ for i in range(instance_count):
29
+ x_coords = prediction[0, i, :, 1] * img_width
30
+ y_coords = prediction[0, i, :, 0] * img_height
31
+ scores = prediction[0, i, :, 2]
32
+ labels = list(KEYPOINT_LABELS.keys())
33
+
34
+ keypoints = np.stack([labels, x_coords, y_coords], axis=-1)
35
+ visible_kpts = keypoints[scores > confidence]
36
+ all_joints.append(visible_kpts)
37
+
38
+ for (a, b), color in SKELETON_EDGES.items():
39
+ if scores[a] > confidence and scores[b] > confidence:
40
+ segment = np.array([[x_coords[a], y_coords[a]], [x_coords[b], y_coords[b]]])
41
+ all_bones.append((segment, color))
42
+
43
+ return np.concatenate(all_joints, axis=0), all_bones
44
+
45
+ def draw_bones(image, keypoints):
46
+ draw = ImageDraw.Draw(image)
47
+ font = ImageFont.load_default()
48
+
49
+ joints, bones = process_keypoints(keypoints, image.height, image.width)
50
+
51
+ for bone, color in bones:
52
+ draw.line((*bone[0], *bone[1]), fill=color, width=3)
53
+
54
+ for label, x, y in joints:
55
+ cx, cy = float(x), float(y)
56
+ radius = 4
57
+ draw.ellipse([(cx - radius, cy - radius), (cx + radius, cy + radius)], fill="#FF0000", outline="#222222")
58
+ draw.text((cx + 5, cy - 5), label, font=font, fill="#0000CC")
59
+
60
+ return joints
61
+
62
+ def movenet(image):
63
+ model_path = snapshot_download("lukassso/movenet-myking")
64
+ model = tf.saved_model.load(model_path).signatures["serving_default"]
65
+ image = tf.cast(image, tf.int32)
66
+ result = model(image)
67
+ return result["output_0"].numpy()