labeltify
Upload frames, and optionally their annotations, into LabelTify from DeepStream, OpenCV, Ultralytics, or any Python pipeline. The only thing it needs is a pipeline token.
pip install labeltify
- On the dataset Upload page, create a pipeline token.
- Upload:
from labeltify import LabelTifyClient
client = LabelTifyClient("labeltify_…") # or set LABELTIFY_TOKEN and call LabelTifyClient()
img = client.upload_image(
"DATASET_ID",
jpeg_bytes,
filename="cam01.jpg",
camera="cam-01",
pipeline="ds-prod",
trigger="low_confidence",
)
client.upload_file("DATASET_ID", "frames/cam02.jpg", camera="cam-02")
The token knows its org, so you don't pass one. Timeouts, 429, and 5xx responses are retried with exponential backoff (max_retries=4 by default). Anything else raises LabelTifyError with the API's message and .status.
Annotations are optional. When you pass labels (COCO, LabelTify, or YOLO JSON bytes), work_state is required: done, in_progress, unsure, or untouched. There is no default; the client raises LabelTifyError before sending if it is missing. Images with no labels stay untouched.
Upload a folder with its annotations
report = client.upload_directory(
"DATASET_ID",
"datasets/parking",
work_state="done",
)
print(len(report["items"]), "images stored in", report["requests"], "requests;", report["imported"])
This sends the folder the way dropping it on the dataset's Upload page would: images, plus COCO (polygons and boxes), YOLO (labels/*.txt with data.yaml or classes.txt), Pascal VOC, LabelMe, CSV, metadata.jsonl, a folder per class, or a LabelTify export. The server reads the annotations; the client only splits the folder into requests (at most 100 images and 80 MB each). A COCO file is cut down to each request's images, so polygons stay polygons.
- The dataset must already exist. Create it on the site. Parking polygons need type Instance segmentation. Traffic boxes need Object detection.
work_state="done"marks every image that received a shape. Images with no shape stay untouched. With any label file in the folder,work_stateis required.- Create the pipeline token on that dataset's Upload page.
- One credit covers 10,000 uploaded images. A free dataset holds 100 photos.
- Frame embeddings follow the dataset setting. Turn them off on the dataset before a large upload when you do not want that charge.
- Use the portable pixel COCO files. The raw Studio shards store boxes as fractions from 0 to 1, and the server divides by width and height again.
- Two images with the same file name in different folders stop the upload before anything is sent; rename one.
- Images over 20 MiB, empty files, and files that are neither images nor annotations are listed in
report["files"]and not sent. A zip is sent as it is when it holds at most 200 files and 20 MB; unzip anything bigger. - If a request fails after its retries, the error says how many images were already stored; run the folder again and only what is missing is uploaded (see below).
compute_dhash=Truealso skips near-duplicate frames, like the Upload page. It needs Pillow:pip install "labeltify[images]".
Migrating thousands of images
export LABELTIFY_TOKEN=labeltify_…
python -m labeltify upload ./photos --dataset DATASET_ID --workers 6 --work-state done
or client.upload_directory(dataset_id, folder, workers=6, progress=print).
- Several requests at once.
workers(default 4, at most 8;1sends one after another) requests run in parallel over kept connections, so there is no new TLS handshake per request. Each request holds at mostmax_request_bytes(default 24 MiB) andmax_request_images(default 50). Files are read from disk only when their request is sent, so memory is aboutworkers× 24 MiB, not the size of the folder. - Resume for free. With
skip_existing=True(default) every image is hashed (SHA-256), the dataset is asked which hashes it already holds (client.images_exist(dataset_id, hashes)), and only the rest is sent. Running the same folder again after an interruption takes seconds, not a second upload. Label files of images that are already there are still sent.force=Trueorskip_existing=Falseturns this off, and an older server without the lookup is handled as before. - Progress.
progress=is called after each request withrequests_done,requests_total,images_storedandskipped_existing; the command line prints it. - Frame embeddings follow the dataset setting and are made in the background after the upload: thousands of images take a while to get their vectors. The dataset's Images tab shows how many are done.
- If a request fails, the requests not started yet are dropped, those already stored stay stored, and the error says how many; run the command again to continue.
upload_bundle(dataset_id, files, work_state=…) sends one such request from bytes you already have (files is a list of {"filename", "data", "content_type"}).
A deployment behind an access proxy can take extra headers on every request: LabelTifyClient(token, base_url=…, headers={"CF-Access-Client-Id": …, "CF-Access-Client-Secret": …}).
Send model proposals to the Loop review queue:
client.propose(
"DATASET_ID",
img["id"],
[{"type": "bbox", "id": "p1", "classId": "car", "box": {"x": 0.1, "y": 0.1, "w": 0.2, "h": 0.2}, "source": "model", "confidence": 0.4}],
model_ref="deepstream@1",
uncertainty=0.8,
)
Boxes are normalized [0,1]. Camera, pipeline, and trigger are stored on the image so Loop can filter by them.
Local API: LabelTifyClient("labeltify_…", base_url="http://localhost:8787").
Download a dataset export
The export tool on the site (or the MCP export tool) hands out a file-list URL. One command pulls every image it lists, plus the annotation bundle:
export LABELTIFY_EXPORT_KEY="<key from the export>"
python -m labeltify download "https://labeltify.com/api/ds/EXPORT_ID/files" --out ./ofan --workers 16
or labeltify download … with the installed console script. From Python:
from labeltify import download_export
summary = download_export("https://labeltify.com/api/ds/…/files", "./ofan", key="<key>", progress=print)
# {"images": 77769, "downloaded": 77000, "skipped": 769, "failed": 0, "bytes": …, "annotations": True, "failures": []}
The export key can be passed via --key / LABELTIFY_EXPORT_KEY, as an explicit key= argument to download_export, or left in the URL as ?key=… (still supported for browsers and old scripts). For scripts and CI, prefer the header or explicit argument so the key does not land in request URLs and logs.
- Resume for free. Files already on disk with the right size (and SHA-256, when the list carries one) are skipped, so re-running after an interruption only fetches what is missing. Partial files land in
name.partand are replaced atomically. - Parallel.
--workers(default 16, at most 64) downloads at once. Network errors,429, and5xxare retried with exponential backoff (--retries, default 5), honouringRetry-After. An expired image link (403/404) is reported as failed, not retried forever. - Annotations are extracted next to the images, unless
--no-annotations. Zip entries that would land outside the output folder are refused. An images-only export has no annotations, so that step is skipped on its own. --no-verifyskips the size and hash checks. An expired file-list link (410) says so clearly. When every file made it, the exit code is 0 and the last stdout line is the JSON summary; otherwise it is 1.
Develop
pip install -e "clients/python[test]"
pytest clients/python/tests
Bump version in pyproject.toml to publish. Each new version that lands on main is published to PyPI by .github/workflows/publish-python.yml.
Metadata
Release files for labeltify 0.4.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| labeltify-0.4.1.tar.gz | 35.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| labeltify-0.4.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 61.3 kB
Release files / labeltify-0.4.1.tar.gz
| Download URL | labeltify-0.4.1.tar.gz |
|---|---|
| Size | 35.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
57ba32e5fffffad1bdc22e44833d58e4082764107b51fea07e47f7673e4c6448
|
|
BLAKE2b-256 checksum How to use checksums |
3c88f2d189618c4579d0fd1267d738c90af9cbe7e9ea964395a2fe81f7afcd49
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.12.15
|
Release files / labeltify-0.4.1-py3-none-any.whl
| Download URL | labeltify-0.4.1-py3-none-any.whl |
|---|---|
| Size | 25.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
424ec2f45b624227d5fec619c60ed217e4654e8ee894789c4975abe59522a035
|
|
BLAKE2b-256 checksum How to use checksums |
e03e5a1d8cc4edc86db05d33d6e6a3b8629246a305971d4933ad7b63e5680e3a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.12.15
|