# druv022/sports-analytics architecture

> Architecture diagram of the GitHub repository druv022/sports-analytics, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-06-24). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/druv022/sports-analytics
- Repository: https://github.com/druv022/sports-analytics

## Overview

1. **What this repo does**
   - This is a Python pipeline that turns a sports broadcast MP4 into per-camera on-screen text timelines.
   - The core output is a set of CSV/JSON artifacts in an output directory, plus an optional interactive timeline viewer.

2. **Top-level execution flow**
   - `main.py` is the primary entrypoint for batch processing.
   - The pipeline is stage-based and resumable, with named steps: `meta → extract → filter → appearance → cameras → ocr → reference → enrich → associate → aggregate`.
   - The README’s “resume / from-step / to-step” behavior indicates these stages are the main architectural boundaries.

3. **Orchestration layer**
   - `src/broadcast_pipeline/` is the central orchestration package.
   - It contains the pipeline controller, config, progress tracking, artifact handling, scene I/O, preflight checks, and the stage-specific coordination code.
   - This layer connects all subsystem outputs into the final timeline datasets.

4. **Video scene handling**
   - `scene_extractor.py`, `scene_filter.py`, `scenes_io.py`, `video_meta.py`, and `preflight.py` handle video ingestion and scene segmentation.
   - This stage converts raw video into scene-level units that downstream camera and OCR logic consume.
   - The output of this stage is the temporal backbone of the whole system.

5. **Camera assignment subsystem**
   - `src/camera_assignemnt/` contains the logic for identifying and grouping broadcast camera shots.
   - It is split into three approaches:
     - `embedding_cluster/` for appearance/embedding-based clustering,
     - `homography/` for court geometry and projection-based validation,
     - `scene_classifier/` for scene-type classification.
   - This is a major decision point in the pipeline because camera identity determines how text timelines are grouped.

6. **Person appearance subsystem**
   - `src/person_appearance/` extracts and links person-level visual signatures.
   - It includes segmentation, color/profile extraction, appearance features, track linking, and signature generation.
   - The model and weights are materially important here: `yolo11n-seg.onnx` is part of the repository, and the README explicitly calls out a YOLO11n-seg download for this stage.

7. **OCR and text extraction subsystem**
   - `src/scene_ocr/` handles OCR-specific extraction, geometric normalization, readability/presence filtering, and optional VLM-assisted text handling.
   - `ocr_runner.py`, `ocr_autotune.py`, `text_reference.py`, `text_enrich.py`, and `text_associate.py` in the pipeline package integrate OCR output into the reference catalog and the final timeline.
   - The OCR path appears to combine standard OCR with an optional visual-language model client for harder cases.

8. **Aggregation and association**
   - `aggregator.py`, `camera_merge.py`, `camera_assignment.py`, and `text_associate.py` are the “join” layer of the system.
   - They merge scene, camera, appearance, and OCR outputs into camera-specific text events.
   - Final deliverables like `aggregated_complete.csv`, `aggregated_partial.csv`, `dropped_text.csv`, and `pipeline_summary.json` come from this layer.

9. **Interactive visualization subsystem**
   - `src/broadcast_pipeline/viz/` plus `static/timeline_viz/` provide the browser-based timeline viewer.
   - The viewer supports timeline search, frame previews, camera collages, and camera comparison.
   - `scripts/serve_timeline_viz.py` runs this locally, while `docker/viz/` packages the same UI for deployment.

10. **Infrastructure and deployment**
   - `docker/viz/` contains a containerized viz service with prewarming and export scripts.
   - `cloudbuild.yaml` suggests Google Cloud Build / Cloud Run deployment for the visualization app.
   - The live demo URL in the README confirms this is an externally hosted, data-baked UI rather than only a local tool.

11. **Acceleration and environment support**
   - `src/accelerator/` abstracts device/runtime selection, with tests around device behavior.
   - The README distinguishes CPU-only vs GPU installs, so hardware capability affects performance and some stage choices.
   - `pyproject.toml` and the extras mentioned in the README indicate the repo is designed for optional feature-based installs.

12. **Supporting assets and third-party code**
   - `third_party/tennis_court_detector/` is vendored court-detection logic used by homography/court mapping workflows.
   - `models/`, `graphify-out/`, and the scripts in `scripts/` support training, evaluation, benchmarking, downloads, and ad hoc analysis.
   - Tests are organized to mirror the main subsystem boundaries, which reinforces the architecture: pipeline, camera assignment, appearance, OCR, accelerator, and viz.

