def pcap_read_packets( pcap_path: str, metadata_path: str, num: int = 0 # not used in this examples ) -> None: """Basic read packets example from pcap file (*pcap_path*) Args: pcap_path: path to the pcap file metadata_path: path to the .json with metadata (aka :class:`.SensorInfo`) """ metadata = read_metadata(metadata_path) source = pcap.Pcap(pcap_path, metadata) # [doc-stag-pcap-read-packets] for packet in source: if isinstance(packet, client.LidarPacket): # Now we can process the LidarPacket. In this case, we access # the encoder_counts, timestamps, and ranges encoder_counts = packet.header(client.ColHeader.ENCODER_COUNT) timestamps = packet.header(client.ColHeader.TIMESTAMP) ranges = packet.field(client.ChanField.RANGE) print(f' encoder counts = {encoder_counts.shape}') print(f' timestamps = {timestamps.shape}') print(f' ranges = {ranges.shape}') if isinstance(packet, client.ImuPacket): # and access ImuPacket content print(f' acceleration = {packet.accel}') print(f' angular_velocity = {packet.angular_vel}')
def packet(real_pcap_path: str, meta: client.SensorInfo) -> client.LidarPacket: # note: don't want to depend on the pcap fixture, since this consumes the # iterator and it can be shared with closing(pcap.Pcap(real_pcap_path, meta)) as real_pcap: for p in real_pcap: if isinstance(p, client.LidarPacket): return p raise RuntimeError("Failed to find lidar packet in test fixture")
def test_pcap_read_wrong_ports(fake_meta, fake_pcap_path) -> None: """Check specifying wrong ports.""" fake_pcap = pcap.Pcap(fake_pcap_path, fake_meta, lidar_port=7505, imu_port=7506) assert fake_pcap.ports == (7505, 7506) assert fake_pcap._guesses == [] assert list(fake_pcap) == []
def test_pcap_guess_real(meta: client.SensorInfo, real_pcap_path: str) -> None: """Check that lidar port for real data can inferred.""" meta_no_ports = copy(meta) meta_no_ports.udp_port_lidar = 0 meta_no_ports.udp_port_imu = 0 real_pcap = pcap.Pcap(real_pcap_path, meta_no_ports) assert real_pcap.ports[0] == 7502
def test_pcap_infer_one_port(fake_meta, fake_pcap_path) -> None: """Check that a matching port is inferred when one is specified. """ fake_pcap = pcap.Pcap(fake_pcap_path, fake_meta, lidar_port=0, imu_port=7503) assert fake_pcap._guesses == [(7502, 7503)] assert fake_pcap.ports == (7502, 7503) assert len(list(fake_pcap)) == 10
def main() -> None: descr = """Visualize pcap or sensor data using simple viz bindings.""" epilog = """When reading data from a sensor, this will autoconfigure the udp destination unless -x is used.""" parser = argparse.ArgumentParser(description=descr, epilog=epilog) required = parser.add_argument_group('one of the following is required') group = required.add_mutually_exclusive_group(required=True) group.add_argument('--sensor', metavar='HOST', help='sensor hostname') group.add_argument('--pcap', metavar='PATH', help='path to pcap file') parser.add_argument('--meta', metavar='PATH', help='path to metadata json') parser.add_argument('--lidar-port', type=int, help='lidar port for sensor') parser.add_argument('-x', '--no-auto-dest', action='store_true', help='do not auto configure udp destination') args = parser.parse_args() if args.sensor: hostname = args.sensor if args.lidar_port or (not args.no_auto_dest): config = client.SensorConfig() if args.lidar_port: config.udp_port_lidar = args.lidar_port print("Configuring sensor...") client.set_config(hostname, config, udp_dest_auto=(not args.no_auto_dest)) config = client.get_config(hostname) print("Initializing...") scans = client.Scans.stream(hostname, config.udp_port_lidar or 7502, complete=False) rate = None elif args.pcap: import ouster.pcap as pcap if args.meta: metadata_path = args.meta else: print("Deducing metadata based on pcap name. " "To provide a different metadata path, use --meta") metadata_path = os.path.splitext(args.pcap)[0] + ".json" with open(metadata_path) as json: info = client.SensorInfo(json.read()) scans = client.Scans(pcap.Pcap(args.pcap, info)) rate = 1.0 SimpleViz(scans.metadata, rate).run(scans)
def test_read_write_lidar_imu(n_lidar, n_imu, fake_meta, tmpdir) -> None: """Test that random packets read back from pcap are identical.""" in_packets = list(fake_packets(fake_meta, n_lidar, n_imu)) file_path = path.join(tmpdir, "pcap_test.pcap") pcap.record(in_packets, file_path) out_packets = list(pcap.Pcap(file_path, fake_meta)) out_bufs = [bytes(p._data) for p in out_packets] in_bufs = [bytes(p._data) for p in in_packets] assert in_bufs == out_bufs
def test_pcap_guess_one_port_valid(fake_meta, fake_pcap_path) -> None: """Check that an unpaired port can be infered. Capture contains just lidar data on 7502, and user expects imu on 7503. """ fake_pcap = pcap.Pcap(fake_pcap_path, fake_meta, lidar_port=0, imu_port=7503) assert fake_pcap._guesses == [(7502, 0)] # no imu packets, no guess assert fake_pcap.ports == (7502, 7503) assert len(list(fake_pcap)) == 10
def test_pcap_guess_one_port_invalid(fake_meta, fake_pcap_path) -> None: """Check that a port belonging to another pair isn't inferred. Capture contains data on (7502, 7503), but user expects lidar data on 7505. """ fake_pcap = pcap.Pcap(fake_pcap_path, fake_meta, lidar_port=7505, imu_port=0) assert fake_pcap._guesses == [] assert fake_pcap.ports == (7505, 0) assert list(fake_pcap) == []
def main(): """Pcap examples runner.""" examples = { "plot-xyz-points": pcap_display_xyz_points, "2d-viewer": pcap_2d_viewer, "read-packets": pcap_read_packets, "plot-one-scan": pcap_show_one_scan, "pcap-to-csv": pcap_to_csv, "pcap-to-pcd": pcap_to_pcd, "pcap-to-las": pcap_to_las, "open3d-one-scan": pcap_3d_one_scan, } description = "Ouster Python SDK Pcap examples. The EXAMPLE must be one of:\n" + str.join( '\n ', examples.keys()) parser = argparse.ArgumentParser( description=description, formatter_class=argparse.RawTextHelpFormatter) parser.add_argument('pcap_path', metavar='PCAP', help='path to pcap file') parser.add_argument('metadata_path', metavar='METADATA', help='path to metadata json') parser.add_argument('example', metavar='EXAMPLE', choices=examples.keys(), help='name of the example to run') parser.add_argument('--scan-num', type=int, default=1, help='index of scan to use') args = parser.parse_args() try: example = examples[args.example] except KeyError: print(f"No such example: {args.example}") print(description) exit(1) if not args.metadata_path or not os.path.exists(args.metadata_path): print(f"Metadata file does not exist: {args.metadata_path}") exit(1) print(f'example: {args.example}') with open(args.metadata_path, 'r') as f: metadata = client.SensorInfo(f.read()) source = pcap.Pcap(args.pcap_path, metadata) with closing(source): example(source, metadata, args.scan_num) # type: ignore
def test_timestamp_read_write(fake_meta, tmpdir) -> None: """Check that timestamps are preserved by a round trip.""" in_packets = list( fake_packets(fake_meta, n_lidar=10, n_imu=10, timestamped=True)) file_path = path.join(tmpdir, "pcap_test.pcap") pcap.record(in_packets, file_path) out_packets = list(pcap.Pcap(file_path, fake_meta)) out_timestamps = [p.capture_timestamp for p in out_packets] in_timestamps = [p.capture_timestamp for p in in_packets] assert len(in_timestamps) == len(out_timestamps) assert in_timestamps == pytest.approx(out_timestamps)
def test_read_write_lidar_imu(n_lidar, n_imu, metadata, tmpdir): """Test that random packets read back from pcap are identical.""" lidar_packets = islice(random_lidar_packets(metadata), n_lidar) imu_packets = islice(random_imu_packets(metadata), n_imu) in_packets = list(chain(lidar_packets, imu_packets)) shuffle(in_packets) file_path = os.path.join(tmpdir, "pcap_test.pcap") pcap.record(in_packets, file_path) out_packets = list(pcap.Pcap(file_path, metadata)) out_bufs = [bytes(p._data) for p in out_packets] in_bufs = [bytes(p._data) for p in in_packets] assert in_bufs == out_bufs
def test_imu_guess_error(metadata, tmpdir): packets = islice(random_imu_packets(metadata), 2) file_path = os.path.join(tmpdir, "pcap_test.pcap") buf_size = 2**16 handle = _pcap.record_initialize(file_path, "127.0.0.1", "127.0.0.1", buf_size) try: _pcap.record_packet(handle, 7502, 7502, (next(packets))._data, 1) _pcap.record_packet(handle, 7503, 7503, (next(packets))._data, 2) finally: _pcap.record_uninitialize(handle) with pytest.raises(ValueError): pcap.Pcap(file_path, metadata)
def test_no_timestamp_read_write(fake_meta, tmpdir) -> None: """Check that capture timestamps are set when recording.""" in_packets = list( fake_packets(fake_meta, n_lidar=10, n_imu=10, timestamped=False)) file_path = path.join(tmpdir, "pcap_test.pcap") current_timestamp = time.time() pcap.record(in_packets, file_path) out_packets = list(pcap.Pcap(file_path, fake_meta)) out_timestamps = [p.capture_timestamp for p in out_packets] in_timestamps = [p.capture_timestamp for p in in_packets] assert len(in_timestamps) == len(out_timestamps) assert all(ts is None for ts in in_timestamps) assert all(ts == pytest.approx(current_timestamp, abs=1.0) for ts in out_timestamps)
def main() -> None: import argparse import os import ouster.pcap as pcap descr = """Example visualizer using the open3d library. Visualize either pcap data (specified using --pcap) or a running sensor (specified using --sensor). If no metadata file is specified, this will look for a file with the same name as the pcap with the '.json' extension, or query it directly from the sensor. Visualizing a running sensor requires the sensor to be configured and sending lidar data to the default UDP port (7502) on the host machine. """ parser = argparse.ArgumentParser(description=descr) parser.add_argument('--pause', action='store_true', help='start paused') parser.add_argument('--start', type=int, help='skip to frame number') parser.add_argument('--meta', metavar='PATH', help='path to metadata json') required = parser.add_argument_group('one of the following is required') group = required.add_mutually_exclusive_group(required=True) group.add_argument('--sensor', metavar='HOST', help='sensor hostname') group.add_argument('--pcap', metavar='PATH', help='path to pcap file') args = parser.parse_args() if args.sensor: scans = client.Scans.stream(args.sensor, metadata=args.meta) elif args.pcap: pcap_path = args.pcap metadata_path = args.meta or os.path.splitext(pcap_path)[0] + ".json" with open(metadata_path, 'r') as f: metadata = client.SensorInfo(f.read()) source = pcap.Pcap(pcap_path, metadata) scans = client.Scans(source) consume(scans, args.start or 0) try: viewer_3d(scans, paused=args.pause) except (KeyboardInterrupt, StopIteration): pass finally: scans.close()
def pcap_show_one_scan(pcap_path: str, metadata_path: str, num: int = 0, destagger: bool = True) -> None: """Show all 4 channels of one scan (*num*) form pcap file (*pcap_path*) Args: pcap_path: path to the pcap file metadata_path: path to the .json with metadata (aka :class:`.SensorInfo`) num: scan number in a given pcap file (satrs from *0*) """ import matplotlib.pyplot as plt # type: ignore # [doc-stag-pcap-show-one] metadata = read_metadata(metadata_path) def prepare_field_image(scan, key, metadata, destagger=True): f = ae(scan.field(key)) if destagger: return client.destagger(metadata, f) return f show_fields = [('range', client.ChanField.RANGE), ('signal', client.ChanField.SIGNAL), ('near_ir', client.ChanField.NEAR_IR), ('reflectivity', client.ChanField.REFLECTIVITY)] with closing(pcap.Pcap(pcap_path, metadata)) as source: scan = nth(client.Scans(source), num) if not scan: return fields_images = [(sf[0], prepare_field_image(scan, sf[1], source.metadata)) for sf in show_fields] fig = plt.figure(constrained_layout=True) axs = fig.subplots(len(fields_images), 1, sharey=True) for ax, field in zip(axs, fields_images): ax.set_title(field[0], fontdict={'fontsize': 10}) ax.imshow(field[1], cmap='gray', resample=False) ax.set_yticklabels([]) ax.set_yticks([]) ax.set_xticks([0, scan.w]) plt.show()
def test_imu_guess_ambiguous(fake_meta, tmpdir) -> None: """Test reading when there's more than one possible imu port.""" packets = fake_packets(fake_meta, n_imu=2) file_path = path.join(tmpdir, "pcap_test.pcap") buf_size = 2**16 handle = _pcap.record_initialize(file_path, "127.0.0.1", "127.0.0.1", buf_size) try: _pcap.record_packet(handle, 7502, 7502, (next(packets))._data, 1) _pcap.record_packet(handle, 7503, 7503, (next(packets))._data, 2) finally: _pcap.record_uninitialize(handle) source = pcap.Pcap(file_path, fake_meta) assert len(source._guesses) > 1 assert source.ports == (0, 7503) # arbitrary but deterministic assert len(list(source)) == 1
def pcap_display_xyz_points(pcap_path: str, metadata_path: str, num: int = 0) -> None: """Display range from a specified scan number (*num*) as 3D points from pcap file located at *pcap_path* Args: pcap_path: path to the pcap file metadata_path: path to the .json with metadata (aka :class:`.SensorInfo`) num: scan number in a given pcap file (satrs from *0*) """ import matplotlib.pyplot as plt # type: ignore # [doc-stag-pcap-plot-xyz-points] metadata = read_metadata(metadata_path) source = pcap.Pcap(pcap_path, metadata) # get single scan scans = client.Scans(source) scan = nth(scans, num) if not scan: print(f'ERROR: Scan # {num} in not present in pcap file: {pcap_path}') return # set up figure plt.figure() ax = plt.axes(projection='3d') r = 6 ax.set_xlim3d([-r, r]) ax.set_ylim3d([-r, r]) ax.set_zlim3d([-r, r]) plt.title("3D Points XYZ for scan") # transform data to 3d points and graph xyzlut = client.XYZLut(metadata) xyz = xyzlut(scan) key = scan.field(client.ChanField.SIGNAL) [x, y, z] = [c.flatten() for c in np.dsplit(xyz, 3)] ax.scatter(x, y, z, c=ae(key.flatten()), s=0.2) plt.show()
def test_timestamp_float_read_write(mode, metadata, tmpdir): lidar_packets = islice(random_lidar_packets(metadata, random_time=mode), 10) imu_packets = islice(random_imu_packets(metadata, random_time=mode), 10) in_packets = list(chain(lidar_packets, imu_packets)) file_path = os.path.join(tmpdir, "pcap_test.pcap") pcap.record(in_packets, file_path) out_packets = list(pcap.Pcap(file_path, metadata)) out_timestamps = [] in_timestamps = [] out_timestamps = [p.capture_timestamp for p in out_packets] in_timestamps = [p.capture_timestamp for p in in_packets] assert len(in_timestamps) == len(out_timestamps) for i, o in zip(in_timestamps, out_timestamps): # Make sure to deal with float rounding issues in the compare assert i == pytest.approx(o, abs=1e-6)
def test_no_timestamp_read_write(metadata, tmpdir): mode = NO_RANDOM_TIME current_timestamp = time.time() lidar_packets = islice(random_lidar_packets(metadata, random_time=mode), 10) imu_packets = islice(random_imu_packets(metadata, random_time=mode), 10) in_packets = list(chain(lidar_packets, imu_packets)) file_path = os.path.join(tmpdir, "pcap_test.pcap") pcap.record(in_packets, file_path) out_packets = list(pcap.Pcap(file_path, metadata)) out_timestamps = [] in_timestamps = [] out_timestamps = [p.capture_timestamp for p in out_packets] in_timestamps = [p.capture_timestamp for p in in_packets] assert len(in_timestamps) == len(out_timestamps) for i, o in zip(in_timestamps, out_timestamps): assert i is None assert current_timestamp == pytest.approx(o, abs=5e-1)
def test_pcap_info_10(pcap_path, metadata) -> None: """Check that reading a test pcap produces the right number of packets.""" res = pcap.Pcap(pcap_path, metadata) ports = {} sizes = {} encap = {} net = {} af = {} for item in pcap._pcap_info(pcap_path): if item.dst_port not in ports: ports[item.dst_port] = 0 ports[item.dst_port] += 1 if item.payload_size not in sizes: sizes[item.payload_size] = 0 sizes[item.payload_size] += 1 if item.encapsulation_protocol not in encap: encap[item.encapsulation_protocol] = 0 encap[item.encapsulation_protocol] += 1 if item.network_protocol not in net: net[item.network_protocol] = 0 net[item.network_protocol] += 1 if item.ip_version not in af: af[item.ip_version] = 0 af[item.ip_version] += 1 # default ports assert res._lidar_port == 7502 # roundrobin -> 5 of each assert ports == {7502: 5, 7503: 5} assert sizes == {6464: 5, 48: 5} assert encap == {ETH_PROTO: 10} assert net == {UDP_PROTO: 10} assert af == {4: 10}
def pcap_obj(metadata, pcap_path): pc = pcap.Pcap(pcap_path, metadata) yield pc pc.close()
def main() -> None: descr = """Visualize pcap or sensor data using simple viz bindings.""" epilog = """When reading data from a sensor, this will currently not configure the sensor or query it for the port to listen on. You will need to set the sensor port and distination settings separately. """ parser = argparse.ArgumentParser(description=descr, epilog=epilog) required = parser.add_argument_group('one of the following is required') group = required.add_mutually_exclusive_group(required=True) group.add_argument('--sensor', metavar='HOST', help='sensor hostname') group.add_argument('--pcap', metavar='PATH', help='path to pcap file') parser.add_argument('--meta', metavar='PATH', help='path to metadata json') parser.add_argument('--lidar-port', type=int, default=7502) args = parser.parse_args() if args.sensor: print("Initializing...") scans = client.Scans.stream(args.sensor, args.lidar_port, complete=False) elif args.pcap: import ouster.pcap as pcap if args.meta: metadata_path = args.meta else: print("Deducing metadata based on pcap name. " "To provide a different metadata path, use --meta") metadata_path = os.path.splitext(args.pcap)[0] + ".json" with open(metadata_path) as json: info = client.SensorInfo(json.read()) scans = client.Scans( pcap.Pcap(args.pcap, info, rate=1.0, lidar_port=args.lidar_port)) viz = PyViz(scans.metadata) def run() -> None: try: for scan in scans: viz.draw(scan) finally: # signal main thread to exit viz.quit() try: print("Starting client thread...") client_thread = threading.Thread(target=run, name="Client") client_thread.start() print("Starting rendering loop...") viz.loop() finally: scans.close() client_thread.join() print("Done")
def main(): """PointViz visualizer examples.""" parser = argparse.ArgumentParser( description=main.__doc__, formatter_class=argparse.RawTextHelpFormatter) parser.add_argument('pcap_path', nargs='?', metavar='PCAP', help='path to pcap file') parser.add_argument('meta_path', nargs='?', metavar='METADATA', help='path to metadata json') args = parser.parse_args() pcap_path = os.getenv("SAMPLE_DATA_PCAP_PATH", args.pcap_path) meta_path = os.getenv("SAMPLE_DATA_JSON_PATH", args.meta_path) if not pcap_path or not meta_path: print("ERROR: Please add SAMPLE_DATA_PCAP_PATH and SAMPLE_DATA_JSON_PATH to" + " environment variables or pass <pcap_path> and <meta_path>") sys.exit() print(f"Using:\n\tjson: {meta_path}\n\tpcap: {pcap_path}") # Getting data sources meta = client.SensorInfo(open(meta_path).read()) packets = pcap.Pcap(pcap_path, meta) scans = iter(client.Scans(packets)) # ============================== print("Ex 0: Empty Point Viz") # [doc-stag-empty-pointviz] # Creating a point viz instance point_viz = viz.PointViz("Example Viz") viz.add_default_controls(point_viz) # ... add objects here # update internal objects buffers and run visualizer point_viz.update() point_viz.run() # [doc-etag-empty-pointviz] # ========================================================================= print("Ex 1.0:\tImages and Labels: the Image object and 2D Image set_position() - height-normalized screen coordinates") label_top = viz.Label("[0, 1]", 0.5, 0.0, align_top=True) label_top.set_scale(2) point_viz.add(label_top) label_bot = viz.Label("[0, -1]", 0.5, 1, align_top=False) label_bot.set_scale(2) point_viz.add(label_bot) # [doc-stag-image-pos-center] img = viz.Image() img.set_image(np.full((10, 10), 0.5)) img.set_position(-0.5, 0.5, -0.5, 0.5) point_viz.add(img) # [doc-etag-image-pos-center] # visualize point_viz.update() point_viz.run() # ========================================================================= print("Ex 1.1:\tImages and Labels: Window-aligned images with 2D Image set_hshift() - width-normalized [-1, 1] horizontal shift") # [doc-stag-image-pos-left] # move img to the left img.set_position(0, 1, -0.5, 0.5) img.set_hshift(-1) # [doc-etag-image-pos-left] # visualize point_viz.update() point_viz.run() # [doc-stag-image-pos-right] # move img to the right img.set_position(-1, 0, -0.5, 0.5) img.set_hshift(1) # [doc-etag-image-pos-right] # visualize point_viz.update() point_viz.run() # [doc-stag-image-pos-right-bottom] # move img to the right bottom img.set_position(-1, 0, -1, 0) img.set_hshift(1) # [doc-etag-image-pos-right-bottom] # visualize point_viz.update() point_viz.run() # remove_objs(point_viz, [label_top, label_mid, label_bot, img]) remove_objs(point_viz, [label_top, label_bot, img]) # ======================================= print("Ex 1.2:\tImages and Labels: Lidar Scan Fields as Images") # [doc-stag-scan-fields-images] scan = next(scans) img_aspect = (meta.beam_altitude_angles[0] - meta.beam_altitude_angles[-1]) / 360.0 img_screen_height = 0.4 # [0..2] img_screen_len = img_screen_height / img_aspect # prepare field data ranges = scan.field(client.ChanField.RANGE) ranges = client.destagger(meta, ranges) ranges = np.divide(ranges, np.amax(ranges), dtype=np.float32) signal = scan.field(client.ChanField.SIGNAL) signal = client.destagger(meta, signal) signal = np.divide(signal, np.amax(signal), dtype=np.float32) # creating Image viz elements range_img = viz.Image() range_img.set_image(ranges) # top center position range_img.set_position(-img_screen_len / 2, img_screen_len / 2, 1 - img_screen_height, 1) point_viz.add(range_img) signal_img = viz.Image() signal_img.set_image(signal) img_aspect = (meta.beam_altitude_angles[0] - meta.beam_altitude_angles[-1]) / 360.0 img_screen_height = 0.4 # [0..2] img_screen_len = img_screen_height / img_aspect # bottom center position signal_img.set_position(-img_screen_len / 2, img_screen_len / 2, -1, -1 + img_screen_height) point_viz.add(signal_img) # [doc-etag-scan-fields-images] # visualize point_viz.update() point_viz.run() print("Ex 1.3:\tImages and Labels: Adding labels") # [doc-stag-scan-fields-images-labels] range_label = viz.Label(str(client.ChanField.RANGE), 0.5, 0, align_top=True) range_label.set_scale(1) point_viz.add(range_label) signal_label = viz.Label(str(client.ChanField.SIGNAL), 0.5, 1 - img_screen_height / 2, align_top=True) signal_label.set_scale(1) point_viz.add(signal_label) # [doc-etag-scan-fields-images-labels] # visualize point_viz.update() point_viz.run() # =============================================================== print("Ex 2.0:\tPoint Clouds: As Structured Points") # [doc-stag-scan-structured] cloud_scan = viz.Cloud(meta) cloud_scan.set_range(scan.field(client.ChanField.RANGE)) cloud_scan.set_key(signal) point_viz.add(cloud_scan) # [doc-etag-scan-structured] # visualize point_viz.update() point_viz.run() remove_objs(point_viz, [cloud_scan]) # =============================================================== print("Ex 2.1:\tPoint Clouds: As Unstructured Points") # [doc-stag-scan-unstructured] # transform scan data to 3d points xyzlut = client.XYZLut(meta) xyz = xyzlut(scan.field(client.ChanField.RANGE)) cloud_xyz = viz.Cloud(xyz.shape[0] * xyz.shape[1]) cloud_xyz.set_xyz(np.reshape(xyz, (-1, 3))) cloud_xyz.set_key(signal.ravel()) point_viz.add(cloud_xyz) # [doc-etag-scan-unstructured] point_viz.camera.dolly(150) # visualize point_viz.update() point_viz.run() # ======================================================= print("Ex 2.2:\tPoint Clouds: Custom Axes Helper as Unstructured Points") # [doc-stag-axes-helper] # basis vectors x_ = np.array([1, 0, 0]).reshape((-1, 1)) y_ = np.array([0, 1, 0]).reshape((-1, 1)) z_ = np.array([0, 0, 1]).reshape((-1, 1)) axis_n = 100 line = np.linspace(0, 1, axis_n).reshape((1, -1)) # basis vector to point cloud axis_points = np.hstack((x_ @ line, y_ @ line, z_ @ line)).transpose() # colors for basis vectors axis_color_mask = np.vstack(( np.full((axis_n, 4), [1, 0.1, 0.1, 1]), np.full((axis_n, 4), [0.1, 1, 0.1, 1]), np.full((axis_n, 4), [0.1, 0.1, 1, 1]))) cloud_axis = viz.Cloud(axis_points.shape[0]) cloud_axis.set_xyz(axis_points) cloud_axis.set_key(np.full(axis_points.shape[0], 0.5)) cloud_axis.set_mask(axis_color_mask) cloud_axis.set_point_size(3) point_viz.add(cloud_axis) # [doc-etag-axes-helper] point_viz.camera.dolly(50) # visualize point_viz.update() point_viz.run() remove_objs(point_viz, [ range_img, range_label, signal_img, signal_label, cloud_axis, cloud_xyz ]) # =============================================================== print("Ex 2.3:\tPoint Clouds: the LidarScanViz class") # [doc-stag-lidar-scan-viz] # Creating LidarScan visualizer (3D point cloud + field images on top) ls_viz = viz.LidarScanViz(meta, point_viz) # adding scan to the lidar scan viz ls_viz.scan = scan # refresh viz data ls_viz.draw() # visualize # update() is not needed for LidatScanViz because it's doing it internally point_viz.run() # [doc-etag-lidar-scan-viz] # =================================================== print("Ex 3.0:\tAugmenting point clouds with 3D Labels") # [doc-stag-lidar-scan-viz-labels] # Adding 3D Labels label1 = viz.Label("Label1: [1, 2, 4]", 1, 2, 4) point_viz.add(label1) label2 = viz.Label("Label2: [2, 1, 4]", 2, 1, 4) label2.set_scale(2) point_viz.add(label2) label3 = viz.Label("Label3: [4, 2, 1]", 4, 2, 1) label3.set_scale(3) point_viz.add(label3) # [doc-etag-lidar-scan-viz-labels] point_viz.camera.dolly(-100) # visualize point_viz.update() point_viz.run() # =============================================== print("Ex 4.0:\tOverlay 2D Images and 2D Labels") # [doc-stag-overlay-images-labels] # Adding image 1 with aspect ratio preserved img = viz.Image() img_data = make_checker_board(10, (2, 4)) mask_data = np.zeros((30, 30, 4)) mask_data[:15, :15] = np.array([1, 0, 0, 1]) img.set_mask(mask_data) img.set_image(img_data) ypos = (0, 0.5) xlen = (ypos[1] - ypos[0]) * img_data.shape[1] / img_data.shape[0] xpos = (0, xlen) img.set_position(*xpos, *ypos) img.set_hshift(-0.5) point_viz.add(img) # Adding Label for image 1: positioned at bottom left corner img_label = viz.Label("ARRrrr!", 0.25, 0.5) img_label.set_rgba((1.0, 1.0, 0.0, 1)) img_label.set_scale(2) point_viz.add(img_label) # Adding image 2: positioned to the right of the window img2 = viz.Image() img_data2 = make_checker_board(10, (4, 2)) mask_data2 = np.zeros((30, 30, 4)) mask_data2[15:25, 15:25] = np.array([0, 1, 0, 0.5]) img2.set_mask(mask_data2) img2.set_image(img_data2) ypos2 = (0, 0.5) xlen2 = (ypos2[1] - ypos2[0]) * img_data2.shape[1] / img_data2.shape[0] xpos2 = (-xlen2, 0) img2.set_position(*xpos2, *ypos2) img2.set_hshift(1.0) point_viz.add(img2) # Adding Label for image 2: positioned at top left corner img_label2 = viz.Label("Second", 1.0, 0.25, align_top=True, align_right=True) img_label2.set_rgba((0.0, 1.0, 1.0, 1)) img_label2.set_scale(1) point_viz.add(img_label2) # [doc-etag-overlay-images-labels] # visualize point_viz.update() point_viz.run() # =============================================================== print("Ex 5.0:\tAdding key handlers: 'R' for random camera dolly") # [doc-stag-key-handlers] def handle_dolly_random(ctx, key, mods) -> bool: if key == 82: # key R dolly_num = random.randrange(-15, 15) print(f"Random Dolly: {dolly_num}") point_viz.camera.dolly(dolly_num) point_viz.update() return True point_viz.push_key_handler(handle_dolly_random) # [doc-etag-key-handlers] # visualize point_viz.update() point_viz.run()
def pcap_to_csv(pcap_path: str, metadata_path: str, num: int = 0, csv_dir: str = ".", csv_prefix: str = "pcap_out", csv_ext: str = "csv") -> None: """Write scans from pcap file (*pcap_path*) to plain csv files (one per lidar scan). If the *csv_ext* ends in ``.gz``, the file is automatically saved in compressed gzip format. :func:`.numpy.loadtxt` can be used to read gzipped files transparently back to :class:`.numpy.ndarray`. Number of saved lines per csv file is always [H x W], which corresponds to a full 2D image representation of a lidar scan. Each line in a csv file is: RANGE (mm), SIGNAL, NEAR_IR, REFLECTIVITY, X (m), Y (m), Z (m) Args: pcap_path: path to the pcap file metadata_path: path to the .json with metadata (aka :class:`.SensorInfo`) num: number of scans to save from pcap to csv files csv_dir: path to the directory where csv files will be saved csv_prefix: the filename prefix that will be appended with frame number and *csv_ext* csv_ext: file extension to use. If it ends with ``.gz`` the output is gzip compressed """ from itertools import islice # ensure that base csv_dir exists if not os.path.exists(csv_dir): os.makedirs(csv_dir) metadata = read_metadata(metadata_path) source = pcap.Pcap(pcap_path, metadata) # [doc-stag-pcap-to-csv] field_names = 'RANGE (mm), SIGNAL, NEAR_IR, REFLECTIVITY, X (m), Y (m), Z (m)' field_fmts = ['%d', '%d', '%d', '%d', '%.8f', '%.8f', '%.8f'] channels = [ client.ChanField.RANGE, client.ChanField.SIGNAL, client.ChanField.NEAR_IR, client.ChanField.REFLECTIVITY ] with closing(pcap.Pcap(pcap_path, metadata)) as source: # precompute xyzlut to save computation in a loop xyzlut = client.XYZLut(metadata) # create an iterator of LidarScans from pcap and bound it if num is specified scans = iter(client.Scans(source)) if num: scans = islice(scans, num) for idx, scan in enumerate(scans): fields_values = [scan.field(ch) for ch in channels] xyz = xyzlut(scan) # get lidar data as one frame of [H x W x 7], "fat" 2D image frame = np.dstack((*fields_values, xyz)) frame = client.destagger(metadata, frame) csv_path = os.path.join(csv_dir, f'{csv_prefix}_{idx:06d}.{csv_ext}') header = '\n'.join([ f'pcap file: {pcap_path}', f'frame num: {idx}', f'metadata file: {metadata_path}', field_names ]) print(f'write frame #{idx}, to file: {csv_path}') np.savetxt(csv_path, np.reshape(frame, (-1, frame.shape[2])), fmt=field_fmts, delimiter=',', header=header)
def packets(real_pcap_path: str, meta: client.SensorInfo) -> client.PacketSource: with closing(pcap.Pcap(real_pcap_path, meta)) as real_pcap: ps = list(real_pcap) return client.Packets(ps, meta)
def pcap_2d_viewer( pcap_path: str, metadata_path: str, num: int = 0, # not used in this example rate: float = 0.0) -> None: """Simple sensor field visualization pipeline as 2D images from pcap file (*pcap_path*) Args: pcap_path: path to the pcap file metadata_path: path to the .json with metadata (aka :class:`.SensorInfo`) rate: read speed of packets from the pcap file (**1.0** - corresponds to real-time by packets timestamp, **0.0** - as fast as it reads from file without any delay) """ import cv2 # type: ignore # [doc-stag-pcap-display-live] metadata = read_metadata(metadata_path) source = pcap.Pcap(pcap_path, metadata, rate=rate) with closing(source) as source: scans = iter(client.Scans(source)) print("press ESC from visualization to exit") channels = [ client.ChanField.RANGE, client.ChanField.SIGNAL, client.ChanField.NEAR_IR, client.ChanField.REFLECTIVITY ] paused = False destagger = True num = 0 scan = next(scans, None) while scan: print("frame id: {}, num = {}".format(scan.frame_id, num)) fields_values = [scan.field(ch) for ch in channels] if destagger: fields_values = [ client.destagger(metadata, field_val) for field_val in fields_values ] fields_images = [ae(field_val) for field_val in fields_values] combined_images = np.vstack( [np.pad(img, 2, constant_values=1.0) for img in fields_images]) cv2.imshow("4 channels: ", combined_images) key = cv2.waitKey(1) & 0xFF # 100 is d if key == 100: destagger = not destagger # 32 is SPACE if key == 32: paused = not paused # 27 is ESC elif key == 27: break if not paused: scan = next(scans, None) num += 1 cv2.destroyAllWindows()
- separate set scan / update is annoying - a proxy run()/quit() on ls_viz would be useful - maybe: ls_viz could initialize underlying viz + expose it - point_viz.run() twice is broken - ideally, run() would open/close window - auto camera movement example? """ from ouster import client, pcap from ouster.sdk import viz meta_path = "/mnt/aux/test_drives/OS1_128_2048x10.json" pcap_path = "/mnt/aux/test_drives/OS1_128_2048x10.pcap" meta = client.SensorInfo(open(meta_path).read()) packets = pcap.Pcap(pcap_path, meta) scans = iter(client.Scans(packets)) point_viz = viz.PointViz("Example Viz") ls_viz = viz.LidarScanViz(meta, point_viz) ls_viz.scan = next(scans) ls_viz.draw() print("Showing first frame, close visuzlier window to continue") point_viz.run() ls_viz.scan = next(scans) ls_viz.draw() print("Showing second frame, close visuzlier window to continue") point_viz.run()
def main(): """Pcap examples runner.""" examples = { "open3d-one-scan": pcap_3d_one_scan, "plot-xyz-points": pcap_display_xyz_points, "pcap-to-csv": pcap_to_csv, "pcap-to-las": pcap_to_las, "pcap-to-pcd": pcap_to_pcd, "pcap-to-ply": pcap_to_ply, "query-scan": pcap_query_scan, "read-packets": pcap_read_packets, } description = "Ouster Python SDK Pcap examples. The EXAMPLE must be one of:\n " + str.join( '\n ', examples.keys()) parser = argparse.ArgumentParser( description=description, formatter_class=argparse.RawTextHelpFormatter) parser.add_argument('pcap_path', metavar='PCAP', help='path to pcap file') parser.add_argument('metadata_path', metavar='METADATA', help='path to metadata json') parser.add_argument('example', metavar='EXAMPLE', choices=examples.keys(), help='name of the example to run') parser.add_argument('--scan-num', type=int, default=1, help='index of scan to use') args = parser.parse_args() try: example = examples[args.example] except KeyError: print(f"No such example: {args.example}") print(description) exit(1) if not args.metadata_path or not os.path.exists(args.metadata_path): print(f"Metadata file does not exist: {args.metadata_path}") exit(1) with open(args.metadata_path, 'r') as f: metadata = client.SensorInfo(f.read()) if (metadata.format.udp_profile_lidar != client.UDPProfileLidar.PROFILE_LIDAR_LEGACY and metadata.format.udp_profile_lidar != client.UDPProfileLidar.PROFILE_LIDAR_RNG19_RFL8_SIG16_NIR16 and args.example != 'query-scan'): print( f"This pcap example is only for pcaps of sensors in LEGACY or SINGLE RETURN mode. Exiting..." ) exit(1) print(f'example: {args.example}') source = pcap.Pcap(args.pcap_path, metadata) with closing(source): example(source, metadata, args.scan_num) # type: ignore
def real_pcap(real_pcap_path: str, meta: client.SensorInfo) -> Iterator[pcap.Pcap]: pcap_obj = pcap.Pcap(real_pcap_path, meta) yield pcap_obj pcap_obj.close()