Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Authored by Souvik Mandal, Ph.D.

Project Leader & Instructor, Computational Behavioral Sciences, LS100, FAS, Harvard University | Linkedin ID: souvik-mandal-phd


Last updated: 2026-07-02

Open in Colab Open in GitHub Codespaces

In this notebook, we will dive into the world of Computer Vision to extract the movement patterns of athletes. This data will allow you to analyze athletic performance, swing mechanics, and body kinematics. Before we touch the code, it is vital to understand the “brain” of the AI you are using. Pose estimation has evolved rapidly from simple shape detection to complex models that understand how human joints relate to each other in 3D space.

Understanding the AI Leaderboard (2026 Edition)

To choose the right tool for your specific sport (whether it’s a single-person golf swing or a 22-person soccer match), you need to compare models across several dimensions. Below is a comprehensive guide to the state-of-the-art models available today.

ModelArchitecture TypeSource / CreatorYear (First/Latest)ProsConsSpeedAccuracyEase of DeploymentBest Use CaseDocumentation
YOLO26-PoseSingle-Stage (CNN/Hybrid)Ultralytics2016 / 2026Natively end-to-end; extremely fast; built-in tracking.Can be “jittery” in extremely crowded scenes.Ultra HighHighVery EasyReal-time edge AI, live sports broadcasting.Docs
MediaPipe (BlazePose)Two-Stage (Detector + Tracker)Google2020 / 202633 Landmarks (includes fingers/toes); runs in browsers.Optimized for single-person; struggles with overlap.Ultra HighModerateVery EasyMobile fitness apps, Yoga, rehab tracking.Docs
ViTPose++Vision Transformer (ViT)Shanghai AI Lab2022 / 2026SOTA accuracy; handles multiple datasets; stable keypoints.Very heavy; requires high-end GPU.LowUltra HighModerateProfessional biomechanics research.Paper
RTMPoseTop-Down (SimCC Head)OpenMMLab2023 / 2025Incredible speed/accuracy balance; avoids heatmap lag.Requires separate person detector.HighHighModerateIndustrial production, high-speed sports tracking.Docs
HRNetHigh-Resolution (Parallel Net)Microsoft2019 / 2024Maintains high-res features; very reliable “gold standard.”Computationally heavy compared to mobile nets.ModerateHighModerateMedical/Gait analysis, precision joint angles.Paper
MoveNetBottom-Up (Heatmap-based)Google2021 / 2025Highly optimized for browsers and mobile phones.Primarily single-person; lower precision.Ultra HighModerateVery EasyMobile fitness apps, Yoga/Pilates home tracking.TF Hub
AlphaPoseTop-Down (Two-Stage)SJTU (MVIG)2017 / 2025Excellent multi-person tracking; handles overlap/occlusion.Heavier resource usage; complex setup.ModerateHighDifficultCrowd analysis, contact sports (Rugby, Wrestling).Docs
PoseidonTemporal TransformerUniv. of Salerno2025 / 2026Uses multi-frame “memory” to see through hidden limbs.Newer research model; higher latency.ModerateUltra HighDifficultFast-action sports (Baseball pitching, Gymnastics).Paper

Key Terminology for Your Project

As you compare these models, you will encounter specific technical metrics. Understanding these will help you justify why you chose a specific model size (Nano vs. Large) for your student project:

  • mAPpose 50-95 (e2e): Mean Average Precision. This is the AI’s “final grade.” It measures how often the predicted joint is within a certain distance of the actual joint. “50-95” means it averages the accuracy across very easy (50%) and very strict (95%) thresholds.

  • mAPpose 50 (e2e): A more “forgiving” score. It counts a detection as correct if the AI is roughly in the right area. If the model guesses the joint location and it overlaps the true location by at least 50%, it gets a passing grade.

  • Speed CPU ONNX (ms): How long it takes to process one frame on a standard laptop (no graphics card). Lower is better. As 1000ms = 1 second, if a model takes 100ms, it processes at 10 Frames Per Second. If it’s 33ms or lower, you can run at 30 FPS (real-time).

  • Speed T4 TensorRT10 (ms): The speed when using a professional NVIDIA GPU. This is usually 10x to 50x faster than a CPU.

  • Params (M): Millions of “Parameters.” Think of this as the size of the AI’s brain. In general, more parameters = better, but slower and requires more memory. It can vary from Nano (~3M) to Extra Large (~60M).

  • FLOPs (B): “Billions of Floating Point Operations” or billions of math operations per second. Higher FLOPs mean your computer will work harder and generate more heat, and your computer’s fan is going to spin a lot louder!

  • Size (pixels): The resolution the image is resized to before the AI processes it. Usually, 640 means the video is resized to 640×640640 \times 640. Higher resolution helps find small objects (like a ball) but slows down the speed.



In this notebook, we will extract the exact mathematical coordinates of human joints from video files using YOLO.

Why YOLO26?

You may have heard of other pose estimation tools. While many are incredibly fast and great for single-person, or webcam-based applications (like a fitness app on your phone), it traditionally struggles when multiple people are in the frame, or if it doesn’t know exactly where to look.

