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

Goal

Compute biomechanical joint angles from pose landmark data.

This notebook takes 2D pose landmarks extracted from YOLO pipleline and computes biomechanical joint angles per frame. It also includes confidence labels, plotting utilities, and optional batch processing.

You define each angle with three landmarks (A, B, C) where the angle is measured at B (angle ABC). This notebook supports both:

  • long format pose tables (landmark_name, x, y, optional visibility)

  • wide format pose tables (<landmark>_x, <landmark>_y, optional <landmark>_visibility)

1. Imports and Environment Check

Python: 3.12.12
Pandas: 2.3.3
NumPy: 2.3.5
Plotly: 6.6.0
Seaborn available: True

2. Load Pose CSV

Loaded: /Users/souvikmandal/Documents/S06_Teaching_Mentoring_Talks/LS100/2026_Sem01/media/Henry/test_videos/final_2ndorder_data_csv/LS100_FInalData_1_final_kinematics_healed_id_1.csv
Shape: (433, 40)
Columns (first 25): ['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']
Detected wide-format pose data.
Wide columns: 18 x-columns and 18 y-columns
Loading...

3. Define Custom Angles

Provide angle definitions as landmark triplets A-B-C where the angle is measured at B.

Example:

angle_defs_h1 = [
    {"name": "left_elbow", "A": "left_shoulder", "B": "left_elbow", "C": "left_wrist"},
]

angle_defs_h2 = [
    {"name": "right_knee", "A": "right_hip", "B": "right_knee", "C": "right_ankle"},
]

Computation Angles

Angle Computation Function

  • The following function calculates the angles you defined.

  • Please change the visibility_thresh according to your desire. This will determine the ‘confidence’ in each angle in each frame.

“good” → all three landmarks ≥ threshold
“low” → exactly one landmark < threshold
“least” → two or three landmarks < threshold

4. Normalize Data and Compute Angles

Normalized shape: (7794, 8)
Normalized columns: ['video', 'final_id', 'frame', 'time_ms', 'landmark_name', 'x', 'y', 'visibility']
Detected humans: ['human_1']
Angles shape: (433, 8)
Loading...

5. Save and Inspect Output

Saved angles to: /Users/souvikmandal/Documents/S06_Teaching_Mentoring_Talks/LS100/2026_Sem01/media/Henry/test_videos/final_2ndorder_data_csv/LS100_FInalData_1_final_kinematics_healed_id_1_angles.csv
Saved shape: (433, 8)
Loading...

6. Plot Computed Angles Over Time

This section visualizes computed angles by frame (or time when available). You can choose one backend: Matplotlib, Seaborn, or Plotly.

Confidence overlays:

  • Yellow: low confidence (one landmark below threshold)

  • Orange: least confidence (two or more landmarks below threshold)

Tip: if multiple humans are present, the plotting cell draws a separate chart per human and per angle.

Loading...
Loading...

7. Batch Process for a Folder of Pose CSV Files

This section processes all matching CSV files in a directory. For each file it:

  • normalizes schema to long format

  • computes angles per human

  • saves one per-file output

  • appends a row to a batch manifest with status and errors

Found 2 file(s) in /Users/souvikmandal/Documents/S06_Teaching_Mentoring_Talks/LS100/2026_Sem01/media/Henry/test_videos/final_2ndorder_data_csv matching '*.csv'.
[1/2] LS100_FInalData_1_final_kinematics_healed_id_1.csv
   -> Saved: LS100_FInalData_1_final_kinematics_healed_id_1_angles.csv (433 rows, 0.03s)
[2/2] LS100_FInalData_1_final_kinematics_healed_id_2.csv
   -> Saved: LS100_FInalData_1_final_kinematics_healed_id_2_angles.csv (432 rows, 0.03s)

Batch complete. Manifest saved to: /Users/souvikmandal/Documents/S06_Teaching_Mentoring_Talks/LS100/2026_Sem01/media/Henry/test_videos/final_2ndorder_data_csv/batch_angles_manifest.csv
Total elapsed: 0.1s

Conclusion

Congratulations! You’ve now completed a full pose analysis workflow in Python:

  1. Extracted 2D body landmark data from videos using MediaPipe (Notebook 1).

  2. Computed joint angles and derived confidence metrics based on landmark visibility.

  3. Visualized these angles interactively and in batches across multiple recordings.

What You’ve Learned

  • How pose landmarks are represented as normalized 2D coordinates.

  • How to calculate geometric angles (∠ABC) for any combination of body joints.

  • How to assess data reliability using a visibility-based confidence system (good, low, least).

  • How to produce meaningful visualizations and batch-process datasets efficiently.

Next Steps

In the next notebook, we will:

  • Derive custom biomechanical indices (e.g., symmetry or smoothness metrics).

  • Explore statistical summaries and comparisons across individuals or sessions.

Remember: Confidence flags and visualization overlays are not just for aesthetics—
they teach you to interpret pose-estimation data critically, separating signal from noise.