OpenPose Real-Time Skeletons
OpenPose finds every visible joint plus a field of limb-direction arrows in one network pass, then links joints into skeletons by checking which connections follow the arrows.
Why Does This Exist?
Multi-person pose has a grouping problem: finding all elbows in a crowd photo is easy, but knowing whose elbow belongs to whose wrist is hard. Top-down systems crop each person first and run pose per crop, so their cost grows with headcount and they miss people the person-detector drops. A busy plaza frame with twenty people needs twenty network passes.
OpenPose (Cao and colleagues, 2017) made the bottom-up answer practical. One network pass predicts every joint location plus, for each limb, a Part Affinity Field (PAF): a dense 2D arrow map pointing along that limb. Linking becomes a scoring problem over arrows, and crowds assemble in milliseconds after the single pass. The general pose setup lives in pose estimation; lifting these flat skeletons into depth lives in 3D human pose estimation. This page covers heatmaps, affinity fields and greedy assembly.
The one idea you need first. A joint heatmap is an image-sized score sheet with one bright peak per visible instance of that joint. Peaks give candidates; they never say which elbow pairs with which wrist, which is exactly the gap the arrow fields fill.
Think of It Like This
Wind arrows between landing beacons
Every airport beacon flashes its position, and wind arrows paint the sky along each approved flight corridor. Controllers connect two beacons when the straight path between them rides the painted wind instead of crossing it. Elbow-to-wrist linking works the same: the candidate pair whose segment best follows the limb arrows belongs to one arm.
Where it stops: when flight corridors cross in a storm, arrows tangle and even good controllers connect the wrong pair. Tangled crowds tangle affinity fields the same way.
How It Actually Works
Heatmaps for joints, arrow fields for limbs
A convolutional backbone feeds refinement stages that output two things. First, one heatmap per joint type (the original build predicts 19 sheets covering 18 joints plus background; COCO builds use its own keypoint set), where each pixel holds the confidence that joint sits there. Candidate joints come from non-maximum suppression over each heatmap. Second, one Part Affinity Field per limb type , where each pixel holds a 2D unit vector pointing from the limb's start joint toward its end joint (19 limbs times 2 direction numbers gives 38 channels in the original build).
Scoring a candidate limb
Take candidate joints (elbow) and (wrist) and the unit vector pointing from to . Sample evenly spaced points through along segment and average how well the field agrees with the segment:
where is the dot product, so each sample scores when the painted arrow points exactly along the segment and near when it points across it. Each limb type is then matched greedily: sort all candidate pairs by , link the best, remove its joints, and repeat. Low-total-score people are pruned at the end.
Worked assembly numbers
Elbow candidates and wrist candidates , with samples each. Segment points along and every field sample reads , a unit arrow whose dot with is ; the sum over 10 samples is , so . Rival segment samples read , each dotting to , so . Segment samples read , each dotting to , so . Greedy order links at first, removes and , then links at . The cross-connection never survives, which is the arrows doing the grouping work.
Code
pairs = [(("E1", "W1"), 0.9), (("E1", "W2"), 0.2), (("E2", "W2"), 0.85)]print(sorted(pairs, key=lambda p: p[1], reverse=True)[0][0])# -> ('E1', 'W1')Watch Out For
Greedy links swapping limbs in tangled crowds
Overlapping arms create plausible wrong pairings with high field scores. The symptom is swapped forearms in hugs, scrums and dance lines. Fix it by raising input resolution so fields separate, or switch to top-down methods when accuracy outranks speed.
Running the 2016-era model file today
Old checkpoints ship outdated joint sets and weak small-person recall. The symptom is missing ears and vanishing distant figures. Fix it by using maintained builds with current COCO keypoint outputs and modern backbones.
The Quick Version
- OpenPose predicts joint heatmaps plus limb-direction arrow fields in a single pass.
- Each candidate limb scores the average arrow alignment along its segment.
- Greedy matching links the best pair first, then removes those joints and repeats.
- Bottom-up cost stays flat as headcount grows, unlike per-person top-down crops.
- Tangled crowds are the accuracy ceiling, where arrows overlap and links swap.