x4d-devkit 0.43.0rc4__tar.gz → 0.45.0rc1__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.43.0rc4/x4d_devkit.egg-info → x4d_devkit-0.45.0rc1}/PKG-INFO +10 -10
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/README.md +9 -9
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/pyproject.toml +1 -1
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_boundary.py +5 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_contract_validation.py +3 -4
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_current_state.py +4 -2
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_dataset_identity.py +5 -2
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_export_bundle.py +2 -2
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_inference_input.py +2 -2
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_inference_schemas.py +4 -3
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_inference_service.py +1 -1
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_label_schemas.py +4 -2
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_loader.py +1 -1
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_models.py +2 -3
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_preannotation_client.py +1 -1
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_sdk_api.py +4 -4
- x4d_devkit-0.45.0rc1/tests/test_source_attributes.py +54 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_training_identity.py +3 -3
- x4d_devkit-0.45.0rc1/tests/test_vector_map.py +119 -0
- x4d_devkit-0.45.0rc1/tests/test_vector_map_geometry.py +76 -0
- x4d_devkit-0.45.0rc1/tests/test_vector_map_session.py +147 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_work_session.py +11 -6
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/__init__.py +12 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/annotation_contract.py +2 -2
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/cli.py +1 -1
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/client/clip_work_sessions.py +12 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/core/loader.py +18 -5
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/core/models.py +6 -6
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/current_state.py +15 -8
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/dataset_contract.py +16 -8
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/dataset_identity.py +14 -8
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/eval/tracking.py +4 -4
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/export_bundle.py +1 -1
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/inference/input.py +1 -1
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/inference/schemas.py +2 -2
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/label_schema.py +54 -1
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/lidar_materialization.py +1 -1
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/materialize.py +32 -6
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/semantic_scene_contract.py +3 -3
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/time_contract.py +1 -1
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/validation/platform_pose.py +1 -1
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/validation/validator.py +14 -7
- x4d_devkit-0.45.0rc1/x4d_devkit/vector_map.py +371 -0
- x4d_devkit-0.45.0rc1/x4d_devkit/vector_map_geometry.py +221 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/work_session.py +21 -1
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1/x4d_devkit.egg-info}/PKG-INFO +10 -10
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit.egg-info/SOURCES.txt +6 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/LICENSE +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/setup.cfg +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_annotation_contract.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_annotation_reference.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_assets_client.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_box.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_build_info.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_clean_basemap.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_clean_basemap_client.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_clean_basemap_preannotation.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_cli_auth.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_client_json_contract.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_client_projects.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_clip_archives.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_coordinate_frames.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_current_state_identity_consumers_example.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_dataset_contract.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_detection_eval.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_evaluate.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_image_review.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_lidar_materialization.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_matching.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_metrics.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_platform_pose.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_reconstruction_client.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_release_gate.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_roadmark_preview.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_semantic_scene_authoring.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_semantic_scene_client.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_semantic_scene_contract.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_semantic_scene_native_clip.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_time_contract.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_token.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_tracking_eval.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_transform.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_transform_chain.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/tests/test_validation.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/annotation_reference.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/build_info.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/clean_basemap.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/client/__init__.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/client/_origin.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/client/annotation_reference.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/client/assets.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/client/bags.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/client/capabilities.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/client/checkpoints.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/client/clean_basemap.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/client/client.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/client/clips.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/client/dataset_identity.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/client/image_review.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/client/label_schemas.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/client/preannotation.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/client/projects.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/client/reconstruction.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/client/semantic_scene.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/client/semantic_scene_authoring.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/client/services.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/client/test_sets.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/consumer_identity.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/coordinate_contract.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/core/__init__.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/core/token.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/core/transform.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/eval/__init__.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/eval/api.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/eval/box.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/eval/detection.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/eval/matching.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/eval/metrics.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/image_review.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/inference/__init__.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/inference/models.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/inference/service.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/inference/tasks.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/json_contract.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/roadmark_preview.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/semantic_scene_authoring.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/training_identity.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/validation/__init__.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/validation/contract.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit/validation/report.py +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit.egg-info/dependency_links.txt +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit.egg-info/entry_points.txt +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit.egg-info/requires.txt +0 -0
- {x4d_devkit-0.43.0rc4 → x4d_devkit-0.45.0rc1}/x4d_devkit.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: x4d-devkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.45.0rc1
|
|
4
4
|
Summary: Official X-4D external SDK and CLI for datasets, platform APIs, training bridges, and automation
|
|
5
5
|
Author: windzu
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -97,18 +97,18 @@ platform-to-platform data flow. See
|
|
|
97
97
|
generic identity model, and
|
|
98
98
|
[docs/training-current-state-data.md](docs/training-current-state-data.md) for
|
|
99
99
|
work-session materialization and conditional annotation writeback. Every
|
|
100
|
-
Dataset 0.
|
|
100
|
+
Dataset 0.18 Clip carries its authoritative Label Schema snapshot at root
|
|
101
101
|
`label_schema.json`.
|
|
102
102
|
Shared validation rules for platform submit, X-Points preflight, local clip
|
|
103
103
|
validation, and training consumers are documented in
|
|
104
104
|
[docs/shared-contract-validation.md](docs/shared-contract-validation.md).
|
|
105
|
-
The Dataset 0.
|
|
105
|
+
The Dataset 0.18 native contract, exact nanosecond time, Multi-LiDAR layout,
|
|
106
106
|
and model materialization are documented in
|
|
107
|
-
[docs/dataset-0.
|
|
107
|
+
[docs/dataset-0.18.md](docs/dataset-0.18.md). The single-file annotation
|
|
108
108
|
authority, semantic revision, and
|
|
109
109
|
derived track views are documented in
|
|
110
110
|
[docs/annotation-contract.md](docs/annotation-contract.md).
|
|
111
|
-
Portable export producers and consumers share the exact `0.
|
|
111
|
+
Portable export producers and consumers share the exact `0.18` manifest
|
|
112
112
|
parser documented in
|
|
113
113
|
[docs/export-bundle-contract.md](docs/export-bundle-contract.md).
|
|
114
114
|
`examples/current_state_identity_consumers.py` provides an offline downstream
|
|
@@ -133,7 +133,7 @@ Included:
|
|
|
133
133
|
- Typed point-level semantic-scene authoring sessions, dirty semantic-chunk upload, and
|
|
134
134
|
complete compare-and-set submit without a per-point mutation API.
|
|
135
135
|
- Strict validation of `x4d-training-identity-v5` manifests.
|
|
136
|
-
- Strict producer-option and manifest parsing for export bundle `0.
|
|
136
|
+
- Strict producer-option and manifest parsing for export bundle `0.18`.
|
|
137
137
|
- Clip preannotation protocol, source-only inference adapter, and service
|
|
138
138
|
wrapper for algorithm projects.
|
|
139
139
|
|
|
@@ -247,7 +247,7 @@ comparison = compare_project_label_schema_snapshots(project_schema, current)
|
|
|
247
247
|
assert comparison.status == "same"
|
|
248
248
|
```
|
|
249
249
|
|
|
250
|
-
The native Dataset 0.
|
|
250
|
+
The native Dataset 0.18 Clip stores the immutable identity in
|
|
251
251
|
`meta.label_schema` and the complete authoritative snapshot in root
|
|
252
252
|
`label_schema.json`. Registry records are used when creating/binding Clips, not
|
|
253
253
|
when loading or validating their current contents.
|
|
@@ -264,8 +264,8 @@ identity manifests, and update plans use one schema field only:
|
|
|
264
264
|
`clip_label_schema` are not aliases and are rejected. A ready platform record
|
|
265
265
|
without the complete identity is also rejected.
|
|
266
266
|
|
|
267
|
-
`x4d-devkit dataset inspect` writes `x4d-remote-identity-
|
|
268
|
-
`x4d-devkit dataset diff` writes `x4d-update-plan-
|
|
267
|
+
`x4d-devkit dataset inspect` writes `x4d-remote-identity-v7`, and
|
|
268
|
+
`x4d-devkit dataset diff` writes `x4d-update-plan-v9`. The v8 plan records the
|
|
269
269
|
requested materialization policy, each cache's reviewed samples/sweeps
|
|
270
270
|
materialization revisions, and every Clip's reviewed target and local
|
|
271
271
|
predecessor identities.
|
|
@@ -402,7 +402,7 @@ asset existence and point-cloud layout checks without downloading the assets.
|
|
|
402
402
|
|
|
403
403
|
Third-party model projects should use `x4d-devkit[inference]` instead of
|
|
404
404
|
hand-writing the X-4D preannotation protocol. The pipeline receives an
|
|
405
|
-
annotation-isolated `InferenceClip` and returns one complete Dataset 0.
|
|
405
|
+
annotation-isolated `InferenceClip` and returns one complete Dataset 0.18
|
|
406
406
|
annotation document.
|
|
407
407
|
|
|
408
408
|
```python
|
|
@@ -54,18 +54,18 @@ platform-to-platform data flow. See
|
|
|
54
54
|
generic identity model, and
|
|
55
55
|
[docs/training-current-state-data.md](docs/training-current-state-data.md) for
|
|
56
56
|
work-session materialization and conditional annotation writeback. Every
|
|
57
|
-
Dataset 0.
|
|
57
|
+
Dataset 0.18 Clip carries its authoritative Label Schema snapshot at root
|
|
58
58
|
`label_schema.json`.
|
|
59
59
|
Shared validation rules for platform submit, X-Points preflight, local clip
|
|
60
60
|
validation, and training consumers are documented in
|
|
61
61
|
[docs/shared-contract-validation.md](docs/shared-contract-validation.md).
|
|
62
|
-
The Dataset 0.
|
|
62
|
+
The Dataset 0.18 native contract, exact nanosecond time, Multi-LiDAR layout,
|
|
63
63
|
and model materialization are documented in
|
|
64
|
-
[docs/dataset-0.
|
|
64
|
+
[docs/dataset-0.18.md](docs/dataset-0.18.md). The single-file annotation
|
|
65
65
|
authority, semantic revision, and
|
|
66
66
|
derived track views are documented in
|
|
67
67
|
[docs/annotation-contract.md](docs/annotation-contract.md).
|
|
68
|
-
Portable export producers and consumers share the exact `0.
|
|
68
|
+
Portable export producers and consumers share the exact `0.18` manifest
|
|
69
69
|
parser documented in
|
|
70
70
|
[docs/export-bundle-contract.md](docs/export-bundle-contract.md).
|
|
71
71
|
`examples/current_state_identity_consumers.py` provides an offline downstream
|
|
@@ -90,7 +90,7 @@ Included:
|
|
|
90
90
|
- Typed point-level semantic-scene authoring sessions, dirty semantic-chunk upload, and
|
|
91
91
|
complete compare-and-set submit without a per-point mutation API.
|
|
92
92
|
- Strict validation of `x4d-training-identity-v5` manifests.
|
|
93
|
-
- Strict producer-option and manifest parsing for export bundle `0.
|
|
93
|
+
- Strict producer-option and manifest parsing for export bundle `0.18`.
|
|
94
94
|
- Clip preannotation protocol, source-only inference adapter, and service
|
|
95
95
|
wrapper for algorithm projects.
|
|
96
96
|
|
|
@@ -204,7 +204,7 @@ comparison = compare_project_label_schema_snapshots(project_schema, current)
|
|
|
204
204
|
assert comparison.status == "same"
|
|
205
205
|
```
|
|
206
206
|
|
|
207
|
-
The native Dataset 0.
|
|
207
|
+
The native Dataset 0.18 Clip stores the immutable identity in
|
|
208
208
|
`meta.label_schema` and the complete authoritative snapshot in root
|
|
209
209
|
`label_schema.json`. Registry records are used when creating/binding Clips, not
|
|
210
210
|
when loading or validating their current contents.
|
|
@@ -221,8 +221,8 @@ identity manifests, and update plans use one schema field only:
|
|
|
221
221
|
`clip_label_schema` are not aliases and are rejected. A ready platform record
|
|
222
222
|
without the complete identity is also rejected.
|
|
223
223
|
|
|
224
|
-
`x4d-devkit dataset inspect` writes `x4d-remote-identity-
|
|
225
|
-
`x4d-devkit dataset diff` writes `x4d-update-plan-
|
|
224
|
+
`x4d-devkit dataset inspect` writes `x4d-remote-identity-v7`, and
|
|
225
|
+
`x4d-devkit dataset diff` writes `x4d-update-plan-v9`. The v8 plan records the
|
|
226
226
|
requested materialization policy, each cache's reviewed samples/sweeps
|
|
227
227
|
materialization revisions, and every Clip's reviewed target and local
|
|
228
228
|
predecessor identities.
|
|
@@ -359,7 +359,7 @@ asset existence and point-cloud layout checks without downloading the assets.
|
|
|
359
359
|
|
|
360
360
|
Third-party model projects should use `x4d-devkit[inference]` instead of
|
|
361
361
|
hand-writing the X-4D preannotation protocol. The pipeline receives an
|
|
362
|
-
annotation-isolated `InferenceClip` and returns one complete Dataset 0.
|
|
362
|
+
annotation-isolated `InferenceClip` and returns one complete Dataset 0.18
|
|
363
363
|
annotation document.
|
|
364
364
|
|
|
365
365
|
```python
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "x4d-devkit"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.45.0rc1"
|
|
8
8
|
description = "Official X-4D external SDK and CLI for datasets, platform APIs, training bridges, and automation"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -77,6 +77,11 @@ def test_top_level_exports_only_stable_sdk_surface():
|
|
|
77
77
|
|
|
78
78
|
assert set(x4d_devkit.__all__) == {
|
|
79
79
|
"__version__",
|
|
80
|
+
"VECTOR_MAP_CONTRACT_VERSION",
|
|
81
|
+
"VectorMapDocument",
|
|
82
|
+
"parse_vector_map",
|
|
83
|
+
"parse_vector_map_change",
|
|
84
|
+
"vector_map_geometry_rules",
|
|
80
85
|
"ANNOTATION_CONTRACT_VERSION",
|
|
81
86
|
"CLEAN_BASEMAP_CONTRACT_VERSION",
|
|
82
87
|
"CleanBasemapManifest",
|
|
@@ -24,9 +24,8 @@ def _time(value: int) -> dict[str, int]:
|
|
|
24
24
|
def _meta(*, keyframe_count: int = 1) -> dict:
|
|
25
25
|
return {
|
|
26
26
|
"clip_id": "clip-a",
|
|
27
|
-
"schema_version": "0.
|
|
27
|
+
"schema_version": "0.18",
|
|
28
28
|
"session_id": "session-a",
|
|
29
|
-
"vehicle_id": "vehicle-a",
|
|
30
29
|
"capture_time": _time(1),
|
|
31
30
|
"duration_s": float(max(0, keyframe_count - 1)),
|
|
32
31
|
"keyframe_count": keyframe_count,
|
|
@@ -152,7 +151,7 @@ def _context(
|
|
|
152
151
|
) -> ClipValidationContext:
|
|
153
152
|
return ClipValidationContext(
|
|
154
153
|
clip_id="clip-a",
|
|
155
|
-
clip_schema_version="0.
|
|
154
|
+
clip_schema_version="0.18",
|
|
156
155
|
label_schema=_schema(),
|
|
157
156
|
samples=[_sample()] if samples is None else samples,
|
|
158
157
|
sample_data=[_sample_data()] if sample_data is None else sample_data,
|
|
@@ -423,7 +422,7 @@ def test_context_rejects_non_list_payloads_and_invalid_meta():
|
|
|
423
422
|
with pytest.raises(TypeError):
|
|
424
423
|
ClipValidationContext(
|
|
425
424
|
clip_id="clip-a",
|
|
426
|
-
clip_schema_version="0.
|
|
425
|
+
clip_schema_version="0.18",
|
|
427
426
|
label_schema=_schema(),
|
|
428
427
|
samples=(),
|
|
429
428
|
sample_data=[],
|
|
@@ -31,9 +31,10 @@ def _identity(**overrides):
|
|
|
31
31
|
"pose_revision": "pose",
|
|
32
32
|
"annotation_revision": current_work_session_payload()["annotation_revision"],
|
|
33
33
|
"semantic_scene_revision": None,
|
|
34
|
+
"vector_map_revision": None,
|
|
34
35
|
"label_schema": schema_identity(),
|
|
35
36
|
"business_metadata_revision": "business-metadata",
|
|
36
|
-
"clip_schema_version": "0.
|
|
37
|
+
"clip_schema_version": "0.18",
|
|
37
38
|
"ready": True,
|
|
38
39
|
"blockers": [],
|
|
39
40
|
}
|
|
@@ -209,6 +210,7 @@ def test_clip_identity_direct_constructor_requires_explicit_pose_revision():
|
|
|
209
210
|
label_schema=label_schema,
|
|
210
211
|
pose_revision=None,
|
|
211
212
|
semantic_scene_revision=None,
|
|
213
|
+
vector_map_revision=None,
|
|
212
214
|
)
|
|
213
215
|
assert identity.to_dict()["pose_revision"] is None
|
|
214
216
|
|
|
@@ -231,5 +233,5 @@ def test_normalize_clip_identity_rejects_removed_source_granularity(field):
|
|
|
231
233
|
|
|
232
234
|
|
|
233
235
|
def test_normalize_clip_identity_rejects_non_current_clip_schema_version():
|
|
234
|
-
with pytest.raises(ValueError, match="clip_schema_version must be '0.
|
|
236
|
+
with pytest.raises(ValueError, match="clip_schema_version must be '0.18'"):
|
|
235
237
|
normalize_clip_identity(_identity(clip_schema_version="0.10"))
|
|
@@ -46,9 +46,10 @@ def _identity(clip_id: str = "clip-a", **overrides):
|
|
|
46
46
|
"pose_revision": "sha256:pose",
|
|
47
47
|
"annotation_revision": "sha256:annotation",
|
|
48
48
|
"semantic_scene_revision": None,
|
|
49
|
+
"vector_map_revision": None,
|
|
49
50
|
"label_schema": schema_identity(),
|
|
50
51
|
"business_metadata_revision": "sha256:business-metadata",
|
|
51
|
-
"clip_schema_version": "0.
|
|
52
|
+
"clip_schema_version": "0.18",
|
|
52
53
|
"ready": True,
|
|
53
54
|
"blockers": [],
|
|
54
55
|
}
|
|
@@ -289,6 +290,7 @@ def test_update_plan_maps_each_change_to_independent_portable_action():
|
|
|
289
290
|
"refresh_pose": ["clip-a"],
|
|
290
291
|
"refresh_annotation": ["clip-a"],
|
|
291
292
|
"refresh_semantic_scene": [],
|
|
293
|
+
"refresh_vector_map": [],
|
|
292
294
|
"refresh_label_schema": ["clip-a"],
|
|
293
295
|
"refresh_business_metadata": ["clip-a"],
|
|
294
296
|
"rematerialize_clip": [],
|
|
@@ -459,6 +461,7 @@ def _local_from_work_session(payload):
|
|
|
459
461
|
"pose_revision",
|
|
460
462
|
"annotation_revision",
|
|
461
463
|
"semantic_scene_revision",
|
|
464
|
+
"vector_map_revision",
|
|
462
465
|
"label_schema",
|
|
463
466
|
"business_metadata_revision",
|
|
464
467
|
"clip_schema_version",
|
|
@@ -855,7 +858,7 @@ def test_sync_composes_schema_and_annotation_into_one_clip_update(tmp_path):
|
|
|
855
858
|
result = sync_update_plan(client, plan=plan, data_root=tmp_path, workers=1)
|
|
856
859
|
|
|
857
860
|
assert result["schema_version"] == SYNC_RESULT_SCHEMA_VERSION
|
|
858
|
-
assert result["schema_version"] == "x4d-sync-result-
|
|
861
|
+
assert result["schema_version"] == "x4d-sync-result-v9"
|
|
859
862
|
assert result["source_transfer"] == {
|
|
860
863
|
"planned_objects": 0,
|
|
861
864
|
"planned_bytes": 0,
|
|
@@ -20,7 +20,7 @@ def _manifest(*, include_sweeps: bool = True) -> dict:
|
|
|
20
20
|
return {
|
|
21
21
|
"export_id": "exp_20260711_a3f8c1d2",
|
|
22
22
|
"exported_at": {"sec": 1_783_735_200, "nanosec": 123_456_000},
|
|
23
|
-
"schema_version": "0.
|
|
23
|
+
"schema_version": "0.18",
|
|
24
24
|
"options": {
|
|
25
25
|
"include_sweeps": include_sweeps,
|
|
26
26
|
"pointcloud_format": "pcd_binary",
|
|
@@ -31,7 +31,7 @@ def _manifest(*, include_sweeps: bool = True) -> dict:
|
|
|
31
31
|
"clips": [
|
|
32
32
|
{
|
|
33
33
|
"clip_id": "20260711_G150-002_003",
|
|
34
|
-
"schema_version": "0.
|
|
34
|
+
"schema_version": "0.18",
|
|
35
35
|
"label_schema": schema_identity(),
|
|
36
36
|
"annotation_frame_id": "base_link",
|
|
37
37
|
"channels": ["cam_front", "lidar"],
|
|
@@ -30,7 +30,7 @@ def _inference_clip(tmp_path) -> tuple[ClipLoader, InferenceClip]:
|
|
|
30
30
|
sweeps_revision=_digest("b"),
|
|
31
31
|
calibration_revision="calibration-1",
|
|
32
32
|
pose_revision="pose-1",
|
|
33
|
-
clip_schema_version="0.
|
|
33
|
+
clip_schema_version="0.18",
|
|
34
34
|
)
|
|
35
35
|
access = PreannotationClipAccess(
|
|
36
36
|
project_id=7,
|
|
@@ -174,7 +174,7 @@ def test_inference_clip_rejects_a_mismatched_source_identity(tmp_path):
|
|
|
174
174
|
sweeps_revision=clip.source_identity.sweeps_revision,
|
|
175
175
|
calibration_revision=clip.source_identity.calibration_revision,
|
|
176
176
|
pose_revision=clip.source_identity.pose_revision,
|
|
177
|
-
clip_schema_version="0.
|
|
177
|
+
clip_schema_version="0.18",
|
|
178
178
|
)
|
|
179
179
|
|
|
180
180
|
with pytest.raises(ValueError, match="source identity"):
|
|
@@ -37,7 +37,7 @@ def _request_payload() -> dict:
|
|
|
37
37
|
"sweeps_revision": _digest("f"),
|
|
38
38
|
"calibration_revision": "calibration-1",
|
|
39
39
|
"pose_revision": "pose-1",
|
|
40
|
-
"clip_schema_version": "0.
|
|
40
|
+
"clip_schema_version": "0.18",
|
|
41
41
|
},
|
|
42
42
|
"label_schema": schema_identity(),
|
|
43
43
|
"base_annotation_revision": _digest("a"),
|
|
@@ -106,8 +106,9 @@ def test_source_identity_projects_only_pure_inference_dependencies():
|
|
|
106
106
|
"pose_revision": "pose-1",
|
|
107
107
|
"annotation_revision": _digest("a"),
|
|
108
108
|
"semantic_scene_revision": _digest("d"),
|
|
109
|
+
"vector_map_revision": None,
|
|
109
110
|
"label_schema": schema_identity(),
|
|
110
|
-
"clip_schema_version": "0.
|
|
111
|
+
"clip_schema_version": "0.18",
|
|
111
112
|
"ready": True,
|
|
112
113
|
"blockers": [],
|
|
113
114
|
}
|
|
@@ -119,7 +120,7 @@ def test_source_identity_projects_only_pure_inference_dependencies():
|
|
|
119
120
|
"sweeps_revision": _digest("f"),
|
|
120
121
|
"calibration_revision": "calibration-1",
|
|
121
122
|
"pose_revision": "pose-1",
|
|
122
|
-
"clip_schema_version": "0.
|
|
123
|
+
"clip_schema_version": "0.18",
|
|
123
124
|
}
|
|
124
125
|
|
|
125
126
|
|
|
@@ -83,7 +83,7 @@ def _fixture(tmp_path, pipeline=None, *, run_inline=True):
|
|
|
83
83
|
sweeps_revision=_digest("e"),
|
|
84
84
|
calibration_revision="calibration-1",
|
|
85
85
|
pose_revision="pose-1",
|
|
86
|
-
clip_schema_version="0.
|
|
86
|
+
clip_schema_version="0.18",
|
|
87
87
|
)
|
|
88
88
|
selected = pipeline or FakePipeline()
|
|
89
89
|
|
|
@@ -282,9 +282,10 @@ def test_batch_resolution_deduplicates_by_task_free_binding():
|
|
|
282
282
|
"pose_revision": "pose-a",
|
|
283
283
|
"annotation_revision": "ann-a",
|
|
284
284
|
"semantic_scene_revision": None,
|
|
285
|
+
"vector_map_revision": None,
|
|
285
286
|
"business_metadata_revision": "business-a",
|
|
286
287
|
"label_schema": identity,
|
|
287
|
-
"clip_schema_version": "0.
|
|
288
|
+
"clip_schema_version": "0.18",
|
|
288
289
|
"ready": True,
|
|
289
290
|
"blockers": [],
|
|
290
291
|
},
|
|
@@ -298,9 +299,10 @@ def test_batch_resolution_deduplicates_by_task_free_binding():
|
|
|
298
299
|
"pose_revision": "pose-b",
|
|
299
300
|
"annotation_revision": "ann-b",
|
|
300
301
|
"semantic_scene_revision": None,
|
|
302
|
+
"vector_map_revision": None,
|
|
301
303
|
"business_metadata_revision": "business-b",
|
|
302
304
|
"label_schema": identity,
|
|
303
|
-
"clip_schema_version": "0.
|
|
305
|
+
"clip_schema_version": "0.18",
|
|
304
306
|
"ready": True,
|
|
305
307
|
"blockers": [],
|
|
306
308
|
},
|
|
@@ -28,7 +28,7 @@ def loader(tmp_path):
|
|
|
28
28
|
|
|
29
29
|
def test_loader_builds_current_typed_models_and_indexes(loader):
|
|
30
30
|
assert isinstance(loader.meta, ClipMeta)
|
|
31
|
-
assert loader.schema_version == "0.
|
|
31
|
+
assert loader.schema_version == "0.18"
|
|
32
32
|
assert loader.annotation_revision.startswith("sha256:")
|
|
33
33
|
assert all(isinstance(item, Sample) for item in loader.samples)
|
|
34
34
|
assert all(isinstance(item, Annotation) for item in loader.annotations)
|
|
@@ -82,9 +82,8 @@ def test_clip_meta_serializes_exact_current_metadata():
|
|
|
82
82
|
sensors = {"cam_front": SensorInfo("camera", "cam_front")}
|
|
83
83
|
meta = ClipMeta(
|
|
84
84
|
clip_id="clip-a",
|
|
85
|
-
schema_version="0.
|
|
85
|
+
schema_version="0.18",
|
|
86
86
|
session_id="session-a",
|
|
87
|
-
vehicle_id="vehicle-a",
|
|
88
87
|
capture_time=TimeNs(1, 2),
|
|
89
88
|
duration_s=1.0,
|
|
90
89
|
num_keyframes=1,
|
|
@@ -96,7 +95,7 @@ def test_clip_meta_serializes_exact_current_metadata():
|
|
|
96
95
|
scene_tags=["urban"],
|
|
97
96
|
)
|
|
98
97
|
wire = meta.to_dict()
|
|
99
|
-
assert wire["schema_version"] == "0.
|
|
98
|
+
assert wire["schema_version"] == "0.18"
|
|
100
99
|
assert wire["capture_time"] == {"sec": 1, "nanosec": 2}
|
|
101
100
|
assert wire["scene_tags"] == ["urban"]
|
|
102
101
|
assert "route_tag" not in wire
|
|
@@ -117,7 +117,7 @@ def _portable_candidate() -> dict:
|
|
|
117
117
|
"sweeps_revision": _digest("f"),
|
|
118
118
|
"calibration_revision": "calibration-1",
|
|
119
119
|
"pose_revision": "pose-1",
|
|
120
|
-
"clip_schema_version": "0.
|
|
120
|
+
"clip_schema_version": "0.18",
|
|
121
121
|
},
|
|
122
122
|
"label_schema": schema_identity(),
|
|
123
123
|
"base_annotation_revision": _digest("0"),
|
|
@@ -11,14 +11,14 @@ from x4d_devkit.eval.matching import MetricData
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def test_package_version_is_current_release():
|
|
14
|
-
assert x4d_devkit.__version__ == "0.
|
|
14
|
+
assert x4d_devkit.__version__ == "0.45.0rc1"
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
def test_loader_exposes_and_enforces_only_current_schema(tmp_path):
|
|
18
18
|
loader = ClipLoader(write_current_clip(tmp_path / "current"))
|
|
19
|
-
assert loader.schema_version == "0.
|
|
20
|
-
loader.require_schema_version("0.
|
|
21
|
-
loader.require_schema_version({"0.
|
|
19
|
+
assert loader.schema_version == "0.18"
|
|
20
|
+
loader.require_schema_version("0.18")
|
|
21
|
+
loader.require_schema_version({"0.18"})
|
|
22
22
|
with pytest.raises(SchemaVersionMismatch):
|
|
23
23
|
loader.require_schema_version("0.10")
|
|
24
24
|
with pytest.raises(SchemaVersionMismatch):
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Vehicle-free metadata and the independent business-metadata boundary."""
|
|
2
|
+
|
|
3
|
+
import copy
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from tests.current_clip_fixtures import current_clip_tables, write_current_clip
|
|
8
|
+
from x4d_devkit.core.loader import ClipLoader
|
|
9
|
+
from x4d_devkit.dataset_contract import (
|
|
10
|
+
DatasetContractError,
|
|
11
|
+
parse_clip_meta,
|
|
12
|
+
project_business_metadata,
|
|
13
|
+
project_source_metadata,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_vehicle_free_clip_loads_and_roundtrips(tmp_path):
|
|
18
|
+
tables = current_clip_tables()
|
|
19
|
+
clip_dir = write_current_clip(tmp_path / "test-clip")
|
|
20
|
+
clip = ClipLoader(clip_dir)
|
|
21
|
+
assert clip.meta.source_attributes is None
|
|
22
|
+
assert clip.meta.to_dict() == tables["meta.json"]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_source_attributes_change_only_business_metadata():
|
|
26
|
+
meta = current_clip_tables()["meta.json"]
|
|
27
|
+
candidate = copy.deepcopy(meta)
|
|
28
|
+
candidate["source_attributes"] = {"vehicle_id": "V01", "vehicle_model": "车型 A"}
|
|
29
|
+
parsed = parse_clip_meta(candidate, include_label_schema=True)
|
|
30
|
+
assert parsed == candidate
|
|
31
|
+
assert project_source_metadata(meta) == project_source_metadata(candidate)
|
|
32
|
+
assert project_business_metadata(meta) == {}
|
|
33
|
+
assert project_business_metadata(candidate) == {
|
|
34
|
+
"source_attributes": candidate["source_attributes"]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@pytest.mark.parametrize("attributes", [{}, [], {"a": None}, {"a": 1}, {"a": " x"}, {"a": ""}, {"Bad key": "x"}])
|
|
39
|
+
def test_source_attributes_reject_inexact_values(attributes):
|
|
40
|
+
meta = current_clip_tables()["meta.json"]
|
|
41
|
+
meta["source_attributes"] = attributes
|
|
42
|
+
with pytest.raises(DatasetContractError):
|
|
43
|
+
parse_clip_meta(meta, include_label_schema=True)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_previous_vehicle_field_and_dataset_version_are_rejected():
|
|
47
|
+
meta = current_clip_tables()["meta.json"]
|
|
48
|
+
meta["vehicle_id"] = "V01"
|
|
49
|
+
with pytest.raises(DatasetContractError):
|
|
50
|
+
parse_clip_meta(meta, include_label_schema=True)
|
|
51
|
+
del meta["vehicle_id"]
|
|
52
|
+
meta["schema_version"] = "0.17"
|
|
53
|
+
with pytest.raises(DatasetContractError):
|
|
54
|
+
parse_clip_meta(meta, include_label_schema=True)
|
|
@@ -38,7 +38,7 @@ def _clip(**overrides):
|
|
|
38
38
|
"annotation_revision": "ann",
|
|
39
39
|
"semantic_scene_revision": None,
|
|
40
40
|
"label_schema": schema_identity(),
|
|
41
|
-
"clip_schema_version": "0.
|
|
41
|
+
"clip_schema_version": "0.18",
|
|
42
42
|
"materialization_policy": "keyframes_only",
|
|
43
43
|
}
|
|
44
44
|
data.update(overrides)
|
|
@@ -133,7 +133,7 @@ def test_training_identity_file_rejects_duplicate_json_fields(tmp_path):
|
|
|
133
133
|
|
|
134
134
|
|
|
135
135
|
def test_training_identity_rejects_non_current_clip_schema_version():
|
|
136
|
-
with pytest.raises(ValueError, match="clip_schema_version must be '0.
|
|
136
|
+
with pytest.raises(ValueError, match="clip_schema_version must be '0.18'"):
|
|
137
137
|
build_training_identity_manifest(
|
|
138
138
|
projects=_projects(),
|
|
139
139
|
clips=[_clip(clip_schema_version="0.10")]
|
|
@@ -251,7 +251,7 @@ def test_project_training_identities_are_dataset_diff_ready(tmp_path):
|
|
|
251
251
|
"annotation_revision": "ann",
|
|
252
252
|
"semantic_scene_revision": None,
|
|
253
253
|
"label_schema": schema_identity(),
|
|
254
|
-
"clip_schema_version": "0.
|
|
254
|
+
"clip_schema_version": "0.18",
|
|
255
255
|
}]
|
|
256
256
|
|
|
257
257
|
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
from tests.vector_map_fixtures import document, map_schema, map_schema_body
|
|
5
|
+
from tests.test_vector_map_geometry import square
|
|
6
|
+
from x4d_devkit.label_schema import parse_label_schema_body
|
|
7
|
+
from x4d_devkit.vector_map import parse_vector_map, parse_vector_map_change
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def parse(value):
|
|
11
|
+
return parse_vector_map(value, map_schema(), clip_id="clip-a")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_shared_boundaries_and_directed_neighbor_survive_canonical_roundtrip():
|
|
15
|
+
value = document()
|
|
16
|
+
parsed = parse(value)
|
|
17
|
+
assert parsed.to_dict() == value
|
|
18
|
+
assert parse(parsed).revision == parsed.revision
|
|
19
|
+
value["features"][0]["geometry"]["coordinates"][0][2] = 100
|
|
20
|
+
assert parsed.to_dict()["features"][0]["geometry"]["coordinates"][0][2] == 2
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.mark.parametrize(
|
|
24
|
+
"change",
|
|
25
|
+
[
|
|
26
|
+
"opposite",
|
|
27
|
+
"swapped",
|
|
28
|
+
"range",
|
|
29
|
+
"dangling",
|
|
30
|
+
"duplicate",
|
|
31
|
+
"foreign_clip",
|
|
32
|
+
"wrong_frame",
|
|
33
|
+
"schema",
|
|
34
|
+
],
|
|
35
|
+
)
|
|
36
|
+
def test_invalid_native_references_geometry_or_identity_fail_closed(change):
|
|
37
|
+
value = document()
|
|
38
|
+
if change == "opposite":
|
|
39
|
+
value["lane_segments"][0]["right_boundary"]["reverse"] = True
|
|
40
|
+
elif change == "swapped":
|
|
41
|
+
lane = value["lane_segments"][0]
|
|
42
|
+
lane["left_boundary"], lane["right_boundary"] = (
|
|
43
|
+
lane["right_boundary"],
|
|
44
|
+
lane["left_boundary"],
|
|
45
|
+
)
|
|
46
|
+
elif change == "range":
|
|
47
|
+
value["lane_segments"][0]["left_boundary"]["end_s_m"] = 100
|
|
48
|
+
elif change == "dangling":
|
|
49
|
+
value["relations"][0]["to_token"] = "missing"
|
|
50
|
+
elif change == "duplicate":
|
|
51
|
+
value["relations"].append({**value["relations"][0], "token": "another"})
|
|
52
|
+
elif change == "foreign_clip":
|
|
53
|
+
value["clip_id"] = "other"
|
|
54
|
+
elif change == "wrong_frame":
|
|
55
|
+
value["frame_id"] = "world"
|
|
56
|
+
elif change == "schema":
|
|
57
|
+
value["provenance"]["label_schema_fingerprint"] = "sha256:" + "0" * 64
|
|
58
|
+
with pytest.raises(ValueError):
|
|
59
|
+
parse(value)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_explicit_successor_cycle_is_legal_without_inferred_reciprocal_edges():
|
|
63
|
+
value = document()
|
|
64
|
+
value["relations"] = [
|
|
65
|
+
{
|
|
66
|
+
"token": "cycle",
|
|
67
|
+
"kind": "lane_successor",
|
|
68
|
+
"from_token": "lane-a",
|
|
69
|
+
"to_token": "lane-a",
|
|
70
|
+
"side": None,
|
|
71
|
+
"source_tokens": [],
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
assert len(parse(value).to_dict()["relations"]) == 1
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_complete_zero_feature_coverage_is_preserved_and_same_category_overlap_rejected():
|
|
78
|
+
value = document()
|
|
79
|
+
value.update(features=[], lane_segments=[], relations=[])
|
|
80
|
+
region = {
|
|
81
|
+
"token": "inspected",
|
|
82
|
+
"geometry": square(),
|
|
83
|
+
"categories": [{"category_id": "area", "state": "complete"}],
|
|
84
|
+
}
|
|
85
|
+
value["coverage"] = [region]
|
|
86
|
+
assert parse(value).to_dict()["coverage"][0]["categories"][0]["state"] == "complete"
|
|
87
|
+
value["coverage"].append({**copy.deepcopy(region), "token": "duplicate-region"})
|
|
88
|
+
with pytest.raises(ValueError, match="overlaps"):
|
|
89
|
+
parse(value)
|
|
90
|
+
value["coverage"][1]["categories"][0]["category_id"] = "boundary"
|
|
91
|
+
parse(value)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def test_schema_geometry_rules_are_mandatory_complete_and_do_not_supply_defaults():
|
|
95
|
+
body = map_schema_body()
|
|
96
|
+
del body["task_views"][0]["geometry_rules"]
|
|
97
|
+
with pytest.raises(ValueError):
|
|
98
|
+
parse_label_schema_body(body)
|
|
99
|
+
body = map_schema_body()
|
|
100
|
+
body["task_views"][0]["geometry_rules"][0]["geometry_types"] = ["polygon", "point"]
|
|
101
|
+
with pytest.raises(ValueError):
|
|
102
|
+
parse_label_schema_body(body)
|
|
103
|
+
value = document()
|
|
104
|
+
value["features"][0]["category_id"] = "area"
|
|
105
|
+
with pytest.raises(ValueError, match="does not permit"):
|
|
106
|
+
parse(value)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def test_component_retain_replace_and_clear_are_distinct_and_explicit():
|
|
110
|
+
for operation in ("retain", "clear"):
|
|
111
|
+
value = {"operation": operation, "base_revision": None, "document": None}
|
|
112
|
+
assert parse_vector_map_change(value, map_schema(), clip_id="clip-a") == value
|
|
113
|
+
value = {"operation": "replace", "base_revision": None, "document": document()}
|
|
114
|
+
assert (
|
|
115
|
+
parse_vector_map_change(value, map_schema(), clip_id="clip-a")["document"]
|
|
116
|
+
== document()
|
|
117
|
+
)
|
|
118
|
+
with pytest.raises(ValueError):
|
|
119
|
+
parse_vector_map_change({"document": None}, map_schema(), clip_id="clip-a")
|