Top view tutorial
This tutorial covers an aerial LiDAR workflow. See Pinhole camera guide for a perspective camera with explicit calibration.
Input and output
read accepts LAS, LAZ, and NPY. The point array has shape (N, 3) for
XYZ or (N, 6) for XYZRGB. LAS color is normalized to 0–1 when present.
NPY arrays must already have one of these shapes. A LAS classification filter
is available with model.read("pointcloud.laz", classification=6).
For sample data, the Sao Paulo M3DC archive contains RGB aerial LiDAR tiles. Check its license and attribution requirements before use. The repository’s animation notes identify a specific tile and prompt workflow.
Automatic instance masks
With no prompt or boxes, segment runs SAM 3 automatic mask generation.
The top view maps XY to image pixels at resolution world units per pixel.
The highest Z point supplies each pixel’s color.
from segment_lidar import SamLidar, TopView
model = SamLidar(resolution=0.25)
points = model.read("pointcloud.las")
ids, labels, image = model.segment(
points,
view=TopView(),
image_path="results/raster.tif",
labels_path="results/labels.tif",
)
model.write(points, ids, save_path="results/segmented.las")
ids contains one label per input point. labels is a 2D integer image;
image is a 3 band RGB image. segment writes the image and label
rasters to the chosen paths. The output LAS/LAZ has a signed 32 bit
segment_id extra dimension. Open it in CloudCompare or another LAS viewer
that displays extra dimensions.
For automatic generation, tune model.automatic_masks.points_per_crop and
points_per_batch (both positive integers) and pred_iou_thresh and
stability_score_thresh (both 0–1). The defaults are 32, 16, 0.65, and
0.7 respectively. More sampled points can take longer.
Text and box prompts
A text prompt finds visible instances of a concept:
ids, labels, image = model.segment(
points, view=TopView(), prompt="building",
image_path="results/building_rgb.tif",
labels_path="results/building_labels.tif",
)
model.text_prompt.text can hold a default prompt. The per-call prompt
argument overrides it. score_threshold and mask_threshold default to
0.5 and must be in 0–1:
model.text_prompt.score_threshold = 0.6
model.text_prompt.mask_threshold = 0.5
Boxes use image pixel coordinates [left, top, right, bottom]. Positive
labels are 1 and negative labels are 0. Every box must have positive area and
fit inside the projected image. A positive box can be used without text; a
negative-only box set requires text.
import numpy as np
ids, labels, image = model.segment(
points,
prompt="tree",
boxes=np.array([[80, 40, 180, 220], [210, 30, 300, 160]]),
box_labels=np.array([1, 0]),
image_path="results/tree_rgb.tif",
labels_path="results/tree_labels.tif",
)
Inspect the projected image before choosing box coordinates. A useful
token-free step is to call TopView().cloud_to_image(points, 0.25) and
display or save the returned array. Box coordinates refer to that image,
not to world XY coordinates.
Ground filtering
Install segment-lidar[csf] first. Filtering can keep ground points from
obscuring aerial objects, and the writer can restore their original order:
from segment_lidar import SamLidar, TopView
model = SamLidar(resolution=0.25)
points = model.read("pointcloud.las")
cloud, non_ground, ground = model.csf(
points, class_threshold=0.5, csf_path="results/ground_flags.las"
)
ids, labels, image = model.segment(
cloud, view=TopView(),
image_path="results/filtered_rgb.tif",
labels_path="results/filtered_labels.tif",
)
model.write(
points, ids, non_ground=non_ground, ground=ground,
save_path="results/segmented.las",
)
non_ground and ground are indices into the original points array.
Ground points receive -1 in the output. To reuse saved flags with the
same point order, call
model.csf(points, csf_path="results/ground_flags.las", exists=True).
Saved raster and label workflows
You can transfer edited or previously computed labels without SAM 3:
from segment_lidar import SamLidar, TopView
model = SamLidar(resolution=0.25)
points = model.read("pointcloud.las")
ids, labels, image = model.segment(
points,
view=TopView(),
image_path="results/raster.tif",
labels_path="results/labels.tif",
image_exists=True,
label_exists=True,
)
model.write(points, ids, save_path="results/from_saved_labels.las")
The RGB image must have three bands and the integer label raster one band,
with matching width and height. Label 0 is background. New top view rasters
include a spatial transform, so a subset of the original point cloud can
reuse them. Older rasters without that transform need the same XY origin and
resolution as the cloud used to create them. See Pinhole camera guide for the
camera parameters required to reuse a perspective raster.