Read the CSV
Use Frame as a dictionary key. Rows may be out of order or missing.
positions = load_positions(csv_path)
row = positions.get(frame_index)YOUR PROJECT / PYTHON + VIDEO + CSV
Read a ball’s coordinates from a CSV, remember its recent positions with a deque, and draw an effect on every video frame. Then change the visual design and test your version.
The default mode renders supplied positions. The optional small-CNN experiment below learns to estimate a center from image pixels. Coordinates can come from manual labels, a simple color rule, or a model. Those methods need different evidence; the same drawing loop can use any of them.
Try it directly in your browser—no notebook download needed. Choose a video, play both views, change the effect, inspect frames and edit coordinates. If you already have the complete local workshop, the Python files are in its take-home-code folder: open them there. The download links make individual copies; they do not include the matching videos and CSV data. Use the setup check below before rendering with Python.
BUILD THE LOOP
Use Frame as a dictionary key. Rows may be out of order or missing.
positions = load_positions(csv_path)
row = positions.get(frame_index)A bounded deque keeps the newest eight observations. A missing position inserts None.
history = deque(maxlen=8)
history.appendleft(point_or_none)Connect adjacent valid observations. Break the line at a missing row; do not invent an unseen path.
success, image = capture.read()
draw_frame(image, history, point)Continue until decoding ends, using the original dimensions and frame rate.
writer.write(image)
frame_index += 1Browser route: no Python, terminal or notebook is required for the interactive preview. Finish the first three guided steps here.
Python route: use Python 3.10 or newer and a complete local workshop copy. A terminal is the window where you type commands. Its current folder matters: open it in take-home-code, beside render_trail.py, check_setup.py and requirements.txt. Keep the neighboring take-home-football-tracker/assets folder; that is where the videos live.
python --version to check the interpreter. On macOS, use python3 consistently if that is your command.python check_setup.py. This read-only check needs no extra packages. Expect the soccer video hash to match and 179 CSV rows. It does not yet check video decoding.python check_setup.py --decode. Expect all 179 frames at 24 fps, size 960 × 540, and in-bounds positions.outputs/soccer-trail.mp4 in a local video player. Compare it with the original before changing the code.If a check fails, read its exact message before proceeding. A matching file hash confirms the supplied video identity; it does not prove the coordinate labels are accurate. The setup check writes no files, installs nothing and makes no network requests.
python -m pip install -r requirements.txt
python render_trail.py --video ../take-home-football-tracker/assets/single-ball.mp4 --positions data/soccer.csv --output outputs/soccer-trail.mp4 --trail-length 8On macOS, use python3. Add --effect rock --radius 105 for a rock sticker, --effect capture for the illustrated ball, or --save-frames outputs/frames to save individual images. The reference writes all decoded frames, including frames with missing CSV positions.
TRY IT NOW / INTERACTIVE BROWSER DEMO
Choose an image to use your own sticker. Transparent PNG keeps the background visible.
The image stays in this browser. Uploading selects “My image sticker” automatically. Artwork radius changes its size; the selected CSV or CNN supplies its position.
Loading the supplied CSV…
The required columns are Frame, Visibility, X and Y. Frame numbers start at zero and coordinates use the original video’s pixel dimensions. Visibility=0 means no point should be drawn for that frame. Source is an extra provenance column.
Frame,Visibility,X,Y,Source
0,0,,,none
1,1,120,80,example_only
2,1,124,83,example_only
3,0,,,noneThe four rows above illustrate the schema only. They are not measurements for either supplied video.
Matching the video, frame numbers, resolution and frame rate is part of your project. A custom video clears old positions so a previous clip’s coordinates cannot silently carry over.
TRAIN IT HERE / THREE SMALL CNNS
Train three randomly initialized networks directly in this browser on the selected video. Each takes a 32 × 18 RGB frame and predicts two normalized center coordinates. All three use the same chronological 60% training / 20% validation / 20% test split and seed 314. The best validation epoch is kept; the final test frames are used only for the comparison table.
Select a video, then train. Everything runs locally; no model download or image upload is needed.
| Method | Parameters | Test center error | Image diagonal | Train + evaluation |
|---|
Compare with the baseline. Predicting the average training position can already work well when a ball barely moves. More layers do not guarantee a better result. Tiny B has fewer parameters than Tiny A because its second pooling step shrinks the dense layer. After training, choose a CNN under Position source above to drive the rock sticker with its actual predictions. CSV trails are hidden in CNN mode; predictions update at up to about 10 Hz.
These are center regressors trained on visible frames from one clip—not pretrained object detectors. They always output a point, including when no ball is visible. The labels are supplied coordinate estimates, so the table measures agreement with held-out CSV coordinates, not verified tracking accuracy. A new clip or edited CSV clears the trained models. Neighboring frames remain related even with a chronological split; testing on a different labeled video is a separate challenge.
Tiny A: Conv(4) → MaxPool → Flatten → Dense(2). Tiny B: Conv(4) → MaxPool → Conv(8) → MaxPool → Flatten → Dense(2). Tiny C: Conv(8) → MaxPool → Conv(12) → MaxPool → Flatten → Dense(2). All convolutions use 3 × 3 kernels, same padding and ReLU; the output uses sigmoid. Adam uses learning rate 0.005. Loss is the mean squared difference between predicted and supplied normalized x/y coordinates.
target = [x / video_width, y / video_height]
loss = ((prediction - target) ** 2).mean()
optimizer.zero_grad()
loss.backward()
optimizer.step()The downloadable Python implementation uses PyTorch. The browser version uses the bundled TensorFlow.js runtime on CPU. Their architectures match; identical weights or cross-runtime results are not promised.
READ AND CHANGE THE PYTHON
Complete the notebook’s CSV/history/frame-loop exercises, then choose a change that affects the result: fade older points, vary line width with speed, draw an original object, or compare two ways to obtain coordinates.
Loading Python…The orange-ball script measures colored connected components in each frame. It is useful for this clip’s color and background, but it can confuse similar colors and lose an occluded ball. It supplies estimates, not ground truth.
python measure_orange_ball.py --video ../take-home-football-tracker/assets/ping-pong.mp4 --output outputs/my-ping-pong.csv
python render_trail.py --video ../take-home-football-tracker/assets/ping-pong.mp4 --positions outputs/my-ping-pong.csv --output outputs/my-ping-pong-trail.mp4
Explore the separate CNN detection demo if you want to compare a learned detector with other coordinate sources.