File size: 6,415 Bytes
fe51a63
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#!/usr/bin/env python3
# SPDX-License-Identifier: Apache-2.0
"""Build the STAGED PandaSet sample of frnet-p150 and its stored CPU-reference outputs (research venv: numpy, onnx,
onnxruntime; CPU only, no device). It does not ship until the user approves PandaSet-derived samples
(research/DATASETS.md N5): it goes to the git-ignored ``staging_samples_pandaset/`` of the bundle checkout (laid out
like the package: ``samples/``, ``calib/``), where the tests find it; the frozen ``sample`` gates and the PCC frame
still use it. The shipped samples are the synthetic scans of ``code/scripts/make_sample.py``.

    cd bundles/frnet-p150 && OMP_NUM_THREADS=4 PYTHONPATH=code \\
        /home/ubuntu/experiments/tt-models/tools/research-venv/bin/python code/scripts/make_samples.py

Writes (from research/frnet/public_data, PandaSet 019 frame 40, CC BY 4.0 + PandaSet Dataset Terms; D18), under
``staging_samples_pandaset/``:

- ``samples/pandaset_019_40.npz``: ``points``, a structured (x, y, z, intensity) float32 array of the
  sensor-frame Pandar64 sweep, byte-identical values to ``shippable/pandaset_019_40_pandar64_xyzi.npy`` (sha256
  checked against its meta);
- ``samples/PANDASET_LICENSE.txt``: the PandaSet Dataset Terms of Use (verbatim, sha256 checked);
- ``calib/pandaset_019_40.json``: the sensor -> base_link transform at full float64 precision (the
  converted input's ``T_base_link_sensor``: its float32 cast is the research golden's crop transform bit for bit;
  the 6-decimal copy in the sample meta is not);
- ``samples/pandaset_019_40.<variant>.reference.json`` (ot128, qt128): the ``to_dict()`` of the
  reference pipeline with ONNX Runtime on the deployed ONNX (the reference of record, S:frnet:361-365), reduced to
  what the container smoke compares (``labels``, ``scores``, ``filter_keep``, npz_compressed) plus counts. The class
  ids are checked against the research golden ``public_data/golden/pandaset_019_40_<variant>.npz`` (equal).
"""
from __future__ import annotations

import argparse
import hashlib
import json
import shutil
import sys
from pathlib import Path

import numpy as np

HERE = Path(__file__).resolve()
CODE = HERE.parents[1]
sys.path.insert(0, str(CODE))

from tt_frnet.io import encode_array  # noqa: E402
from tt_frnet.reference.config import VARIANTS  # noqa: E402

WS = Path("/home/ubuntu/experiments/tt-models")
PUB = WS / "research" / "frnet" / "public_data"
STEM = "pandaset_019_40"
KEEP = ("model", "frame_id", "num_points", "num_points_raw", "class_names", "class_counts")


def sha256(path: Path) -> str:
    return hashlib.sha256(path.read_bytes()).hexdigest()


def main() -> int:
    ap = argparse.ArgumentParser(description=__doc__.split("\n\n")[0])
    ap.add_argument("--weights-dir", default=str(WS / "assets" / "frnet" / "hf_lidar_frnet"))
    ap.add_argument("--threads", type=int, default=4)
    ap.add_argument("--variants", nargs="*", default=list(VARIANTS))
    a = ap.parse_args()
    staging = CODE.parent / "staging_samples_pandaset"
    samples, calib = staging / "samples", staging / "calib"
    samples.mkdir(parents=True, exist_ok=True)
    calib.mkdir(parents=True, exist_ok=True)
    meta = json.loads((PUB / "shippable" / f"{STEM}_meta.json").read_text())
    src = PUB / "shippable" / f"{STEM}_pandar64_xyzi.npy"
    lic = PUB / "shippable" / "LICENSE.txt"
    for path in (src, lic):
        if sha256(path) != meta["files"][path.name]:
            sys.exit(f"{path}: sha256 differs from {STEM}_meta.json")
    xyzi = np.load(src, allow_pickle=False)
    pts = np.zeros(len(xyzi), dtype=[("x", "<f4"), ("y", "<f4"), ("z", "<f4"), ("intensity", "<f4")])
    for i, name in enumerate(("x", "y", "z", "intensity")):
        pts[name] = xyzi[:, i]
    np.savez_compressed(samples / f"{STEM}.npz", points=pts)
    shutil.copyfile(lic, samples / "PANDASET_LICENSE.txt")
    T = np.load(PUB / "inputs" / f"{STEM}.npz", allow_pickle=False)["T_base_link_sensor"].astype(np.float64)
    preset = {"name": STEM,
              "description": "PandaSet 019 frame 40: static Pandar64 sensor -> base_link transform (fitted mount; "
                             "base_link = the lidar-pose origin projected to the road, x forward) for the ego crop box",
              "source": "research/frnet/public_data/inputs/pandaset_019_40.npz T_base_link_sensor "
                        "(calib/pandaset_pandar64_mount.json fit)",
              "T_base_link_from_sensor": [[float(v) for v in row] for row in T]}
    (calib / f"{STEM}.json").write_text(json.dumps(preset, indent=1) + "\n")
    print(f"sample {len(pts)} points, calibration preset {STEM}", flush=True)

    from tt_frnet.reference.pipeline import FRNetReference

    for variant in a.variants:
        ref = FRNetReference(a.weights_dir, variant, backend="ort", threads=a.threads)
        run = ref.run(samples / f"{STEM}.npz",
                      calibration={"T_base_link_from_sensor": preset["T_base_link_from_sensor"]})
        gold = np.load(PUB / "golden" / f"{STEM}_{variant}.npz", allow_pickle=False)
        out = run.output
        if not (np.array_equal(run.prepared.inputs.points, gold["autoware_points"]) and
                np.array_equal(out.label_ids, gold["autoware_class_id"]) and
                np.array_equal(out.filter_keep, gold["autoware_filter_keep"])):
            sys.exit(f"{variant}: the reference output differs from the research golden")
        body = {k: v for k, v in out.to_dict().items() if k in KEEP}
        body["labels"] = encode_array(out.label_ids, fmt="npz_compressed", key="labels")
        body["scores"] = encode_array(out.scores, fmt="npz_compressed", key="scores")
        body["filter_keep"] = encode_array(out.filter_keep, fmt="npz_compressed", key="filter_keep")
        body["meta"] = {"variant": variant, "reference": "onnxruntime fp32 (deployed ONNX)",
                        "onnx_sha256": ref.weights.onnx_sha256, "num_unique_coors": run.prepared.inputs.num_voxels,
                        "cropped_by_ego_box": int((~run.prepared.inputs.keep_mask).sum())}
        path = samples / f"{STEM}.{variant}.reference.json"
        path.write_text(json.dumps(body) + "\n")
        print(f"{variant}: N {out.num_points if hasattr(out, 'num_points') else len(out)} "
              f"M {run.prepared.inputs.num_voxels} -> {path.name} ({path.stat().st_size / 1e6:.2f} MB)", flush=True)
    return 0


if __name__ == "__main__":
    sys.exit(main())