From 2db157160aad6330adf2ac8d06618b2fcdfa1da0 Mon Sep 17 00:00:00 2001 From: Stevenson Chittumuri Date: Thu, 25 Jun 2026 18:12:47 -0400 Subject: [PATCH] feat(install): torch-free default install; tracking/export are opt-in extras MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ultralytics + boxmot (both pull ~2-3GB of PyTorch) move out of base.txt into requirements/tracking.txt and requirements/export.txt. The default install is now torch-free — detection via the prebuilt-ONNX download, tracking via the built-in IoU fallback. Opt in with --with-tracking / --with-export / --full. CI-safe: dev.txt references both extras (-r tracking.txt / -r export.txt), so `tools/install.py --ci --dev` still installs boxmot + ultralytics for the test suite. Dry-run verified: lean default excludes them; --ci --dev includes them. Co-Authored-By: Claude Opus 4.8 --- docs/installation.md | 29 +++++++++++++-- requirements/README.md | 21 ++++++++++- requirements/base.txt | 20 ++++------ requirements/dev.txt | 6 +++ requirements/export.txt | 15 ++++++++ requirements/tracking.txt | 16 ++++++++ tests/test_install.py | 77 +++++++++++++++++++++++++++++++++++++++ tools/install.py | 56 +++++++++++++++++++++++++++- 8 files changed, 221 insertions(+), 19 deletions(-) create mode 100644 requirements/export.txt create mode 100644 requirements/tracking.txt diff --git a/docs/installation.md b/docs/installation.md index f913a27..26f7e30 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -33,11 +33,32 @@ python tools/install.py --editable python -m autoptz ``` -- `requirements/base.txt` — full stack: ONNX Runtime, OpenCV, PySide6, PyAV, - ultralytics, boxmot, insightface, PTZ libs, plus OS-specific camera helpers - through pip environment markers. +The default `python tools/install.py` is **torch-free** (~2–3 GB lighter): +detection runs on ONNX Runtime, multi-object tracking uses the built-in +lightweight IoU tracker, and detector/pose weights provision via the +prebuilt-ONNX download. The two PyTorch-heavy fallbacks are opt-in extras: + +```bash +python tools/install.py --editable # lean, torch-free default +python tools/install.py --full --editable # + tracking + export extras +python tools/install.py --with-tracking --editable # boxmot only +python tools/install.py --with-export --editable # ultralytics only +``` + +- **tracking** (`requirements/tracking.txt`, `boxmot`) — occlusion-robust + BoT-SORT/DeepOCSORT/ByteTrack trackers and the OSNet ReID backend used for + body-appearance recovery after occlusion. +- **export** (`requirements/export.txt`, `ultralytics`) — the YOLO11 `.pt` → + ONNX export fallback, used only when the prebuilt ONNX download is unreachable. + +- `requirements/base.txt` — torch-free core: ONNX Runtime, OpenCV, PySide6, + PyAV, insightface, PTZ libs, plus OS-specific camera helpers through pip + environment markers. +- `requirements/tracking.txt` / `requirements/export.txt` — optional torch + extras (above); `--dev` and CI install both automatically (the test suite + needs them). - `requirements/ui.txt` — UI-only (no ML stack), for quick UI work. -- `requirements/dev.txt` — pytest, ruff, mypy. +- `requirements/dev.txt` — pytest, ruff, mypy (plus the tracking + export extras). - `tools/install.py` — one readable install entry point that selects the right profile and prevents multiple `onnxruntime*` wheels from coexisting. diff --git a/requirements/README.md b/requirements/README.md index b7668d2..c45160a 100644 --- a/requirements/README.md +++ b/requirements/README.md @@ -13,14 +13,31 @@ normal pip environment markers for OS-specific packages such as macOS AVFoundation support. GPU selection needs `tools/install.py` or an explicit accelerator file because pip requirements cannot inspect your GPU hardware. +The **default install is torch-free** (~2–3 GB lighter): detection runs on ONNX +Runtime, multi-object tracking uses the built-in lightweight IoU tracker, and +detector/pose weights provision via the prebuilt-ONNX download. The two +torch-heavy fallbacks are opt-in extras: + +```bash +python tools/install.py --editable # lean, torch-free default +python tools/install.py --full --editable # + tracking + export extras +python tools/install.py --with-tracking --editable # + boxmot tracking only +python tools/install.py --with-export --editable # + ultralytics export only +``` + +`--dev` (and CI) install both extras automatically because the test suite needs +them — `dev.txt` references `tracking.txt` and `export.txt`. + | File | When to install | | --- | --- | -| `base.txt` | Always — full app: inference, tracking, PTZ, UI. Ships CPU ONNX Runtime (+ CoreML on macOS) and OS-specific camera helpers via markers. | +| `base.txt` | Always — torch-free core: ONNX inference, IoU tracking, PTZ, UI. Ships CPU ONNX Runtime (+ CoreML on macOS) and OS-specific camera helpers via markers. | | `requirements.txt` | Compatibility entry point for tools that expect the conventional filename, including GitHub Dependency Graph. Includes `base.txt`. | +| `tracking.txt` | Optional extra — `boxmot` adds BoT-SORT/DeepOCSORT/ByteTrack + OSNet ReID for occlusion-robust tracking. Pulls in PyTorch. `--with-tracking` / `--full`. | +| `export.txt` | Optional extra — `ultralytics` adds the YOLO11 `.pt` → ONNX export fallback (used only when the prebuilt download is unreachable). Pulls in PyTorch. `--with-export` / `--full`. | | `gpu-nvidia.txt` | NVIDIA GPU (Windows/Linux) — TensorRT + CUDA. Replaces the CPU `onnxruntime`. | | `gpu-directml.txt` | AMD/Intel GPU on Windows — DirectML. Replaces the CPU `onnxruntime`. | | `openvino.txt` | Intel CPU/iGPU (any OS) — OpenVINO. Replaces the CPU `onnxruntime`. | -| `dev.txt` | Contributors — ruff, mypy, pytest. | +| `dev.txt` | Contributors — ruff, mypy, pytest. Also pulls in `tracking.txt` + `export.txt` (the test suite needs them). | | `packaging.txt` | Building installers — PyInstaller (see [docs/building.md](../docs/building.md)). | | `ui.txt` | UI-only iteration — minimal, no ML stack. | diff --git a/requirements/base.txt b/requirements/base.txt index 1fcf8c3..cd8b9a9 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -78,18 +78,14 @@ onnx==1.21.0 # AUTOPTZ_MODEL_PATH). The UI uses cached models unless the user explicitly # enables automatic downloads in Engine > Models. # -# Ultralytics is an OPTIONAL *fallback* used only when the prebuilt download is -# unreachable; it exports the YOLO11 .pt → ONNX but pulls in torch (heavy). Omit -# it on minimal installs — detection still works via the prebuilt download, and -# the engine degrades to live-preview-only only if BOTH paths fail. -ultralytics>=8.3.0 - -# BoxMOT: multi-object tracker collection (BoT-SORT, DeepOCSORT, ByteTrack). -# Also provides the OSNet ReID backend used by engine/pipeline/reid.py for -# body-appearance recovery after occlusion (weights auto-download on first use). -# Optional at import time — Tracker / BodyReID degrade gracefully if absent. -# Pin to a specific release once a stable wheel is available for all platforms. -boxmot>=10.0.91 +# The default install is torch-free: detection runs on ONNX Runtime, tracking +# falls back to the built-in IoU tracker, and weights provision via the prebuilt +# ONNX download. The two torch-heavy fallbacks live in OPTIONAL extras (omit them +# on a default install): +# requirements/tracking.txt — boxmot (BoT-SORT/DeepOCSORT/ByteTrack + OSNet ReID) +# requirements/export.txt — ultralytics (YOLO11 .pt → ONNX export fallback) +# Install both with `python tools/install.py --full` (or --with-tracking / +# --with-export). `--dev` and CI pull them in automatically via dev.txt. # ── face identity ─────────────────────────────────────────────────── # InsightFace: SCRFD face detector + ArcFace 512-d embeddings (engine/pipeline/ diff --git a/requirements/dev.txt b/requirements/dev.txt index 1b0fbe2..54265ea 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -2,6 +2,12 @@ # Install after base: # python tools/install.py --dev --editable +# The test suite needs the torch-heavy tracking + export extras (tracker tests +# and model-export tests import boxmot / ultralytics), so contributors and CI +# always get them even though the default end-user install stays torch-free. +-r tracking.txt +-r export.txt + # ── testing ─────────────────────────────────────────────────────────────────── pytest==9.0.3 pytest-timeout==2.3.1 diff --git a/requirements/export.txt b/requirements/export.txt new file mode 100644 index 0000000..e3005b1 --- /dev/null +++ b/requirements/export.txt @@ -0,0 +1,15 @@ +# AutoPTZ — optional export extra (heavy: pulls in PyTorch via ultralytics) +# Install: +# python tools/install.py --with-export # or --full +# pip install -r requirements/export.txt +# +# The DEFAULT install is torch-free: ModelManager provisions detector/pose +# weights via the prebuilt-ONNX download (engine/runtime/models.py). This extra +# adds the YOLO11 .pt → ONNX export fallback used only when that download is +# unreachable. + +# Ultralytics is an OPTIONAL *fallback* used only when the prebuilt download is +# unreachable; it exports the YOLO11 .pt → ONNX but pulls in torch (heavy). Omit +# it on minimal installs — detection still works via the prebuilt download, and +# the engine degrades to live-preview-only only if BOTH paths fail. +ultralytics>=8.3.0 diff --git a/requirements/tracking.txt b/requirements/tracking.txt new file mode 100644 index 0000000..3a57bb5 --- /dev/null +++ b/requirements/tracking.txt @@ -0,0 +1,16 @@ +# AutoPTZ — optional tracking extra (heavy: pulls in PyTorch via boxmot) +# Install: +# python tools/install.py --with-tracking # or --full +# pip install -r requirements/tracking.txt +# +# The DEFAULT install is torch-free: detection runs on ONNX Runtime and tracking +# falls back to the built-in lightweight IoU tracker (engine/pipeline/track.py). +# This extra adds the occlusion-robust BoT-SORT/DeepOCSORT/ByteTrack trackers and +# the OSNet ReID backend. + +# BoxMOT: multi-object tracker collection (BoT-SORT, DeepOCSORT, ByteTrack). +# Also provides the OSNet ReID backend used by engine/pipeline/reid.py for +# body-appearance recovery after occlusion (weights auto-download on first use). +# Optional at import time — Tracker / BodyReID degrade gracefully if absent. +# Pin to a specific release once a stable wheel is available for all platforms. +boxmot>=10.0.91 diff --git a/tests/test_install.py b/tests/test_install.py index 9c0a5be..24e90c6 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -33,6 +33,83 @@ def test_ci_install_is_hardware_independent() -> None: assert any("hardware-independent" in note for note in plan.notes) +# --------------------------------------------------------------------------- +# Torch-free default + opt-in extras +# --------------------------------------------------------------------------- + + +def test_default_install_is_torch_free() -> None: + """The bare default plan must NOT include the torch-heavy extras.""" + system = SystemInfo("Darwin", "arm64", ("Apple M3",)) + + plan = plan_install(system) + + assert "tracking" not in plan.profiles + assert "export" not in plan.profiles + args = [step.args for step in plan.steps] + assert ("install", "-r", "requirements/tracking.txt") not in args + assert ("install", "-r", "requirements/export.txt") not in args + assert any("torch-free" in note for note in plan.notes) + + +def test_dev_install_pulls_extras_via_dev_txt() -> None: + """--dev installs dev.txt, which references tracking.txt + export.txt, so the + extras are NOT added as separate steps (avoids redundant pip work).""" + system = SystemInfo("Linux", "x86_64", ()) + + plan = plan_install(system, dev=True) + + args = [step.args for step in plan.steps] + assert ("install", "-r", "requirements/dev.txt") in args + # dev.txt's `-r tracking.txt` / `-r export.txt` cover these, so install.py + # must not duplicate them. + assert ("install", "-r", "requirements/tracking.txt") not in args + assert ("install", "-r", "requirements/export.txt") not in args + + +def test_full_install_adds_both_extras() -> None: + system = SystemInfo("Linux", "x86_64", ()) + + plan = plan_install(system, full=True) + + assert plan.profiles == ("base", "tracking", "export") + args = [step.args for step in plan.steps] + assert ("install", "-r", "requirements/tracking.txt") in args + assert ("install", "-r", "requirements/export.txt") in args + + +def test_with_tracking_adds_only_tracking() -> None: + system = SystemInfo("Linux", "x86_64", ()) + + plan = plan_install(system, with_tracking=True) + + args = [step.args for step in plan.steps] + assert ("install", "-r", "requirements/tracking.txt") in args + assert ("install", "-r", "requirements/export.txt") not in args + + +def test_with_export_adds_only_export() -> None: + system = SystemInfo("Linux", "x86_64", ()) + + plan = plan_install(system, with_export=True) + + args = [step.args for step in plan.steps] + assert ("install", "-r", "requirements/export.txt") in args + assert ("install", "-r", "requirements/tracking.txt") not in args + + +def test_ui_only_never_adds_extras() -> None: + """--ui-only deliberately omits the ML stack even with --full.""" + system = SystemInfo("Linux", "x86_64", ()) + + plan = plan_install(system, ui_only=True, full=True) + + assert plan.profiles == ("ui",) + args = [step.args for step in plan.steps] + assert ("install", "-r", "requirements/tracking.txt") not in args + assert ("install", "-r", "requirements/export.txt") not in args + + # --------------------------------------------------------------------------- # Windows auto-select # --------------------------------------------------------------------------- diff --git a/tools/install.py b/tools/install.py index 4e15a99..f102d63 100644 --- a/tools/install.py +++ b/tools/install.py @@ -261,10 +261,25 @@ def plan_install( ui_only: bool = False, ci: bool = False, upgrade_pip: bool = False, + with_tracking: bool = False, + with_export: bool = False, + full: bool = False, ) -> InstallPlan: - """Build a deterministic pip plan without running it.""" + """Build a deterministic pip plan without running it. + + The default install is torch-free: the heavy boxmot (tracking) and + ultralytics (export) extras are opt-in via ``--with-tracking`` / + ``--with-export`` / ``--full``. ``--dev`` pulls them in through dev.txt + (the test suite needs them), so they are not added again here when dev is + selected. + """ if ui_only and packaging: raise ValueError("--ui-only and --packaging target different environments.") + # dev.txt already references tracking.txt + export.txt, so contributors and + # CI (--dev) always get them. The explicit extras only matter for non-dev + # installs that opt in. + want_tracking = with_tracking or full + want_export = with_export or full requested = "cpu" if ci and accelerator == "auto" else accelerator selected = None if ui_only else _resolve_accelerator(system, requested) @@ -305,6 +320,27 @@ def plan_install( if dev: profiles.append("dev") steps.append(_requirement_step("dev")) + # The torch-heavy extras. dev.txt already references both, so only add them + # explicitly when opted in without --dev (avoids a redundant pip step). They + # are skipped in --ui-only mode, which deliberately omits the ML stack. + if not ui_only and not dev: + if want_tracking: + profiles.append("tracking") + steps.append(_requirement_step("tracking")) + notes.append( + "tracking: boxmot adds BoT-SORT/DeepOCSORT/ByteTrack + OSNet ReID (pulls in torch)." + ) + if want_export: + profiles.append("export") + steps.append(_requirement_step("export")) + notes.append( + "export: ultralytics adds the YOLO11 .pt → ONNX export fallback (pulls in torch)." + ) + if not ui_only and not dev and not (want_tracking or want_export): + notes.append( + "Lean torch-free default: detection + IoU tracking only. Add --full" + " (or --with-tracking / --with-export) for boxmot/ultralytics." + ) if packaging: profiles.append("packaging") steps.append(_requirement_step("packaging")) @@ -372,6 +408,21 @@ def _build_parser() -> argparse.ArgumentParser: parser.add_argument( "--ci", action="store_true", help="Use hardware-independent CI dependency choices." ) + parser.add_argument( + "--with-tracking", + action="store_true", + help="Add the boxmot tracking extra (BoT-SORT/DeepOCSORT/ReID; pulls in torch).", + ) + parser.add_argument( + "--with-export", + action="store_true", + help="Add the ultralytics export extra (.pt → ONNX fallback; pulls in torch).", + ) + parser.add_argument( + "--full", + action="store_true", + help="Add both torch-heavy extras (tracking + export). Default install is torch-free.", + ) parser.add_argument( "--upgrade-pip", action="store_true", help="Upgrade pip before installing profiles." ) @@ -396,6 +447,9 @@ def main(argv: Sequence[str] | None = None) -> int: ui_only=args.ui_only, ci=args.ci, upgrade_pip=args.upgrade_pip, + with_tracking=args.with_tracking, + with_export=args.with_export, + full=args.full, ) except ValueError as exc: parser.error(str(exc))