x4d-devkit 0.46.0rc3__tar.gz → 0.47.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.
Files changed (135) hide show
  1. {x4d_devkit-0.46.0rc3/x4d_devkit.egg-info → x4d_devkit-0.47.0}/PKG-INFO +1 -1
  2. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/pyproject.toml +1 -1
  3. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_clean_basemap.py +74 -0
  4. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_sdk_api.py +1 -1
  5. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/clean_basemap.py +64 -5
  6. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/client/reconstruction.py +9 -1
  7. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0/x4d_devkit.egg-info}/PKG-INFO +1 -1
  8. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/LICENSE +0 -0
  9. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/README.md +0 -0
  10. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/setup.cfg +0 -0
  11. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_annotation_contract.py +0 -0
  12. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_annotation_reference.py +0 -0
  13. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_assets_client.py +0 -0
  14. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_boundary.py +0 -0
  15. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_box.py +0 -0
  16. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_build_info.py +0 -0
  17. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_clean_basemap_client.py +0 -0
  18. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_cli_auth.py +0 -0
  19. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_client_json_contract.py +0 -0
  20. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_client_projects.py +0 -0
  21. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_clip_archives.py +0 -0
  22. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_contract_validation.py +0 -0
  23. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_coordinate_frames.py +0 -0
  24. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_current_state.py +0 -0
  25. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_current_state_identity_consumers_example.py +0 -0
  26. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_dataset_contract.py +0 -0
  27. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_dataset_identity.py +0 -0
  28. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_detection_eval.py +0 -0
  29. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_evaluate.py +0 -0
  30. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_export_bundle.py +0 -0
  31. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_image_review.py +0 -0
  32. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_inference_input.py +0 -0
  33. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_inference_schemas.py +0 -0
  34. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_inference_service.py +0 -0
  35. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_label_schemas.py +0 -0
  36. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_lidar_materialization.py +0 -0
  37. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_loader.py +0 -0
  38. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_matching.py +0 -0
  39. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_metrics.py +0 -0
  40. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_models.py +0 -0
  41. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_platform_pose.py +0 -0
  42. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_preannotation_client.py +0 -0
  43. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_reconstruction_client.py +0 -0
  44. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_release_gate.py +0 -0
  45. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_roadmark_preview.py +0 -0
  46. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_semantic_scene_authoring.py +0 -0
  47. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_semantic_scene_client.py +0 -0
  48. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_semantic_scene_contract.py +0 -0
  49. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_semantic_scene_independent.py +0 -0
  50. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_semantic_scene_native_clip.py +0 -0
  51. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_source_attributes.py +0 -0
  52. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_time_contract.py +0 -0
  53. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_token.py +0 -0
  54. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_tracking_eval.py +0 -0
  55. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_training_identity.py +0 -0
  56. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_transform.py +0 -0
  57. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_transform_chain.py +0 -0
  58. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_validation.py +0 -0
  59. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_vector_map.py +0 -0
  60. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_vector_map_geometry.py +0 -0
  61. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_vector_map_session.py +0 -0
  62. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_work_session.py +0 -0
  63. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/tests/test_work_session_validation_reuse.py +0 -0
  64. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/__init__.py +0 -0
  65. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/annotation_contract.py +0 -0
  66. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/annotation_reference.py +0 -0
  67. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/build_info.py +0 -0
  68. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/cli.py +0 -0
  69. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/client/__init__.py +0 -0
  70. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/client/_origin.py +0 -0
  71. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/client/annotation_reference.py +0 -0
  72. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/client/assets.py +0 -0
  73. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/client/bags.py +0 -0
  74. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/client/capabilities.py +0 -0
  75. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/client/checkpoints.py +0 -0
  76. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/client/clean_basemap.py +0 -0
  77. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/client/client.py +0 -0
  78. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/client/clip_work_sessions.py +0 -0
  79. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/client/clips.py +0 -0
  80. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/client/dataset_identity.py +0 -0
  81. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/client/image_review.py +0 -0
  82. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/client/label_schemas.py +0 -0
  83. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/client/preannotation.py +0 -0
  84. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/client/projects.py +0 -0
  85. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/client/semantic_scene.py +0 -0
  86. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/client/semantic_scene_authoring.py +0 -0
  87. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/client/services.py +0 -0
  88. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/client/test_sets.py +0 -0
  89. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/consumer_identity.py +0 -0
  90. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/coordinate_contract.py +0 -0
  91. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/core/__init__.py +0 -0
  92. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/core/loader.py +0 -0
  93. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/core/models.py +0 -0
  94. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/core/token.py +0 -0
  95. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/core/transform.py +0 -0
  96. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/current_state.py +0 -0
  97. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/dataset_contract.py +0 -0
  98. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/dataset_identity.py +0 -0
  99. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/eval/__init__.py +0 -0
  100. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/eval/api.py +0 -0
  101. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/eval/box.py +0 -0
  102. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/eval/detection.py +0 -0
  103. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/eval/matching.py +0 -0
  104. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/eval/metrics.py +0 -0
  105. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/eval/tracking.py +0 -0
  106. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/export_bundle.py +0 -0
  107. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/image_review.py +0 -0
  108. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/inference/__init__.py +0 -0
  109. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/inference/input.py +0 -0
  110. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/inference/models.py +0 -0
  111. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/inference/schemas.py +0 -0
  112. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/inference/service.py +0 -0
  113. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/inference/tasks.py +0 -0
  114. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/json_contract.py +0 -0
  115. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/label_schema.py +0 -0
  116. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/lidar_materialization.py +0 -0
  117. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/materialize.py +0 -0
  118. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/roadmark_preview.py +0 -0
  119. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/semantic_scene_authoring.py +0 -0
  120. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/semantic_scene_contract.py +0 -0
  121. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/time_contract.py +0 -0
  122. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/training_identity.py +0 -0
  123. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/validation/__init__.py +0 -0
  124. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/validation/contract.py +0 -0
  125. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/validation/platform_pose.py +0 -0
  126. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/validation/report.py +0 -0
  127. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/validation/validator.py +0 -0
  128. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/vector_map.py +0 -0
  129. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/vector_map_geometry.py +0 -0
  130. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit/work_session.py +0 -0
  131. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit.egg-info/SOURCES.txt +0 -0
  132. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit.egg-info/dependency_links.txt +0 -0
  133. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit.egg-info/entry_points.txt +0 -0
  134. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/x4d_devkit.egg-info/requires.txt +0 -0
  135. {x4d_devkit-0.46.0rc3 → x4d_devkit-0.47.0}/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.46.0rc3
