x4d-devkit 0.5.0__tar.gz → 0.6.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.5.0/x4d_devkit.egg-info → x4d_devkit-0.6.0}/PKG-INFO +2 -4
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/pyproject.toml +3 -4
- x4d_devkit-0.6.0/tests/test_v06_api_additions.py +162 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/x4d_devkit/__init__.py +14 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/x4d_devkit/core/__init__.py +2 -1
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/x4d_devkit/core/loader.py +51 -1
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/x4d_devkit/eval/__init__.py +4 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/x4d_devkit/eval/detection.py +9 -4
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0/x4d_devkit.egg-info}/PKG-INFO +2 -4
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/x4d_devkit.egg-info/SOURCES.txt +1 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/x4d_devkit.egg-info/requires.txt +0 -3
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/LICENSE +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/README.md +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/setup.cfg +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/tests/test_coordinate_frames.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/tests/test_detection_eval.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/tests/test_fusion.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/tests/test_loader.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/tests/test_matching.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/tests/test_metrics.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/tests/test_models.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/tests/test_nuscenes_converter.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/tests/test_token.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/tests/test_transform.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/tests/test_transform_chain.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/tests/test_validation.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/x4d_devkit/cli.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/x4d_devkit/client/__init__.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/x4d_devkit/client/checkpoints.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/x4d_devkit/client/client.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/x4d_devkit/client/clips.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/x4d_devkit/client/projects.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/x4d_devkit/client/test_sets.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/x4d_devkit/converters/__init__.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/x4d_devkit/converters/nuscenes.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/x4d_devkit/core/models.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/x4d_devkit/core/token.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/x4d_devkit/core/transform.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/x4d_devkit/eval/matching.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/x4d_devkit/eval/metrics.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/x4d_devkit/fusion.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/x4d_devkit/validation/__init__.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/x4d_devkit/validation/report.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/x4d_devkit/validation/validator.py +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/x4d_devkit.egg-info/dependency_links.txt +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.0}/x4d_devkit.egg-info/entry_points.txt +0 -0
- {x4d_devkit-0.5.0 → x4d_devkit-0.6.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.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: X-4D dataset format SDK — load, validate, evaluate, and convert autonomous driving datasets
|
|
5
5
|
Author: windzu
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -20,14 +20,12 @@ Requires-Python: >=3.10
|
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
22
|
Requires-Dist: numpy>=1.24
|
|
23
|
+
Requires-Dist: httpx>=0.27
|
|
23
24
|
Provides-Extra: converters
|
|
24
25
|
Requires-Dist: nuscenes-devkit>=1.1.0; extra == "converters"
|
|
25
|
-
Provides-Extra: client
|
|
26
|
-
Requires-Dist: httpx>=0.27; extra == "client"
|
|
27
26
|
Provides-Extra: dev
|
|
28
27
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
29
28
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
30
|
-
Requires-Dist: httpx>=0.27; extra == "dev"
|
|
31
29
|
Dynamic: license-file
|
|
32
30
|
|
|
33
31
|
# x4d-devkit
|
|
@@ -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.6.0"
|
|
8
8
|
description = "X-4D dataset format SDK — load, validate, evaluate, and convert autonomous driving datasets"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -24,12 +24,11 @@ classifiers = [
|
|
|
24
24
|
"Programming Language :: Python :: 3.12",
|
|
25
25
|
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
26
26
|
]
|
|
27
|
-
dependencies = ["numpy>=1.24"]
|
|
27
|
+
dependencies = ["numpy>=1.24", "httpx>=0.27"]
|
|
28
28
|
|
|
29
29
|
[project.optional-dependencies]
|
|
30
30
|
converters = ["nuscenes-devkit>=1.1.0"]
|
|
31
|
-
|
|
32
|
-
dev = ["pytest>=7.0", "pytest-cov", "httpx>=0.27"]
|
|
31
|
+
dev = ["pytest>=7.0", "pytest-cov"]
|
|
33
32
|
|
|
34
33
|
[project.urls]
|
|
35
34
|
Homepage = "https://github.com/windzu/x4d-devkit"
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"""Unit tests for v0.6.0 API additions.
|
|
2
|
+
|
|
3
|
+
Covers:
|
|
4
|
+
- x4d_devkit.__version__
|
|
5
|
+
- ClipLoader.schema_version property + require_schema_version + constructor kwarg
|
|
6
|
+
- SchemaVersionMismatch exception
|
|
7
|
+
- calc_ap / calc_tp public re-exports (and underscore aliases still work)
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
import pytest
|
|
16
|
+
|
|
17
|
+
import x4d_devkit
|
|
18
|
+
from x4d_devkit import (
|
|
19
|
+
ClipLoader,
|
|
20
|
+
SchemaVersionMismatch,
|
|
21
|
+
calc_ap,
|
|
22
|
+
calc_tp,
|
|
23
|
+
)
|
|
24
|
+
from x4d_devkit.eval.matching import MetricData
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _write_minimal_clip(clip_dir: Path, schema_version: str) -> None:
|
|
28
|
+
"""Write the minimum JSON files ClipLoader.__init__ needs to succeed."""
|
|
29
|
+
clip_dir.mkdir(parents=True, exist_ok=True)
|
|
30
|
+
meta = {
|
|
31
|
+
"clip_id": "20260504_test_001",
|
|
32
|
+
"schema_version": schema_version,
|
|
33
|
+
"session_id": "20260504_test",
|
|
34
|
+
"vehicle_id": "test",
|
|
35
|
+
"capture_time_utc": "2026-05-04T00:00:00Z",
|
|
36
|
+
"duration_s": 0.0,
|
|
37
|
+
"keyframe_count": 0,
|
|
38
|
+
"sweep_count": 0,
|
|
39
|
+
"sensors": [],
|
|
40
|
+
}
|
|
41
|
+
(clip_dir / "meta.json").write_text(json.dumps(meta))
|
|
42
|
+
for empty in (
|
|
43
|
+
"calibrated_sensor.json",
|
|
44
|
+
"ego_pose.json",
|
|
45
|
+
"sample.json",
|
|
46
|
+
"sample_data.json",
|
|
47
|
+
"annotation.json",
|
|
48
|
+
"instance.json",
|
|
49
|
+
):
|
|
50
|
+
(clip_dir / empty).write_text("[]")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_package_has_version_attribute():
|
|
54
|
+
"""__version__ is a non-empty string matching expected shape."""
|
|
55
|
+
assert isinstance(x4d_devkit.__version__, str)
|
|
56
|
+
assert x4d_devkit.__version__ # non-empty
|
|
57
|
+
# Either a real version (e.g. "0.6.0", "0.6.0.post1") or the editable fallback.
|
|
58
|
+
assert "." in x4d_devkit.__version__ or x4d_devkit.__version__.startswith("0.0.0")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_loader_schema_version_property(tmp_path):
|
|
62
|
+
_write_minimal_clip(tmp_path, "0.5")
|
|
63
|
+
loader = ClipLoader(tmp_path)
|
|
64
|
+
assert loader.schema_version == "0.5"
|
|
65
|
+
assert loader.schema_version == loader.meta.schema_version
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_loader_require_schema_version_match(tmp_path):
|
|
69
|
+
_write_minimal_clip(tmp_path, "0.5")
|
|
70
|
+
loader = ClipLoader(tmp_path)
|
|
71
|
+
loader.require_schema_version("0.5") # no raise
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_loader_require_schema_version_mismatch(tmp_path):
|
|
75
|
+
_write_minimal_clip(tmp_path, "0.4")
|
|
76
|
+
loader = ClipLoader(tmp_path)
|
|
77
|
+
with pytest.raises(SchemaVersionMismatch) as exc:
|
|
78
|
+
loader.require_schema_version("0.5")
|
|
79
|
+
assert exc.value.actual == "0.4"
|
|
80
|
+
assert exc.value.expected == "0.5"
|
|
81
|
+
assert "0.4" in str(exc.value) and "0.5" in str(exc.value)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_loader_require_schema_version_set(tmp_path):
|
|
85
|
+
_write_minimal_clip(tmp_path, "0.4")
|
|
86
|
+
loader = ClipLoader(tmp_path)
|
|
87
|
+
loader.require_schema_version({"0.4", "0.5"}) # no raise
|
|
88
|
+
with pytest.raises(SchemaVersionMismatch):
|
|
89
|
+
loader.require_schema_version({"0.5", "0.6"})
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_loader_constructor_strict_check_match(tmp_path):
|
|
93
|
+
_write_minimal_clip(tmp_path, "0.5")
|
|
94
|
+
ClipLoader(tmp_path, expected_schema_version="0.5") # no raise
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def test_loader_constructor_strict_check_mismatch(tmp_path):
|
|
98
|
+
_write_minimal_clip(tmp_path, "0.4")
|
|
99
|
+
with pytest.raises(SchemaVersionMismatch):
|
|
100
|
+
ClipLoader(tmp_path, expected_schema_version="0.5")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def test_loader_constructor_strict_check_set(tmp_path):
|
|
104
|
+
_write_minimal_clip(tmp_path, "0.5")
|
|
105
|
+
ClipLoader(tmp_path, expected_schema_version={"0.4", "0.5"}) # no raise
|
|
106
|
+
_write_minimal_clip(tmp_path, "0.3")
|
|
107
|
+
with pytest.raises(SchemaVersionMismatch):
|
|
108
|
+
ClipLoader(tmp_path, expected_schema_version={"0.4", "0.5"})
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _md_with_precision(prec: np.ndarray) -> MetricData:
|
|
112
|
+
"""Build a MetricData with just enough fields for calc_ap / calc_tp."""
|
|
113
|
+
n = len(prec)
|
|
114
|
+
md = MetricData(
|
|
115
|
+
recall=np.linspace(0, 1, n),
|
|
116
|
+
precision=prec,
|
|
117
|
+
confidence=np.linspace(1, 0, n),
|
|
118
|
+
trans_err=np.zeros(n),
|
|
119
|
+
vel_err=np.zeros(n),
|
|
120
|
+
scale_err=np.zeros(n),
|
|
121
|
+
orient_err=np.zeros(n),
|
|
122
|
+
attr_err=np.zeros(n),
|
|
123
|
+
)
|
|
124
|
+
return md
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def test_calc_ap_public_export():
|
|
128
|
+
"""calc_ap is importable from x4d_devkit and x4d_devkit.eval, returns expected value."""
|
|
129
|
+
from x4d_devkit.eval import calc_ap as eval_calc_ap
|
|
130
|
+
|
|
131
|
+
assert eval_calc_ap is calc_ap
|
|
132
|
+
|
|
133
|
+
# Constant precision = 1.0 across recall, min_recall=0, min_precision=0 → AP=1.0
|
|
134
|
+
md = _md_with_precision(np.ones(101))
|
|
135
|
+
ap = calc_ap(md, min_recall=0.0, min_precision=0.0)
|
|
136
|
+
assert ap == pytest.approx(1.0)
|
|
137
|
+
|
|
138
|
+
# Constant 0 precision → AP=0.0
|
|
139
|
+
md = _md_with_precision(np.zeros(101))
|
|
140
|
+
ap = calc_ap(md, min_recall=0.0, min_precision=0.0)
|
|
141
|
+
assert ap == pytest.approx(0.0)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def test_calc_tp_public_export():
|
|
145
|
+
"""calc_tp is importable and returns mean of metric in valid recall range."""
|
|
146
|
+
from x4d_devkit.eval import calc_tp as eval_calc_tp
|
|
147
|
+
|
|
148
|
+
assert eval_calc_tp is calc_tp
|
|
149
|
+
|
|
150
|
+
md = _md_with_precision(np.ones(101))
|
|
151
|
+
md.trans_err[:] = 0.5
|
|
152
|
+
# min_recall=0 → first_ind=1; max_recall_ind defaults; mean over [1..max] of 0.5 = 0.5
|
|
153
|
+
tp = calc_tp(md, min_recall=0.0, metric_name="trans_err")
|
|
154
|
+
assert tp == pytest.approx(0.5)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def test_underscore_aliases_still_work():
|
|
158
|
+
"""Old _calc_ap / _calc_tp names remain importable (deprecated alias)."""
|
|
159
|
+
from x4d_devkit.eval.detection import _calc_ap, _calc_tp
|
|
160
|
+
|
|
161
|
+
assert _calc_ap is calc_ap
|
|
162
|
+
assert _calc_tp is calc_tp
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
"""X-4D dataset format SDK."""
|
|
2
2
|
|
|
3
|
+
from importlib.metadata import PackageNotFoundError, version as _pkg_version
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
__version__ = _pkg_version("x4d-devkit")
|
|
7
|
+
except PackageNotFoundError: # source checkout without installed metadata
|
|
8
|
+
__version__ = "0.0.0+unknown"
|
|
9
|
+
|
|
3
10
|
from x4d_devkit.client import X4DClient
|
|
4
11
|
from x4d_devkit.core import (
|
|
5
12
|
Annotation,
|
|
@@ -10,6 +17,7 @@ from x4d_devkit.core import (
|
|
|
10
17
|
Instance,
|
|
11
18
|
Sample,
|
|
12
19
|
SampleData,
|
|
20
|
+
SchemaVersionMismatch,
|
|
13
21
|
Transform,
|
|
14
22
|
TransformChain,
|
|
15
23
|
generate_token,
|
|
@@ -18,12 +26,15 @@ from x4d_devkit.eval import (
|
|
|
18
26
|
DetectionConfig,
|
|
19
27
|
DetectionEval,
|
|
20
28
|
DetectionResult,
|
|
29
|
+
calc_ap,
|
|
30
|
+
calc_tp,
|
|
21
31
|
nusc_detection_config,
|
|
22
32
|
)
|
|
23
33
|
from x4d_devkit.validation import validate_clip, ValidationReport
|
|
24
34
|
from x4d_devkit.fusion import LIDAR_FUSED_DTYPE, FusionSource, fuse_pointclouds # noqa: E402,F401
|
|
25
35
|
|
|
26
36
|
__all__ = [
|
|
37
|
+
"__version__",
|
|
27
38
|
"Annotation",
|
|
28
39
|
"CalibratedSensor",
|
|
29
40
|
"LIDAR_FUSED_DTYPE",
|
|
@@ -38,10 +49,13 @@ __all__ = [
|
|
|
38
49
|
"Instance",
|
|
39
50
|
"Sample",
|
|
40
51
|
"SampleData",
|
|
52
|
+
"SchemaVersionMismatch",
|
|
41
53
|
"Transform",
|
|
42
54
|
"TransformChain",
|
|
43
55
|
"ValidationReport",
|
|
44
56
|
"X4DClient",
|
|
57
|
+
"calc_ap",
|
|
58
|
+
"calc_tp",
|
|
45
59
|
"generate_token",
|
|
46
60
|
"nusc_detection_config",
|
|
47
61
|
"validate_clip",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Core layer: data models, clip loader, transforms."""
|
|
2
2
|
|
|
3
|
-
from x4d_devkit.core.loader import ClipLoader
|
|
3
|
+
from x4d_devkit.core.loader import ClipLoader, SchemaVersionMismatch
|
|
4
4
|
from x4d_devkit.core.models import (
|
|
5
5
|
Annotation,
|
|
6
6
|
CalibratedSensor,
|
|
@@ -22,6 +22,7 @@ __all__ = [
|
|
|
22
22
|
"Instance",
|
|
23
23
|
"Sample",
|
|
24
24
|
"SampleData",
|
|
25
|
+
"SchemaVersionMismatch",
|
|
25
26
|
"Transform",
|
|
26
27
|
"TransformChain",
|
|
27
28
|
"generate_token",
|
|
@@ -24,6 +24,24 @@ FrameType = Literal["sensor", "ego", "world"]
|
|
|
24
24
|
_VALID_FRAMES = {"sensor", "ego", "world"}
|
|
25
25
|
|
|
26
26
|
|
|
27
|
+
class SchemaVersionMismatch(ValueError):
|
|
28
|
+
"""Raised when a clip's ``meta.schema_version`` doesn't match the consumer's expectations.
|
|
29
|
+
|
|
30
|
+
Attributes:
|
|
31
|
+
actual: the version found in ``meta.json``.
|
|
32
|
+
expected: the version (or set of versions) the consumer required.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(self, actual: str, expected: "str | set[str]") -> None:
|
|
36
|
+
self.actual = actual
|
|
37
|
+
self.expected = expected
|
|
38
|
+
if isinstance(expected, str):
|
|
39
|
+
msg = f"clip schema_version is {actual!r}, expected {expected!r}"
|
|
40
|
+
else:
|
|
41
|
+
msg = f"clip schema_version is {actual!r}, expected one of {sorted(expected)!r}"
|
|
42
|
+
super().__init__(msg)
|
|
43
|
+
|
|
44
|
+
|
|
27
45
|
class ClipLoader:
|
|
28
46
|
"""Load and index an X4D clip directory.
|
|
29
47
|
|
|
@@ -49,11 +67,18 @@ class ClipLoader:
|
|
|
49
67
|
anns = loader.annotations_for_sample(sample.token, frame="ego")
|
|
50
68
|
"""
|
|
51
69
|
|
|
52
|
-
def __init__(
|
|
70
|
+
def __init__(
|
|
71
|
+
self,
|
|
72
|
+
clip_dir: str | Path,
|
|
73
|
+
*,
|
|
74
|
+
expected_schema_version: "str | set[str] | None" = None,
|
|
75
|
+
) -> None:
|
|
53
76
|
self._clip_dir = Path(clip_dir)
|
|
54
77
|
|
|
55
78
|
# Load and parse all JSON
|
|
56
79
|
self._meta = self._load_meta()
|
|
80
|
+
if expected_schema_version is not None:
|
|
81
|
+
self.require_schema_version(expected_schema_version)
|
|
57
82
|
self._calibrated_sensors = self._load_calibrated_sensors()
|
|
58
83
|
self._ego_poses = self._load_ego_poses()
|
|
59
84
|
self._samples = self._load_samples()
|
|
@@ -228,6 +253,31 @@ class ClipLoader:
|
|
|
228
253
|
"""Clip metadata (from meta.json)."""
|
|
229
254
|
return self._meta
|
|
230
255
|
|
|
256
|
+
@property
|
|
257
|
+
def schema_version(self) -> str:
|
|
258
|
+
"""Shortcut for ``self.meta.schema_version`` — the X-4D format version this clip was written in."""
|
|
259
|
+
return self._meta.schema_version
|
|
260
|
+
|
|
261
|
+
def require_schema_version(self, expected: "str | set[str]") -> None:
|
|
262
|
+
"""Raise ``SchemaVersionMismatch`` if the clip's schema_version isn't in ``expected``.
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
expected: a single version string (e.g. ``"0.5"``) or a set of accepted
|
|
266
|
+
versions (e.g. ``{"0.4", "0.5"}``).
|
|
267
|
+
|
|
268
|
+
Example::
|
|
269
|
+
|
|
270
|
+
loader = ClipLoader(path)
|
|
271
|
+
loader.require_schema_version("0.5") # raises if not 0.5
|
|
272
|
+
"""
|
|
273
|
+
actual = self._meta.schema_version
|
|
274
|
+
if isinstance(expected, str):
|
|
275
|
+
if actual != expected:
|
|
276
|
+
raise SchemaVersionMismatch(actual, expected)
|
|
277
|
+
else:
|
|
278
|
+
if actual not in expected:
|
|
279
|
+
raise SchemaVersionMismatch(actual, set(expected))
|
|
280
|
+
|
|
231
281
|
@property
|
|
232
282
|
def samples(self) -> list[Sample]:
|
|
233
283
|
"""All samples, sorted by timestamp."""
|
|
@@ -4,6 +4,8 @@ from x4d_devkit.eval.detection import (
|
|
|
4
4
|
DetectionConfig,
|
|
5
5
|
DetectionEval,
|
|
6
6
|
DetectionResult,
|
|
7
|
+
calc_ap,
|
|
8
|
+
calc_tp,
|
|
7
9
|
detzero_detection_config,
|
|
8
10
|
nusc_detection_config,
|
|
9
11
|
)
|
|
@@ -12,6 +14,8 @@ __all__ = [
|
|
|
12
14
|
"DetectionConfig",
|
|
13
15
|
"DetectionEval",
|
|
14
16
|
"DetectionResult",
|
|
17
|
+
"calc_ap",
|
|
18
|
+
"calc_tp",
|
|
15
19
|
"detzero_detection_config",
|
|
16
20
|
"nusc_detection_config",
|
|
17
21
|
]
|
|
@@ -197,7 +197,7 @@ class DetectionResult:
|
|
|
197
197
|
tp_scores: dict[str, float]
|
|
198
198
|
|
|
199
199
|
|
|
200
|
-
def
|
|
200
|
+
def calc_ap(md: MetricData, min_recall: float, min_precision: float) -> float:
|
|
201
201
|
"""Calculate Average Precision from MetricData."""
|
|
202
202
|
prec = np.copy(md.precision)
|
|
203
203
|
prec = prec[round(100 * min_recall) + 1:]
|
|
@@ -208,7 +208,7 @@ def _calc_ap(md: MetricData, min_recall: float, min_precision: float) -> float:
|
|
|
208
208
|
return float(np.mean(prec) / (1.0 - min_precision))
|
|
209
209
|
|
|
210
210
|
|
|
211
|
-
def
|
|
211
|
+
def calc_tp(md: MetricData, min_recall: float, metric_name: str) -> float:
|
|
212
212
|
"""Calculate mean TP metric between min_recall and max_recall."""
|
|
213
213
|
first_ind = round(100 * min_recall) + 1
|
|
214
214
|
last_ind = md.max_recall_ind
|
|
@@ -218,6 +218,11 @@ def _calc_tp(md: MetricData, min_recall: float, metric_name: str) -> float:
|
|
|
218
218
|
return float(np.nanmean(metric_arr[first_ind:last_ind + 1]))
|
|
219
219
|
|
|
220
220
|
|
|
221
|
+
# Deprecated aliases — removed in 0.7.0. Use calc_ap / calc_tp.
|
|
222
|
+
_calc_ap = calc_ap
|
|
223
|
+
_calc_tp = calc_tp
|
|
224
|
+
|
|
225
|
+
|
|
221
226
|
def _extract_boxes(
|
|
222
227
|
clip: ClipLoader,
|
|
223
228
|
config: DetectionConfig,
|
|
@@ -339,7 +344,7 @@ class DetectionEval:
|
|
|
339
344
|
label_aps[class_name] = {}
|
|
340
345
|
for dist_th in cfg.dist_thresholds:
|
|
341
346
|
md = metric_data[class_name][str(dist_th)]
|
|
342
|
-
label_aps[class_name][str(dist_th)] =
|
|
347
|
+
label_aps[class_name][str(dist_th)] = calc_ap(md, cfg.min_recall, cfg.min_precision)
|
|
343
348
|
|
|
344
349
|
mean_dist_aps = {
|
|
345
350
|
cn: float(np.mean(list(label_aps[cn].values())))
|
|
@@ -360,7 +365,7 @@ class DetectionEval:
|
|
|
360
365
|
elif class_name == "barrier" and metric_name in ("attr_err", "vel_err"):
|
|
361
366
|
label_tp_errors[class_name][metric_name] = float("nan")
|
|
362
367
|
else:
|
|
363
|
-
label_tp_errors[class_name][metric_name] =
|
|
368
|
+
label_tp_errors[class_name][metric_name] = calc_tp(md, cfg.min_recall, metric_name)
|
|
364
369
|
|
|
365
370
|
tp_errors = {}
|
|
366
371
|
for metric_name in TP_METRICS:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: x4d-devkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: X-4D dataset format SDK — load, validate, evaluate, and convert autonomous driving datasets
|
|
5
5
|
Author: windzu
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -20,14 +20,12 @@ Requires-Python: >=3.10
|
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
22
|
Requires-Dist: numpy>=1.24
|
|
23
|
+
Requires-Dist: httpx>=0.27
|
|
23
24
|
Provides-Extra: converters
|
|
24
25
|
Requires-Dist: nuscenes-devkit>=1.1.0; extra == "converters"
|
|
25
|
-
Provides-Extra: client
|
|
26
|
-
Requires-Dist: httpx>=0.27; extra == "client"
|
|
27
26
|
Provides-Extra: dev
|
|
28
27
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
29
28
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
30
|
-
Requires-Dist: httpx>=0.27; extra == "dev"
|
|
31
29
|
Dynamic: license-file
|
|
32
30
|
|
|
33
31
|
# x4d-devkit
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|