Main entry points: [`src/broadcast_pipeline/orchestrator.py`](https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/orchestrator.py) (Orchestrator), [`src/broadcast_pipeline/camera_assignment.py`](https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/camera_assignment.py) (Camera assign), [`src/broadcast_pipeline/aggregator.py`](https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/aggregator.py) (Aggregator), [`src/person_appearance/extractor.py`](https://github.com/druv022/sports-analytics/blob/main/src/person_appearance/extractor.py) (Appearance)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_group_orchestration["Pipeline core"]
  node_node_main["main.py<br/>entrypoint<br/>[main.py]"]
  node_node_orchestrator["Orchestrator<br/>pipeline control<br/>[orchestrator.py]"]
  node_node_config["Config<br/>[config.py]"]
  node_node_artifacts[("Artifacts<br/>artifact io<br/>[artifacts.py]")]
  node_node_progress(("Progress<br/>tracking<br/>[progress.py]"))
  node_node_preflight{{"Preflight<br/>checks<br/>[preflight.py]"}}
  node_node_video_meta["Video meta<br/>metadata<br/>[video_meta.py]"]
  node_node_scenes[("Scenes I/O<br/>scene io<br/>[scenes_io.py]")]
  node_node_scene_extract["Scene extract<br/>scene segmentation<br/>[scene_extractor.py]"]
  node_node_scene_filter["Scene filter<br/>[scene_filter.py]"]
  node_node_aggregator[("Aggregator<br/>final join<br/>[aggregator.py]")]
end

subgraph group_group_camera["Camera assignment"]
  node_node_camera_assign["Camera assign<br/>camera join"]
  node_node_camera_merge["Camera merge<br/>[camera_merge.py]"]
  node_node_embed_cluster["Embed cluster<br/>appearance clustering<br/>[pipeline.py]"]
  node_node_homography["Homography<br/>court geometry"]
  node_node_scene_classifier["Scene classifier<br/>shot classifier<br/>[classifier.py]"]
end

subgraph group_group_appearance["Person appearance"]
  node_node_appearance["Appearance<br/>person visuals<br/>[extractor.py]"]
  node_node_appearance_sig["Signatures<br/>identity features<br/>[signature.py]"]
end

subgraph group_group_ocr["OCR and text"]
  node_node_ocr_runner["OCR runner<br/>ocr orchestration<br/>[ocr_runner.py]"]
  node_node_scene_ocr["Scene OCR<br/>ocr core<br/>[extractor.py]"]
  node_node_text_ref["Text ref<br/>catalog<br/>[text_reference.py]"]
  node_node_text_enrich["Text enrich<br/>enrichment<br/>[text_enrich.py]"]
  node_node_text_assoc["Text associate<br/>association<br/>[text_associate.py]"]
end

subgraph group_group_viz["Timeline viz"]
  node_node_viz_server["Viz server<br/>viewer backend<br/>[server.py]"]
  node_node_viz_app["Timeline UI<br/>frontend<br/>[app.js]"]
  node_node_docker_viz["Viz deploy<br/>container deploy"]
end

subgraph group_group_support["Support systems"]
  node_node_accelerator{{"Accelerator<br/>device runtime<br/>[device.py]"}}
  node_node_court_vendor["Court detector<br/>vendored court model<br/>[homography.py]"]
  node_node_yolo_model[("YOLO seg model<br/>model asset<br/>[yolo11n-seg.onnx]")]
end

node_node_main -->|"starts with"| node_node_preflight
node_node_main -->|"invokes"| node_node_orchestrator
node_node_orchestrator -->|"reads"| node_node_config
node_node_orchestrator -->|"tracks"| node_node_progress
node_node_orchestrator -->|"persists"| node_node_artifacts
node_node_orchestrator -->|"stage"| node_node_video_meta
node_node_orchestrator -->|"stage"| node_node_scene_extract
node_node_scene_extract -->|"feeds"| node_node_scene_filter
node_node_scene_filter -->|"writes"| node_node_scenes
node_node_scenes -->|"loads"| node_node_camera_assign
node_node_camera_assign -->|"uses"| node_node_embed_cluster
node_node_camera_assign -->|"uses"| node_node_homography
node_node_camera_assign -->|"uses"| node_node_scene_classifier
node_node_camera_assign -->|"produces"| node_node_camera_merge
node_node_orchestrator -->|"stage"| node_node_appearance
node_node_appearance -->|"builds"| node_node_appearance_sig
node_node_appearance -->|"uses"| node_node_yolo_model
node_node_orchestrator -->|"stage"| node_node_ocr_runner
node_node_ocr_runner -->|"delegates"| node_node_scene_ocr
node_node_scene_ocr -->|"feeds"| node_node_text_ref
node_node_text_ref -->|"enriches"| node_node_text_enrich
node_node_text_enrich -->|"associates"| node_node_text_assoc
node_node_text_assoc -->|"joins into"| node_node_aggregator
node_node_camera_merge -->|"joins into"| node_node_aggregator
node_node_orchestrator -->|"finalizes"| node_node_aggregator
node_node_aggregator -->|"serves"| node_node_viz_server
node_node_viz_server -->|"renders"| node_node_viz_app
node_node_viz_server -->|"deploys via"| node_node_docker_viz
node_node_orchestrator -->|"selects runtime"| node_node_accelerator
node_node_homography -->|"uses"| node_node_court_vendor

click node_node_main "https://github.com/druv022/sports-analytics/blob/main/main.py"
click node_node_orchestrator "https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/orchestrator.py"
click node_node_config "https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/config.py"
click node_node_artifacts "https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/artifacts.py"
click node_node_progress "https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/progress.py"
click node_node_preflight "https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/preflight.py"
click node_node_video_meta "https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/video_meta.py"
click node_node_scenes "https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/scenes_io.py"
click node_node_scene_extract "https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/scene_extractor.py"
click node_node_scene_filter "https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/scene_filter.py"
click node_node_camera_assign "https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/camera_assignment.py"
click node_node_camera_merge "https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/camera_merge.py"
click node_node_embed_cluster "https://github.com/druv022/sports-analytics/blob/main/src/camera_assignemnt/embedding_cluster/pipeline.py"
click node_node_homography "https://github.com/druv022/sports-analytics/blob/main/src/camera_assignemnt/homography/homography_estimator.py"
click node_node_scene_classifier "https://github.com/druv022/sports-analytics/blob/main/src/camera_assignemnt/scene_classifier/classifier.py"
click node_node_appearance "https://github.com/druv022/sports-analytics/blob/main/src/person_appearance/extractor.py"
click node_node_appearance_sig "https://github.com/druv022/sports-analytics/blob/main/src/person_appearance/signature.py"
click node_node_ocr_runner "https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/ocr_runner.py"
click node_node_scene_ocr "https://github.com/druv022/sports-analytics/blob/main/src/scene_ocr/extractor.py"
click node_node_text_ref "https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/text_reference.py"
click node_node_text_enrich "https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/text_enrich.py"
click node_node_text_assoc "https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/text_associate.py"
click node_node_aggregator "https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/aggregator.py"
click node_node_viz_server "https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/viz/server.py"
click node_node_viz_app "https://github.com/druv022/sports-analytics/blob/main/static/timeline_viz/app.js"
click node_node_docker_viz "https://github.com/druv022/sports-analytics/tree/main/docker/viz/Dockerfile"
click node_node_accelerator "https://github.com/druv022/sports-analytics/blob/main/src/accelerator/device.py"
click node_node_court_vendor "https://github.com/druv022/sports-analytics/blob/main/third_party/tennis_court_detector/homography.py"
click node_node_yolo_model "https://github.com/druv022/sports-analytics/blob/main/yolo11n-seg.onnx"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_node_main,node_node_orchestrator,node_node_config,node_node_artifacts,node_node_progress,node_node_preflight,node_node_video_meta,node_node_scenes,node_node_scene_extract,node_node_scene_filter,node_node_aggregator toneBlue
class node_node_camera_assign,node_node_camera_merge,node_node_embed_cluster,node_node_homography,node_node_scene_classifier toneAmber
class node_node_appearance,node_node_appearance_sig toneMint
class node_node_ocr_runner,node_node_scene_ocr,node_node_text_ref,node_node_text_enrich,node_node_text_assoc toneRose
class node_node_viz_server,node_node_viz_app,node_node_docker_viz toneIndigo
class node_node_accelerator,node_node_court_vendor,node_node_yolo_model toneTeal
```

## Components

### Pipeline core

Resumable batch pipeline, stage coordination, artifacts, and shared runtime concerns.

- [main.py](https://github.com/druv022/sports-analytics/blob/main/main.py): entrypoint · Batch entrypoint for running the broadcast pipeline end to end. · `main.py`
- [Orchestrator](https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/orchestrator.py): pipeline control · Coordinates resumable stage execution and step boundaries. · `src/broadcast_pipeline/orchestrator.py`
- [Config](https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/config.py): runtime config · Pipeline configuration and run-time options. · `src/broadcast_pipeline/config.py`
- [Artifacts](https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/artifacts.py): artifact io · Reads and writes pipeline outputs and intermediate datasets. · `src/broadcast_pipeline/artifacts.py`
- [Progress](https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/progress.py): tracking · Progress reporting and stage status tracking. · `src/broadcast_pipeline/progress.py`
- [Preflight](https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/preflight.py): checks · Input validation and environment readiness checks. · `src/broadcast_pipeline/preflight.py`
- [Video meta](https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/video_meta.py): metadata · Video-level metadata extraction used before scene processing. · `src/broadcast_pipeline/video_meta.py`
- [Scenes I/O](https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/scenes_io.py): scene io · Scene persistence and loading for downstream stages. · `src/broadcast_pipeline/scenes_io.py`
- [Scene extract](https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/scene_extractor.py): scene segmentation · Derives temporal scene units from the source video. · `src/broadcast_pipeline/scene_extractor.py`
- [Scene filter](https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/scene_filter.py): scene filter · Filters and refines extracted scenes for later stages. · `src/broadcast_pipeline/scene_filter.py`
- [Aggregator](https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/aggregator.py): final join · Produces final aggregated CSV and JSON outputs. · `src/broadcast_pipeline/aggregator.py`

### Camera assignment

Subsystems that classify and validate broadcast camera identity.

- [Camera assign](https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/camera_assignment.py): camera join · Coordinates camera identity assignment across scene outputs. · `src/broadcast_pipeline/camera_assignment.py`
- [Camera merge](https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/camera_merge.py): merge · Merges camera-specific outputs into unified camera timelines. · `src/broadcast_pipeline/camera_merge.py`
- [Embed cluster](https://github.com/druv022/sports-analytics/blob/main/src/camera_assignemnt/embedding_cluster/pipeline.py): appearance clustering · Embedding-based clustering approach for grouping similar camera shots. · `src/camera_assignemnt/embedding_cluster/pipeline.py`
- [Homography](https://github.com/druv022/sports-analytics/blob/main/src/camera_assignemnt/homography/homography_estimator.py): court geometry · Court geometry and projection-based validation for camera assignment. · `src/camera_assignemnt/homography/homography_estimator.py`
- [Scene classifier](https://github.com/druv022/sports-analytics/blob/main/src/camera_assignemnt/scene_classifier/classifier.py): shot classifier · Scene-type classifier used as a camera assignment signal. · `src/camera_assignemnt/scene_classifier/classifier.py`

### Person appearance

Extraction of person-level visual signatures and linking.

- [Appearance](https://github.com/druv022/sports-analytics/blob/main/src/person_appearance/extractor.py): person visuals · Person segmentation and visual signature extraction. · `src/person_appearance/extractor.py`
- [Signatures](https://github.com/druv022/sports-analytics/blob/main/src/person_appearance/signature.py): identity features · Builds and links person-level appearance signatures. · `src/person_appearance/signature.py`

### OCR and text

Text extraction, cleanup, enrichment, and association.

- [OCR runner](https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/ocr_runner.py): ocr orchestration · Runs OCR over scenes and collects text observations. · `src/broadcast_pipeline/ocr_runner.py`
- [Scene OCR](https://github.com/druv022/sports-analytics/blob/main/src/scene_ocr/extractor.py): ocr core · OCR extraction, geometry normalization, readability, and presence logic. · `src/scene_ocr/extractor.py`
- [Text ref](https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/text_reference.py): catalog · Builds the reference catalog for detected text entries. · `src/broadcast_pipeline/text_reference.py`
- [Text enrich](https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/text_enrich.py): enrichment · Adds structure and metadata to OCR text records. · `src/broadcast_pipeline/text_enrich.py`
- [Text associate](https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/text_associate.py): association · Associates text events with cameras and scenes. · `src/broadcast_pipeline/text_associate.py`

### Timeline viz

Browser viewer and its serving/deployment stack.

- [Viz server](https://github.com/druv022/sports-analytics/blob/main/src/broadcast_pipeline/viz/server.py): viewer backend · Serves the interactive timeline viewer and data APIs. · `src/broadcast_pipeline/viz/server.py`
- [Timeline UI](https://github.com/druv022/sports-analytics/blob/main/static/timeline_viz/app.js): frontend · Browser-based timeline viewer for search, previews, collages, and camera comparison. · `static/timeline_viz/app.js`
- [Viz deploy](https://github.com/druv022/sports-analytics/tree/main/docker/viz/Dockerfile): container deploy · Containerized viewer packaging and cloud build deployment. · `docker/viz/Dockerfile`

### Support systems

Acceleration, vendored models, and external court geometry helpers.

- [Accelerator](https://github.com/druv022/sports-analytics/blob/main/src/accelerator/device.py): device runtime · Device and runtime selection for CPU/GPU execution. · `src/accelerator/device.py`
- [Court detector](https://github.com/druv022/sports-analytics/blob/main/third_party/tennis_court_detector/homography.py): vendored court model · Vendored court detection and homography helpers used by camera workflows. · `third_party/tennis_court_detector/homography.py`
- [YOLO seg model](https://github.com/druv022/sports-analytics/blob/main/yolo11n-seg.onnx): model asset · Bundled segmentation model used by person appearance extraction. · `yolo11n-seg.onnx`

## Connections

- main.py → Preflight: starts with · Entry point validates inputs and environment before running stages.
- main.py → Orchestrator: invokes · Main hands control to the resumable pipeline coordinator.
- Orchestrator → Config: reads · Pipeline execution is driven by configuration.
- Orchestrator → Progress: tracks · Stage execution reports progress and status.
- Orchestrator → Artifacts: persists · Orchestrator manages intermediate and final artifacts.
- Orchestrator → Video meta: stage · Initial metadata extraction feeds scene processing.
- Orchestrator → Scene extract: stage · Extracts temporal scene units from video.
- Scene extract → Scene filter: feeds · Raw scenes are filtered before downstream use.
- Scene filter → Scenes I/O: writes · Filtered scenes are stored for later stages.
- Scenes I/O → Camera assign: loads · Camera assignment consumes scene-level inputs.
- Camera assign → Embed cluster: uses · One branch groups shots by embedding similarity.
- Camera assign → Homography: uses · One branch validates assignment with court geometry.
- Camera assign → Scene classifier: uses · One branch predicts camera/shot type from scene content.
- Camera assign → Camera merge: produces · Assignment outputs are merged into camera timelines.
- Orchestrator → Appearance: stage · Person appearance processing runs as a major pipeline stage.
- Appearance → Signatures: builds · Visual features are turned into signatures and links.
- Appearance → YOLO seg model: uses · Segmentation depends on the bundled YOLO model asset.
- Orchestrator → OCR runner: stage · OCR stage is orchestrated from the pipeline controller.
- OCR runner → Scene OCR: delegates · OCR runner invokes the scene OCR subsystem.
- Scene OCR → Text ref: feeds · Detected text is organized into a reference catalog.
- Text ref → Text enrich: enriches · Reference text is expanded with derived structure.
- Text enrich → Text associate: associates · Enriched text is linked to cameras and scenes.
- Text associate → Aggregator: joins into · Associated text becomes part of the final aggregation.
- Camera merge → Aggregator: joins into · Merged camera outputs are consumed by aggregation.
- Orchestrator → Aggregator: finalizes · Aggregator produces the end-of-pipeline deliverables.
- Aggregator → Viz server: serves · Aggregated outputs back the interactive viewer.
- Viz server → Timeline UI: renders · Backend serves the browser timeline UI.
- Viz server → Viz deploy: deploys via · Viewer can be packaged and deployed as a containerized service.
- Orchestrator → Accelerator: selects runtime · Pipeline stages can use CPU or GPU acceleration.
- Homography → Court detector: uses · Homography workflows depend on vendored court-detection helpers.