3
+ Version: 0.47.0
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "x4d-devkit"
7
- version = "0.46.0rc3"
7
+ version = "0.47.0"
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"
@@ -54,6 +54,7 @@ def fixture():
54
54
  raw_point_ordinals=[0, 1, 2],
55
55
  timestamps_ns=[100, 100, 100],
56
56
  decisions=decisions[:3],
57
+ intensities=[2.5, 19.0, 240.0],
57
58
  )
58
59
  profile = {
59
60
  "algorithm": {"id": "clean_static_basemap", "version": "0.6.0"},
@@ -102,6 +103,7 @@ def fixture():
102
103
  "bounds": bounds,
103
104
  "geometry": descriptor,
104
105
  "mapping": mapping.descriptor("mapping"),
106
+ "intensity_range": [2.5, 240.0],
105
107
  }
106
108
  ],
107
109
  "diagnostics": {
@@ -139,6 +141,9 @@ def validate(manifest, assets):
139
141
  manifest,
140
142
  read_asset=assets.__getitem__,
141
143
  observation_to_world=lambda sweep, ordinals, times: original[ordinals],
144
+ observation_intensity=lambda sweep, ordinals, times: np.asarray(
145
+ [2.5, 19.0, 240.0], dtype=np.float32
146
+ )[ordinals],
142
147
  )
143
148
 
144
149
 
@@ -155,6 +160,7 @@ def remap(manifest, assets, **overrides):
155
160
  "raw_point_ordinals": mapping["raw_point_ordinal"],
156
161
  "timestamps_ns": mapping["timestamp_ns"],
157
162
  "decisions": mapping["decision"],
163
+ "intensities": mapping["intensity"],
158
164
  }
