Instructions to use camstack/camstack-trainer with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- ultralytics
How to use camstack/camstack-trainer with ultralytics:
from huggingface_hub import hf_hub_download from ultralytics import YOLO # pick the weights file from this repo's "Files and versions" tab weights = hf_hub_download("camstack/camstack-trainer", "<weights>.pt") model = YOLO(weights) source = 'http://images.cocodataset.org/val2017/000000039769.jpg' model.predict(source=source, save=True) - Notebooks
- Google Colab
- Kaggle
camstack-train 0.1.0: YOLOv9 root detector fine-tune (job.json in, result.json + bundle.tar out)
ebeed67 verified Download camstack_train/evaluate.py from camstack/camstack-trainer: direct link, hf CLI and curl.
- Browser
- Download file 4.62 kB
-
https://huggingface.co/camstack/camstack-trainer/resolve/main/camstack_train/evaluate.py
- Command line
-
hf download hf://camstack/camstack-trainer/camstack_train/evaluate.py
-
curl -L -o evaluate.py https://huggingface.co/camstack/camstack-trainer/resolve/main/camstack_train/evaluate.py
4.62 kB
| """Baseline vs candidate on the holdout, through the hub's own evaluation harness. | |
| The harness (``_vendor/camstack_eval``, byte-identical to | |
| ``scripts/eval/camstack_eval``) runs both models with the inference pool's | |
| letterbox and decode, scores P/R/AP50 per macro class, camera, day/night and | |
| small subjects, and proposes the candidate's floors by the D739 rule against | |
| the baseline's shipped floors. Only COMPLETE holdout frames are ground truth. | |
| The baseline is the catalog ONNX the hub names (``baseline.onnxUrl``), fetched | |
| once into the cache volume. The candidate is the exported ONNX; the INT8 IR is | |
| evaluated separately, as its own model with its own floors. | |
| """ | |
| from __future__ import annotations | |
| import hashlib | |
| from dataclasses import dataclass | |
| from pathlib import Path | |
| from typing import Callable, Optional, Sequence | |
| from ._vendor import ensure_pipeline_on_path | |
| from ._vendor.camstack_eval.compare import compare_models | |
| from ._vendor.camstack_eval.dataset import EvalFrame, load_export | |
| from ._vendor.camstack_eval.floors import FloorPolicy | |
| from ._vendor.camstack_eval.metrics import Detection | |
| from .replay import Fetch | |
| from .spec import JobSpec | |
| Detector = Callable[[bytes], Sequence[Detection]] | |
| DetectorFactory = Callable[[str, int], Detector] | |
| def harness_detector(model_path: str, input_size: int) -> Detector: | |
| ensure_pipeline_on_path() | |
| from ._vendor.camstack_eval.pipeline import make_detector | |
| return make_detector(model_path, input_size).detect | |
| def policy_of(job: JobSpec) -> FloorPolicy: | |
| return FloorPolicy(min_support=job.floors.min_support, clamp=job.floors.clamp) | |
| def baseline_model(job: JobSpec, cache: Path, fetch: Fetch) -> Path: | |
| digest = hashlib.sha256(job.baseline.onnx_url.encode()).hexdigest()[:16] | |
| target = cache / "baselines" / f"{digest}.onnx" | |
| if not target.is_file(): | |
| target.parent.mkdir(parents=True, exist_ok=True) | |
| partial = target.with_suffix(".part") | |
| partial.write_bytes(fetch(job.baseline.onnx_url)) | |
| partial.rename(target) | |
| return target | |
| def holdout_frames(dataset_root: Path, frame_ids: set[str]) -> list[EvalFrame]: | |
| return [f for f in load_export(str(dataset_root), complete_only=True) if f.truth.frame_id in frame_ids] | |
| def map50(report: Optional[dict]) -> Optional[float]: | |
| """Mean AP50 of the candidate over the classes that have one.""" | |
| if report is None: | |
| return None | |
| values = [c["candidate"]["ap50"] for c in report.get("classes", {}).values() if c["candidate"]["ap50"] is not None] | |
| return round(sum(values) / len(values), 4) if values else None | |
| class Evaluation: | |
| candidate: Optional[dict] | |
| candidate_int8: Optional[dict] | |
| holdout_frames: int | |
| def evaluate( | |
| job: JobSpec, | |
| frames: Sequence[EvalFrame], | |
| baseline_path: Path, | |
| candidate_onnx: Path, | |
| candidate_int8: Optional[Path], | |
| make: DetectorFactory = harness_detector, | |
| ) -> Evaluation: | |
| """Each report holds ``baseline`` and ``candidate`` per class (the harness's shape).""" | |
| if not frames: | |
| return Evaluation(None, None, 0) | |
| baseline = make(str(baseline_path), job.baseline.imgsz) | |
| policy = policy_of(job) | |
| floors = dict(job.baseline.floors) | |
| candidate = compare_models(frames, baseline, make(str(candidate_onnx), job.base.imgsz), floors, policy=policy) | |
| int8 = None | |
| if candidate_int8 is not None: | |
| int8 = compare_models(frames, baseline, make(str(candidate_int8), job.base.imgsz), floors, policy=policy) | |
| return Evaluation(candidate, int8, len(frames)) | |
| def split_reports(evaluation: Evaluation) -> dict: | |
| """The result.json ``evaluation`` block: baseline / candidate / candidateInt8.""" | |
| def side(report: Optional[dict], key: str) -> Optional[dict]: | |
| if report is None: | |
| return None | |
| return { | |
| "frames": report["frames"], | |
| "classes": {cls: {"positives": c["positives"], **c[key]} for cls, c in report["classes"].items()}, | |
| } | |
| def deltas(report: Optional[dict]) -> Optional[dict]: | |
| if report is None: | |
| return None | |
| return {cls: c["recallDeltaByCamera"] for cls, c in report["classes"].items()} | |
| return { | |
| "holdoutFrames": evaluation.holdout_frames, | |
| "baseline": side(evaluation.candidate, "baseline"), | |
| "candidate": side(evaluation.candidate, "candidate"), | |
| "candidateInt8": side(evaluation.candidate_int8, "candidate"), | |
| "recallDeltaByCamera": deltas(evaluation.candidate), | |
| "map50": {"candidate": map50(evaluation.candidate), "candidateInt8": map50(evaluation.candidate_int8)}, | |
| } | |