Thanks for your interest! Trio Retina is a small, model-agnostic perception/state layer. Contributions that keep it small and beautiful are very welcome.
Retina has a numpy-only core, so a basic environment is light:
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e '.[dev]' # or just: pip install numpy pytest ruffThe core is numpy-only and pytest reads retina straight from the source
tree (pythonpath = ["."]), so pytest -q runs on a bare checkout — the
editable install above is a convenience, not a requirement.
make test # pytest -q
make lint # ruff check .
make format # ruff format . (line-length = 100)
make help # all tasksOptional: pip install pre-commit && pre-commit install runs the same ruff
checks on every commit. Both ruff check . and pytest -q must be green before
you open a PR — CI runs exactly these across Python 3.10–3.13.
Please keep changes aligned with these:
- Small & beautiful. Prefer a few sharp, composable primitives over broad surface area. New abstractions need to earn their keep.
- Pure-Python core, numpy-only. Everything in
retina/that's always importable must depend on nothing heavier thannumpy. - Model / hardware / app-agnostic. The core never assumes a specific detector, tracker, camera, accelerator, or downstream application.
- Heavy deps are optional extras. Anything that pulls in a model runtime or
large library lives behind an extra and is imported lazily:
[yolo](ultralytics),[norfair],[grounding](transformers/torch),[video](opencv). Import the optional package inside the class that needs it, with a friendly error if it's missing.
The building blocks are tiny Protocols in retina/ — implement one and it
drops straight into a pipeline. Anything pipeable composes with | (see
retina/compose.py and retina/nodes.py):
pipe = YoloDetector("yolo11n.pt") | IoUTracker() | ZoneRule(dock) | JsonlSink("e.jsonl")- Detector (
retina/detect.py) —__call__(self, frame: np.ndarray) -> list[Detection]. Turns an image into detections. SeeYoloDetector,VlmDetector. - Tracker (
retina/track.py) —update(self, detections, t) -> list[Track]. Gives detections identity over time. SeeIoUTracker,NorfairTracker. - EventRule (
retina/rules.py) —update(self, tracks, t, frame_idx) -> list[Event]. Emits symbolic events from tracks. SeeZoneRule,LineRule,CountRule. - EventSink (
retina/export.py) —__call__(self, event: Event) -> None. Consumes events. SeeJsonlSink,WebhookSink.
To wrap a plain function or a custom object, see the Node types in
retina/nodes.py; shipped detectors/trackers/rules/sinks auto-wrap, so you
rarely need an explicit node.
- Keep PRs focused; one concern per PR.
- Add or update tests for behavior you change.
- Make sure
ruff check .andpytest -qpass locally. - Describe the why, not just the what, in the PR description.
By contributing you agree your work is licensed under the project's Apache-2.0 license.