File size: 6,594 Bytes
ac1609a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
#!/usr/bin/env python3
# SPDX-License-Identifier: Apache-2.0
"""Build the PandaSet sample of streampetr-p150 from the research ship sample (reproducible; run once per change):

    python3 code/scripts/make_samples.py [--src research/streampetr/public_data/ship_sample/pandaset_019_f40_43]
                                         [--dest staging|package]

``--dest staging`` (default) writes into the git-ignored ``staging_samples_pandaset/`` of the bundle checkout (a
layout that mirrors ``code/tt_streampetr/``): the sample does not ship until the user approves PandaSet-derived
samples (PLAN.md 6.3, research/DATASETS.md N5). ``--dest package`` writes into ``code/tt_streampetr/`` (the swap-in,
staging_samples_pandaset/README.md). The shipped sample is the synthetic one of ``code/scripts/make_sample.py``.

Writes (relative to the destination, which mirrors ``code/tt_streampetr/``):
- ``samples/pandaset_019/<frame>_<CAMERA>.jpg``: the 20 PandaSet sequence 019 JPEGs (frames 40-43,
  the five model cameras), unmodified bytes, renamed to the model slot names; ``LICENSE.txt`` (PandaSet Dataset
  Terms, CC BY 4.0) and ``SHA256SUMS``;
- ``calib/pandaset_019.json``: the calibration preset (per camera ``P`` = ``[K | 0]`` of the
  1920x1080 image, ``T_ref_from_camera`` = camera optical frame -> base_link, ``image_size``);
- ``samples/pandaset_019_f<40..43>.json``: one request manifest per frame (images, the preset,
  per-camera stamps, ``stream`` = id, anchor CAM_FRONT stamp, ``T_world_from_ego`` = map <- base_link at that stamp;
  frame 40 resets the stream: the expected outputs are a cold start).

Source: research/streampetr/public_data (PandaSet 019 converted to the Autoware input contract; see its README for
how the extrinsics, poses and stamps were derived). Contains data from PandaSet (Scale AI and Hesai),
https://pandaset.org, licensed under CC BY 4.0 and the PandaSet Dataset Terms. Standard library + numpy only.
"""
from __future__ import annotations

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

import numpy as np

BUNDLE = Path(__file__).resolve().parents[2]
PKG = BUNDLE / "code" / "tt_streampetr"
DEFAULT_SRC = Path("/home/ubuntu/experiments/tt-models/research/streampetr/public_data/ship_sample/pandaset_019_f40_43")
SLOTS = ["CAM_FRONT", "CAM_FRONT_LEFT", "CAM_BACK_LEFT", "CAM_FRONT_RIGHT", "CAM_BACK_RIGHT"]
ATTRIBUTION = ("Contains data from PandaSet (Scale AI and Hesai), https://pandaset.org, licensed under CC BY 4.0 and the "
               "PandaSet Dataset Terms. Changes: JPEGs renamed (<frame>_<model camera slot>.jpg), unmodified bytes; "
               "calibration, poses and stamps converted to the Autoware base_link / camera-optical-frame conventions "
               "(research/streampetr/public_data). Scale AI and Hesai do not endorse this work. Cite: P. Xiao et al., "
               "PandaSet: Advanced Sensor Suite Dataset for Autonomous Driving, ITSC 2021.")


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


def main() -> None:
    ap = argparse.ArgumentParser()
    ap.add_argument("--src", type=Path, default=DEFAULT_SRC)
    ap.add_argument("--dest", choices=("staging", "package"), default="staging")
    a = ap.parse_args()
    global PKG
    if a.dest == "staging":
        PKG = BUNDLE / "staging_samples_pandaset"
    man = json.loads((a.src / "manifest.json").read_text())
    em = np.load(a.src / "ego_motion.npz")
    slot_file = man["slots"]                                     # model slot -> manifest camera name
    out_img = PKG / "samples" / "pandaset_019"
    out_img.mkdir(parents=True, exist_ok=True)
    (PKG / "samples").mkdir(parents=True, exist_ok=True)
    sums = []
    for fr in man["frames"]:
        for s in SLOTS:
            src = a.src / fr["imgs"][slot_file[s]]
            dst = out_img / f"{fr['source_frame']}_{s}.jpg"
            shutil.copyfile(src, dst)
            sums.append(f"{sha256(dst)}  {dst.name}")
    shutil.copyfile(a.src / "LICENSE.txt", out_img / "LICENSE.txt")
    sums.append(f"{sha256(out_img / 'LICENSE.txt')}  LICENSE.txt")
    (out_img / "SHA256SUMS").write_text("\n".join(sums) + "\n")

    h, w = man["img_hw"]
    cams = {}
    for s in SLOTS:
        c = man["cams"][slot_file[s]]
        cams[s] = {"intrinsics": c["K"], "P": c["P"], "T_ref_from_camera": c["T_ego_cam"],
                   "source_camera": c["source_camera"]}
    calib = {"_doc": "Calibration preset of the shipped PandaSet sequence 019 sample (samples/pandaset_019). Per camera: "
                     "the intrinsics of the 1920x1080 image AS SENT (P = [K | 0], CameraInfo.P of a rectified "
                     "pinhole camera) and T_ref_from_camera = camera optical frame (x right, y down, z forward) -> "
                     "base_link (static extrinsics). base_link: " + man["base_link"] + ". " + ATTRIBUTION,
             "frame_id": "base_link", "image_size": [w, h], "cameras": cams}
    (PKG / "calib").mkdir(exist_ok=True)
    (PKG / "calib" / "pandaset_019.json").write_text(json.dumps(calib, indent=1) + "\n")

    for k, fr in enumerate(man["frames"]):
        f = fr["source_frame"]
        req = {"_doc": f"StreamPETR sample request: PandaSet sequence 019 frame {f} (5 cameras, 1920x1080 JPEG, "
                       "unmodified bytes). model(**tt_streampetr.load_sample(path)). 'stream' carries the frame's "
                       "anchor (CAM_FRONT) stamp and ego pose (map <- base_link at that stamp; the map frame is "
                       "PandaSet's world frame re-centred on frame 40)" + (", and resets the stream: frame 40 is a "
                       "cold start (zero temporal memory)" if k == 0 else "; send frames 40, 41, ... in order on one "
                       "stream id") + ". License: CC BY 4.0 + PandaSet Dataset Terms (pandaset_019/LICENSE.txt).",
               "images": {s: f"pandaset_019/{f}_{s}.jpg" for s in SLOTS},
               "calibration": {"preset": "pandaset_019"},
               "frame": f,
               "camera_stamps_s": {s: fr["stamps"][s] for s in SLOTS},
               "stream": {"id": "pandaset_019", "timestamp_s": fr["anchor_stamp"],
                          "T_world_from_ego": np.asarray(em["T_map_base"][k], np.float64).tolist()}}
        if k == 0:
            req["stream"]["reset"] = True
        (PKG / "samples" / f"pandaset_019_f{f}.json").write_text(json.dumps(req, indent=1) + "\n")
    print(f"wrote {len(sums) - 1} JPEGs, calib/pandaset_019.json and {len(man['frames'])} request manifests to {PKG}")


if __name__ == "__main__":
    main()