We are using YOLO26-pose (You Only Look Once) as YOLO is built differently. It is fundamentally an Object Detection model.

  1. First, it scans the entire image to find all the humans.

  2. Then, it draws a bounding box around every person it finds.

  3. Finally, it calculates the skeletal keypoints inside each of those boxes.

This makes YOLO incredibly robust for sports where multiple people are moving around. The beautiful thing about this model is that it is “pre-trained.” Researchers have already shown it hundreds of thousands of images of humans, so it already knows how to find 17 specific body joints (called keypoints) right out of the box.

If you have not already, please install the ultralytics library, which contains the YOLO models.

1. Loading the Model

Now we will import the library and load our model. We are using yolo11n-pose.pt.

  • 26 is the version.

  • n stands for “nano”, meaning it’s the smallest, fastest version (perfect for processing video quickly on a laptop).

  • pose means it’s specifically designed to find skeletal keypoints, not just draw boxes around things.

  • You can use the other models as follows. Change the name of the model accordingly in the next cell.

2. Running Inference (Making Predictions)

Let’s test the AI on a single image. You can upload an image of a person to your notebook and change the file name below, or we can use a sample image provided by the library.

When we pass an image through the model, it returns a results object containing all the math and coordinates it calculated.


image 1/1 /Users/souvikmandal/Documents/S06_Teaching_Mentoring_Talks/LS100/2026_Sem01/media/Henry/Position_right_cam_corrected.jpg: 384x640 1 person, 39.0ms
Speed: 1.7ms preprocess, 39.0ms inference, 0.2ms postprocess per image at shape (1, 3, 384, 640)
<Figure size 1000x800 with 1 Axes>
Ultralytics 8.4.27 🚀 Python-3.12.12 torch-2.11.0 CPU (Apple M2 Max)
Setup complete ✅ (12 CPUs, 64.0 GB RAM, 1072.7/1858.2 GB disk)

Total CPU cores detected: 12

3. Configure Video Inputs and YOLO Settings

This cell prepares the pipeline before full-frame processing begins.

The following cell will:

  1. Accept either a single video path or a folder containing multiple videos (INPUT_VIDEO_PATH).

  2. Collect and validate supported video files (INPUT_VIDEO_FILES).

  3. Print which video files were found for analysis.

  4. Set YOLO pose-tracking parameters (model, confidence, IoU, image size, tracker).

  5. Create default output file paths (RAW_CSV_PATH, HEALED_CSV_PATH, FINAL_CSV_PATH, OUTPUT_VIDEO_PATH) for downstream steps.

  6. Define the COCO keypoint name list (KEYPOINT_NAMES).

Found 4 video(s) for analysis.
  - LS100_FInalData_1.mov
  - LS100_FInalData_2.mov
  - LS100_FInalData_3.mov
  - LS100_FInalData_4.mov

Step 1: Raw Keypoint Extraction with YOLO Tracking

This step runs the core pose-tracking engine on each input video and saves frame-by-frame raw coordinates.

What the next code cell does:

  1. Defines step_1_extract_raw_data(...) to process one video end-to-end.

  2. Detects hardware automatically (CUDA GPU if available; otherwise CPU, including a stable CPU fallback on Apple Silicon).

  3. Reads video metadata (FPS, width, height, total frame count) using OpenCV.

  4. Runs YOLO(...).track(...) with your dashboard settings (CONFIDENCE, IOU_THRESHOLD, IMAGE_SIZE, TRACKER_FILE).

  5. Iterates through each frame result, extracts tracked person IDs and 17 keypoints, and builds a raw pose table.

  6. Saves one raw CSV per video (e.g., *_raw_tracks.csv).

  7. Repeats this for all videos in INPUT_VIDEO_FILES, stores per-video run info in video_run_registry, and combines all raw outputs into combined_raw_df.

  8. Keeps backward compatibility by setting default downstream variables (RAW_CSV_PATH, FPS, WIDTH, HEIGHT, TOTAL_FRAMES) from the first selected input video.

Index(['frame', 'raw_id', 'nose_x', 'nose_y', 'left_eye_x', 'left_eye_y',
       'right_eye_x', 'right_eye_y', 'left_ear_x', 'left_ear_y', 'right_ear_x',
       'right_ear_y', 'left_shoulder_x', 'left_shoulder_y', 'right_shoulder_x',
       'right_shoulder_y', 'left_elbow_x', 'left_elbow_y', 'right_elbow_x',
       'right_elbow_y', 'left_wrist_x', 'left_wrist_y', 'right_wrist_x',
       'right_wrist_y', 'left_hip_x', 'left_hip_y', 'right_hip_x',
       'right_hip_y', 'left_knee_x', 'left_knee_y', 'right_knee_x',
       'right_knee_y', 'left_ankle_x', 'left_ankle_y', 'right_ankle_x',
       'right_ankle_y'],
      dtype='str')

Step 2: Identity Healing & Smart Anchoring