159
165
  encoded = encode_clean_basemap_mapping(
160
166
  geometry_raw_sha256=chunk["geometry"]["raw_sha256"], **(arguments | overrides)
@@ -190,6 +196,67 @@ def test_complete_scene_and_exact_original_observation_roundtrip():
190
196
  parsed.data["profile"]["voxel_size_m"] = 1
191
197
 
192
198
 
199
+ def test_intensity_is_original_float32_and_does_not_change_geometry():
200
+ manifest, assets, _ = fixture()
201
+ before = copy.deepcopy(manifest["scene_source"])
202
+ assert MAPPING_DTYPE.itemsize == 24
203
+ remap(manifest, assets, intensities=[2.5, 20.0, 240.0])
204
+ assert manifest["scene_source"] == before
205
+ with pytest.raises(ValueError, match="intensity differs from its original"):
206
+ validate(manifest, assets)
207
+
208
+
209
+ @pytest.mark.parametrize(
210
+ "values",
211
+ [
212
+ [1, 2],
213
+ [True, False, True],
214
+ [1, float("nan"), 3],
215
+ [1, float("inf"), 3],
216
+ [1, 1e100, 3],
217
+ ],
218
+ )
219
+ def test_invalid_intensity_cannot_be_encoded(values):
220
+ manifest, assets, _ = fixture()
221
+ with pytest.raises(ValueError, match="intensit"):
222
+ remap(manifest, assets, intensities=values)
223
+
224
+
225
+ def test_intensity_range_cannot_hide_wrong_values():
226
+ manifest, assets, _ = fixture()
227
+ manifest["chunks"][0]["intensity_range"] = [0.0, 255.0]
228
+ manifest["basemap_revision"] = compute_clean_basemap_revision(manifest)
229
+ with pytest.raises(ValueError, match="intensity range"):
230
+ validate(manifest, assets)
231
+
232
+
233
+ def test_old_manifest_and_mapping_are_rejected():
234
+ manifest, assets, _ = fixture()
235
+ manifest["contract_version"] = "x4d-clean-static-basemap-v4"
236
+ manifest["basemap_revision"] = compute_clean_basemap_revision(manifest)
237
+ with pytest.raises(ValueError):
238
+ parse_clean_basemap_manifest(manifest)
239
+ chunk = manifest["chunks"][0]
240
+ descriptor = chunk["mapping"]
241
+ raw = bytearray(zstandard.ZstdDecompressor().decompress(assets[descriptor["path"]]))
242
+ raw[:8] = b"X4DCBM2\0"
243
+ payload = zstandard.ZstdCompressor().compress(raw)
244
+ descriptor = dict(
245
+ descriptor,
246
+ path=f"generated/reconstruction/clean_basemap/mapping/{sha256_bytes(payload)[7:]}.zst",
247
+ sha256=sha256_bytes(payload),
248
+ raw_sha256=sha256_bytes(bytes(raw)),
249
+ size_bytes=len(payload),
250
+ )
251
+ with pytest.raises(ValueError):
252
+ decode_clean_basemap_mapping(
253
+ payload,
254
+ descriptor=descriptor,
255
+ geometry_raw_sha256=chunk["geometry"]["raw_sha256"],
256
+ point_count=chunk["point_count"],
257
+ )
258
+
259
+
193
260
  def test_decision_proof_reuse_is_within_one_validation_call(monkeypatch):
194
261
  manifest, assets, _ = fixture()
195
262
  decoded = []
@@ -229,6 +296,9 @@ def test_cached_decision_proof_still_checks_every_carrier_read(changed):
229
296
  manifest,
230
297
  read_asset=read_asset,
231
298
  observation_to_world=lambda sweep, ordinals, times: original[ordinals],
299
+ observation_intensity=lambda sweep, ordinals, times: np.asarray(
300
+ [2.5, 19.0, 240.0], dtype=np.float32
301
+ )[ordinals],
232
302
  )
233
303
 
234
304
 
@@ -319,6 +389,9 @@ def test_original_observation_projection_is_required(kind):
319
389
  manifest,
320
390
  read_asset=assets.__getitem__,
321
391
  observation_to_world=lambda *args: result,
392
+ observation_intensity=lambda sweep, ordinals, times: np.asarray(
393
+ [2.5, 19.0, 240.0], dtype=np.float32
394
+ )[ordinals],
322
395
  )
323
396
 
324
397
 
@@ -387,6 +460,7 @@ def test_every_byte_in_last_record_obeys_exact_decision_domain(family):
387
460
  raw_point_ordinals=np.arange(count),
388
461
  timestamps_ns=np.full(count, 100),
389
462
  decisions=np.full(count, KEEP_GROUND),
463
+ intensities=np.arange(count, dtype=np.float32),
390
464
  )
391
465
  domain, decode = KEEP_CODES, decode_clean_basemap_mapping
392
466
  arguments = dict(geometry_raw_sha256=anchor, point_count=count)
@@ -11,7 +11,7 @@ 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.46.0rc3"
14
+ assert x4d_devkit.__version__ == "0.47.0"
15
15
 
16
16
 
17
17
  def test_loader_exposes_and_enforces_only_current_schema(tmp_path):
@@ -36,9 +36,9 @@ from x4d_devkit.semantic_scene_contract import (
36
36
  )
37
37
  from x4d_devkit.time_contract import parse_time_ns
38
38
 
