"""
Copyright (c) 2021, Ouster, Inc.
All rights reserved.
Example of custom visualizer use.
Intended to run with `python -m ouster.sdk.examples.viz`
"""
import argparse
import weakref
from typing import Iterable
from ouster import client, pcap
from ouster.sdk import viz
import os
import sys
import numpy as np
import random
[docs]def make_checker_board(square_size, reps):
"""Makes a test checker board image."""
img_data = np.full((square_size, square_size), 0)
img_data = np.hstack([img_data, np.logical_xor(img_data, 1)])
img_data = np.vstack([img_data, np.logical_xor(img_data, 1)])
img_data = np.tile(img_data, reps)
return img_data
# Helper method to remove list of objects from point_viz
def remove_objs(point_viz, objs):
for obj in objs:
point_viz.remove(obj)
# currently we need to always call update()/run_once() after some object removes
point_viz.update()
point_viz.run_once()
[docs]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()
# That's all folks! Happy hacking!
if __name__ == "__main__":
main()