x4d-devkit 0.14.2__tar.gz → 0.16.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.
- x4d_devkit-0.16.0/PKG-INFO +534 -0
- x4d_devkit-0.16.0/README.md +500 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/pyproject.toml +9 -4
- x4d_devkit-0.16.0/tests/test_boundary.py +99 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/tests/test_box.py +69 -1
- x4d_devkit-0.16.0/tests/test_build_info.py +119 -0
- x4d_devkit-0.16.0/tests/test_cli_auth.py +104 -0
- x4d_devkit-0.16.0/tests/test_client_projects.py +108 -0
- x4d_devkit-0.16.0/tests/test_clip_archives.py +45 -0
- x4d_devkit-0.16.0/tests/test_contract_validation.py +537 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/tests/test_coordinate_frames.py +41 -41
- x4d_devkit-0.16.0/tests/test_current_state.py +83 -0
- x4d_devkit-0.16.0/tests/test_current_state_identity_consumers_example.py +62 -0
- x4d_devkit-0.16.0/tests/test_dataset_identity.py +724 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/tests/test_detection_eval.py +40 -0
- x4d_devkit-0.16.0/tests/test_evaluate.py +477 -0
- x4d_devkit-0.16.0/tests/test_inference_builders.py +72 -0
- x4d_devkit-0.16.0/tests/test_inference_schemas.py +83 -0
- x4d_devkit-0.16.0/tests/test_inference_service.py +159 -0
- x4d_devkit-0.16.0/tests/test_label_schemas.py +565 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/tests/test_loader.py +8 -7
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/tests/test_nuscenes_converter.py +61 -23
- x4d_devkit-0.16.0/tests/test_training_identity.py +194 -0
- x4d_devkit-0.16.0/tests/test_training_manifest.py +824 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/tests/test_transform_chain.py +7 -7
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/tests/test_v06_api_additions.py +2 -3
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/tests/test_v10_schema.py +141 -0
- x4d_devkit-0.16.0/tests/test_work_session.py +601 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/x4d_devkit/__init__.py +23 -30
- x4d_devkit-0.16.0/x4d_devkit/build_info.py +344 -0
- x4d_devkit-0.16.0/x4d_devkit/cli.py +1164 -0
- x4d_devkit-0.16.0/x4d_devkit/client/__init__.py +32 -0
- x4d_devkit-0.16.0/x4d_devkit/client/bags.py +28 -0
- x4d_devkit-0.16.0/x4d_devkit/client/capabilities.py +19 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/x4d_devkit/client/checkpoints.py +0 -3
- x4d_devkit-0.16.0/x4d_devkit/client/client.py +160 -0
- x4d_devkit-0.16.0/x4d_devkit/client/clip_work_sessions.py +185 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/x4d_devkit/client/clips.py +93 -11
- x4d_devkit-0.16.0/x4d_devkit/client/dataset_identity.py +51 -0
- x4d_devkit-0.16.0/x4d_devkit/client/label_schemas.py +65 -0
- x4d_devkit-0.16.0/x4d_devkit/client/projects.py +113 -0
- x4d_devkit-0.16.0/x4d_devkit/client/services.py +29 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/x4d_devkit/client/test_sets.py +1 -30
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/x4d_devkit/converters/nuscenes.py +64 -4
- x4d_devkit-0.16.0/x4d_devkit/coordinate_contract.py +46 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/x4d_devkit/core/loader.py +187 -3
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/x4d_devkit/core/models.py +4 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/x4d_devkit/core/transform.py +2 -0
- x4d_devkit-0.16.0/x4d_devkit/current_state.py +203 -0
- x4d_devkit-0.16.0/x4d_devkit/dataset_identity.py +720 -0
- x4d_devkit-0.16.0/x4d_devkit/eval/__init__.py +42 -0
- x4d_devkit-0.16.0/x4d_devkit/eval/api.py +400 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/x4d_devkit/eval/box.py +79 -1
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/x4d_devkit/eval/detection.py +37 -7
- x4d_devkit-0.16.0/x4d_devkit/eval/matching.py +542 -0
- x4d_devkit-0.16.0/x4d_devkit/inference/__init__.py +54 -0
- x4d_devkit-0.16.0/x4d_devkit/inference/builders.py +97 -0
- x4d_devkit-0.16.0/x4d_devkit/inference/models.py +33 -0
- x4d_devkit-0.16.0/x4d_devkit/inference/schemas.py +192 -0
- x4d_devkit-0.16.0/x4d_devkit/inference/service.py +201 -0
- x4d_devkit-0.16.0/x4d_devkit/inference/tasks.py +47 -0
- x4d_devkit-0.16.0/x4d_devkit/label_schema.py +1451 -0
- x4d_devkit-0.16.0/x4d_devkit/manifest.py +932 -0
- x4d_devkit-0.16.0/x4d_devkit/materialize.py +375 -0
- x4d_devkit-0.16.0/x4d_devkit/training_identity.py +337 -0
- x4d_devkit-0.16.0/x4d_devkit/validation/__init__.py +19 -0
- x4d_devkit-0.16.0/x4d_devkit/validation/contract.py +954 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/x4d_devkit/validation/validator.py +137 -52
- x4d_devkit-0.16.0/x4d_devkit/work_session.py +388 -0
- x4d_devkit-0.16.0/x4d_devkit.egg-info/PKG-INFO +534 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/x4d_devkit.egg-info/SOURCES.txt +38 -2
- x4d_devkit-0.16.0/x4d_devkit.egg-info/entry_points.txt +2 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/x4d_devkit.egg-info/requires.txt +5 -0
- x4d_devkit-0.14.2/PKG-INFO +0 -144
- x4d_devkit-0.14.2/README.md +0 -114
- x4d_devkit-0.14.2/tests/test_evaluate.py +0 -154
- x4d_devkit-0.14.2/tests/test_fusion.py +0 -134
- x4d_devkit-0.14.2/x4d_devkit/cli.py +0 -131
- x4d_devkit-0.14.2/x4d_devkit/client/__init__.py +0 -13
- x4d_devkit-0.14.2/x4d_devkit/client/client.py +0 -73
- x4d_devkit-0.14.2/x4d_devkit/client/projects.py +0 -36
- x4d_devkit-0.14.2/x4d_devkit/eval/__init__.py +0 -26
- x4d_devkit-0.14.2/x4d_devkit/eval/api.py +0 -205
- x4d_devkit-0.14.2/x4d_devkit/eval/matching.py +0 -179
- x4d_devkit-0.14.2/x4d_devkit/fusion.py +0 -104
- x4d_devkit-0.14.2/x4d_devkit/validation/__init__.py +0 -4
- x4d_devkit-0.14.2/x4d_devkit.egg-info/PKG-INFO +0 -144
- x4d_devkit-0.14.2/x4d_devkit.egg-info/entry_points.txt +0 -2
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/LICENSE +0 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/setup.cfg +0 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/tests/test_matching.py +0 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/tests/test_metrics.py +0 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/tests/test_models.py +0 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/tests/test_token.py +0 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/tests/test_transform.py +0 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/tests/test_v06_schema.py +0 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/tests/test_validation.py +0 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/x4d_devkit/converters/__init__.py +0 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/x4d_devkit/core/__init__.py +0 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/x4d_devkit/core/token.py +0 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/x4d_devkit/eval/metrics.py +0 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/x4d_devkit/validation/report.py +0 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/x4d_devkit.egg-info/dependency_links.txt +0 -0
- {x4d_devkit-0.14.2 → x4d_devkit-0.16.0}/x4d_devkit.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: x4d-devkit
|
|
3
|
+
Version: 0.16.0
|
|
4
|
+
Summary: Official X-4D external SDK and CLI for datasets, platform APIs, training bridges, and automation
|
|
5
|
+
Author: windzu
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/windzu/x4d-devkit
|
|
8
|
+
Project-URL: Repository, https://github.com/windzu/x4d-devkit
|
|
9
|
+
Project-URL: Issues, https://github.com/windzu/x4d-devkit/issues
|
|
10
|
+
Keywords: autonomous-driving,dataset,evaluation,annotation,lidar,camera,cli,sdk
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: numpy>=1.24
|
|
23
|
+
Requires-Dist: httpx>=0.27
|
|
24
|
+
Provides-Extra: converters
|
|
25
|
+
Requires-Dist: nuscenes-devkit>=1.1.0; extra == "converters"
|
|
26
|
+
Provides-Extra: inference
|
|
27
|
+
Requires-Dist: pydantic>=2.0; extra == "inference"
|
|
28
|
+
Requires-Dist: fastapi>=0.110; extra == "inference"
|
|
29
|
+
Requires-Dist: uvicorn>=0.27; extra == "inference"
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# x4d-devkit
|
|
36
|
+
|
|
37
|
+
Canonical contract library and `x4d-devkit` CLI for consuming X-4D data and
|
|
38
|
+
public platform capabilities.
|
|
39
|
+
|
|
40
|
+
`x4d-devkit` owns X-4D data semantics, identity, coordinate/schema contracts,
|
|
41
|
+
platform IO adapters, and official evaluation primitives. It is the stable
|
|
42
|
+
interpretation layer between X-4D platform state and consumers such as training
|
|
43
|
+
platforms, X-Points, external inference services, evaluation scripts, and
|
|
44
|
+
automation.
|
|
45
|
+
|
|
46
|
+
It is not a downstream training, annotation-tool UI, experiment, deployment, or
|
|
47
|
+
framework-specific cache-policy package. It may call public platform APIs or
|
|
48
|
+
read standard X-4D files, but it must not import backend-private `app.*`
|
|
49
|
+
modules or mutate DB/MinIO internals.
|
|
50
|
+
|
|
51
|
+
Design decisions for this boundary are tracked in GitHub issues, starting with:
|
|
52
|
+
|
|
53
|
+
- [#42](https://github.com/windzu/x4d-devkit/issues/42) - official external SDK and CLI boundary
|
|
54
|
+
- [#43](https://github.com/windzu/x4d-devkit/issues/43) - private source install in X-4D runtime
|
|
55
|
+
- [#44](https://github.com/windzu/x4d-devkit/issues/44) - capability registry and CLI sync
|
|
56
|
+
|
|
57
|
+
## Installation
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install x4d-devkit
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
X-4D platform runtime installs this package from the checked-out private source
|
|
64
|
+
repo/submodule, not from an unconstrained package-index version. External
|
|
65
|
+
environments may install from a pinned package release or directly from GitHub,
|
|
66
|
+
depending on deployment policy.
|
|
67
|
+
|
|
68
|
+
Optional heavy dependencies:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# NuScenes format converter
|
|
72
|
+
pip install x4d-devkit[converters]
|
|
73
|
+
|
|
74
|
+
# External inference service SDK
|
|
75
|
+
pip install x4d-devkit[inference]
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The platform API client and `x4d-devkit` CLI are base package features.
|
|
79
|
+
|
|
80
|
+
## Scope
|
|
81
|
+
|
|
82
|
+
See [docs/module-boundary.md](docs/module-boundary.md) for the module ownership
|
|
83
|
+
table and command boundary.
|
|
84
|
+
|
|
85
|
+
Internal consumers should use the current-state identity contract for
|
|
86
|
+
platform-to-platform data flow. See
|
|
87
|
+
[docs/current-state-identity.md](docs/current-state-identity.md) for the
|
|
88
|
+
generic identity model, and
|
|
89
|
+
[docs/training-current-state-data.md](docs/training-current-state-data.md) for
|
|
90
|
+
diff/sync, work-session materialization, training manifests, and conditional
|
|
91
|
+
annotation writeback flows built on that model.
|
|
92
|
+
Shared validation rules for platform submit, X-Points preflight, local clip
|
|
93
|
+
validation, and training consumers are documented in
|
|
94
|
+
[docs/shared-contract-validation.md](docs/shared-contract-validation.md).
|
|
95
|
+
`examples/current_state_identity_consumers.py` provides an offline downstream
|
|
96
|
+
fixture for OpenPCDet-style, mmdetection3d-style, and annotation/review
|
|
97
|
+
consumers.
|
|
98
|
+
|
|
99
|
+
Included:
|
|
100
|
+
|
|
101
|
+
- Local X-4D dataset loading, validation, transforms, annotations, calibration,
|
|
102
|
+
ego pose, manifests, converters, and evaluation helpers.
|
|
103
|
+
- Platform API client and public `x4d-devkit` CLI commands for external
|
|
104
|
+
workflows such as clip listing/download, screening preview, model/checkpoint
|
|
105
|
+
registration, status queries, and capability discovery.
|
|
106
|
+
- Training bridge workflows such as multi-project manifests, archive download,
|
|
107
|
+
split validation, and dataloader-friendly metadata.
|
|
108
|
+
- External inference service wrappers for third-party model-serving projects.
|
|
109
|
+
|
|
110
|
+
Excluded:
|
|
111
|
+
|
|
112
|
+
- Backend migrations, DB/MinIO repair scripts, cache rebuild internals,
|
|
113
|
+
emergency operations, and one-off delivery importers.
|
|
114
|
+
- Any code that imports backend-private `app.*`.
|
|
115
|
+
- Downstream experiment taxonomy, `CLASS_NAMES`, model-class mappings, sampler
|
|
116
|
+
policy, model acceptance thresholds, deployment policy, and framework-specific
|
|
117
|
+
artifacts such as OpenPCDet infos/dbinfos or mmdetection3d pickle views.
|
|
118
|
+
|
|
119
|
+
Promotion rule: an internal operation becomes an `x4d-devkit` command only
|
|
120
|
+
after it is productized as an external SDK/API workflow with explicit
|
|
121
|
+
permissions, side effects, idempotency, and failure modes. Data deletion, clip
|
|
122
|
+
production, and internal service dispatch stay out of devkit.
|
|
123
|
+
|
|
124
|
+
Training-specific convenience utilities must remain framework-neutral. Devkit
|
|
125
|
+
may expose raw category stats, identity fingerprints, coordinate/schema
|
|
126
|
+
validation, cache completeness checks, and official X-4D metrics; downstream
|
|
127
|
+
training repos own taxonomy mapping, training-format generation, sampler
|
|
128
|
+
configuration, and experiment lifecycle.
|
|
129
|
+
|
|
130
|
+
## Quick Start
|
|
131
|
+
|
|
132
|
+
### Connect to an X-4D platform
|
|
133
|
+
|
|
134
|
+
The CLI can authenticate with a bearer token passed explicitly, through
|
|
135
|
+
environment variables, or from the user config written by `x4d-devkit login`.
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
x4d-devkit --api-url http://host:8000 --token <token> projects list
|
|
139
|
+
x4d-devkit --api-url http://host:8000 --token <token> clips list --project-id 1
|
|
140
|
+
x4d-devkit --api-url http://host:8000 --token <token> clips download --clip-ids clip_a,clip_b --output /data/x4d/clips
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
export X4D_API_URL=http://host:8000
|
|
145
|
+
export X4D_TOKEN=<token>
|
|
146
|
+
x4d-devkit projects list
|
|
147
|
+
x4d-devkit clips list --project-name nuscenes-mini --has-archive true --format json
|
|
148
|
+
x4d-devkit clips download --project-name nuscenes-mini --output /data/x4d/nuscenes-mini/clips --workers 8
|
|
149
|
+
x4d-devkit clips download --project-name nuscenes-mini --archive-profile keyframes_only --output /data/x4d/nuscenes-mini/keyframes --workers 8
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
x4d-devkit --api-url http://host:8000 login --username <user> --password <password>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
`login` stores `api_url` and the returned access token as `token` in
|
|
157
|
+
`~/.config/x4d/config.toml`. Archive downloads go through the platform file
|
|
158
|
+
proxy and extract standard X4D clip directories; training containers do not
|
|
159
|
+
need database or MinIO access.
|
|
160
|
+
|
|
161
|
+
Clip archive downloads are profile-aware. `--archive-profile keyframes_only` is
|
|
162
|
+
the default and requests the smaller archive with sweeps removed and metadata
|
|
163
|
+
filtered accordingly. Use `--archive-profile full` when a consumer needs the
|
|
164
|
+
complete clip payload including `sweeps/`. If the requested profile has not been
|
|
165
|
+
built, is stale, or failed to build, the devkit reports that profile-specific
|
|
166
|
+
reason instead of falling back to another tar.
|
|
167
|
+
|
|
168
|
+
### Create a multi-project training manifest
|
|
169
|
+
|
|
170
|
+
Training projects can combine multiple X-4D projects by downloading standard
|
|
171
|
+
clip archives and generating one local manifest. The downstream training
|
|
172
|
+
framework consumes only the manifest and local clip files; it does not need
|
|
173
|
+
database, MinIO, or platform-private file paths.
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
x4d-devkit manifest create \
|
|
177
|
+
--projects hg1.0 hg1.5 \
|
|
178
|
+
--data-root /data/x4d/cache/hg_mix \
|
|
179
|
+
--output /data/x4d/cache/hg_mix/manifest.json \
|
|
180
|
+
--download-missing \
|
|
181
|
+
--archive-profile keyframes_only \
|
|
182
|
+
--split-policy by_clip \
|
|
183
|
+
--val-ratio 0.2 \
|
|
184
|
+
--seed 42
|
|
185
|
+
|
|
186
|
+
x4d-devkit manifest validate \
|
|
187
|
+
--manifest /data/x4d/cache/hg_mix/manifest.json \
|
|
188
|
+
--data-root /data/x4d/cache/hg_mix
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Internal current-state consumers can snapshot remote clip identity before
|
|
192
|
+
reviewing diffs or syncing local caches:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
x4d-devkit dataset inspect \
|
|
196
|
+
--project-name hg1.5 \
|
|
197
|
+
--include-asset-revision \
|
|
198
|
+
--out remote_identity.json
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Training runs can validate their consumed-data identity record:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
x4d-devkit manifest identity-validate --manifest training_identity.json
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Dataset update planning can also compare consumer-owned training identity
|
|
208
|
+
fields, such as split membership, class-mapping fingerprint, data policy, or a
|
|
209
|
+
generic consumer identity fingerprint:
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
x4d-devkit dataset diff \
|
|
213
|
+
--local-manifest current_training_identity.json \
|
|
214
|
+
--previous-training-identity previous_training_identity.json \
|
|
215
|
+
--out update_plan.json
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
`manifest validate` prints bounded warning output by default. It reports
|
|
219
|
+
warning totals grouped by type, category, and project/category, then shows a
|
|
220
|
+
small set of examples. Use `--max-warnings`, `--all-warnings`, or
|
|
221
|
+
`--warnings-output warnings.jsonl` when full warning details are needed.
|
|
222
|
+
|
|
223
|
+
Downloaded clips are stored under
|
|
224
|
+
`projects/<project-name>/clips/<clip-id>/`. Manifest file paths are relative to
|
|
225
|
+
`--data-root`, so the dataset can be moved as a directory.
|
|
226
|
+
|
|
227
|
+
The platform is expected to assign globally unique `clip_id` values. The devkit
|
|
228
|
+
validator checks duplicate clip identities and split leakage, but it does not
|
|
229
|
+
perform expensive semantic near-duplicate detection across projects.
|
|
230
|
+
|
|
231
|
+
```python
|
|
232
|
+
from x4d_devkit import X4DClient
|
|
233
|
+
|
|
234
|
+
client = X4DClient.from_config()
|
|
235
|
+
manifest = client.create_training_manifest(
|
|
236
|
+
projects=["hg1.0", "hg1.5"],
|
|
237
|
+
data_root="/data/x4d/cache/hg_mix",
|
|
238
|
+
output="/data/x4d/cache/hg_mix/manifest.json",
|
|
239
|
+
download_missing=True,
|
|
240
|
+
split_policy="by_clip",
|
|
241
|
+
val_ratio=0.2,
|
|
242
|
+
seed=42,
|
|
243
|
+
)
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
The detection manifest keeps boxes in their native X-4D annotation frame
|
|
247
|
+
(`meta.sensors[meta.annotation_source_channel].frame_id`). It does not align
|
|
248
|
+
poses across clips; each clip's `clip_world` remains local to that clip.
|
|
249
|
+
|
|
250
|
+
### Consume the project label schema
|
|
251
|
+
|
|
252
|
+
X-4D is the source of truth for label schemas. External training and inference
|
|
253
|
+
projects should read the effective project schema from the platform instead of
|
|
254
|
+
maintaining their own category list.
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
x4d-devkit label-schemas project-get --project-id 1 --format json
|
|
258
|
+
x4d-devkit label-schemas get \
|
|
259
|
+
--schema-id object_3d_low_speed_roadside \
|
|
260
|
+
--version 1.0.0 \
|
|
261
|
+
--format json
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
```python
|
|
265
|
+
from x4d_devkit import X4DClient
|
|
266
|
+
from x4d_devkit.label_schema import compare_project_label_schema_snapshot
|
|
267
|
+
|
|
268
|
+
client = X4DClient.from_config()
|
|
269
|
+
project = client.projects.resolve(project_name="nuscenes-mini")
|
|
270
|
+
project_schema = client.label_schemas.resolve_project(project_name="nuscenes-mini")
|
|
271
|
+
label_schema = project_schema.label_schema
|
|
272
|
+
|
|
273
|
+
print(project["id"], project["name"])
|
|
274
|
+
print(project_schema.binding.to_dict())
|
|
275
|
+
print(sorted(label_schema.class_ids))
|
|
276
|
+
|
|
277
|
+
label_schema.validate_category("tanker_truck")
|
|
278
|
+
|
|
279
|
+
stored_snapshot = project_schema.to_dict()
|
|
280
|
+
current = client.label_schemas.resolve_project(project_id=project["id"])
|
|
281
|
+
comparison = compare_project_label_schema_snapshot(stored_snapshot, current)
|
|
282
|
+
assert comparison.status == "same"
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
The registry schema is the fine-grained annotation contract. Training code may
|
|
286
|
+
still define experiment-specific class mappings, but those mappings should be
|
|
287
|
+
validated against the project schema before a job starts.
|
|
288
|
+
|
|
289
|
+
Training manifests keep the immutable project contract separate from the
|
|
290
|
+
observed/configured class catalog:
|
|
291
|
+
|
|
292
|
+
```text
|
|
293
|
+
label_schema.projects.<project>.contract # binding + full registry record
|
|
294
|
+
label_schema.projects.<project>.class_catalog # training/observed class catalog
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
The deprecated per-project `classes`, `source`, and `schema_version` aliases
|
|
298
|
+
remain in manifest v0.1 for existing consumers. New consumers must parse
|
|
299
|
+
`contract` with `parse_project_label_schema_snapshot()` and treat
|
|
300
|
+
`class_catalog` as non-authoritative observation metadata.
|
|
301
|
+
|
|
302
|
+
This strict parser/view contract is introduced in x4d-devkit `0.16.0`.
|
|
303
|
+
Consumers tracking `main` should still record `get_build_info()["source"]` in
|
|
304
|
+
training identity so an untagged development run remains reproducible by exact
|
|
305
|
+
commit. Deployments that copy a source tree without its Git directory must set
|
|
306
|
+
`X4D_DEVKIT_COMMIT` to the copied 40- or 64-hex commit; `get_build_info()` records
|
|
307
|
+
that value with `commit_source=environment`.
|
|
308
|
+
|
|
309
|
+
Project resolution by name/key is exact and handles platform pagination
|
|
310
|
+
internally; ambiguous or missing project references raise structured resolver
|
|
311
|
+
errors instead of requiring callers to scan project lists themselves.
|
|
312
|
+
|
|
313
|
+
### Load a clip
|
|
314
|
+
|
|
315
|
+
```python
|
|
316
|
+
from x4d_devkit import ClipLoader
|
|
317
|
+
|
|
318
|
+
loader = ClipLoader("/path/to/clip")
|
|
319
|
+
print(loader.meta)
|
|
320
|
+
|
|
321
|
+
for sample in loader.samples:
|
|
322
|
+
for sd in loader.sample_data_for_sample(sample.token):
|
|
323
|
+
print(sd.channel, sd.file_path)
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### Open a platform work session
|
|
327
|
+
|
|
328
|
+
Internal tools such as X-Points can open the platform's current clip state
|
|
329
|
+
without waiting for a prebuilt archive:
|
|
330
|
+
|
|
331
|
+
```python
|
|
332
|
+
from x4d_devkit import ClipLoader, X4DClient, materialize_work_session_clip
|
|
333
|
+
from x4d_devkit.client import WorkSessionConflictError
|
|
334
|
+
from x4d_devkit.core.loader import CLIP_WORLD_FRAME_ID
|
|
335
|
+
|
|
336
|
+
client = X4DClient.from_config()
|
|
337
|
+
manifest = client.clip_work_sessions.open(project_id=1812, clip_id="clip-id")
|
|
338
|
+
materialize_work_session_clip(
|
|
339
|
+
manifest,
|
|
340
|
+
output_dir="/data/x4d_cache/clips/clip-id",
|
|
341
|
+
client=client,
|
|
342
|
+
asset_policy="keyframes_only",
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
loader = ClipLoader.from_work_session(manifest, api_url=client.api_url)
|
|
346
|
+
anns_world = loader.annotations_for_sample("sample-token", frame=CLIP_WORLD_FRAME_ID)
|
|
347
|
+
lidar_asset = loader.point_cloud_asset("sample-token", manifest.annotation_source_channel)
|
|
348
|
+
|
|
349
|
+
try:
|
|
350
|
+
response = client.clip_work_sessions.submit_annotations(
|
|
351
|
+
manifest=manifest,
|
|
352
|
+
annotations=manifest.annotations,
|
|
353
|
+
instances=manifest.instances,
|
|
354
|
+
)
|
|
355
|
+
print(response["annotation_revision"])
|
|
356
|
+
except WorkSessionConflictError as exc:
|
|
357
|
+
print(exc.current_identities)
|
|
358
|
+
raise
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
Materialization preserves both schema facts atomically: the clip's recorded
|
|
362
|
+
identity remains in `meta.label_schema`, while the effective full registry
|
|
363
|
+
record is written to `label_schema.json`. Full and annotation-only refreshes
|
|
364
|
+
write the same schema snapshot, so local validation does not require a live
|
|
365
|
+
registry lookup.
|
|
366
|
+
|
|
367
|
+
A ready work session is accepted only when `label_schema_fingerprint`, the full
|
|
368
|
+
registry record, `clip_label_schema`, and `meta.label_schema` declare the same
|
|
369
|
+
identity. A not-ready session may expose a stale clip identity alongside the
|
|
370
|
+
current project schema so migration tooling can report the mismatch, but it
|
|
371
|
+
cannot be materialized.
|
|
372
|
+
|
|
373
|
+
Submit is conditional on the manifest's `source_revision`,
|
|
374
|
+
`annotation_revision`, and `label_schema_fingerprint`. If the platform state
|
|
375
|
+
changed after the session was opened, the client raises
|
|
376
|
+
`WorkSessionConflictError` with machine-readable `conflicts`,
|
|
377
|
+
`base_identities`, and `current_identities`.
|
|
378
|
+
|
|
379
|
+
### Coordinate frame transforms
|
|
380
|
+
|
|
381
|
+
Frames are real frame_id strings: any node in the calibration tree (e.g.
|
|
382
|
+
`"LIDAR_TOP"`, `"base_link"`, `"cam_front"`) plus the constant
|
|
383
|
+
`"clip_world"` (the SLAM-anchored clip-local world). The legacy aliases
|
|
384
|
+
`"sensor"`, `"ego"`, `"world"` are not accepted.
|
|
385
|
+
|
|
386
|
+
Training converters should read the clip's self-described frame contract rather
|
|
387
|
+
than assume fixed channel names. The native annotation frame is derived from
|
|
388
|
+
`meta.sensors[meta.annotation_source_channel].frame_id`; `clip_world` is local
|
|
389
|
+
to one clip and is not a global frame across clips.
|
|
390
|
+
|
|
391
|
+
The installed package exposes the training coordinate contract for logs and
|
|
392
|
+
debugging:
|
|
393
|
+
|
|
394
|
+
```bash
|
|
395
|
+
x4d-devkit dataset coordinate-contract
|
|
396
|
+
x4d-devkit dataset coordinate-contract --format json
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
```python
|
|
400
|
+
from x4d_devkit import COORDINATE_CONTRACT_VERSION, get_training_coordinate_contract
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
```python
|
|
404
|
+
from x4d_devkit import ClipLoader
|
|
405
|
+
from x4d_devkit.core.loader import CLIP_WORLD_FRAME_ID
|
|
406
|
+
|
|
407
|
+
loader = ClipLoader("/path/to/clip")
|
|
408
|
+
sd = loader.sample_data_for_channel("LIDAR_TOP")[0]
|
|
409
|
+
|
|
410
|
+
# Load point cloud in different frames
|
|
411
|
+
pts_sensor = loader.load_point_cloud(sd) # raw sensor (default)
|
|
412
|
+
pts_ego = loader.load_point_cloud(sd, frame=loader.ego_pose_frame_id) # sensor → ego
|
|
413
|
+
pts_world = loader.load_point_cloud(sd, frame=CLIP_WORLD_FRAME_ID) # sensor → clip_world
|
|
414
|
+
|
|
415
|
+
# Get annotations transformed to clip-local world
|
|
416
|
+
anns_world = loader.annotations_for_sample(sample.token, frame=CLIP_WORLD_FRAME_ID)
|
|
417
|
+
|
|
418
|
+
# Or to a specific sensor's frame
|
|
419
|
+
anns_lidar = loader.annotations_for_sample(sample.token, frame="LIDAR_TOP")
|
|
420
|
+
|
|
421
|
+
# Get the transform matrix directly (sd is required when clip_world is involved)
|
|
422
|
+
T = loader.get_transform(loader.sensor_frame_id(sd), CLIP_WORLD_FRAME_ID, sd=sd)
|
|
423
|
+
pts_world = T.apply(pts_sensor[:, :3]) # or use T.as_matrix for 4x4
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### Validate a clip
|
|
427
|
+
|
|
428
|
+
```bash
|
|
429
|
+
x4d-devkit validate /path/to/clip
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
```python
|
|
433
|
+
from x4d_devkit import validate_clip
|
|
434
|
+
|
|
435
|
+
report = validate_clip("/path/to/clip")
|
|
436
|
+
print(report)
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
### Serve an external inference backend
|
|
440
|
+
|
|
441
|
+
Third-party model projects should use `x4d-devkit[inference]` instead of
|
|
442
|
+
hand-writing the X-4D inference HTTP protocol.
|
|
443
|
+
|
|
444
|
+
```python
|
|
445
|
+
from x4d_devkit.inference import DetectionModel, InferenceService, detection_3d_item
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
class MyDetector(DetectionModel):
|
|
449
|
+
model_id = "centerpoint-v1"
|
|
450
|
+
display_name = "CenterPoint v1"
|
|
451
|
+
raw_classes = ["car", "truck", "pedestrian"]
|
|
452
|
+
|
|
453
|
+
def predict_clip(self, clip, config):
|
|
454
|
+
return [
|
|
455
|
+
detection_3d_item(
|
|
456
|
+
sample_token=clip.samples[0].token,
|
|
457
|
+
raw_class="car",
|
|
458
|
+
score=0.91,
|
|
459
|
+
translation=(1.0, 2.0, 0.5),
|
|
460
|
+
size=(4.5, 1.8, 1.6),
|
|
461
|
+
yaw=0.2,
|
|
462
|
+
)
|
|
463
|
+
]
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
InferenceService(service_name="my-detector", models=[MyDetector()]).run(port=9000)
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
See [docs/external-inference.md](docs/external-inference.md) and
|
|
470
|
+
`examples/external_detection_service.py` for the standard integration flow.
|
|
471
|
+
|
|
472
|
+
### Detection evaluation
|
|
473
|
+
|
|
474
|
+
```python
|
|
475
|
+
from x4d_devkit.eval import DetectionEval, DetectionConfig
|
|
476
|
+
|
|
477
|
+
config = DetectionConfig(
|
|
478
|
+
class_names=["car", "pedestrian", "bicycle"],
|
|
479
|
+
dist_thresholds=[0.5, 1.0, 2.0, 4.0],
|
|
480
|
+
)
|
|
481
|
+
evaluator = DetectionEval(config, gt_clips=[...], pred_clips=[...])
|
|
482
|
+
result = evaluator.evaluate()
|
|
483
|
+
print(f"mAP: {result.mAP:.3f}, NDS: {result.NDS:.3f}")
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
For detectors that intentionally do not predict velocity, construct 7D boxes
|
|
487
|
+
and evaluate with `with_velocity=False` so velocity error is excluded from the
|
|
488
|
+
primary detection score:
|
|
489
|
+
|
|
490
|
+
```python
|
|
491
|
+
from x4d_devkit.eval import Box, DetectionConfig, evaluate
|
|
492
|
+
|
|
493
|
+
pred = {
|
|
494
|
+
"sample_1": [
|
|
495
|
+
Box.from_xyzlwhyaw([0, 0, 0, 4, 2, 1.5, 0.0], category="car", score=0.9)
|
|
496
|
+
]
|
|
497
|
+
}
|
|
498
|
+
config = DetectionConfig(
|
|
499
|
+
class_names=["car"],
|
|
500
|
+
dist_thresholds=[0.5, 1.0, 2.0, 4.0],
|
|
501
|
+
dist_th_tp=2.0,
|
|
502
|
+
min_recall=0.1,
|
|
503
|
+
min_precision=0.1,
|
|
504
|
+
max_boxes_per_sample=500,
|
|
505
|
+
class_range={"car": 50.0},
|
|
506
|
+
with_velocity=False,
|
|
507
|
+
)
|
|
508
|
+
result = evaluate(gt, pred, config)
|
|
509
|
+
assert result.with_velocity is False
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
### Convert from NuScenes
|
|
513
|
+
|
|
514
|
+
```python
|
|
515
|
+
from x4d_devkit.converters import NuScenesConverter
|
|
516
|
+
|
|
517
|
+
converter = NuScenesConverter("/path/to/nuscenes")
|
|
518
|
+
converter.convert_scene("scene-0001", output_dir="/path/to/output")
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
## Modules
|
|
522
|
+
|
|
523
|
+
| Module | Description |
|
|
524
|
+
|--------|-------------|
|
|
525
|
+
| `core` | Data models, token generation, coordinate transforms, clip loader |
|
|
526
|
+
| `eval` | Detection evaluation (mAP, TP metrics, NDS) |
|
|
527
|
+
| `converters` | Format converters (NuScenes → X4D) |
|
|
528
|
+
| `manifest` | Training manifest creation, validation, and clip archive download |
|
|
529
|
+
| `validation` | Clip structure and data validation |
|
|
530
|
+
| `client` | X-4D platform API client |
|
|
531
|
+
|
|
532
|
+
## License
|
|
533
|
+
|
|
534
|
+
Apache License 2.0
|