labeltify 0.2.0__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- labeltify-0.4.0/PKG-INFO +104 -0
- labeltify-0.4.0/README.md +83 -0
- {labeltify-0.2.0 → labeltify-0.4.0}/labeltify/__init__.py +14 -2
- labeltify-0.4.0/labeltify/client.py +580 -0
- labeltify-0.4.0/labeltify/dhash.py +55 -0
- labeltify-0.4.0/labeltify.egg-info/PKG-INFO +104 -0
- {labeltify-0.2.0 → labeltify-0.4.0}/labeltify.egg-info/SOURCES.txt +1 -0
- labeltify-0.4.0/labeltify.egg-info/requires.txt +6 -0
- {labeltify-0.2.0 → labeltify-0.4.0}/pyproject.toml +2 -1
- labeltify-0.4.0/tests/test_client.py +407 -0
- labeltify-0.2.0/PKG-INFO +0 -74
- labeltify-0.2.0/README.md +0 -55
- labeltify-0.2.0/labeltify/client.py +0 -188
- labeltify-0.2.0/labeltify.egg-info/PKG-INFO +0 -74
- labeltify-0.2.0/labeltify.egg-info/requires.txt +0 -3
- labeltify-0.2.0/tests/test_client.py +0 -127
- {labeltify-0.2.0 → labeltify-0.4.0}/labeltify.egg-info/dependency_links.txt +0 -0
- {labeltify-0.2.0 → labeltify-0.4.0}/labeltify.egg-info/top_level.txt +0 -0
- {labeltify-0.2.0 → labeltify-0.4.0}/setup.cfg +0 -0
labeltify-0.4.0/PKG-INFO
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: labeltify
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Upload frames and optional annotations into LabelTify with a pipeline token.
|
|
5
|
+
Author: CleverTify
|
|
6
|
+
Project-URL: Homepage, https://labeltify.com
|
|
7
|
+
Project-URL: Source, https://github.com/CleverTify/LabelTify/tree/main/clients/python
|
|
8
|
+
Keywords: labeltify,annotation,computer-vision,deepstream,active-learning,dataset
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Image Recognition
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
Provides-Extra: test
|
|
18
|
+
Requires-Dist: pytest>=8; extra == "test"
|
|
19
|
+
Provides-Extra: images
|
|
20
|
+
Requires-Dist: Pillow>=10; extra == "images"
|
|
21
|
+
|
|
22
|
+
# labeltify
|
|
23
|
+
|
|
24
|
+
Upload frames, and optionally their annotations, into [LabelTify](https://labeltify.com) from DeepStream, OpenCV, Ultralytics, or any Python pipeline. The only thing it needs is a pipeline token.
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
pip install labeltify
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
1. On the dataset **Upload** page, create a pipeline token.
|
|
31
|
+
2. Upload:
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from labeltify import LabelTifyClient
|
|
35
|
+
|
|
36
|
+
client = LabelTifyClient("labeltify_…") # or set LABELTIFY_TOKEN and call LabelTifyClient()
|
|
37
|
+
|
|
38
|
+
img = client.upload_image(
|
|
39
|
+
"DATASET_ID",
|
|
40
|
+
jpeg_bytes,
|
|
41
|
+
filename="cam01.jpg",
|
|
42
|
+
camera="cam-01",
|
|
43
|
+
pipeline="ds-prod",
|
|
44
|
+
trigger="low_confidence",
|
|
45
|
+
)
|
|
46
|
+
client.upload_file("DATASET_ID", "frames/cam02.jpg", camera="cam-02")
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
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`.
|
|
50
|
+
|
|
51
|
+
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.
|
|
52
|
+
|
|
53
|
+
### Upload a folder with its annotations
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
report = client.upload_directory(
|
|
57
|
+
"DATASET_ID",
|
|
58
|
+
"datasets/parking",
|
|
59
|
+
work_state="done",
|
|
60
|
+
)
|
|
61
|
+
print(len(report["items"]), "images stored in", report["requests"], "requests;", report["imported"])
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
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.
|
|
65
|
+
|
|
66
|
+
- The dataset must already exist. Create it on the site. Parking polygons need type **Instance segmentation**. Traffic boxes need **Object detection**.
|
|
67
|
+
- `work_state="done"` marks every image that received a shape. Images with no shape stay untouched. With any label file in the folder, `work_state` is required.
|
|
68
|
+
- Create the pipeline token on that dataset's **Upload** page.
|
|
69
|
+
- One credit covers 10,000 uploaded images. A free dataset holds 100 photos.
|
|
70
|
+
- Frame embeddings follow the dataset setting. Turn them off on the dataset before a large upload when you do not want that charge.
|
|
71
|
+
- 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.
|
|
72
|
+
- Two images with the same file name in different folders stop the upload before anything is sent; rename one.
|
|
73
|
+
- 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.
|
|
74
|
+
- If a request fails after its retries, the error says how many images were already stored; run the folder again and exact duplicates are reported instead of stored twice.
|
|
75
|
+
- `compute_dhash=True` also skips near-duplicate frames, like the Upload page. It needs Pillow: `pip install "labeltify[images]"`.
|
|
76
|
+
|
|
77
|
+
`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"}`).
|
|
78
|
+
|
|
79
|
+
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": …})`.
|
|
80
|
+
|
|
81
|
+
Send model proposals to the Loop review queue:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
client.propose(
|
|
85
|
+
"DATASET_ID",
|
|
86
|
+
img["id"],
|
|
87
|
+
[{"type": "bbox", "id": "p1", "classId": "car", "box": {"x": 0.1, "y": 0.1, "w": 0.2, "h": 0.2}, "source": "model", "confidence": 0.4}],
|
|
88
|
+
model_ref="deepstream@1",
|
|
89
|
+
uncertainty=0.8,
|
|
90
|
+
)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Boxes are normalized `[0,1]`. Camera, pipeline, and trigger are stored on the image so Loop can filter by them.
|
|
94
|
+
|
|
95
|
+
Local API: `LabelTifyClient("labeltify_…", base_url="http://localhost:8787")`.
|
|
96
|
+
|
|
97
|
+
## Develop
|
|
98
|
+
|
|
99
|
+
```sh
|
|
100
|
+
pip install -e "clients/python[test]"
|
|
101
|
+
pytest clients/python/tests
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Bump `version` in `pyproject.toml` to publish. Each new version that lands on `main` is published to PyPI by `.github/workflows/publish-python.yml`.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# labeltify
|
|
2
|
+
|
|
3
|
+
Upload frames, and optionally their annotations, into [LabelTify](https://labeltify.com) from DeepStream, OpenCV, Ultralytics, or any Python pipeline. The only thing it needs is a pipeline token.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
pip install labeltify
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
1. On the dataset **Upload** page, create a pipeline token.
|
|
10
|
+
2. Upload:
|
|
11
|
+
|
|
12
|
+
```python
|
|
13
|
+
from labeltify import LabelTifyClient
|
|
14
|
+
|
|
15
|
+
client = LabelTifyClient("labeltify_…") # or set LABELTIFY_TOKEN and call LabelTifyClient()
|
|
16
|
+
|
|
17
|
+
img = client.upload_image(
|
|
18
|
+
"DATASET_ID",
|
|
19
|
+
jpeg_bytes,
|
|
20
|
+
filename="cam01.jpg",
|
|
21
|
+
camera="cam-01",
|
|
22
|
+
pipeline="ds-prod",
|
|
23
|
+
trigger="low_confidence",
|
|
24
|
+
)
|
|
25
|
+
client.upload_file("DATASET_ID", "frames/cam02.jpg", camera="cam-02")
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
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`.
|
|
29
|
+
|
|
30
|
+
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.
|
|
31
|
+
|
|
32
|
+
### Upload a folder with its annotations
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
report = client.upload_directory(
|
|
36
|
+
"DATASET_ID",
|
|
37
|
+
"datasets/parking",
|
|
38
|
+
work_state="done",
|
|
39
|
+
)
|
|
40
|
+
print(len(report["items"]), "images stored in", report["requests"], "requests;", report["imported"])
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
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.
|
|
44
|
+
|
|
45
|
+
- The dataset must already exist. Create it on the site. Parking polygons need type **Instance segmentation**. Traffic boxes need **Object detection**.
|
|
46
|
+
- `work_state="done"` marks every image that received a shape. Images with no shape stay untouched. With any label file in the folder, `work_state` is required.
|
|
47
|
+
- Create the pipeline token on that dataset's **Upload** page.
|
|
48
|
+
- One credit covers 10,000 uploaded images. A free dataset holds 100 photos.
|
|
49
|
+
- Frame embeddings follow the dataset setting. Turn them off on the dataset before a large upload when you do not want that charge.
|
|
50
|
+
- 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.
|
|
51
|
+
- Two images with the same file name in different folders stop the upload before anything is sent; rename one.
|
|
52
|
+
- 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.
|
|
53
|
+
- If a request fails after its retries, the error says how many images were already stored; run the folder again and exact duplicates are reported instead of stored twice.
|
|
54
|
+
- `compute_dhash=True` also skips near-duplicate frames, like the Upload page. It needs Pillow: `pip install "labeltify[images]"`.
|
|
55
|
+
|
|
56
|
+
`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"}`).
|
|
57
|
+
|
|
58
|
+
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": …})`.
|
|
59
|
+
|
|
60
|
+
Send model proposals to the Loop review queue:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
client.propose(
|
|
64
|
+
"DATASET_ID",
|
|
65
|
+
img["id"],
|
|
66
|
+
[{"type": "bbox", "id": "p1", "classId": "car", "box": {"x": 0.1, "y": 0.1, "w": 0.2, "h": 0.2}, "source": "model", "confidence": 0.4}],
|
|
67
|
+
model_ref="deepstream@1",
|
|
68
|
+
uncertainty=0.8,
|
|
69
|
+
)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Boxes are normalized `[0,1]`. Camera, pipeline, and trigger are stored on the image so Loop can filter by them.
|
|
73
|
+
|
|
74
|
+
Local API: `LabelTifyClient("labeltify_…", base_url="http://localhost:8787")`.
|
|
75
|
+
|
|
76
|
+
## Develop
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
pip install -e "clients/python[test]"
|
|
80
|
+
pytest clients/python/tests
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Bump `version` in `pyproject.toml` to publish. Each new version that lands on `main` is published to PyPI by `.github/workflows/publish-python.yml`.
|
|
@@ -2,11 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
from importlib.metadata import PackageNotFoundError, version
|
|
4
4
|
|
|
5
|
-
from .client import DEFAULT_BASE_URL, LabelTifyClient, LabelTifyError
|
|
5
|
+
from .client import DEFAULT_BASE_URL, LabelTifyClient, LabelTifyError, UploadFile, UploadReport, slice_coco
|
|
6
|
+
from .dhash import dhash_from_gray9x8, format_dhash, parse_dhash
|
|
6
7
|
|
|
7
8
|
try:
|
|
8
9
|
__version__ = version("labeltify")
|
|
9
10
|
except PackageNotFoundError: # running from a checkout
|
|
10
11
|
__version__ = "0.0.0"
|
|
11
12
|
|
|
12
|
-
__all__ = [
|
|
13
|
+
__all__ = [
|
|
14
|
+
"DEFAULT_BASE_URL",
|
|
15
|
+
"LabelTifyClient",
|
|
16
|
+
"LabelTifyError",
|
|
17
|
+
"UploadFile",
|
|
18
|
+
"UploadReport",
|
|
19
|
+
"__version__",
|
|
20
|
+
"dhash_from_gray9x8",
|
|
21
|
+
"format_dhash",
|
|
22
|
+
"parse_dhash",
|
|
23
|
+
"slice_coco",
|
|
24
|
+
]
|