The Problem: “Clinch Amnesia” & “The Walkaway” Combat sports are chaotic. When fighters clinch, grapple, or obscure each other from the camera, the AI briefly loses sight of them. Worse, the moving fighter often strikes, walks completely out of the camera frame, and re-enters seconds later.

When this happens, the raw tracking data treats them as a brand-new person, leaving us with fragmented IDs (e.g., human_1, human_2, human_3). To fix this, we will stitch these broken trajectories back together in three phases:

1. Finding the Center of Mass (The Torso) Raw tracking data gives us points for wrists, ankles, and noses. Because limbs move wildly during a strike, they are terrible points to track for overall body movement. First, our code will average the coordinates of the shoulders and hips to calculate a highly stable “Torso Center” for each person.

2. The Hungarian Algorithm (Connecting the Dots) Next, we use a classic mathematical optimization method called the Hungarian Algorithm to fix minor ID swaps.

  • It looks at the last known position of a fighter and compares it to unidentified bodies in the current frame.

  • It calculates the physical pixel distance and finds the most logical 1-to-1 pairing.

  • We apply a max_distance speed limit (100 pixels) so the model doesn’t accidentally “teleport” a fighter across the screen if they temporarily leave the frame.

3. Smart Anchoring (The Process of Elimination)| Finally, we need to definitively lock in who is the stationary target (like a coach holding mitts) and who is the moving fighter. Since we know the moving fighter often leaves the frame, we can’t just count on having only two IDs. Instead, we use a smart process of elimination:

  • Filter Glitches: We ignore any ID that appears for less than 15 frames (half a second) to filter out AI ghost detections (like a referee’s foot).

  • Find the Anchor: We look at the total variance (spread of movement) for the remaining IDs over the entire video. The person who moved the least is mathematically locked in as human_1 (Stationary).

  • Process of Elimination: Since we know human_1 is the stationary target, any other valid ID must be our moving fighter! We merge all remaining valid tracks into human_2. This perfectly stitches together their data, even if they left the frame and came back as a “new” person!

Step 3: Signal Smoothing (Getting Rid of the Jitters)

The Problem: “The Amplification of Noise” AI pose tracking is incredible, but it isn’t perfect. Even when a fighter is standing still, the detected coordinates for their joints will vibrate or “jitter” by a few pixels from frame to frame.

If we calculate the speed of a strike using raw, jittery data, the math will exaggerate every tiny mistake. A 5-pixel AI glitch in a fraction of a second will look like the fighter’s hand suddenly accelerated to 100 miles per hour!

The Solution: The Rolling Average Filter To fix this, we need to pass our data through a mathematical iron to smooth out the wrinkles. We will use a Rolling Average Filter.

  • For every single frame, the code will look at a small “window” of frames around it (e.g., the 3 frames before and the 3 frames after).

  • It calculates the average position of the joint across that window and replaces the jittery center point with this smooth average.

  • Crucial Rule: We must apply this filter to human_1 and human_2 separately. If we don’t group the data by their ID first, the code might accidentally average the stationary fighter’s coordinates with the moving fighter’s coordinates, teleporting them both to the middle of the ring.

We will utilize Pandas’ built-in .rolling() function. It is incredibly robust, easy to read, and handles edge cases (like the very first frame of the video) gracefully by using the min_periods=1 argument.

Step 4: Seperating data for each individual

In this step, we will separate the rows that belongs to each individual and create separate dataframes. This will be helpful for downstream movement analysis of individuals.

Step 5: Visualizing the Healed Skeletons

The Payoff: Creating the “After” Video We have successfully identified the stationary target (human_1), isolated our moving fighter (human_2), fixed the ID swaps, and ironed out the AI tracking jitters. Now, it is time to prove it.

Optional: Generating the “Before” Video

What does raw AI tracking look like? Before we clean up the data, let’s see what the AI actually “sees” out of the box. We are going to run the raw YOLO26 model on our video and tell it to save the output.

When you watch this video, pay close attention to the bounding boxes and the ID numbers floating above the fighters. You will likely notice:

  • The Clinch Amnesia: IDs swapping when the fighters cross paths.

  • The Ghost Detections: The AI briefly thinking a poster or a referee is one of the fighters.

  • The Jitters: The skeleton vibrating even when the fighter is still.

This chaotic video is why we needed to build our data pipeline!

Step 6: Visualizing the Healed Skeletons

The Payoff: Creating the “After” Video We have successfully identified the stationary target (human_1), isolated our moving fighter (human_2), fixed the ID swaps, and ironed out the AI tracking jitters. Now, it is time to prove it.

We are going to use OpenCV (Computer Vision) to open the original video, read our cleaned data frame-by-frame, and draw our custom skeletons directly onto the video.

To make it easy to see our success, we will color-code the fighters:

  • 🔵 Blue: The Stationary Anchor (human_1)

  • 🔴 Red: The Moving Fighter (human_2)

Watch the resulting video carefully. You should see the red and blue skeletons stay perfectly locked onto the correct fighters, even when they cross paths or leave the frame!