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/_vendor/camstack_eval/dataset.py from camstack/camstack-trainer: direct link, hf CLI and curl.
- Browser
- Download file 6.32 kB
-
https://huggingface.co/camstack/camstack-trainer/resolve/main/camstack_train/_vendor/camstack_eval/dataset.py
- Command line
-
hf download hf://camstack/camstack-trainer/camstack_train/_vendor/camstack_eval/dataset.py
-
curl -L -o dataset.py https://huggingface.co/camstack/camstack-trainer/resolve/main/camstack_train/_vendor/camstack_eval/dataset.py
6.32 kB
| """The hub's annotated export (`annotations.json`, schema | |
| `camstack.retrain-export/v2`) as an evaluation set. | |
| Only frames the operator marked ``complete`` ("all subjects boxed") are ground | |
| truth by default: on any other frame an unboxed person would be scored as a | |
| false positive. ``subject`` annotations of a detector macro class are the | |
| positives; ``model_error`` boxes mark a region the model got wrong and are not | |
| positives (a detection there is a false positive, as it should be). | |
| Works on the tar itself or on an extracted directory. | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import tarfile | |
| from dataclasses import dataclass | |
| from pathlib import Path | |
| from typing import Callable, Optional | |
| from .classes import DETECTOR_CLASSES | |
| from .metrics import FrameTruth, GroundTruthBox | |
| MANIFEST = "annotations.json" | |
| class EvalFrame: | |
| truth: FrameTruth | |
| path: str | |
| captured_at: Optional[int] | |
| read: Callable[[], bytes] | |
| def _night(entry: dict) -> Optional[bool]: | |
| lighting = entry.get("lighting") | |
| return None if not isinstance(lighting, dict) else bool(lighting.get("night")) | |
| def _truth(entry: dict, classes: tuple[str, ...]) -> FrameTruth: | |
| boxes = [] | |
| for a in entry.get("annotations", []): | |
| if a.get("kind") != "subject" or a.get("macroClass") not in classes: | |
| continue | |
| b = a["bbox"] | |
| boxes.append(GroundTruthBox(a["macroClass"], (b["x"], b["y"], b["w"], b["h"]))) | |
| return FrameTruth( | |
| frame_id=entry["frameId"], | |
| camera=int(entry["deviceId"]), | |
| night=_night(entry), | |
| boxes=tuple(boxes), | |
| ) | |
| def load_export( | |
| source: str, | |
| complete_only: bool = True, | |
| classes: tuple[str, ...] = DETECTOR_CLASSES, | |
| ) -> list[EvalFrame]: | |
| root = Path(source) | |
| if root.is_dir(): | |
| manifest = json.loads((root / MANIFEST).read_text("utf-8")) | |
| def reader(path: str) -> Callable[[], bytes]: | |
| return lambda: (root / path).read_bytes() | |
| else: | |
| with tarfile.open(root) as tar: | |
| member = tar.extractfile(MANIFEST) | |
| if member is None: | |
| raise ValueError(f"{source}: no {MANIFEST}") | |
| manifest = json.loads(member.read().decode("utf-8")) | |
| def reader(path: str) -> Callable[[], bytes]: | |
| def read() -> bytes: | |
| with tarfile.open(root) as tar: | |
| f = tar.extractfile(path) | |
| if f is None: | |
| raise FileNotFoundError(path) | |
| return f.read() | |
| return read | |
| out = [] | |
| for entry in manifest.get("frames", []): | |
| if complete_only and entry.get("complete") is not True: | |
| continue | |
| out.append( | |
| EvalFrame( | |
| truth=_truth(entry, classes), | |
| path=entry["path"], | |
| captured_at=entry.get("capturedAt"), | |
| read=reader(entry["path"]), | |
| ) | |
| ) | |
| return out | |
| def _file_reader(path: Path) -> Callable[[], bytes]: | |
| return lambda: path.read_bytes() | |
| def load_coco( | |
| annotations: str, | |
| images_dir: str, | |
| skip: int = 0, | |
| limit: Optional[int] = None, | |
| classes: tuple[str, ...] = DETECTOR_CLASSES, | |
| ) -> list[EvalFrame]: | |
| """COCO ``instances_*.json`` as an evaluation set, in macro classes. | |
| Images are taken in FILE-NAME order — the order the INT8 calibration reads | |
| the same folder (`convert_lib.calibration_images`) — after the first | |
| ``skip``, so ``skip`` = the calibration subset size keeps every calibration | |
| image out of the holdout. Every image counts, including those with no | |
| object of a macro class: a detection there is a false positive. A crowd | |
| box (``iscrowd``) is "don't care", as in COCO's own evaluation. | |
| """ | |
| from .classes import macro_of | |
| data = json.loads(Path(annotations).read_text("utf-8")) | |
| names = {c["id"]: c["name"] for c in data["categories"]} | |
| by_image: dict[int, list[dict]] = {} | |
| for a in data["annotations"]: | |
| by_image.setdefault(a["image_id"], []).append(a) | |
| images = sorted(data["images"], key=lambda im: im["file_name"])[skip:] | |
| if limit is not None: | |
| images = images[:limit] | |
| root = Path(images_dir) | |
| out = [] | |
| for im in images: | |
| w, h = float(im["width"]), float(im["height"]) | |
| boxes, dont_care = [], [] | |
| for a in by_image.get(im["id"], []): | |
| macro = macro_of(names[a["category_id"]]) | |
| if macro not in classes: | |
| continue | |
| x, y, bw, bh = a["bbox"] | |
| gt = GroundTruthBox(macro, (x / w, y / h, bw / w, bh / h)) | |
| (dont_care if a.get("iscrowd") else boxes).append(gt) | |
| out.append( | |
| EvalFrame( | |
| truth=FrameTruth( | |
| frame_id=im["file_name"], camera=0, night=None, | |
| boxes=tuple(boxes), dont_care=tuple(dont_care), | |
| ), | |
| path=im["file_name"], | |
| captured_at=None, | |
| read=_file_reader(root / im["file_name"]), | |
| ) | |
| ) | |
| return out | |
| def load_pseudo_gt(source: str, frames_dir: Optional[str] = None) -> list[EvalFrame]: | |
| """A pseudo ground truth (schema ``camstack.pseudo-gt/v1``): per frame the | |
| positives and the "don't care" boxes of `pseudo_gt.pseudo_ground_truth`, | |
| normalised ``(x, y, w, h)``. Frames are read from ``frames_dir`` (default: | |
| the folder holding the JSON).""" | |
| path = Path(source) | |
| data = json.loads(path.read_text("utf-8")) | |
| if data.get("schema") != "camstack.pseudo-gt/v1": | |
| raise ValueError(f"{source}: not a camstack.pseudo-gt/v1 file") | |
| root = Path(frames_dir) if frames_dir else path.parent | |
| def boxes(rows: list[dict]) -> tuple[GroundTruthBox, ...]: | |
| return tuple(GroundTruthBox(r["cls"], tuple(r["bbox"])) for r in rows) | |
| return [ | |
| EvalFrame( | |
| truth=FrameTruth( | |
| frame_id=f["file"], | |
| camera=int(f.get("deviceId", 0)), | |
| night=f.get("night"), | |
| boxes=boxes(f["boxes"]), | |
| dont_care=boxes(f.get("dontCare", [])), | |
| ), | |
| path=f["file"], | |
| captured_at=f.get("capturedAt"), | |
| read=_file_reader(root / f["file"]), | |
| ) | |
| for f in data["frames"] | |
| ] | |