Find a body's keypoints
A detector boxes each person, then a pose model finds 17 keypoints inside each box.
Your photos and camera frames are processed on this device and never uploaded.
left side right side nose The person's own left and right. Tap a box to choose a person.
1. Find each person YOLOS-Tiny
Loading the recorded results
2. Find 17 keypoints in each box ViTPose+ small
Loading the recorded results
Challenges
0 of 4 complete
- On the shot put photo, where her legs leave the frame, find a keypoint of the shot putter that the model still places but scores under 0.30. (not yet)
- On the handstand, find where the model puts his ankles. (not yet)
- Read the judo photo as one box, so both judoka are in the box the pose model reads. (not yet)
- The detector finds no person in the yoga statue. Get a skeleton for it anyway. (not yet)
Try this
- On the balance beam, raise the threshold slowly and watch which keypoints disappear first.
- Switch to Heatmap and step through the keypoints in the table. Where is the bump tight, and where is it spread out?
- On the shot put photo, find the keypoints below the edge of the frame. Where does the model put them, and how sure is it?
- Read the marathon start with Whole picture, then with One per person.
- With both models downloaded, stand in front of the camera and turn sideways, cross your arms, or hide a hand behind your back.
Reading a keypoint from its heatmapPython with NumPy
import numpy as np
# A pose model's heatmap for one keypoint: 64 rows by 48 columns, a quarter of
# its 256 x 192 input. Training taught it to draw a bump where the keypoint is;
# here we draw one at column 30.4, row 20.7, peaking at 0.9.
rows, cols = np.mgrid[0:64, 0:48]
heatmap = 0.9 * np.exp(-((cols - 30.4) ** 2 + (rows - 20.7) ** 2) / (2 * 2.0 ** 2))
# Take the peak: the highest cell, and its value as the confidence.
row, col = np.unravel_index(np.argmax(heatmap), heatmap.shape)
print(f"peak at column {col}, row {row}, score {heatmap[row, col]:.2f}")
# Refine to a fraction of a cell. The log of a Gaussian bump is a parabola,
# so fit one through the peak and its neighbors on each axis.
log = np.log(heatmap)
dx = (log[row, col + 1] - log[row, col - 1]) / 2
dxx = log[row, col + 1] - 2 * log[row, col] + log[row, col - 1]
dy = (log[row + 1, col] - log[row - 1, col]) / 2
dyy = log[row + 1, col] - 2 * log[row, col] + log[row - 1, col]
x, y = col - dx / dxx, row - dy / dyy
print(f"refined to column {x:.2f}, row {y:.2f}")
# Map back to the photo. The person's box became a region 300 x 400 pixels
# centered at (500, 350); the heatmap's first and last cells sit on its edges.
cx, cy, w, h = 500, 350, 300, 400
px = cx - w / 2 + x * w / (48 - 1)
py = cy - h / 2 + y * h / (64 - 1)
print(f"keypoint in the photo: x {px:.1f}, y {py:.1f}")peak at column 30, row 21, score 0.87
refined to column 30.40, row 20.70
keypoint in the photo: x 544.0, y 281.4Photos, all CC0 on Wikimedia Commons: Balance beam by Anefo (Nationaal Archief); Hurdles by Anefo (Nationaal Archief); Marathon start by Anefo (Nationaal Archief); Judo by Ron Kroon for Anefo (Nationaal Archief); Handstand by Nationaal Archief; Shot put by Anefo (Nationaal Archief); High jump by Eric Koch for Anefo (Nationaal Archief); Yoga statue by Apurv013. The Dutch National Archives (Nationaal Archief), which holds the rights to the Anefo photos and the handstand, released them into the public domain.