Skip to content
Back to skills

Event Anchored Frame Sync

ASecurity

Align low-Hz sensor data to high-fps video by anchoring a named event (e.g. ball_snap) to a known frame index and converting time offsets via fps

  • 61 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 12, 2026
toolspython

Works with

  • cli

Security analysis

A100/100

Scanned September 12, 2026

npx -y skills add wenmin-wu/ds-skills --skill event-anchored-frame-sync --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Event Anchored Frame Sync?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Event Anchored Frame Sync
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/wenmin-wu-event-anchored-frame-sync/badge)](https://www.skillsdirectory.com/skills/wenmin-wu-event-anchored-frame-sync)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: timeseries-event-anchored-frame-sync
description: Align low-Hz sensor data to high-fps video by anchoring a named event (e.g. ball_snap) to a known frame index and converting time offsets via fps
---

## Overview

Sensor and video streams are rarely recorded with perfectly synchronized clocks. In the NFL Helmet Assignment competition, player tracking (NGS) is 10Hz with wall-clock timestamps while video is ~60fps counted from frame 0 — you cannot join them by timestamp. The trick is that both streams share a discrete labeled event: the `ball_snap`. If you know which video frame contains the snap (a constant per dataset), you can anchor the NGS row for `event == 'ball_snap'` to that frame, compute a `snap_offset` in seconds, multiply by fps, and derive `est_frame` for every NGS row. Result: a deterministic join key between the two streams with millisecond-level accuracy.

## Quick Start

```python
def add_est_frame(tracks, fps=59.94, snap_frame=10):
    tracks = tracks.copy()
    tracks['game_play'] = (tracks['gameKey'].astype(str) + '_'
                           + tracks['playID'].astype(str).str.zfill(6))
    tracks['time'] = pd.to_datetime(tracks['time'])

    # Anchor: for each play, find the wall-clock time of the ball_snap row
    snap_time = (tracks.query('event == "ball_snap"')
                 .groupby('game_play')['time'].first().to_dict())
    tracks['snap'] = tracks['game_play'].map(snap_time)

    # Offset from snap in seconds, then convert to frame index
    tracks['snap_offset'] = (
        (tracks['time'] - tracks['snap']).dt.total_seconds()
    )
    tracks['est_frame'] = (
        (tracks['snap_offset'] * fps) + snap_frame
    ).round().astype(int)
    return tracks
```

## Workflow

1. Identify a discrete event that appears in both streams (ball_snap, first-touch, shutter release)
2. Pick one row per session where `event == <anchor>` in the sensor stream
3. Find the known video frame index of the anchor event (often a constant from dataset metadata)
4. Compute `offset_sec = sensor_time - anchor_time` for every sensor row
5. Convert to `est_frame = round(offset_sec * fps + anchor_frame)`

## Key Decisions

- **Anchor event must be discrete and unique**: events that repeat (e.g. "whistle") break the alignment.
- **Watch for fps fractions**: NTSC video is 59.94 not 60; using integer fps drifts ~0.1% over long clips.
- **Nearest-neighbor lookup downstream**: even after sync, multiple sensor rows may fall between frames — use `find_nearest(est_frames, video_frame)` to pick one.
- **vs. timestamp-only join**: wall-clock timestamps drift between recorders; event anchoring cancels the absolute offset entirely.

## References

- [NFL Helmet Assignment - Getting Started Guide](https://www.kaggle.com/code/robikscube/nfl-helmet-assignment-getting-started-guide)
- [NFL Baseline - Simple Helmet Mapping](https://www.kaggle.com/code/its7171/nfl-baseline-simple-helmet-mapping)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.