39
- CLEAN_BASEMAP_CONTRACT_VERSION = "x4d-clean-static-basemap-v4"
39
+ CLEAN_BASEMAP_CONTRACT_VERSION = "x4d-clean-static-basemap-v5"
40
40
  CLEAN_BASEMAP_OPERATION = "reconstruction.prepare_clean_static_basemap"
41
- CLEAN_BASEMAP_MAPPING_FORMAT = "x4d-clean-static-basemap-mapping-v2"
41
+ CLEAN_BASEMAP_MAPPING_FORMAT = "x4d-clean-static-basemap-mapping-v3"
42
42
  CLEAN_BASEMAP_DECISION_FORMAT = "x4d-clean-static-basemap-decisions-v3"
43
43
  _DECISION_CACHE_BYTES = 128 << 20
44
44
  CLEAN_BASEMAP_INPUT_FIELDS = frozenset(
@@ -55,7 +55,7 @@ DECISION_CODES = frozenset({0, 1, 3, 18, 32, 33, 34})
55
55
  KEEP_CODES = frozenset({0, 1, 3})
56
56
  _HASH = re.compile(r"^sha256:[0-9a-f]{64}$")
57
57
  _HEADER = struct.Struct("<8sI32s")
58
- _MAPPING_MAGIC = b"X4DCBM2\0"
58
+ _MAPPING_MAGIC = b"X4DCBM3\0"
59
59
  _DECISION_MAGIC = b"X4DCBD3\0"
60
60
  MAPPING_DTYPE = np.dtype(
61
61
  [
@@ -63,7 +63,8 @@ MAPPING_DTYPE = np.dtype(
63
63
  ("raw_point_ordinal", "<u4"),
64
64
  ("timestamp_ns", "<u8"),
65
65
  ("decision", "u1"),
66
- ("reserved", "u1", (7,)),
66
+ ("intensity", "<f4"),
67
+ ("reserved", "u1", (3,)),
67
68
  ]
68
69
  )
69
70
  _MAX_U32 = (1 << 32) - 1
@@ -108,6 +109,21 @@ def _bounds(value: Any, path: str) -> None:
108
109
  raise ValueError(f"{path} is inverted")
109
110
 
110
111
 
112
+ def _intensity_range(value: Any, path: str) -> None:
113
+ if (
114
+ not isinstance(value, list)
115
+ or len(value) != 2
116
+ or any(
117
+ isinstance(v, bool)
118
+ or not isinstance(v, (int, float))
119
+ or not math.isfinite(v)
120
+ for v in value
121
+ )
122
+ or value[0] > value[1]
123
+ ):
124
+ raise ValueError(f"{path} must contain finite ordered intensity bounds")
125
+
126
+
111
127
  def _freeze(value: Any) -> Any:
112
128
  if isinstance(value, Mapping):
113
129
  return MappingProxyType({k: _freeze(v) for k, v in value.items()})
@@ -349,7 +365,16 @@ def parse_clean_basemap_manifest(
349
365
  for i, chunk in enumerate(chunks):
350
366
  path = f"chunks[{i}]"
351
367
  _object(
352
- chunk, {"ordinal", "point_count", "bounds", "geometry", "mapping"}, path
368
+ chunk,
369
+ {
370
+ "ordinal",
371
+ "point_count",
372
+ "bounds",
373
+ "intensity_range",
374
+ "geometry",
375
+ "mapping",
376
+ },
377
+ path,
353
378
  )
354
379
  if chunk["ordinal"] != i or type(chunk["ordinal"]) is not int:
355
380
  raise ValueError("chunk ordinals must be contiguous in canonical order")
@@ -360,6 +385,7 @@ def parse_clean_basemap_manifest(
360
385
  f"{path}.point_count",
361
386
  )
362
387
  _bounds(chunk["bounds"], f"{path}.bounds")
388
+ _intensity_range(chunk["intensity_range"], f"{path}.intensity_range")
363
389
  bounds.append(chunk["bounds"])
364
390
  _descriptor(chunk["geometry"], "geometry", f"{path}.geometry")
365
391
  _descriptor(chunk["mapping"], "mapping", f"{path}.mapping")
@@ -467,6 +493,7 @@ def encode_clean_basemap_mapping(
467
493
  raw_point_ordinals: Any,
468
494
  timestamps_ns: Any,
469
495
  decisions: Any,
496
+ intensities: Any,
470
497
  ) -> CleanBasemapPayload:
471
498
  vectors = {
472
499
  "sweep_index": _vector(sweep_indices, _MAX_U32, "sweep_indices"),
@@ -487,6 +514,19 @@ def encode_clean_basemap_mapping(
487
514
  records = np.zeros(count, dtype=MAPPING_DTYPE)
488
515
  for key, vector in vectors.items():
489
516
  records[key] = vector
517
+ intensity = np.asarray(intensities)
518
+ if (
519
+ intensity.shape != (count,)
520
+ or intensity.dtype.kind not in "iuf"
521
+ or not np.isfinite(intensity).all()
522
+ ):
523
+ raise ValueError(
524
+ "intensities must contain one finite numeric value per geometry point"
525
+ )
526
+ with np.errstate(over="ignore", invalid="ignore"):
527
+ records["intensity"] = intensity
528
+ if not np.isfinite(records["intensity"]).all():
529
+ raise ValueError("intensities must be representable as finite float32")
490
530
  return _encode(_MAPPING_MAGIC, geometry_raw_sha256, records)
491
531
 
492
532
 
@@ -511,6 +551,7 @@ def decode_clean_basemap_mapping(
511
551
  np.any(records["reserved"] != 0)
512
552
  or np.any(~np.isin(records["decision"], tuple(KEEP_CODES)))
513
553
  or np.any(records["timestamp_ns"] > _MAX_I64)
554
+ or not np.isfinite(records["intensity"]).all()
514
555
  ):
515
556
  raise ValueError(
516
557
  "mapping records are noncanonical or contain removed observations"
@@ -604,6 +645,9 @@ def validate_clean_basemap_resource(
604
645
  observation_to_world: Callable[
605
646
  [Mapping[str, Any], np.ndarray, np.ndarray], np.ndarray
606
647
  ],
648
+ observation_intensity: Callable[
649
+ [Mapping[str, Any], np.ndarray, np.ndarray], np.ndarray
650
+ ],
607
651
  ) -> CleanBasemapManifest:
608
652
  """Verify exact original observations and byte-exact complete scene correspondence."""
609
653
  parsed = parse_clean_basemap_manifest(manifest)
@@ -681,6 +725,11 @@ def validate_clean_basemap_resource(
681
725
  geometry_raw_sha256=descriptor["raw_sha256"],
682
726
  point_count=chunk["point_count"],
683
727
  )
728
+ if [
729
+ float(mapping["intensity"].min()),
730
+ float(mapping["intensity"].max()),
731
+ ] != chunk["intensity_range"]:
732
+ raise ValueError("basemap intensity range differs from its actual points")
684
733
  if np.any(mapping["sweep_index"] >= len(data["sweep_sources"])):
685
734
  raise ValueError("mapping source index is outside the pinned source tables")
686
735
  for index in np.unique(mapping["sweep_index"]):
@@ -715,6 +764,16 @@ def validate_clean_basemap_resource(
715
764
  raise ValueError(
716
765
  "basemap geometry differs from its original observations"
717
766
  )
767
+ intensity = np.asarray(observation_intensity(sweep, ordinals, times))
768
+ if (
769
+ intensity.shape != ordinals.shape
770
+ or intensity.dtype.kind not in "iuf"
771
+ or not np.isfinite(intensity).all()
772
+ or not np.array_equal(intensity, mapping["intensity"][selected])
773
+ ):
774
+ raise ValueError(
775
+ "basemap intensity differs from its original observations"
776
+ )
718
777
  return parsed
719
778
 
720
779
 
@@ -307,7 +307,7 @@ class ReconstructionAPI:
307
307
  "clean basemap chunk ordinal is outside the exact resource"
308
308
  )
309
309
  chunk = chunks[chunk_ordinal]
310
- return decode_clean_basemap_mapping(
310
+ mapping = decode_clean_basemap_mapping(
311
311
  self._client.assets.fetch(
312
312
  state.asset_urls[chunk["mapping"]["path"]]
313
313
  ).content,
@@ -315,6 +315,14 @@ class ReconstructionAPI:
315
315
  geometry_raw_sha256=chunk["geometry"]["raw_sha256"],
316
316
  point_count=chunk["point_count"],
317
317
  )
318
+ if [
319
+ float(mapping["intensity"].min()),
320
+ float(mapping["intensity"].max()),
321
+ ] != list(chunk["intensity_range"]):
322
+ raise ValueError(
323
+ "clean basemap intensity range differs from its descriptor"
324
+ )
325
+ return mapping
318
326
 
319
327
  def cancel_clean_basemap_task(
320
328
  self, *, project_id: int, task_id: str
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: x4d-devkit
3
- Version: 0.46.0rc3
3
+ Version: 0.47.0
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
File without changes
File without changes
File without changes